【问题标题】:Understanding disassembled C code: 16 bytes stack allocation, movl $0x0,-0x4(%ebp) and nop了解反汇编的 C 代码:16 字节堆栈分配,movl $0x0,-0x4(%ebp) 和 nop
【发布时间】:2019-07-20 15:07:17
【问题描述】:

我试图理解一段反汇编代码中的行,如下所示。我想知道以下内容:

  • sub $0x10,%esp:为什么我们要在栈顶分配 16 个字节?我期望指针变量只有 4 个字节。
  • movl $0x0,-0x8(%ebp) :为什么我们要将值 0x0 移到堆栈上?这是二进制文件的起始地址吗?
  • nop:这段代码中 nop 指令的目的是什么?

这是反汇编的二进制文件:

Contents of section .text:
 0000 5589e583 ec10c745 fc000000 0090c9c3  U......E........
Contents of section .rodata:
 0000 48656c6c 6f00                        Hello.          
Contents of section .comment:
 0000 00474343 3a202855 62756e74 7520352e  .GCC: (Ubuntu 5.
 0010 342e302d 36756275 6e747531 7e31362e  4.0-6ubuntu1~16.
 0020 30342e31 30292035 2e342e30 20323031  04.10) 5.4.0 201
 0030 36303630 3900                        60609.          
Contents of section .eh_frame:
 0000 14000000 00000000 017a5200 017c0801  .........zR..|..
 0010 1b0c0404 88010000 1c000000 1c000000  ................
 0020 00000000 10000000 00410e08 8502420d  .........A....B.
 0030 054cc50c 04040000                    .L......        

Disassembly of section .text:

00000000 <my_function>:
   0:   55                      push   %ebp
   1:   89 e5                   mov    %esp,%ebp
   3:   83 ec 10                sub    $0x10,%esp
   6:   c7 45 fc 00 00 00 00    movl   $0x0,-0x4(%ebp)
   d:   90                      nop
   e:   c9                      leave  
   f:   c3                      ret   

这是 C 代码:

void my_function () { 
   char* my_string = "Hello";
}

【问题讨论】:

  • 此代码是否来自未经优化编译的目标文件 (.o)?
  • 代码是在没有优化的情况下构建的。减 16 的使用可能与 64 位 System V ABI 有关,该 ABI 在函数调用中要求 16 字节对齐。您似乎正在转储目标文件。 c7 45 fc 00 00 00 00 movl $0x0,-0x4(%ebp) 的值为 0 被移动,因为它是一个将被重新定位的占位符。使用-r 选项和objdump 让它转储重定位条目。如果您要转储可执行文件,则应修复重定位。
  • 在这种情况下,nop 是某些版本的 GCC 在没有优化的情况下编译时生成的某种工件。打开优化,它应该与功能的其余部分一起消失;-)
  • @melpomene :我在评论中建议优化可能会消除函数的主体,因为您对字符串不做任何事情。尝试将my_string 设为volatile 指针以强制它为该字符串变量生成代码。例如:godbolt.org/z/JCFEsl

标签: assembly x86


【解决方案1】:

我无法解释nop,但其余的

首先,让我们注意char* my_string 是一个局部变量——这意味着它是堆栈分配的,我们可以看到它位于-4(%ebp)。虽然并非绝对必要,但编译器使用帧指针进行局部变量访问(而不是直接使用 %esp)。

接下来,让我们观察"hello" 是一个字符串字面量——而字符串字面量是存储在.rodata 部分中的常量。

最后让我们观察一下,您的反汇编程序没有以有意义的方式打印从.text.rodata 的重定位,因此它只是在指令中将0 用于"hello" 的地址。如果打印得更好,movl 指令中的 4 个字节的 0 将具有.rodata + 0 的地址,该地址就是我们想要移动到my_string 中的值。

至于 16 字节的选择,编译器只是简单地四舍五入。有充分的理由进行汇总,但在这里并不明显。返回地址使用的总堆栈空间是 4 个字节加上旧的%ebp 的 4 个字节,再加上 16。至少总数是 8 的倍数,这有利于对齐,因为这样很容易有 8 个字节堆栈上的值是 8 字节对齐的。

【讨论】:

  • 使用objdump -dr 将打印重定位信息,以便您可以在.o 上有用地使用它
猜你喜欢
  • 2019-11-20
  • 2015-12-23
  • 1970-01-01
  • 2011-01-24
  • 2017-08-18
  • 2011-06-16
  • 2011-12-05
  • 1970-01-01
  • 2014-05-29
相关资源
最近更新 更多