C到汇编代码 - 它是什么意思

3 c x86 assembly disassembly

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

        .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
Run Code Online (Sandbox Code Playgroud)

原始代码:

#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);
}
Run Code Online (Sandbox Code Playgroud)

rod*_*igo 11

你没有表现出编译命令,这可能是有用的,但它似乎已启用的优化,所以实际上有局部变量没有空间,它们会被优化了:

main:
        leal    4(%esp), %ecx   
        andl    $-16, %esp      
        pushl   -4(%ecx)     
        pushl   %ebp     
        movl    %esp, %ebp
Run Code Online (Sandbox Code Playgroud)

上面的所有代码都设置了堆栈帧.因为它与main标准堆栈框架略有不同:它确保了堆栈的对齐andl $-16, %esp,以防万一.

        pushl   %ecx
Run Code Online (Sandbox Code Playgroud)

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

        subl    $36, %esp
Run Code Online (Sandbox Code Playgroud)

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

        movl    $11, 8(%esp)
        movl    $6, 4(%esp)
        movl    $2, (%esp)
Run Code Online (Sandbox Code Playgroud)

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

        call    calc         // function call to calc
Run Code Online (Sandbox Code Playgroud)

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)
Run Code Online (Sandbox Code Playgroud)

这些是调用参数__printf_chk,从右到左:(1, .LC0, 2, 6, 11, %eax),其中%eax是的返回值calc()(记住,任何局部变量!),并.LC0为文字字符串的地址,看看这些线在组装的顶部:

.LC0:
        .string "x=%d, y=%d, z=%d, result=%d\n"
Run Code Online (Sandbox Code Playgroud)

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

        call    __printf_chk
Run Code Online (Sandbox Code Playgroud)

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

        addl    $36, %esp
Run Code Online (Sandbox Code Playgroud)

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

        popl    %ecx 
Run Code Online (Sandbox Code Playgroud)

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

        popl    %ebp
        leal    -4(%ecx), %esp
Run Code Online (Sandbox Code Playgroud)

这将恢复先前的堆栈帧.

        ret
Run Code Online (Sandbox Code Playgroud)

并且返回时没有值,因为您没有写入返回值main.

  • +1好消化.很好,我有几个上升票,我仍然放弃我的答案. (2认同)