3

我试图弄清楚以下汇编代码到底发生了什么。有人可以逐行解释发生了什么吗?我输入了我认为正在发生的事情(见评论),但需要澄清。

        .file   "testcalc.c"
        .section        .rodata.str1.1,"aMS",@progbits,1
.LC0:
        .string "x=%d, y=%d, z=%d, result=%d\n"
        .text
.globl main
        .type   main, @function
main:
        leal    4(%esp), %ecx   // establish stack frame
        andl    $-16, %esp      // decrement %esp by 16, align stack
        pushl   -4(%ecx)     // push original stack pointer
        pushl   %ebp     // save base pointer
        movl    %esp, %ebp     // establish stack frame 
        pushl   %ecx         // save to ecx
        subl    $36, %esp      // alloc 36 bytes for local vars
        movl    $11, 8(%esp)     // store 11 in z
        movl    $6, 4(%esp)      // store 6 in y 
        movl    $2, (%esp)    // store 2 in x
        call    calc         // function call to calc
        movl    %eax, 20(%esp)  // %esp + 20 into %eax
        movl    $11, 16(%esp)  // WHAT
        movl    $6, 12(%esp)  // WHAT
        movl    $2, 8(%esp)  // WHAT
        movl    $.LC0, 4(%esp)  // WHAT?!?!
        movl    $1, (%esp) // move result into address of %esp
        call    __printf_chk  // call printf function
        addl    $36, %esp  // WHAT?
        popl    %ecx 
        popl    %ebp
        leal    -4(%ecx), %esp
        ret
        .size   main, .-main
        .ident  "GCC: (Ubuntu 4.3.3-5ubuntu4) 4.3.3"
        .section        .note.GNU-stack,"",@progbits

原始代码:

#include <stdio.h>

int calc(int x, int y, int z);

int main()
{
        int x = 2;
        int y = 6;
        int z = 11;
        int result;

        result = calc(x,y,z);

        printf("x=%d, y=%d, z=%d, result=%d\n",x,y,z,result);
}
4

1 回答 1

11

您没有显示编译命令,这可能很有用,但似乎您已启用优化,因此实际上没有空间用于局部变量,它们已被优化:

main:
        leal    4(%esp), %ecx   
        andl    $-16, %esp      
        pushl   -4(%ecx)     
        pushl   %ebp     
        movl    %esp, %ebp

上面的所有代码都设置了堆栈帧。由于它是 ,main它与标准堆栈帧有点不同:它确保堆栈与 对齐andl $-16, %esp,以防万一。

        pushl   %ecx

它保存对齐校正前的原始值esp,以在最后恢复它。

        subl    $36, %esp

它分配 36 字节的堆栈空间,不是用于局部变量,而是用于调用参数。

        movl    $11, 8(%esp)
        movl    $6, 4(%esp)
        movl    $2, (%esp)

它设置calc从右到左调用的参数,即常量,(2, 6, 11)

        call    calc         // function call to calc

calc它使用指向的参数调用函数esp

        movl    %eax, 20(%esp)
        movl    $11, 16(%esp)
        movl    $6, 12(%esp)
        movl    $2, 8(%esp)
        movl    $.LC0, 4(%esp)
        movl    $1, (%esp)

这些是调用的参数__printf_chk,从右到左:(1, .LC0, 2, 6, 11, %eax),其中%eaxcalc()(记住,没有局部变量!)的返回值,并且.LC0是文字字符串的地址,请查看程序集顶部的这些行:

.LC0:
        .string "x=%d, y=%d, z=%d, result=%d\n"

但是那个神秘1呢?好吧,在 Ubuntu 中,标准编译选项 ( -D_FORTIFY_SOURCE) 将创建printf一个内联函数,用于转发__printf_chk(1, ...)或类似的东西,对参数进行额外检查。

        call    __printf_chk

这是对printf替代函数的调用。

        addl    $36, %esp

这将删除添加到堆栈中的 36 个字节subl $36, %esp

        popl    %ecx 

这会将可能未对齐的堆栈指针恢复为ecx.

        popl    %ebp
        leal    -4(%ecx), %esp

这将恢复先前的堆栈帧。

        ret

这返回没有值,因为您没有为main.

于 2013-11-04T03:49:01.423 回答