c - 在理解从 C 代码生成的基本汇编代码方面需要帮助

标签 c assembly

我正在学习汇编,我想了解如何从 C 代码生成汇编。

我创建了以下虚拟 C 代码:

#include <stdio.h>

int add(int x, int y){
    int result = x + y;
    return result;
}

int main(int argc, char *argv[]){

    int x = 1 * 10;
    int y = 2 * 5;
    int firstArg = x + y;
    int secondArg = firstArg / 2;
    int value;
    value = add(firstArg, secondArg);
    return value;
}

得到如下汇编代码

.file   "first.c"
    .text
    .globl  add
    .type   add, @function
add:
.LFB39:
    .cfi_startproc
    movl    8(%esp), %eax
    addl    4(%esp), %eax
    ret
    .cfi_endproc
.LFE39:
    .size   add, .-add
    .globl  main
    .type   main, @function
main:
.LFB40:
    .cfi_startproc
    movl    $30, %eax
    ret
    .cfi_endproc
.LFE40:
    .size   main, .-main
    .ident  "GCC: (Ubuntu 4.8.2-19ubuntu1) 4.8.2"
    .section    .note.GNU-stack,"",@progbits

我很惊讶 main 中的所有这些算术运算在哪里消失了? 我不明白我们是如何从无处获得 30 美元的(好吧,我想这是 add 函数的返回值,但为什么我没有看到任何获取此值的命令,实际上我看到返回值已经被推送到 eax 中add 函数,那么为什么我们需要再次将 $30 移动到 eax 呢?)。 所有本地主要变量都在哪里声明?我特地创建了其中的5个,看看一个是如何入栈的。

你能帮我理解那些.LFE39 .LFB40: .LFB39: 是什么意思吗?

我已经准备好这本书了,但它并没有为我阐明这个案例。 实际上书上说所有函数都必须从堆栈初始化开始:

  pushl   %ebp
  movl    %esp, %ebp

以及当函数结束时,它需要用 pop 指令完成它。

上面的代码不是这样的。我没有看到任何堆栈初始化。

谢谢!

最佳答案

您正在编译启用优化。 GCC 足够聪明,可以在编译时执行所有这些计算,并用一个简单的常量替换所有无用的代码。

首先,xy 将被替换为它们的常量表达式:

    int x = 10;
    int y = 10;

然后,使用这些变量的地方将改为获取它们的常量值:

    int firstArg = 20;
    int secondArg = 10;

接下来,您的 add 函数又小又普通,因此它肯定会被内联:

    value = firstArg + secondArg;

现在这些也是常量,所以整个事情将被替换为:

int main(int argc, char *argv[]) {
    return 30;
}

虽然大多数函数都会有一个如您所展示的序言,但您的程序什么都不做 而是返回 30。更具体地说,它不再使用任何局部变量,也不再调用其他函数。因此 main 不需要调用堆栈上的帧或保留空间。因此,编译器无需发出序言/结尾。

main:
    movl    $30, %eax
    ret

这些是您的程序将运行的唯一两条指令(C 运行时启动代码除外)。


另外请注意,由于您的add 函数未标记为static,因此编译器必须假定外部人员可能会调用它。出于这个原因,我们仍然在生成的程序集中看到 add,即使没有人调用它:

add:
    movl    8(%esp), %eax
    addl    4(%esp), %eax
    ret

关于c - 在理解从 C 代码生成的基本汇编代码方面需要帮助,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26479853/

相关文章:

c - 没有规则使目标 '%.o'

c++ - sqlite3程序收到信号SIGSEGV,sqlite3_get_table()中出现段错误

c - 构建并发送 RAW 数据报

c - gcc objdump 程序集调试

c - 如何从汇编指令到 C 代码

assembly - 我尝试打印号码时出错

c++ - 读取/写入 void* 变量的单个字节

c - Visual Studio 和 C 项目上的运行之间不一致

linux - 在适用于 Linux 的 Windows 子系统上的 Ubuntu 上使用 INT 0x80 汇编编译的可执行文件不产生输出

assembly - asm中字符串的DB定义中0x0a是什么意思