【问题标题】:How can I tell whether a binary is compiled with frame pointers or not on Linux?如何判断二进制文件是否在 Linux 上使用帧指针编译?
【发布时间】:2011-03-23 02:08:09
【问题描述】:

我在 Linux 中有一个二进制/库。如何确定它是用帧指针编译的?

【问题讨论】:

  • 你查看反汇编的目标文件,看看它是否使用了帧指针。不幸的是,我不记得那是什么样子了,所以这不是一个真正的答案。
  • 为了澄清反汇编:objdump -d <libYourLibrary.so> 可以解决问题,以防万一您正在寻找反汇编程序。

标签: linux assembly


【解决方案1】:

赞/黑翼:

使用/不使用帧指针优化编译一些简单的东西,并在反汇编输出上使用diff -u 提供了一些线索:

$ diff -u with*
--- with-fp 2011-03-23 09:49:29.366277002 +0000
+++ without-fp  2011-03-23 09:49:35.046277002 +0000
@@ -5,14 +5,12 @@
 Disassembly of section .text:

 00000000 <func>:
-   0:  55                      push   %ebp
+   0:  53                      push   %ebx
    1:  31 c0                   xor    %eax,%eax
-   3:  89 e5                   mov    %esp,%ebp
-   5:  53                      push   %ebx
-   6:  81 ec 00 04 00 00       sub    $0x400,%esp
-   c:  8b 4d 08                mov    0x8(%ebp),%ecx
-   f:  8d 9d fc fb ff ff       lea    -0x404(%ebp),%ebx
-  15:  8d 76 00                lea    0x0(%esi),%esi
+   3:  81 ec 00 04 00 00       sub    $0x400,%esp
+   9:  8b 8c 24 08 04 00 00    mov    0x408(%esp),%ecx
+  10:  89 e3                   mov    %esp,%ebx
+  12:  8d b6 00 00 00 00       lea    0x0(%esi),%esi
   18:  8b 14 81                mov    (%ecx,%eax,4),%edx
   1b:  89 14 83                mov    %edx,(%ebx,%eax,4)
   1e:  83 c0 01                add    $0x1,%eax
@@ -28,5 +26,4 @@
   3e:  75 f0                   jne    30 <func+0x30>
   40:  81 c4 00 04 00 00       add    $0x400,%esp
   46:  5b                      pop    %ebx
-  47:  5d                      pop    %ebp
-  48:  c3                      ret    
+  47:  c3                      ret    

您会看到多种变化:

  1. 带有帧指针的代码将始终包含 both 两条指令push %ebp mov %esp, %ebp
    无帧指针代码可能(在所示情况下没有,因为它没有使用%ebp 注册任何东西)有push %ebp,但不会有@ 987654327@ 1,因为不需要初始化帧指针。
  2. 带有帧指针的代码访问堆栈上相对于帧指针的参数,如所示示例中的mov 0x8(%ebp), %ecx
    无帧指针的代码相对于 stackpointer 执行此操作,并附加了函数堆栈帧大小的偏移量,例如 mov 0x408(%esp), %ecx
    局部变量可能也是如此,在显示的代码中,使用帧指针的代码为lea -0x404(%ebp), %ebx,而没有帧指针的代码为mov %esp, %ebx(可能是lea 0x0(%esp), %ebx)。
  3. 两者之间的寄存器分配可能会发生一些变化,尤其是当代码变得足够复杂以使用 %ebp 寄存器作为局部变量时(所示示例未显示)

编译器优化级别对生成的代码的实际外观有相当大的影响,但提到的这些特定项目(mov %esp, %ebp 和使用%ebp-参数/局部变量的相对寻址)仅 em> 曾在使用帧指针的代码中发现过,如果您使用 -fomit-frame-pointer 编译则会丢失。

【讨论】:

  • 请注意,即使在来自-fomit-frame-pointer 的编译器输出中,某些函数仍将使用%ebp 作为帧指针。堆栈上的 C99 可变长度数组会发生这种情况。我认为主要原因是用于异常处理/调试的堆栈展开数据无法处理可变大小的堆栈帧。也可以看到与超过 16B 对齐的本地数组/对象,因为 gcc / clang 使用 and $-32, %rsp 来实现它。
  • 对于使用x86_64 的用户,要查找的寄存器是64 位等效项:%rbp%rsp - 概念是一样的!
  • 有什么简单的工具来判断它是否带有帧指针,而不是深入组装细节?
  • @hunter_tech 我不知道比查看生成的代码“更简单”的工具。毕竟,它是可互操作的(使用帧指针的代码和无帧指针的代码可以相互调用,例如优化的库被未优化的代码调用没有限制)。至少gccclang(UN*X 平台上的两个常见编译器)不会将创建的目标文件注释为“[not] using framepointers”。
  • @kgibm 这太简单了。误报示例:mov %esp, 20(%ebp, %eax) 将匹配您的正则表达式 - 但绝对不是帧指针使用的指示。我同意“如果该计数很高”(例如,所有指令的一千分之一,按顺序排列),这很有可能。对该正则表达式的一次点击不一定是决定性的。
猜你喜欢
  • 2011-12-11
  • 2013-11-24
  • 2010-12-03
  • 1970-01-01
  • 2023-03-03
  • 1970-01-01
  • 1970-01-01
  • 2011-06-03
  • 2021-03-11
相关资源
最近更新 更多