gpt4 book ai didi

c++ - 虚拟方法错误 (0x0) 地址

转载 作者:太空狗 更新时间:2023-10-29 21:32:51 24 4
gpt4 key购买 nike

在一些调用虚拟成员函数的代码中偶尔会出现奇怪的段错误。段错误大约平均每 3 万次调用发生一次。

我正在使用虚拟方法来实现模板方法模式。

它出现的代码行是

的第一行
GenericDevice::updateValue()
{
...
double tmpValue=getValue();
Value=tmpValue;
...
}

class GenericDevice
{
public:
void updateValue();
void print(string& result);
...
protected:
virtual double getValue()const=0;
...
private:
std::atomic<double> Value;
...
}

稍后通过在运行时加载动态库来提供类 GenericDevice

class SpecializedDeviced : public
{
...
virtual double getValue()const final;
...
}

出现问题时,我能够得到一个coredump,并查看了汇编代码:

0x55cd3ef036f4 GenericDevice::updateValue()+92   mov    -0x38(%rbp),%rax   
0x55cd3ef036f8 GenericDevice::updateValue()+96 mov (%rax),%rax
0x55cd3ef036fb GenericDevice::updateValue()+99 add $0x40,%rax
0x55cd3ef036ff GenericDevice::updateValue()+103 mov (%rax),%rax
0x55cd3ef03702 GenericDevice::updateValue()+106 mov -0x38(%rbp),%rdx
0x55cd3ef03706 GenericDevice::updateValue()+110 mov %rdx,%rdi
0x55cd3ef03709 GenericDevice::updateValue()+113 callq *%rax
0x55cd3ef0370b <GenericDevice::updateValue()+115> movq %xmm0,%rax
0x55cd3ef03710 <GenericDevice::updateValue()+120> mov %rax,-0x28(%rbp)
0x55cd3ef03714 <GenericDevice::updateValue()+124> mov -0x38(%rbp),%rax
0x55cd3ef03718 <GenericDevice::updateValue()+128> lea 0x38(%rax),%rdx
0x55cd3ef0371c <GenericDevice::updateValue()+132> mov -0x28(%rbp),%rax
0x55cd3ef03720 <GenericDevice::updateValue()+136> mov %rax,-0x40(%rbp)
0x55cd3ef03724 <GenericDevice::updateValue()+140> movsd -0x40(%rbp),%xmm0

段错误预计发生在 0x55cd3ef03709 GenericDevice::updateValue()+113。

where
#0 0x000055cd3ef0370a in MyNamespace::GenericDevice::updateValue (this=0x55cd40586698) at ../src/GenericDevice.cpp:22
#1 0x000055cd3ef038d2 in MyNamespace::GenericDevice::print (this=0x55cd40586698,result="REDACTED"...) at ../src/GenericDevice.cpp:50
...

函数 GenericDevice::updateValue() 被按预期调用了

<GenericDevice::print(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&)+301>  callq  0x55cd3ef03698 <GenericDevice::updateValue()>

原因是 rax 被设置为 0x0。

Register group: general
rax 0x0 0
rbx 0x5c01b8a2 1543616674
rcx 0x2 2
rdx 0x28 40
rsi 0x2 2
rdi 0x55cd40586630 94340036191792
rbp 0x7ffe39086e60 0x7ffe39086e60
rsp 0x7ffe39086e20 0x7ffe39086e20
r8 0x7fbb06e7e8a0 140441251473568
r9 0x3 3
r10 0x33 51
r11 0x206 518
r12 0x55cd3ef19438 94340012676152
r13 0x7ffe39089010 140729855283216
r14 0x0 0
r15 0x0 0
rip 0x55cd3ef0370a 0x55cd3ef0370a<GenericDevice::updateValue()+114> eflags 0x10206 [ PF IF RF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0

通过执行汇编摘录的计算,我能够确认汇编代码及其使用的数据与预期的虚函数调用相匹配,并以正确的数据开始:

  1. 使用对象的this指针

    (gdb) x /g $rbp-0x38  
    0x7ffe39086e28: 0x000055cd40586698
    (gdb) p this
    $1 = (GenericDevice * const) 0x55cd40586698
  2. 指向 vtable 的指针是正确的(*this 的第一个元素)

    (gdb) x 0x000055cd40586698  
    0x55cd40586698: 0x00007fbb070c1aa0
    (gdb) info vtbl this
    vtable for 'GenericDevice' @ 0x7fbb070c1aa0 (subobject @ 0x55cd40586698):
  3. vtable 包含我们正在寻找的方法的地址。

    (gdb) info vtbl this  
    vtable for 'GenericDevice' @ 0x7fbb070c1aa0 (subobject @ 0x55cd40586698):
    ...
    [8]: 0x7fbb06e7bf50 non-virtual thunk to MyNamespace::SpecializedDevice::getValue() const.
  4. 使用了正确的 vtable 偏移量

    (gdb) x 0x00007fbb070c1aa0+0x40  
    0x7fbb070c1ae0 <_ZTVN12MyNamespace11SpecializedDeviceE+168>: 0x00007fbb06e7bf50

到目前为止的结论:通过逐步执行汇编代码,使用正确的数据和指令得到验证。

  • 使用了正确的数据:可以排除内存损坏。
  • 汇编指令似乎正确:可以排除编码/编译错误
  • vtable 看起来不错:可以排除在运行时加载库时的错误:而且函数通常可以正常运行数万次。

请随时指出我推理中的任何错误。

但寄存器 rax 中的值仍然为零,而不是预期的 0x7fbb070c1ae0

  • 这是否表示一个(很少使用的)cpu 核心出现硬件错误?会解释罕见和随机发生的情况,但我预计其他程序和操作系统也会出现问题。

处理器型号为 Intel(R) Core(TM) i7-4770 CPU @ 3.40GHz

提前致谢!

更新:我找到了 $RIP 标记
0x55cd3ef0370a MyNamespace::GenericDevice::updateValue()+114 shlb 0x48(%rsi)

gdb 显示的程序集在滚动后似乎发生了变化。这就是为什么我在第一次尝试时没有看到标记。启动 gdb 并输入 layout asm 后,我得到:

>0x55cd3ef0370a <MyNamespace::GenericDevicer::updateValue()+114>  shlb   0x48(%rsi)           
0x55cd3ef0370d <MyNamespace::GenericDevicer::updateValue()+117> movd %mm0,%eax
0x55cd3ef03710 <MyNamespace::GenericDevicer::updateValue()+120> mov %rax,-0x28(%rbp)
0x55cd3ef03714 <MyNamespace::GenericDevicer::updateValue()+124> mov -0x38(%rbp),%rax
0x55cd3ef03718 <MyNamespace::GenericDevicer::updateValue()+128> lea 0x38(%rax),%rdx
0x55cd3ef0371c <MyNamespace::GenericDevicer::updateValue()+132> mov -0x28(%rbp),%rax
0x55cd3ef03720 <MyNamespace::GenericDevicer::updateValue()+136> mov %rax,-0x40(%rbp)
0x55cd3ef03724 <MyNamespace::GenericDevicer::updateValue()+140> movsd -0x40(%rbp),%xmm0

...

在 gdb 中滚动 ams 后,我得到了原始问题中发布的代码。原始问题中的代码与可执行文件中的代码匹配。上面发布的代码确实部分偏离了可执行文件。

shlb 指令对我来说毫无意义。连说明书都找不到 Intel® 64 and IA-32 Architectures Software Developer’s Manual .最接近的匹配项是 shl。

最佳答案

正如@Jester 所指出的,您的其他寄存器值与您所说的发生崩溃的代码不匹配。

I was able to obtain a coredump when the problem occurred and looked at the assembly code: ... The segfault occured in the last line of the assembly excerpt.

你怎么知道的? where 的输出是什么?

通常情况下,应该有一个当前的$RIP标记,像这样:

   0x55cd3ef036f4 GenericDevice::updateValue()+92   mov    -0x38(%rbp),%rax   
0x55cd3ef036f8 GenericDevice::updateValue()+96 mov (%rax),%rax
0x55cd3ef036fb GenericDevice::updateValue()+99 add $0x40,%rax
0x55cd3ef036ff GenericDevice::updateValue()+103 mov (%rax),%rax
0x55cd3ef03702 GenericDevice::updateValue()+106 mov -0x38(%rbp),%rdx
0x55cd3ef03706 GenericDevice::updateValue()+110 mov %rdx,%rdi
0x55cd3ef03709 GenericDevice::updateValue()+113 callq *%rax
=> 0x55cd3ef0370e GenericDevice::updateValue()+118 ....

你看到那个标记了吗?

如果不是,您的崩溃可能发生在其他地方(但分析您的数据的工作做得很好)。

如果您确实看到了标记,则其他详细信息,例如确切的处理器品牌和型号可能很重要(参见例如 this 问题和答案)。

关于c++ - 虚拟方法错误 (0x0) 地址,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53596525/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com