3.汇编
汇编指令格式
AMD64 Linux 平台下 AT&T 汇编指令的基本格式为:
每一条汇编指令通常都由两部分组成:
操作码:操作码指示CPU执行什么操作,比如是执行加法,减法还是读写内存。每条指令都必须要有操作码。
操作数:操作数是操作的对象,比如加法操作需要两个加数,这两个加数就是这条指令的操作数。操作数的个数一般是0~2个。
为了更好的理解AT&T格式的汇编指令,这里先对其格式做一个简要的说明:
AT&T格式的汇编指令中,寄存器名需要加
%
作为前缀有2个操作数的指令中,第一个操作数是源操作数,第二个是目的操作数,如,
mov %eax,%esi
,这条指令表示把eax
寄存器中的值拷贝给esi
立即操作数需要加上
$
符号做前缀,如mov $0x1 %rdi
这条指令中第一个操作数不是寄存器,也不是内存地址,而是直接写在指令中的一个常数,这种操作数叫做立即操作数。这条指令表示把数值0x1
放入rdi
寄存器中。括号表示间接寻址,格式为
offset(%register)
,如果offset为0,则可以略去偏移不写直接写成(%register)
。比如mov 16(%rax), (%rsp)
这条指令,rsp
的值是一个内存地址,表示把rax
寄存器中向上偏移 16 个字节的地址赋值给rsp
寄存器存储的地址指向的内存上与内存相关的一些指令的操作码会加上
b, w, l, q
字母表示操作的内存是1,2,4还是8个字节,比如指令movl $0x0,-0x8(%rbp)
操作码movl
的后缀字母l
说明我们要把从-0x8(%rbp)
这个地址开始的4个内存单元赋值为0。可能有读者会问,那如果我要操作3个,或5个内存单元呢?很遗憾的是cpu没有提供相应的单条指令,我们只能通过多条指令组合起来达到目的。
常用指令详解
mov A B
: 拷贝(加载)A 至 B
leaq A B
: 拷贝(加载)A 的地址至 B
add/sub A B
: 将 A 累加/减 到 B 上
call A
: 调用 A 函数
ret
: 函数返回
jmp/je/jle/jg/jge
等 j
开头的指令: 跳转。与高级编程语言中的 goto
和 if
等语句对应
push/pop
:入栈出栈指令,这两个指令都会自动修改rsp寄存器。
push A
: 将 A 压入栈。rsp
寄存器的值先减去8
把位置留出来,然后把 A 复制到rsp
所指位置。常用语函数调用。函数被调用时,函数的返回地址被压入栈中,以便函数执行完毕后能够返回到调用者的位置。pop %rax
: 从栈中弹出数据到寄存器%rax
中,然后rsp
寄存器的值加8
。函数返回:函数返回时,使用
pop
命令将返回地址弹出并存储到程序计数器寄存器中,以便程序可以返回到正确的位置继续执行。异常处理:在发生异常时,程序将当前状态压入堆栈,包括程序计数器、寄存器和其他相关状态。当异常处理程序完成时,使用
pop
命令将状态恢复到原始状态其他程序:许多其他程序需要使用堆栈来存储临时变量、参数和其他数据,使用
pop
弹出数据
leave
: 函数结束时,恢复栈指针到之前的函数。
rbq
寄存器保存着上一个函数的起始地址,这个指令将rsp
指向rbq
、rbq
指向上一个函数的起始地址,这样就恢复到之前的函数了
16(%rsp)
: 1表示从栈指针寄存器 (%rsp)
向上偏移 16 个字节的地址
Last updated