【问题标题】:What is the purpose of these lines of assembly code?这些汇编代码行的目的是什么?
【发布时间】:2017-10-09 12:45:33
【问题描述】:
_main:
; create stack frame
pushl   %ebp
movl    %esp, %ebp
; save one local variable
subl    $8, %esp
; zero four rightmost bits of esp
andl    $-16, %esp
; set eax to (0 + 15 + 15) / 2^4 * 2^4 = 16
movl    $0, %eax
addl    $15, %eax
addl    $15, %eax
shrl    $4, %eax
sall    $4, %eax
; set local variable to eax (16)
movl    %eax, -4(%ebp)
movl    -4(%ebp), %eax
; call allocation and main
call    __alloca
call    ___main
; set eax to zero (return value)
movl    $0, %eax
; fold stack frame and return to caller
leave
ret

我使用gcc -S return_zero.c(在Windows上)编译了int main(){return 0;} C代码,这就是我得到的(我删除了汇编程序指令并添加了我所理解的解释注释。如果我错了,请纠正我.)。

我不明白三件事:

  • 为什么编译器将 esp 对齐到 16?
  • 为什么eax设置为16,为什么做的这么复杂,为什么还有一个局部变量也设置为16?
  • 什么是 __alloca 和 ___main?

我在互联网上对#1和#3都有一些不清楚的解释,所以我想如果有人可以更深入地回答,对于#2我没有找到任何解释,所以如果有人可以解释它会很棒.

如果需要任何进一步的信息,请发表评论,我会发布。

谢谢!

【问题讨论】:

  • 尝试优化编译。
  • @KerrekSB 我用 -O3 编译。它删除了整个 eax = 16 的东西,并在最后使用 xorl 而不是 movl 将 eax 设置为 0,但它仍然执行所有其他操作(包括声明局部变量)。我也很好奇它为什么会这样做。
  • 这是用于什么平台的?我无法在 Linux 上重现它。
  • @KerrekSB 我正在使用带有 Intel Core i5 的 Windows 8.1。
  • 检查 Windows ABI,特别是注册溢出和红色区域。

标签: c gcc assembly compiler-construction x86


【解决方案1】:

Intel Core i5 使用 64 位架构。
解决问题 1 和部分问题 2,来自 an Overview of x64 Calling Conventions:

对齐
大多数结构与其自然对齐对齐。首要的 堆栈指针和 malloc 或 alloca 内存除外,它们 对齐到 16 个字节以提高性能。上面对齐 16 字节必须手动完成,但由于 16 字节是常见的 XMM 操作的对齐大小,这应该适用于大多数代码。为了 有关结构布局和对齐的更多信息,请参阅类型和 贮存。有关堆栈布局的信息,请参阅堆栈使用。

关于你的问题的一部分:...为什么它以如此复杂的方式完成。只是一个猜测,但从报价来看,复杂性可能部分是为了适应便携性。

从 this post 借用,(也有标签 gcc、compiler-construction 和 assembly )是对您尝试的一些相同代码的逐行解释解释。摘录:

在步骤 1 中,我们将指向旧堆栈帧的指针保存在堆栈上 呼叫,pushl %ebp。由于 main 是第一个调用的函数,所以我有 也不知道 %ebp 之前的值是多少。

第 2 步,我们正在输入一个新的堆栈帧,因为我们正在输入一个 新功能(主要)。因此,我们必须设置一个新的栈帧基础 指针。我们使用 esp 中的值作为堆栈的开始 框架。

步骤 3. 在堆栈上分配 8 个字节的空间。正如我们提到的 上面,堆栈向低地址增长,因此减去 8, 将栈顶移动 8 个字节。
...

关于调用 __alloca & __main

...
步骤 12 和 13 设置 c 库。

【讨论】:

  • 好的,所以对齐是某种 Windows 约定?但我仍然没有得到 eax 和局部变量部分。它们是对 __alloca 的某种论证吗?这没有意义。
  • 但是它不保存以前的esp 的值,它保存的是16 的值,不是吗?
  • Neo - subl $8, %esp 在这里,编译器似乎在堆栈上保留空间。我不确定它为什么保留 8。同样,可能与对齐直接相关。清除最右边的位后,它可能会包含 esp 的最左边的位。
  • 所以这是__alloca的某种论据?
  • @Neo - andl $-16, %esp 将 esp 对齐到 16 字节边界。 (来自参考文献 here 和 here)
猜你喜欢
  • 2015-01-14
  • 1970-01-01
  • 1970-01-01
  • 2019-02-27
  • 1970-01-01
  • 1970-01-01
  • 2016-06-03
  • 1970-01-01
  • 2019-09-09
相关资源
最近更新 更多