yale_OS(4)——Intel IA32架构CPU的汇编编程

IA32架构CPU下的汇编编程学习

通常掌握某一特定机器的汇编编程技术需要一定的时间。然而,如果掌握其他处理器的汇编编程(例如ARM,MIPS等),那么学习IA32结构CPU下的汇编编程将会省去很多时间。以下对IA32架构下的汇编语言编程进行简单的总结,方便以后回头来温习

1.指令语法

关于IA32架构的CPU的汇编语言的语法和表示有两种约定:Intel和AT&T,大多数的文件使用Intel的约定,而GNU汇编器使用AT&T的约定,这两种约定之间主要的不同如下表所示:


GNU 语法(AT&T) Intel
立即数 前面需要加上"$"
例如:push $4
movl $0xd00a, %eax
没有限制
例如:push 4
mov ebx d00ah
寄存器 前面需要加上“%”
例如:%eax
无限制
例如:eax
参数顺序(例如:
把C变量“foo”加到
寄存器EAX中)
source1, [source2,] dest
例如:addl $_foo, %eax
des, source1 [,source2]
例如:add eax, _foo
单操作数 指明操作数的大小(用{b, w, l})
例如:movb foo, %al
用寄存器名间接的指明操作数的大小,
byte ptr, word ptr 和dword ptr
例如:mov al, foo
寻址一个C变量"foo" _foo [_foo]
寻址寄存器指定的内存(例如,EAX) (%eax) [eax]
寻址寄存器中值的变量偏移值 _foo(%eax) [eax + _foo]
寻址32-bit整数的数组"foo"中的值 _foo(,%eax,4) [eax * 4 + foo]
等效于C代码中的*(p +1) 1(%eax) 如果EAX中存储的是p的值,则[eax + 1]


2. 内存操作

IA32处理器使用分段内存架构,也就是说内存位置需要通过段选择子和偏移来确定。

  • 段选择子指定包含操作数的段。
  • 偏移(从段的开始位置到操作数所在位置的第一个byte的字节数)指定了操作数的线性地址或有效地址

3.经常使用的指令

下表中所示是一些经常使用的指令。

类别 指令 解释

数据转移

mov{l,w,b} source, dest

移动source到dest

xchg{l,w,b} dest1, dest2

交换

cmpxchg{l,w,b} dest1, dest2

比较和交换

push/pop{l,w}

入栈/出栈

movsb

移动DS:(E)SI 的bytes到地址ES:(E)DI处,典型的使用前缀rep

算术

add/sub{l,w,b} source, dest

加减

imul/mul{l,w,b} formats

有符号/无符号乘

idiv/div{l,w,b} dest

有符号/无符号除

inc/dec/neg{l,w,b} dest

增1/减1/取负

cmp{l,w,b} source1, source2

比较

逻辑

and/or/xor/not{l,w,b} source, dest

逻辑与/或/异或/非操作

sal/sar{l,w,b} formats

算术左移/右移

shl/shr{l,w,b} formats

逻辑左移/右移

控制转移

jmp address

无条件跳转

call address

保存EIP到堆栈中,然后跳转到address处

ret

返回到通过call调用保存的EIP处

leave

从堆栈中恢复EBP; pop off the stack frame

j{e,ne,l,le,g,ge} address

如果{=,!=,<,<=,>,>=},跳转到address处

loop address

对ECX或 CX进行减1操作;如果 = 0,则跳转

rep

重发串操作的前缀

int number

软件中断

iret

中断返回,EFLAGS 从堆栈中出栈


另外,用于长跳转的名字为ljmp,长调用为lcall

这里只列了一小部分的命令,IA32 Intel Architecture Software Developer's Manual, Volume 2的3.2节对IA32的所有指令进行了详细的描述,在Intel手册中使用的指令名字是按照Intel汇编语法约定的。


4.汇编指令

GNU汇编器指令名字以句点“.”开始,然后剩下的是小写字母,以下是常用的一些指令:

.ascii "string foo" 定义了ASCII串“string foo”

.asciz "string foo" 定义了ASCII串“string foo”,以0结尾

.string "string foo" 与.asicz "string foo"相同

.align 4 以双字边界来进行内存对齐

.byte 10, 13, 0 定义了三个字节

.word 0x0456, 0x1234 定义了两个字

.long 0x001234, 0x12345 定义了两个长字

.equ STACK_SEGMENT, 0X9000 设置符号STACK_SEGMENT的值为0x9000

.globl symbol 让“symbol”全局可见(对于定义全局标签和程序名字非常有用)

.code16 告诉汇编器插入适合的重写前缀以其运行在实模式下


5. 内联汇编(Inline Assembly)

通过gcc编译器生成的内联汇编代码的最基本的格式如下:

asm volatile("assembly-instruction");

assembly-instruction将会内联 到asm语句所在的地方,关键字volatile是可选的。该关键字告诉gcc编译器不要去优化该条指令。对于没有寄存器的内联汇编指令写起来非常的方便,例如:

asm volatile("cli");

用于禁止中断

asm volatile("sti");

用于开启中断


在C代码中编写内联汇编代码的通用格式如下:

asm [volatile] ("statements": output_regs: input_regs: used_regs);

其中statements是汇编指令, 如果有多于一条指令,可以使用“\n\t”来分开它们,让其看起来舒服点,

“input_regs”告诉gcc编译器哪个C变量移动到哪个寄存器,例如,想加载变量"foo"到寄存器EAX中,以及变量"bar"到寄存器ECX,可以些微:

:“a”(foo), "c"(bar)

gcc使用单个字母来表示所有的寄存器

单个字母 寄存器
a eax
b ebx
c ecx
d edx
S esi
D edi
I 常数值(0到31)
q 从EAX,EBX,ECX,EDX分配一个寄存器
r 从EAX,EBX,ECX,EDX,ESI,EDI中分配一个寄存器

"output_regs"提供了输出寄存器,一种方便的方法是让gcc编译器来选择寄存器,只需要指定“=q”或“=r”来让gcc编译器选择寄存器,可以通过利用“%0”来指定第一个分配的寄存器,“%1”指定第二个寄存器,等等。在汇编指令中,如果在输入寄存器列表中指定了寄存器。可以只需要“0”或“1”,不要前缀“%”

“used_regs”列出了在汇编代码中使用的寄存器。

下面给出了一段内联汇编包含在C代码中的例子。

    asm ("leal (%1,%1,4), %0"
         : "=r" (x_times_5)
         : "r" (x) );

和

    asm ("leal (%0,%0,4), %0"
        : "=r" (x)
        : "0" (x) );


6. 汇编程序结构和调用约定

最简单的学习汇编编程的方法是把简单的C程序编译为汇编源代码,该源代码将会显示常用的操作码,指令和寻址语法。这是一种有效的方法来学习汇编编程。

下面通过一个例如来说明汇编程序结构和调用约定,考虑如下的C程序hello.c

#include 
static char buf[4096];

int foo(int n)
{
	return n - 1;
}

int main(void)
{
	printf("Hello world\n");
	return f00(5);
}

通过如下的命令对该C源代码进行编译

gcc -S hello.c

gcc编译器将会编译hello.c为汇编源代码hello.s,

以下是hello.s的源代码

	.file	"hello.c"
	.local	buf
	.comm	buf,4096,32
	.text
.globl foo
	.type	foo, @function
foo:
.LFB0:
	.cfi_startproc
	pushq	%rbp
	.cfi_def_cfa_offset 16
	movq	%rsp, %rbp
	.cfi_offset 6, -16
	.cfi_def_cfa_register 6
	movl	%edi, -4(%rbp)
	movl	-4(%rbp), %eax
	subl	$1, %eax
	leave
	ret
	.cfi_endproc
.LFE0:
	.size	foo, .-foo
	.section	.rodata
.LC0:
	.string	"Hello world"
	.text
.globl main
	.type	main, @function
main:
.LFB1:
	.cfi_startproc
	pushq	%rbp
	.cfi_def_cfa_offset 16
	movq	%rsp, %rbp
	.cfi_offset 6, -16
	.cfi_def_cfa_register 6
	movl	$.LC0, %edi
	call	puts
	movl	$5, %edi
	movl	$0, %eax
	call	f00
	leave
	ret
	.cfi_endproc
.LFE1:
	.size	main, .-main
	.ident	"GCC: (Ubuntu 4.4.3-4ubuntu5.1) 4.4.3"
	.section	.note.GNU-stack,"",@progbits

可以编写一些其他的简单C代码,然后编译为汇编代码来查看汇编源代码


来自于http://zoo.cs.yale.edu/classes/cs422/2011/ref/pc-arch#memory

你可能感兴趣的:(yale_OS(4)——Intel IA32架构CPU的汇编编程)