如何在Linux上判断二进制文件是否编译时启用了帧指针?

9

我在Linux上有一个二进制/库文件。如何确定它是否使用了帧指针进行编译?


你需要查看反汇编的目标文件,以确定它是否使用了帧指针。不幸的是,我不记得它的样子,所以这不是一个真正的答案。 - Zan Lynx
1
为了澄清反汇编的问题:如果你正在寻找一个反汇编器,那么 objdump -d <libYourLibrary.so> 就可以胜任。 - FrankH.
1个回答

13

Zan/blackwing:

将一些简单的东西进行编译,包括启用/禁用帧指针优化并对反汇编输出使用 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. 包含帧指针的代码始终包含两个指令push %ebpmov %esp,%ebp
    没有帧指针的代码可能(在所示情况下没有使用%ebp寄存器,因此没有)有push %ebp,但不会有mov %esp,%ebp,因为初始化帧指针并不是必要的。
  2. 使用帧指针的代码相对于帧指针访问堆栈中的参数,例如所示代码中的mov 0x8(%ebp),%ecx
    没有帧指针的代码相对于堆栈指针进行访问,并加上函数堆栈大小的附加偏移量,例如mov 0x408(%esp),%ecx
    对于本地变量也可能是如此,在所示代码中,使用帧指针的代码为lea -0x404(%ebp),%ebx,而没有帧指针的代码为mov %esp,%ebx (也可以是lea 0x0(%esp),%ebx)。
  3. 在这两者之间可能会有一些寄存器分配方面的变化,特别是在代码足够复杂以使用%ebp寄存器作为本地变量时(所示示例未显示此操作)。

编译器优化级别对生成的代码实际外观有相当大的影响,但这些特定项(mov %esp,%ebp和使用%ebp相对寻址参数/局部变量)仅出现在使用帧指针的代码中,并且如果使用-fomit-frame-pointer编译,则会缺少它们。


1
请注意,即使在使用“-fomit-frame-pointer”编译器选项的输出中,某些函数仍将使用%ebp作为帧指针。这种情况发生在堆栈上具有C99可变长度数组的情况下。我认为主要原因是异常处理/调试的堆栈展开数据无法处理大小可变的堆栈帧。当本地数组/对象对齐到超过16B时也会出现这种情况,因为例如gcc / clang使用and $-32, %rsp实现它。 - Peter Cordes
1
对于那些使用 x86_64 的人来说,要查找的寄存器是 64 位的等效寄存器:%rbp%rsp - 不过概念是相同的! - Jacek Sieka
1
有没有简单的工具可以告诉我们它是否带有帧指针,而不必深入汇编细节? - hunter_tech
1
@hunter_tech 我不知道比查看生成的代码更简单的工具。毕竟,它是互操作的(使用帧指针和不使用帧指针的代码可以相互调用,例如优化库被未经优化的代码调用时没有限制)。至少在 UN*X 平台上,gccclang(两个常见的编译器)不会将创建的目标文件注释为“[不]使用帧指针”。 - FrankH.
2
@kgibm 这太简单了。误报的例子:mov %esp,20(%ebp,%eax)将匹配您的正则表达式——但绝不是帧指针使用的指示。我会同意“如果计数很高”(比如说,千分之一的所有指令,按数量级来说),这是相当可能的。对于那个正则表达式的单个匹配并不一定具有决定性的证据。 - FrankH.
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接