【问题标题】:C to Assembly code - what does it mean [closed]C到汇编代码 - 这是什么意思[关闭]
【发布时间】:2013-11-04 03:32:25
【问题描述】:

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

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

【问题讨论】:

  • 最好询问一个特定的输出特性。

标签: c assembly x86 disassembly


【解决方案1】:

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

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

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

        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 编写返回。

【讨论】:

  • +1 不错的摘要。太好了,我有几票赞成,但我仍然放弃了这个答案。
  • @WhozCraig:很高兴知道你喜欢它;-)
  • 没问题。不想丢失它,__printf_chkcan be found here.an 实现的链接,原型为 int __printf_chk (int flag, const char *fmt, ...),以防有人对它的外观和宏实际在做什么感兴趣。
  • @some: 到深夜...
  • @WhozCraig:很好,但是您链接的不是__printf_chk 的实际实现,而是在 GCC 的测试套件中使用的一个。 This one 似乎是真实版本的最新版本。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多