本文起源:想看看C/C++中的Strict Aliasing,可是找到的资料几乎都用C/C++对应的汇编代码来解释一些概念。看不懂这些汇编,只好先简单整理一点汇编相关的资料了。
找几个编译器,找个最最简单的C程序,动手试试,于是...
比如,一个简单的函数:
// hello.c void func(){}
如何得到相应的汇编代码呢?
gcc -S hello.c |
hello.s |
clang -S hello.c |
hello.s |
gcc -S -masm=intel hello.c |
hello.s |
cl /FAs /C hello.c |
hello.asm |
看些结果:
.globl func .type func, @function func: pushl %ebp movl %esp, %ebp popl %ebp ret
.globl func .align 16, 0x90 .type func,@function func: pushl %ebp movl %esp, %ebp popl %ebp ret .Ltmp0: .size func, .Ltmp0-func
.globl func .type func, @function func: push ebp mov ebp, esp pop ebp ret
_func PROC ; 1 : void func(){} push ebp mov ebp, esp pop ebp ret 0 _func ENDP
.global func .type func,#function func: save %sp,-96,%sp jmp %i7+8 restore ...
.global func func: retl ! Result = nop .type func,2 .size func,(.-func) ...
这些东西太复杂了,关注点只能限制到X86架构了。先看一下X86下常用的两种汇编有哪些不同。
X86 下常用的汇编有 AT&T 与 Intel 两种(fix me?),二者在语法上有一定的差异:
注:本节内容来自http://oss.org.cn,文字和格式进行了重新整理。
区别:
Intel语法 |
AT&T语法 |
||
寄存器 |
加前缀 % |
||
立即数 |
加前缀 $ |
||
十六进制 |
加后缀h |
数字前加 0x |
|
二进制 |
加后缀b |
例子:
Intel语法 |
AT&T语法 |
mov eax,8 |
movl $8,%eax |
mov ebx,0ffffh |
movl $0xffff,%ebx |
int 80h |
int $0x80 |
AT&T中,第一个数是源操作数,第二个数是目的操作数。(更符合阅读习惯哈)
例子:
Intel语法(<==) |
AT&T语法(==>) |
mov eax,[ecx] |
movl (%ecx),%eax |
而在AT&T中,用“()”括起来。
Intel |
AT&T |
mov eax,[ebx+5] |
movl 5(%ebx),%eax |
Intel的指令格式是segreg:[base+index*scale+disp]
AT&T的格式是%segreg:disp(base,index,scale)
Intel语法 |
AT&T语法 |
指令 foo,segreg:[base+index*scale+disp] |
指令 %segreg:disp(base,index,scale),foo |
mov eax,[ebx+20h] |
Movl 0x20(%ebx),%eax |
add eax,[ebx+ecx*2h] |
Addl (%ebx,%ecx,0x2),%eax |
lea eax,[ebx+ecx] |
Leal (%ebx,%ecx),%eax |
sub eax,[ebx+ecx*4h-20h] |
Subl -0x20(%ebx,%ecx,0x4),%eax |
在AT&T的操作码后加后缀,“l”(long,32位),“w”(word,16位),“b”(byte,8位)
例子:
Intel语法 |
AT&T语法 |
Mov al,bl |
movb %bl,%al |
Mov ax,bx |
movw %bx,%ax |
Mov eax,ebx |
movl %ebx,%eax |
Mov eax, dword ptr [ebx] |
movl (%ebx),%eax |
int func(int i) { return 2 * i; } int main() { int s = func(255); return 0; }
func: |
|
pushl %ebp |
将ebp内容压栈保存 |
movl %esp, %ebp |
|
movl 8(%ebp), %eax |
注意到立即数255在main中压栈后,先后有IP和本函数内的ebp压栈,故,栈顶+8指向立即数255 |
addl %eax, %eax |
乘法操作变成了加法,eax存放返回值 |
popl %ebp |
ebp出栈 |
ret |
函数返回,(IP出栈) |
main: |
|
pushl %ebp |
将ebp内容压栈保存, |
movl %esp, %ebp |
将栈顶保存到ebp中 |
subl $20, %esp |
栈顶下移20字节,用来保存局部变量 |
movl $255, (%esp) |
将立即数255放入栈顶所指位置 |
call func |
调用函数func,(此时将下条指令地址IP压入栈中) |
movl %eax, -4(%ebp) |
将func的返回值放入局部变量中(-4(%ebp)就是变量s的位置) |
movl $0, %eax |
0送入eax,准备返回值 |
leave |
leave等价于 movl %ebp,%esp 和 popl %ebp |
ret |
http://oss.org.cn/kernel-book/ch02/2.6.1.htm
http://bbs.bccn.net/thread-106533-1-1.html