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

时间:2013-11-04 03:32:25

标签: c assembly x86 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

原始代码:

#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);
}

1 个答案:

答案 0 :(得分: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

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

        popl    %ecx 

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

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

这将恢复先前的堆栈帧。

        ret

这会返回没有值,因为你没有为main写回报。