c函数的汇编代码

Ang*_*gus 5 c x86 assembly

我正在尝试理解C函数的汇编代码.我无法理解为什么andl -16要在主要方面完成.是为局部变量分配空间.如果subl 32是这样,为什么要为主要做.

我无法理解拆卸的func1.随着读取,堆栈从高阶地址增长到8086处理器的低阶地址.那么为什么在ebp的正侧(对于参数偏移)进行访问以及为什么不在ebp的负侧进行访问.func1中的局部变量是3 +返回地址+保存的寄存器 - 所以它必须是20,但为什么它是24?(subl $24,esp)

#include<stdio.h>
int add(int a, int b){
 int res = 0;
 res = a + b;
 return res;
}
int func1(int a){
 int s1,s2,s3;
 s1 = add(a,a);
 s2 = add(s1,a);
 s3 = add(s1,s2);
 return s3;
}
int main(){
 int a,b;
 a = 1;b = 2;
 b = func1(a);
 printf("\n a : %d b : %d \n",a,b);
 return 0;
}
Run Code Online (Sandbox Code Playgroud)

汇编代码:

       .file   "sample.c"
        .text
.globl add
        .type   add, @function
add:
        pushl   %ebp
        movl    %esp, %ebp
        subl    $16, %esp
        movl    $0, -4(%ebp)
        movl    12(%ebp), %eax
        movl    8(%ebp), %edx
        leal    (%edx,%eax), %eax
        movl    %eax, -4(%ebp)
        movl    -4(%ebp), %eax
        leave
        ret
        .size   add, .-add
.globl func1
        .type   func1, @function
func1:
        pushl   %ebp
        movl    %esp, %ebp
        subl    $24, %esp
        movl    8(%ebp), %eax
        movl    %eax, 4(%esp)
        movl    8(%ebp), %eax
        movl    %eax, (%esp)
        call    add
        movl    %eax, -4(%ebp)
        movl    8(%ebp), %eax
        movl    %eax, 4(%esp)
        movl    -4(%ebp), %eax
        movl    %eax, (%esp)
        call    add
        movl    %eax, -8(%ebp)
        movl    -8(%ebp), %eax
        movl    %eax, 4(%esp)
        movl    -4(%ebp), %eax
        movl    %eax, (%esp)
                                      call    add
        movl    %eax, -12(%ebp)
        movl    -12(%ebp), %eax
        leave
        ret
        .size   func1, .-func1
        .section        .rodata
.LC0:
        .string "\n a : %d b : %d \n"
        .text
.globl main
        .type   main, @function
main:
        pushl   %ebp
        movl    %esp, %ebp
        andl    $-16, %esp
        subl    $32, %esp
        movl    $1, 28(%esp)
        movl    $2, 24(%esp)
        movl    28(%esp), %eax
        movl    %eax, (%esp)
        call    func1
        movl    %eax, 24(%esp)
        movl    $.LC0, %eax
        movl    24(%esp), %edx
        movl    %edx, 8(%esp)
        movl    28(%esp), %edx
        movl    %edx, 4(%esp)
        movl    %eax, (%esp)
        call    printf
        movl    $0, %eax
        leave
        ret
        .size   main, .-main
        .ident  "GCC: (Ubuntu/Linaro 4.4.4-14ubuntu5) 4.4.5"
        .section        .note.GNU-stack,"",@progbits
Run Code Online (Sandbox Code Playgroud)

Eri*_*hil 5

所述andl $-16, %esp对准堆栈指针为16个字节的倍数,通过清除低四位.

唯一使用正偏移的地方(%ebp)是参数访问.

您没有说明您的目标平台是什么或者您用于编译的开关.汇编代码显示已插入一些Ubuntu标识符,但我不熟悉它使用的ABI,除此之外它可能类似于通常与Intel x86架构一起使用的ABI.所以我猜测ABI在例程调用时需要8字节对齐,因此编译器使堆栈帧为func124字节而不是20字节,以便保持8字节对齐.

我将进一步猜测,如果编译器main使用喜欢16字节对齐的SSE指令或其他更喜欢16字节对齐的操作,编译器会在编译器中将堆栈对齐为16字节,作为编译器中的"首选项". .

所以,我们有:

main,andl $-16, %esp将堆栈与16字节的倍数对齐作为编译器首选项.内main,28(%esp)并且24(%esp)指的是编译器在栈上保存临时值,而8(%esp),4(%esp)(%esp)用于将参数传递给func1printf.我们从汇编代码调用的事实中看到,printf但是在代码中注释掉了你已粘贴的C源代码,这些代码与用于生成汇编代码的C源代码不同:这不是从代码生成的正确汇编代码C源代码.

func1,堆栈上分配24个字节而不是20个,以保持8字节对齐.在里面func1,通过8(%ebp)和访问参数4(%ebp).从位置-12(%ebp)-4(%ebp)用于保存您的变量的值.4(%esp)(%esp)用于传递参数add.

这是堆栈框架func1:

    - 4(%ebp) = 20(%esp): s1.
    - 8(%ebp) = 16(%esp): s2.
    -12(%ebp) = 12(%esp): s3.
    -16(%ebp) =  8(%esp): Unused padding.
    -20(%ebp) =  4(%esp): Passes second parameter of add.
    -24(%ebp) =  0(%esp): Passes first parameter of add.

  • gcc手册中的引用可能会有所帮助:-mpreferred-stack-boundary = num:尝试将堆栈边界保持对齐2到num字节边界.如果未指定-mpreferred-stack-boundary,则默认值为4(16字节或128位). (2认同)