我在Linux上有一个二进制/库文件。如何确定它是否使用了帧指针进行编译?
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
你会看到多种不同的变化:
push %ebp
和mov %esp,%ebp
。%ebp
寄存器,因此没有)有push %ebp
,但不会有mov %esp,%ebp
,因为初始化帧指针并不是必要的。mov 0x8(%ebp),%ecx
。mov 0x408(%esp),%ecx
。lea -0x404(%ebp),%ebx
,而没有帧指针的代码为mov %esp,%ebx
(也可以是lea 0x0(%esp),%ebx
)。%ebp
寄存器作为本地变量时(所示示例未显示此操作)。编译器优化级别对生成的代码实际外观有相当大的影响,但这些特定项(mov %esp,%ebp
和使用%ebp
相对寻址参数/局部变量)仅出现在使用帧指针的代码中,并且如果使用-fomit-frame-pointer
编译,则会缺少它们。
%ebp
作为帧指针。这种情况发生在堆栈上具有C99可变长度数组的情况下。我认为主要原因是异常处理/调试的堆栈展开数据无法处理大小可变的堆栈帧。当本地数组/对象对齐到超过16B时也会出现这种情况,因为例如gcc / clang使用and $-32, %rsp
实现它。 - Peter Cordesx86_64
的人来说,要查找的寄存器是 64 位的等效寄存器:%rbp
和 %rsp
- 不过概念是相同的! - Jacek Siekagcc
和 clang
(两个常见的编译器)不会将创建的目标文件注释为“[不]使用帧指针”。 - FrankH.mov %esp,20(%ebp,%eax)
将匹配您的正则表达式——但绝不是帧指针使用的指示。我会同意“如果计数很高”(比如说,千分之一的所有指令,按数量级来说),这是相当可能的。对于那个正则表达式的单个匹配并不一定具有决定性的证据。 - FrankH.
objdump -d <libYourLibrary.so>
就可以胜任。 - FrankH.