指令后缀

与X86汇编相比, X64汇编的一个显著区别是增加了对64bit数据的操作, 对于所有的数据传输指令, 都可以添加指令后缀来明确具体数据的具体长度, 后缀的关系如下表所示

C语言声明 Intel数据类型 汇编后缀 字节长度
char 字节(byte) b 1
short 字(word) w 2
int 双字(double word) l 4
long 四字(quad word) q 8
char* 四字(quad word) q 8
float 单精度(single) s 4
double 双精度(double) l 8

双字使用l作为后缀, 因此双字也被认为是一种长字节(long word). 浮点数指令和整数指令后缀有一些冲突, 但实际上由于浮点数指令是一套单独的指令, 因此并不会构成冲突.

寄存器结构

X64寄存器数量和寄存器长度都在X86的基础上再次翻倍, 每个寄存器都是64位长度, 并且新加入的8个寄存器, 新加入的寄存器分别命名为%r8~%r15. 所有寄存器和使用规则如下表所示

寄存器 使用规则 寄存器 使用规则
%rax 返回值 %r8 第5个参数
%rbx 被调用者保存 %r9 第6个参数
%rcx 第4个参数 %r10 调用者保存
%rdx 第3个参数 %r11 调用者保存
%rsi 第2个参数 %r12 被调用者保存
%rdi 第1个参数 %r13 被调用者保存
%rbp 被调用者保存 %r14 被调用者保存
%rps 栈指针 %r15 被调用者保存

与X86汇编一样, 可以通过类似%eax, %ax, %ah, %al的方式访问原有的8个寄存器的低位部分. 对于新增的寄存器, 也可以使用类似%r8d, %r8w, %r8b的方式访问r8寄存器的低32位, 低16位和低8位.

由于寄存器数量有明显的增加, 因此与X86相比, 一个显著的变化就是大部分时候的函数调用不需要再进行参数入栈的操作, 大部分时候函数调用的参数都可以直接用寄存器传递.

将数据移动到寄存器时, 如果移动的数据是1字节或2字节, 则寄存器的高位不变. 如果移动的数据是4字节, 则将高位数据置零

操作数指示符

格式

$0x1234 | 立即数0x1234 | 立即数寻址
%rax | 取%rax的值 | 寄存器寻址

数据传送指令

mov指令结合四种长度后缀可以表示四种不同长度的数据传输指令, 即movb, movw, movlmovq. mov指令既可以在寄存器之间传送数据, 也可以将立即数传入寄存器. 但movq指令只能接受32位的有符号立即数, 将其进行符号扩展到64位, 并传入寄存器. 如果需要传送64位立即数, 则需要使用movabsq指令进行绝对传送.

mov指令有两种变形, 分别是movzmovs. 两个指令分别表示零扩展和符号扩展. 例如movzbl表示将一个字节的数据先进行零扩展变为一个双字长度, 然后传送到目标位置, movzwq表示将一个字长度的数据进行零扩展变成四字长度后传送到目标位置.

数据传送指令虽然指定了数据的长度, 但不能与操作的寄存器发生冲突. 例如movl %rax, (%rbx) 似乎希望传送%rax的两个字节到内存, 但并没有这种用法, 如果需要传送低位, 只能使用%eax替换.

cltq指令是movslq %eax %rax指令的简化指令, 表示将%eax的数据符号扩展到%rax

栈操作

栈操作与X86汇编没有太大区别, 只是入栈和出栈的基本单元的大小都设置为8字节.

最后更新: 2024年03月28日 23:43

版权声明:本文为原创文章,转载请注明出处

原始链接: https://lizec.top/2020/08/10/CSAPP%E7%AC%94%E8%AE%B0%E4%B9%8B%E6%B1%87%E7%BC%96%E8%AF%AD%E8%A8%80/