c - 优化版本代码中的 SIGSEGV

标签 c gcc assembly segmentation-fault

我对英特尔指令集的了解有点生疏。你能告诉我为什么我的函数的优化版本可能会出现段错误吗(如果你能告诉我为什么我没有在代码的 -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/

相关文章:

c - FLEX- Bison : error: expected identifier or ‘(’ before string constant

c++ - 在 gdb 中检查模板参数包

assembly - x86汇编代码中 "mov eax, cr3; mov cr3, eax"的作用是什么?

谁能告诉我上述代码之间的逻辑区别

C - 指针、函数和递归

objective-c - 编译 objective-c(++) 时 gcc#define 是什么?

assembly - NASM 汇编器 - 生成的机器代码中不需要的 66

assembly - 通过将 EFLAGS.VM 设置为 1 从 32 位保护模式切换到 v8086 模式时出现问题

c - 在 C 中直接传递变量地址和传递指针(指向同一变量)之间的区别?

c++ - 链接分配对性能有何影响