操作环境为 Ubuntu 18.04,X86 下常用的汇编有 AT&T 与 Intel 两种,二者在语法上有一定的差异。
寄存器
AT&T 需加前缀 %
。
立即数
进制 AT&T 加前缀 $
;
十六进制 Intel 加后缀 h
,AT&T 加前缀 $0x
;
二进制 Intel 加前缀 b
,AT&T 加前缀 $
。
二者方向正好相反
Intel 语法,第一个是目的操作数,第二个是源操作数。
AT&T中,第一个数是源操作数,第二个数是目的操作数。(更符合阅读习惯)
内存单元操作数
在 Intel 的语法中,基寄存器用 []
括起来。
而在 AT&T 中,用 ()
括起来。
间接寻址方式
Intel 的指令格式是 [base + index*scale + disp]
AT&T 的格式是 disp(base, index, scale)
操作码的后缀
在 AT&T 的操作码后加后缀,l
(long,32 位),w
(word,16 位),b
(byte,8 位)
在 Intel 的语法中,在操作数的前加 byte ptr
、word ptr
或 dword ptr
样例如下:
Intel | AT&T |
---|---|
mov eax,8 |
movl $8,%eax |
mov ebx,0ffffh |
movl $0xffff,%ebx |
int 80h |
int $0x80 |
mov eax,[ecx] |
movl (%ecx),%eax |
mov al,bl |
movb %bl,%al |
mov ax,bx |
movw %bx,%ax |
Mov eax, dword ptr [ebx] |
movl (%ebx),%eax |
比如,一个简单的函数:
int func(int i) {
return 2 * i;
}
int main() {
int s = func(255);
return 0;
}
利用 GCC 进行汇编得到汇编指令,常用的汇编命令如下:
命令 | 输出文件 |
---|---|
gcc -S hello.c |
hello.c |
gcc -S -masm=intel hello.c |
hello.c |
AT&T 的主要汇编代码如下:
func:
pushq %rbp
movq %rsp, %rbp
movl %edi, -4(%rbp)
movl -4(%rbp), %eax
addl %eax, %eax
popq %rbp
ret
main:
pushq %rbp
movq %rsp, %rbp
subq $16, %rsp
movl $255, %edi
call func
movl %eax, -4(%rbp)
movl $0, %eax
leave
ret
Intel 的主要汇编代码如下:
func:
push rbp
mov rbp, rsp
mov DWORD PTR -4[rbp], edi
mov eax, DWORD PTR -4[rbp]
add eax, eax
pop rbp
ret
main:
push rbp
mov rbp, rsp
sub rsp, 16
mov edi, 255
call func
mov DWORD PTR -4[rbp], eax
mov eax, 0
leave
ret