上下文:我知道——或者至少,an——这个问题的答案;我将其发布在这里是因为几个小时前,当我没有这样做时,我无法通过搜索这些关键字找到任何内容。
有时,当我查看反汇编输出时,我会看到一个条件跳转到下一行,这是没有意义的——要么你去那里因为检查失败而你没有跳转,要么你去那里因为检查通过并且你确实跳到那里了。
例如,如果我编译以下小型 C++ 程序
#include <stdexcept>
bool f(int x, int y, int z)
{
switch(x)
{
case 0: return z < y;
case 1: return y < z;
default: throw std::runtime_error("bad x");
}
}
下到一个
.o
文件,然后使用objdump -C -d
对其进行反汇编,部分反汇编如下所示:
0000000000000000 <f(int, int, int)>:
0: f3 0f 1e fa endbr64
4: 39 d6 cmp %edx,%esi
6: 0f 9f c0 setg %al
9: 85 ff test %edi,%edi
b: 75 03 jne 10 <f(int, int, int)+0x10>
d: c3 ret
e: 66 90 xchg %ax,%ax
10: 83 ff 01 cmp $0x1,%edi
13: 0f 85 00 00 00 00 jne 19 <f(int, int, int)+0x19>
19: 39 d6 cmp %edx,%esi
1b: 0f 9c c0 setl %al
1e: c3 ret
指令
0f 85 00 00 00 00
,条件跳转到相对偏移量0,在反汇编中显示为jne 19
。这似乎毫无意义,因为无论条件如何,它似乎都分支到下一条指令。
这里到底发生了什么?
如果我们查看更多的反汇编,我们会注意到一些事情;
Disassembly of section .text:
0000000000000000 <f(int, int, int)>:
0: f3 0f 1e fa endbr64
4: 39 d6 cmp %edx,%esi
6: 0f 9f c0 setg %al
9: 85 ff test %edi,%edi
b: 75 03 jne 10 <f(int, int, int)+0x10>
d: c3 ret
e: 66 90 xchg %ax,%ax
10: 83 ff 01 cmp $0x1,%edi
13: 0f 85 00 00 00 00 jne 19 <f(int, int, int)+0x19>
19: 39 d6 cmp %edx,%esi
1b: 0f 9c c0 setl %al
1e: c3 ret
Disassembly of section .text.unlikely:
0000000000000000 <f(int, int, int) [clone .cold]>:
0: 55 push %rbp
1: bf 10 00 00 00 mov $0x10,%edi
6: 53 push %rbx
7: 50 push %rax
8: e8 00 00 00 00 call d <f(int, int, int) [clone .cold]+0xd>
d: 48 8d 35 00 00 00 00 lea 0x0(%rip),%rsi # 14 <f(int, int, int) [clone .cold]+0x14>
[snip]
我们应该在单独的部分中跳转到的代码,.text.unlikely。
跳转的
00 00 00 00
相对地址是一个占位符,链接器将填充该占位符,以指向最终放置 .text.unlikely 中的代码的位置。 .o
文件中有一些 objdump
未显示的元数据告诉它执行此操作。
如果您不是编译为
.o
文件,而是在反汇编之前一直编译为可执行文件,则不会出现此问题,因为链接器已经完成其工作并填写了地址。
您还可以通过简单地将
g++
停止在汇编代码级别来绕过此问题,而无需使用 .o
标志生成 -S
文件。 然而,与使用 objdump
相比,这有一个缺点,即 objdump
可以对名称进行分解,而 g++ -S
显然不能。