X86汇编学习小结

 

X86汇编学习小结

生成汇编代码

比如,一个简单的函数:

// 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

看些结果:

  • gcc 4.5.2, Ubuntu11.04 X86 默认的汇编

.globl func
        .type   func, @function
func:
        pushl   %ebp
        movl    %esp, %ebp
        popl    %ebp
        ret
  • clang 2.8, Ubuntu11.04 X86

    .globl  func
    .align  16, 0x90
    .type   func,@function
func:
    pushl   %ebp
    movl    %esp, %ebp
    popl    %ebp
    ret
.Ltmp0:
    .size   func, .Ltmp0-func
  • gcc 4.5.2, Ubuntu11.04 X86 Intel格式的汇编

.globl func
        .type   func, @function
func:
        push    ebp
        mov     ebp, esp
        pop     ebp
        ret
  • cl 16 (即VC10), Windows Xp

_func   PROC
; 1    : void func(){}
        push    ebp
        mov     ebp, esp
        pop     ebp
        ret     0
_func   ENDP
  • 顺便看一个Sun Studio 11: C 5.8 compiler,SPARC架构(服务器来自unix-center)

        .global func
        .type   func,#function
func:
        save    %sp,-96,%sp
        jmp     %i7+8
        restore
...
  • 以及GCC 4.0.1,SPARC架构(服务器来自unix-center)

        .global func
func:   
        retl    ! Result = 
        nop
        .type   func,2
        .size   func,(.-func)
...

这些东西太复杂了,关注点只能限制到X86架构了。先看一下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

操作数的方向

  • 二者方向正好相反
  • Intel语法,第一个是目的操作数,第二个是源操作数。
  • AT&T中,第一个数是源操作数,第二个数是目的操作数。(更符合阅读习惯哈)

例子:

Intel语法(<==)

AT&T语法(==>)

mov eax,[ecx]

movl (%ecx),%eax

内存单元操作数

  • 在Intel的语法中,基寄存器用“[]”括起来
  • 而在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的语法中,在操作数的前加byte ptr、 word ptr 或 dword ptr

例子:

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

找个例子看看

  • hello.c

int func(int i)
{
    return 2 * i;
}

int main()
{
    int s = func(255);
    return 0;
}
  • hello.s (gcc4.5.2, x86/ubuntu11.04)

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


你可能感兴趣的:(X86汇编学习小结)