我试图理解/调试我没有源代码的库。在这样做时,我遇到了以下函数(以及其他一些更复杂的函数,例如[0]):
0000000000000000 <old_bern_poly_power02(double, double)>:
0: 66 0f 28 d0 movapd %xmm0,%xmm2
4: 66 0f 28 c1 movapd %xmm1,%xmm0
8: f2 0f 59 d1 mulsd %xmm1,%xmm2
c: f2 0f 5c 05 00 00 00 subsd 0x0(%rip),%xmm0 # 14 <old_bern_poly_power02(double, double)+0x14>
13: 00
14: f2 0f 59 c2 mulsd %xmm2,%xmm0
18: c3 retq
19: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)
除了subsd指令之外,该函数的作用非常明显。但是,我对subsd指令正在做什么感到困惑。指向指令的值0x0(%rip)与此计算有什么关系?
所以,这是我的问题:&#34; subsd&#34;的目的是什么?上面的指示?我假设该函数是用GCC编译的。为什么GCC(或汇编语言程序员)会包含这样的指令? [1]
[0]这是另一个类似的例子:
0000000000000020 <old_bern_poly_power03(double, double)>:
20: 66 0f 28 d0 movapd %xmm0,%xmm2
24: 66 0f 28 d8 movapd %xmm0,%xmm3
28: 66 0f 28 c1 movapd %xmm1,%xmm0
2c: f2 0f 59 15 00 00 00 mulsd 0x0(%rip),%xmm2 # 34 <old_bern_poly_power03(double, double)+0x14>
33: 00
34: f2 0f 5c 05 00 00 00 subsd 0x0(%rip),%xmm0 # 3c <old_bern_poly_power03(double, double)+0x1c>
3b: 00
3c: f2 0f 59 d9 mulsd %xmm1,%xmm3
40: f2 0f 59 c3 mulsd %xmm3,%xmm0
44: f2 0f 58 c2 addsd %xmm2,%xmm0
48: f2 0f 59 c3 mulsd %xmm3,%xmm0
4c: c3 retq
4d: 0f 1f 00 nopl (%rax)
[1]注意:基于逐步执行gdb中的函数,当%xmm0远离0.0时,subsd指令似乎没有实际效果,因为0x0(%rip)很小(我不确定这是否正确):
(gdb) x $rip
0x4004e2 <old_bern_poly_power02+12>: 0x00000000055c0ff2
(gdb) p (double)(void *)(0x00000000055c0ff2)
$22 = 4.4426122995515177e-316
为了进一步研究subsd指令的效果,我创建了一个不包含subsd指令的old_bern_poly_power02函数的版本,但在其他方面与上面的列表相同。下表比较了给定各种输入的每个函数版本的返回值。
实际上,返回值是相同的,除非xmm1参数为0,在这种情况下,原始结果为-0(0x8000000000000000),没有subsd指令的版本为0(0x0)。
xmm0 xmm1 ret_val_with_subsd (as hex) ret_val_without_subsd (as hex)
0.000000 0.000000 -0.0000000000 (8000000000000000) 0.0000000000 ( 0)
0.000000 1.000000 0.0000000000 ( 0) 0.0000000000 ( 0)
0.000000 2.000000 0.0000000000 ( 0) 0.0000000000 ( 0)
0.000000 3.000000 0.0000000000 ( 0) 0.0000000000 ( 0)
1.000000 0.000000 -0.0000000000 (8000000000000000) 0.0000000000 ( 0)
1.000000 1.000000 1.0000000000 (3ff0000000000000) 1.0000000000 (3ff0000000000000)
1.000000 2.000000 4.0000000000 (4010000000000000) 4.0000000000 (4010000000000000)
1.000000 3.000000 9.0000000000 (4022000000000000) 9.0000000000 (4022000000000000)
2.000000 0.000000 -0.0000000000 (8000000000000000) 0.0000000000 ( 0)
2.000000 1.000000 2.0000000000 (4000000000000000) 2.0000000000 (4000000000000000)
2.000000 2.000000 8.0000000000 (4020000000000000) 8.0000000000 (4020000000000000)
2.000000 3.000000 18.0000000000 (4032000000000000) 18.0000000000 (4032000000000000)
3.000000 0.000000 -0.0000000000 (8000000000000000) 0.0000000000 ( 0)
3.000000 1.000000 3.0000000000 (4008000000000000) 3.0000000000 (4008000000000000)
3.000000 2.000000 12.0000000000 (4028000000000000) 12.0000000000 (4028000000000000)
3.000000 3.000000 27.0000000000 (403b000000000000) 27.0000000000 (403b000000000000)
编辑1:Per Harold和Ross的评论,我将库链接到一个简单的可执行文件(生成上表的那个)并在GDB中反汇编函数,如下所示:
$ gdb ./tmp
[snp]
(gdb) b old_bern_poly_power02
Breakpoint 1 at 0x400516
(gdb) r
Starting program: [...]/tmp
xmm0 xmm1 ret_val_with_subsd (as hex) ret_val_without_subsd (as hex)
Breakpoint 1, 0x0000000000400516 in old_bern_poly_power02 ()
(gdb) disassemble
Dump of assembler code for function old_bern_poly_power02:
=> 0x0000000000400516 <+0>: movapd %xmm0,%xmm2
0x000000000040051a <+4>: movapd %xmm1,%xmm0
0x000000000040051e <+8>: mulsd %xmm1,%xmm2
0x0000000000400522 <+12>: subsd 0x0(%rip),%xmm0 # 0x40052a <old_bern_poly_power02+20>
0x000000000040052a <+20>: mulsd %xmm2,%xmm0
0x000000000040052e <+24>: retq
0x000000000040052f <+25>: nopl (%rax)
End of assembler dump.
这里似乎仍然对0-offset-to-%rip有相同的引用,尽管我仍然可能做错了?
编辑2:在上面的清单中,我正在反汇编我从未链接的静态库的反汇编中复制和粘贴的版本,然后汇编并链接到我上面的测试程序中。当我从原始静态库中反汇编函数时,链接到我的真实程序中,确实填补了偏移量:
(gdb) disassemble old_bern_poly_power02
Dump of assembler code for function _Z21old_bern_poly_power02dd:
0x00007f65cf642a50 <+0>: movapd %xmm0,%xmm2
0x00007f65cf642a54 <+4>: movapd %xmm1,%xmm0
0x00007f65cf642a58 <+8>: mulsd %xmm1,%xmm2
0x00007f65cf642a5c <+12>: subsd 0x38cc(%rip),%xmm0 # 0x7f65cf646330
0x00007f65cf642a64 <+20>: mulsd %xmm2,%xmm0
0x00007f65cf642a68 <+24>: retq
End of assembler dump.
所以我猜这个subsd指令只是在这里引用表中的常量。