gpt4 book ai didi

c++ - cdecl 调用约定如何破坏 ESP?

转载 作者:塔克拉玛干 更新时间:2023-11-03 00:58:31 25 4
gpt4 key购买 nike

我的应用程序崩溃了,因为我调用的库函数更改了 ESP,尽管它被声明为 cdecl。

库 (libclang.dll) 是使用 MinGW 编译的,我在 VC++ 项目中使用它。这些函数作为 C 函数导出,Dependency Walker 告诉我它们具有正确的 cdecl 调用约定。通过包含 Clang 的“index.h”文件,使用 dllimport 将函数导入到我的项目中。似乎并不是所有函数都破坏了 ESP,因此一些函数执行成功,其他函数导致崩溃。

这是一个工作函数的集合:

// call to clang_getNumDiagnostics(TU); - works!
5AF3EFAB mov esi,esp
5AF3EFAD mov eax,dword ptr [ebp-30h]
5AF3EFB0 push eax
5AF3EFB1 call dword ptr [__imp__clang_getNumDiagnostics (5AF977E0h)]
5AF3EFB7 add esp,4
5AF3EFBA cmp esi,esp
5AF3EFBC call @ILT+7135(__RTC_CheckEsp) (5AF16BE4h)

以下函数调用将更改 esp(加 4),因此由于 __RTC_CheckEsp 中的运行时检查而导致崩溃。

// call to clang_getTranslationUnitCursor(TU); - fails!
5AF3EFC1 mov esi,esp
5AF3EFC3 mov eax,dword ptr [ebp-30h]
5AF3EFC6 push eax
5AF3EFC7 lea ecx,[ebp-234h]
5AF3EFCD push ecx
5AF3EFCE call dword ptr [__imp__clang_getTranslationUnitCursor (5AF9780Ch)]
5AF3EFD4 add esp,8
5AF3EFD7 cmp esi,esp
5AF3EFD9 call @ILT+7135(__RTC_CheckEsp) (5AF16BE4h)

我已经发布了 question对于这个问题,但我想我特别询问了调用约定 cdecl 以检索有关 currupted esp 可能性的更多具体信息,因为我认为这可能是问题的根源......因此请原谅这个“双重帖子”。

源头也可能在于调用了错误的函数(可能是由于我使用 dlltool 创建的 def 文件中存在问题,后来从中创建了导入库 - 尽管序数与 Dependency Walker 显示的不同 - 我试过了修正了序数,但没有变化)。我觉得这不太可能是问题的根源,因为其他函数调用工作正常并返回正确的值......

谢谢!

[更新]

按要求组装 __imp__clang_getTranslationUnitCursor

6660A4A0  push        ebp  
6660A4A1 mov ebp,esp
6660A4A3 push edi
6660A4A4 push ebx
6660A4A5 mov eax,dword ptr [ebp+8]
6660A4A8 mov ebx,eax
6660A4AA mov al,0
6660A4AC mov edx,14h
6660A4B1 mov edi,ebx
6660A4B3 mov ecx,edx
6660A4B5 rep stos byte ptr es:[edi]
6660A4B7 mov eax,dword ptr [ebp+8]
6660A4BA mov dword ptr [eax],12Ch
6660A4C0 mov eax,dword ptr [ebp+8]
6660A4C3 mov edx,dword ptr [ebp+0Ch]
6660A4C6 mov dword ptr [eax+10h],edx
6660A4C9 mov eax,dword ptr [ebp+8]
6660A4CC pop ebx
6660A4CD pop edi
6660A4CE pop ebp
6660A4CF ret 4

[更新 2]由于 VC++ 和 GCC 都使用 cdecl 作为默认值,并且没有办法在 GCC 中强制另一个默认调用约定而不在函数声明中明确声明它(对于有问题的函数没有这样做),我实际上确信 cdecl 无处不在.

我找到了这个 link 不过,这说明了一些差异,可以解释为什么某些功能有效而其他功能无效:

Visual C++/Win32

  • 内存中返回大于 8 字节的对象。

  • 当在内存中进行返回时,调用者将指向内存位置的指针作为第一个参数(隐藏)传递。被调用者填充内存,并返回指针。 caller 将隐藏指针与其余参数一起弹出。

MinGW g++/Win32

  • 内存中返回大于 8 字节的对象。

  • 当在内存中进行返回时,调用者将指向内存位置的指针作为第一个参数(隐藏)传递。被调用者填充内存,并返回指针。 callee 在返回时从栈中弹出隐藏的指针。

这可能是问题所在吗?有什么办法可以解决这个问题吗?还是我必须更改 Clang 的 Index.h 并切换到 stdCall?

[更新 3]

这是根据GCC-Bug .在 4.6(64 位)和 4.7(32 位)中,您似乎可以使用新的 ms_abi 函数属性 来修复 [更新 2] 中描述的问题。

最佳答案

GCC 和 Visual C++ 没有实现相同的 cdecl 调用约定。 Wikipedia explains :

There are some variations in the interpretation of cdecl, particularly in how to return values. As a result, x86 programs compiled for different operating system platforms and/or by different compilers can be incompatible, even if they both use the "cdecl" convention and do not call out to the underlying environment. [...] To pass "in memory", the caller allocates memory and passes a pointer to it as a hidden first parameter; the callee populates the memory and returns the pointer, popping the hidden pointer when returning.

最后一句话很重要:GCC 版本的 cdecl 使被调用者清理隐藏指针,而 Visual C++ 版本的 cdecl 则留给调用者清理。

关于c++ - cdecl 调用约定如何破坏 ESP?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9265154/

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