【问题标题】:How are pointers resolved across heaps in C++如何在 C++ 中跨堆解析指针
【发布时间】:2019-01-24 02:40:55
【问题描述】:

我基本上是在我的 C++ 应用程序中使用 LoadLibrary() 调用 C++ DLL。应用程序导致随机 0xc0000005(访问冲突) 错误。我对 DLL 有自己的堆及其问题进行了大量研究。

到目前为止我确定要做的事情:

在 DLL 中:

  1. 所有分配均以 C++ 标准完成。 (不使用 malloc 或 calloc)
  2. 所有新的都具有可访问的等效删除。
  3. 在主机 exe 中释放的 DLL 内没有分配内存,反之亦然。
  4. 两者之间的数据传输是通过 POD(特别是 char*)完成的。没有 STL。
  5. 所有导出函数的调用约定为 __stdcall
  6. 构建 DLL 时指定 extern "C" 和一个 DEF 文件。

在宿主程序中:

  1. 使用 HeapAlloc()GetProcessHeap() 分配内存
  2. 指针被传递给 DLL,该 DLL 使用 memcpy()
  3. DLL 函数 typedef 是正确的。
  4. DLL 和 exe 的编译器是相同的。(内置于 VS2010)。

崩溃发生在随机位置:

  1. 在调试时我观察到,当我们在 DLL 中越过“}”函数结束大括号时,就会发生异常。
  2. 从 DLL 调用成功返回后。崩溃随机发生。

所有事件日志都显示 “故障模块名称”DLL

考虑到我之前所说的所有要点,如果有人指导我在哪里寻找异常原因,我将不胜感激。

我发送给 DLL 的指针是否也被解析为 memcpy() 中的正确 HEAP?但是主机exe中的数据是正确的。 GetProcessHeaps() 返回 4 个 HEAPS。

编辑 由于政策,无法发布完整代码。 (再次注意,我已经解释了大多数常见错误)。

发生错误的函数(DLL)

extern "C"  void __stdcall BuildApplicationsList();

exe中的类型定义

typedef void(__stdcall *buildAppsList)(void);

更新

回应@RalfFriedl。你是对的!。程序在此位置崩溃。

}

5822593F  mov         byte ptr [esp+7A0h],7  
58225947  cmp         dword ptr [esp+0A0h],0  
5822594F  jne         BuildApplicationsList+1CE2h (58225992h)  
58225951  mov         eax,dword ptr [esp+74h]  
58225955  test        eax,eax  
58225957  je          BuildApplicationsList+1CB1h (58225961h)  
58225959  mov         ecx,dword ptr [eax]  
5822595B  mov         edx,dword ptr [ecx+8]    // Crash Occurs here. 
5822595E  push        eax  
5822595F  call        edx  
58225961  mov         eax,dword ptr [esp+70h]  
58225965  test        eax,eax  
58225967  je          BuildApplicationsList+1CC1h (58225971h)  
58225969  mov         ecx,dword ptr [eax]  
5822596B  mov         edx,dword ptr [ecx+8]  
5822596E  push        eax  
5822596F  call        edx  
58225971  mov         eax,dword ptr [esp+6Ch]  
58225975  test        eax,eax  
58225977  je          BuildApplicationsList+1CD1h (58225981h)  
58225979  mov         ecx,dword ptr [eax]  
5822597B  mov         edx,dword ptr [ecx+8]  
5822597E  push        eax  
5822597F  call        edx  
58225981  call        dword ptr [__imp__CoUninitialize@0 (5823F2C8h)]  

edx 和 ecx 为 0,显然访问 0x00000008 是违规行为。 下一步去哪里?

【问题讨论】:

  • Windows“堆”是同一内存空间的一部分——不需要“解析”指针。这些堆与人们在 C++ 中称为“堆”的东西无关。
  • 下一个要看的地方是BuildApplicationsList。 (顺便说一下,DLL 中的CoUninitialize 看起来有点奇怪。)
  • mov ecx,dword ptr [eax]:从eax的对象中获取vtable指针; mov edx,dword ptr [ecx+8]:获取虚表中偏移8处的函数指针; push eax :添加this 参数; call edx: 调用函数。 eax 不是零,但edx 是,所以 vtable 指针必须为空。
  • mov ecx, dword ptr [eax] 检索地址eax 处的值,而不是eax 的值。 (也就是说,它正在取消引用一个指针。)
  • 刚刚意识到我在解释虚函数调用的评论中写了edx,而我的意思是ecx。编辑太晚了,但我不妨自己挑剔。

标签: c++ memory dll


【解决方案1】:

当您越过函数的末尾时,将调用本地析构函数。切换到调试汇编代码并找出导致问题的析构函数。

您的问题可能与主程序和 DLL 之间的分离无关,而只是对可能发生的无效指针的引用。

编辑

58225951  mov         eax,dword ptr [esp+74h]  
58225955  test        eax,eax  
58225957  je          BuildApplicationsList+1CB1h (58225961h)  
58225959  mov         ecx,dword ptr [eax]  
5822595B  mov         edx,dword ptr [ecx+8]    // Crash Occurs here. 

esp+74h 有一个局部变量,它似乎是一个指向具有虚函数的类的指针。该指针的值非零。但是由于不为指针的目标调用析构函数,因此您可能有一个封装指针的类,并从类析构函数的指针上调用delete。问题可能是目标对象的析构函数已经被调用并且空间被释放了。

在进入函数末尾之前,找出esp+74h 的值。那是地址esp+74h,而不是存储在该地址的值。检查所有局部变量的地址。其中一个必须等​​于esp+74h。这是导致问题的析构函数。

您也可以尝试禁用内联。那么您的调试器可能会停在正确的位置。

【讨论】:

  • 会这样做并回复您!
  • 更新了@RalfFriedl 的问题
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-07
  • 1970-01-01
  • 2018-05-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多