我对英特尔指令集的了解有点生疏。你能告诉我为什么我的函数的优化版本可能会出现段错误吗(如果你能告诉我为什么我没有在代码的 -O0 构建中得到它,则加分。
是GCC 4.1.2编译的C代码。
这是崩溃时 GDB 的“disas”命令的结果:
0x00000000004263e5 <+0>: sub $0x8,%rsp
0x00000000004263e9 <+4>: movsd %xmm2,(%rsp)
0x00000000004263ee <+9>: divsd %xmm1,%xmm0
0x00000000004263f2 <+13>: callq 0x60f098 <log@plt>
=> 0x00000000004263f7 <+18>: andpd 0x169529(%rip),%xmm0
0x00000000004263ff <+26>: movsd (%rsp),%xmm1
0x0000000000426404 <+31>: ucomisd %xmm0,%xmm1
0x0000000000426408 <+35>: seta %al
0x000000000042640b <+38>: movzbl %al,%eax
0x000000000042640e <+41>: add $0x8,%rsp
0x0000000000426412 <+45>: retq
这是函数的原始来源:
char is_within_range(double a, double b, double range) {
double ratio = a / b;
double logRatio = fabs(log(ratio));
return logRatio < range;
}
引用这里的代码的非优化版本:
0x00000000004263e5 <+0>: push %rbp
0x00000000004263e6 <+1>: mov %rsp,%rbp
0x00000000004263e9 <+4>: sub $0x30,%rsp
0x00000000004263ed <+8>: movsd %xmm0,-0x18(%rbp)
0x00000000004263f2 <+13>: movsd %xmm1,-0x20(%rbp)
0x00000000004263f7 <+18>: movsd %xmm2,-0x28(%rbp)
0x00000000004263fc <+23>: movsd -0x18(%rbp),%xmm0
0x0000000000426401 <+28>: divsd -0x20(%rbp),%xmm0
0x0000000000426406 <+33>: movsd %xmm0,-0x10(%rbp)
0x000000000042640b <+38>: mov -0x10(%rbp),%rax
0x000000000042640f <+42>: mov %rax,-0x30(%rbp)
0x0000000000426413 <+46>: movsd -0x30(%rbp),%xmm0
0x0000000000426418 <+51>: callq 0x610608 <log@plt>
0x000000000042641d <+56>: movapd %xmm0,%xmm1
0x0000000000426421 <+60>: movsd 0x16b6b7(%rip),%xmm0
0x0000000000426429 <+68>: andpd %xmm1,%xmm0
0x000000000042642d <+72>: movsd %xmm0,-0x8(%rbp)
0x0000000000426432 <+77>: movsd -0x8(%rbp),%xmm1
0x0000000000426437 <+82>: movsd -0x28(%rbp),%xmm0
0x000000000042643c <+87>: ucomisd %xmm1,%xmm0
0x0000000000426440 <+91>: seta %al
0x0000000000426443 <+94>: movzbl %al,%eax
0x0000000000426446 <+97>: leaveq
0x0000000000426447 <+98>: retq
最佳答案
=> 0x00000000004263f7 <+18>: andpd 0x169529(%rip),%xmm0
0x00000000004263ff <+26>: movsd (%rsp),%xmm1
当andpd
指令取内存操作数时,需要对齐到16字节边界。
对于 %rip
相对寻址,偏移量应用于后续指令的地址。因此,这里的内存操作数位于 0x4263ff + 0x169529 = 0x58f928
,这不是 16 字节对齐的。因此出现了段错误。
编译器直接为 fabs()
生成代码,使用带有适当位掩码的 AND 来清除符号位;位掩码常量值本应放置在充分对齐的数据部分中的适当偏移处,但实际上并没有。这可能是那个(旧)版本的 GCC 中的错误,或者可能是其他地方与链接器相关的问题。
关于c - 优化版本代码中的 SIGSEGV,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8303176/