函数调用

函数调用_第1张图片
大多数CPU上的程序实现使用栈来支持函数调用操作。栈被用来传递函数参数、存储返回信息(返回地址,调用者的ebp),临时保存寄存器原有值以备恢复以及用来存储局部数据。

一个栈桢由两个寄存器指定,栈底ebp,栈顶esp。

Intel CPU,所有函数必须遵守的寄存器用法统一惯例:
eax, edx, ecx由调用者自己负责保存,ebx, esi, edi由被调用者来保护。

相关汇编指令:push pop call ret iret
push压栈,pop从栈中弹出。
call:把返回地址入栈,并跳转到被调用函数开始处执行。(返回地址是程序中紧随调用指令call后面一天指令的地址)。
ret:(此时esp指向调用者栈桢的顶部,也就是存放返回地址的地方)弹出栈顶处的地址并跳转到该地址处。
这是通过一条汇编指令leave实现的,leave实际上是下面两天汇编:

mov %ebp, %esp # 恢复原esp的值(esp=ebp,esp此时指向被调用者栈桢的开始处)
pop %ebp       # 恢复原ebp的值(esp=esp+4,它执行了调用者的返回地址处,ebp也指向了调用者的栈底)

举个例子:

#include

void swap(int *a, int *b) {
    int c;
    c = *a; *a = *b; *b = c;
}

int main() {
    int a, b;
    a = 16; b = 32;
    swap(&a, &b);

    printf("%d\n", (a-b));
    return (a-b);
}

函数调用_第2张图片

编译:

nadeMacBook-Pro:testc nazhou$ gcc -Wall -S -m32 -o exch.s exch.c
nadeMacBook-Pro:testc nazhou$ ls
a       a.c     a.s     bar.c       bar.o       exch.c      exch.s      foo.asm     foo.o       hello       hello.asm   hello.o
nadeMacBook-Pro:testc nazhou$ clear

nadeMacBook-Pro:testc nazhou$ cat exch.s

汇编代码:

    .section    __TEXT,__text,regular,pure_instructions
    .macosx_version_min 10, 13
    .globl  _swap                   ## -- Begin function swap
    .p2align    4, 0x90
_swap:                                  ## @swap
    .cfi_startproc
## BB#0:
    pushl   %ebp                          // 将main的栈底压入栈中
Lcfi0:
    .cfi_def_cfa_offset 8
Lcfi1:
    .cfi_offset %ebp, -8
    movl    %esp, %ebp                    // ebp=esp,就是刚压入的main的ebp,这就开始swap的栈了
Lcfi2:
    .cfi_def_cfa_register %ebp
    pushl   %esi// 将esi入栈
    subl    $12, %esp                     // 
Lcfi3:
    .cfi_offset %esi, -12
    movl    12(%ebp), %eax               // eax = &b
    movl    8(%ebp), %ecx                // ecx = &a
    movl    8(%ebp), %edx                // edx = &a,edx相当于变量c
    movl    (%edx), %edx                 // edx = a
    movl    %edx, -8(%ebp)               // 将a入栈
    movl    12(%ebp), %edx               // edx = &b
    movl    (%edx), %edx                 // edx = b
    movl    8(%ebp), %esi                // esi = &a
    movl    %edx, (%esi)                 // a = b
    movl    -8(%ebp), %edx               // edx = a
    movl    12(%ebp), %esi               // esi = &b
    movl    %edx, (%esi)                 // b = a (原来的a,它在被改变前压入了栈中)
    movl    %eax, -12(%ebp)         ## 4-byte Spill // &b入栈
    movl    %ecx, -16(%ebp)         ## 4-byte Spill // &a入栈
    addl    $12, %esp                    // esp指向esi
    popl    %esi                         // esi出栈
    popl    %ebp                         // ebp又指向了main的栈底,esp指向了返回地址处
    retl                                 // 弹出栈顶处的地址,并跳转到该地址处
    .cfi_endproc
                                        ## -- End function
    .globl  _main                   ## -- Begin function main
    .p2align    4, 0x90
_main:                                  ## @main
    .cfi_startproc
## BB#0:
    pushl   %ebp                 // ebp入栈,下一步ebp就要被改变了
Lcfi4:
    .cfi_def_cfa_offset 8
Lcfi5:
    .cfi_offset %ebp, -8
    movl    %esp, %ebp         // ebp = esp,从现在开始是main的栈桢
Lcfi6:
    .cfi_def_cfa_register %ebp
    subl    $40, %esp          // esp = esp -40
    calll   L1$pb              // call把返回地址入栈,并跳转到被调用函数处开始执行
L1$pb:
    popl    %eax               // 虽然是call调用到函数,但没有开始一个新的栈桢,还是main的栈桢,eax存放的是这个函数后面的指令
    leal    -8(%ebp), %ecx     // ecx指向ebp-8处(&a)
    leal    -12(%ebp), %edx    // ebx指向ebp-12处 (&b)
    movl    $0, -4(%ebp)       // 0入栈
    movl    $16, -8(%ebp)      // 16入栈 a(ecx指向)
    movl    $32, -12(%ebp)     // 32入栈 b(ebx指向)
    movl    %ecx, (%esp)       // &a入栈
    movl    %edx, 4(%esp)      // &b入栈
    movl    %eax, -16(%ebp)         ## 4-byte Spill // 返回地址入栈
    calll   _swap              // 返回地址入栈,并跳转到被调用函数开始处执行
    movl    -16(%ebp), %eax         ## 4-byte Reload
    leal    L_.str-L1$pb(%eax), %ecx
    movl    -8(%ebp), %edx
    subl    -12(%ebp), %edx
    movl    %ecx, (%esp)
    movl    %edx, 4(%esp)
    calll   _printf
    movl    -8(%ebp), %ecx
    subl    -12(%ebp), %ecx
    movl    %eax, -20(%ebp)         ## 4-byte Spill
    movl    %ecx, %eax
    addl    $40, %esp
    popl    %ebp
    retl
    .cfi_endproc
                                        ## -- End function
    .section    __TEXT,__cstring,cstring_literals
L_.str:                                 ## @.str
    .asciz  "%d\n"


.subsections_via_symbols

swap的栈帧:
函数调用_第3张图片

main的栈帧:
函数调用_第4张图片

你可能感兴趣的:(c,汇编)