-
程序计数器(PC)在x86-64中用%rip表示,其中给出了将要执行的下一条指令在内存中的地址
-
在Linux下查看汇编代码的两种方式:
- 对于一可执行文件hello,使用
objdump -d hello
即可在控制台打印其反汇编代码 - 在编译C代码时,使用gcc的-S参数生成汇编代码文件,可以用文本编辑器查看
gcc -Og -S hello.c -o hello.s
-
所有以
.
开头的行都是知道汇编器和链接器工作的伪指令,通常可以忽略。 -
AT&T与Intel汇编代码格式:两种格式在许多方面不同,在gcc/objdump工具中默认格式是AT&T,使用下面的命令可以让GCC产生Intel格式的代码:
gcc -Og -S -masm=intel hello.c
-
操作数指示符:
其中,Imm表示立即数(immediate的缩写);R[]数组为寄存器内容数组,如R[r_a]即表示寄存器r_a中的值;M[]数组表示内存内容数组,如M[Imm]即表示内存第Imm号单元中存放的数据。各个寄存器的大小如下:
-
数据传送指令MOV类:包括movb, movw, movl, movq等指令,它们的区别在于操作数据的大小不同。
源操作数S指定的值是一个立即数(常数),存储在寄存器或内存中。目的操作数D指定一个位置,要么是一个寄存器,要么是一个内存地址。x86-64限制传送指令的两个操作数S和D不能都指向内存位置,两个内存位置间复制数据必须通过寄存器中转。记住,MOV A B指令中,源操作数在前,目标操作数在后,是把A复制到B中!
注意:源操作数只有以$开头的才是立即数,其它的以%开头的是表示寄存器中存放的数据,有括号的表示内存某一地址中的数据。目标操作数以%开头的表示某一寄存器,是将源操作数复制到这个寄存器中(不是按寄存器中保存的数据在内存中寻址);有括号的表示内存的一地址,是将源操作数复制到内存中这个地址对应的内存单元当中。
movb, movw, movl, movq都要求源操作数和目标操作数表示的数据大小相同,还有其他指令如movzbw,movzbl,movzwl,movzbq,movzwq,movsbw,movsbl,movswl,movsbq,movswq,movslq,cltq,是将较小的源值复制到较大的目的时使用,其中涉及到高位扩展填充(零扩展/符号扩展),这里不再详细解释。值得注意的是,如果源操作数的数据大小小于目标操作数表示的数据大小,movb, movw, movl, movq指令只会修改源操作数对应的位,不会修改其他的位,唯一的例外是movl(这是由于x86-64的惯例原因,这里不解释)。 -
栈操作
在x86-64机器中,栈是向低地址方向增长的,也就是说,栈底的地址值更大。%rsp寄存器专门用于存储栈顶地址。故指令pushq %rbq
等价于两条指令subq $8,%rsp
和movq %rbq,(%rsp)
,指令popq %rax
等价于两条指令movq (%rsp),%rax
addq $8,%rsp
。 -
跳转指令
jmp跳转指令分直接跳转和简介跳转,直接跳转是给出一个标号作为跳转目标,间接跳转是"*"后跟一个操作数指示符(寄存器或内存地址)。如jmp *%rax
用寄存器rax中的值作为跳转目标,jmp *(%rax)
以%rax中的值作为读地址,从内存中读出跳转目标。
-
8086中的寄存器:
(1) 8个通用寄存器:
数据寄存器
AX—Accumulator Register,累加寄存器
BX—Base Segment Register,基址寄存器
CX—Count Segment Register,计数寄存器
DX—Data Segment Register,数据寄存器字符串
地址指针寄存器
SI—Source Index Register,源变址寄存器
DI—Destination Index Register,目的变址寄存器
SP—Stack Pointer Register,堆栈寄存器
BP—Base Pointer Register,基址指针寄存器
(2) 6个段寄存器:
CS—Code Segment Register,代码段寄存器
DS—Data Segment Register,数据段寄存器
SS—Stack Segment Register,堆栈段寄存器
FS—Flag Segment Register,标志段寄存器
FS寄存器指向当前活动线程的TEB结构(线程结构)。
GS—Global Segment Register,全局段寄存器it
(3) 2个控制寄存器:
IP—Instruction Pointer,指令指针,即PC(Program counter),程序计数器。
PSW—Processor State Word,微处理器状态字
即在8086中,%rip就是PC,%rsp为栈顶指针 -
转移控制(call,ret指令)
call指令有一个目标,即指明被调用过程起始的指令地址。调用可以是直接的(call Label),也可以是间接的(call *Operand)。一个call指令相当于一条push指令接一条jmp指令,先将原过程中call指令的下一条指令的地址push到栈中,再跳转到目标过程。而ret指令相当于一条pop指令接一条jmp指令,先从栈中弹出之前在栈中push的地址(即之前保存的"call的下一条指令的地址"),然后跳转到这个地址,完成ret指令,继续执行原过程。