我试图了解C函数的汇编代码。我不明白为什么andl -16是主要完成的。是否为局部变量分配空间。如果是这样,为什么对main完成subl 32

我无法理解func1的反汇编。随着读取,对于8086处理器,堆栈从高位地址增长到低位地址。所以这里为什么访问在ebp的正侧(对于参数offset),为什么不在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;
}

汇编代码:
       .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

最佳答案

andl $-16, %esp通过清除低四位将堆栈指针对齐到16字节的倍数。

参数访问是将唯一偏移量与(%ebp)一起使用的地方。

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

我将进一步猜测,如果编译器使用更喜欢16字节对齐的SSE指令或更喜欢16字节对齐的其他操作,则在main的开头将栈对齐为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):未使用的填充。
-20(%ebp)= 4(%esp):传递add的第二个参数。
-24(%ebp)= 0(%esp):传递add的第一个参数。

关于c - c函数的汇编代码,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/15753646/

10-12 17:18