Cod*_*lad 4 c++ assembly mingw calling-convention visual-c++
我的应用程序崩溃了,因为我调用的库函数更改了ESP,尽管它被声明为cdecl.
库(libclang.dll)是使用MinGW编译的,我在VC++项目中使用它.函数导出为C函数,Dependency Walker告诉我他们有正确的cdecl调用约定.使用dllimport将函数导入到我的项目中,包括Clang的"index.h"文件.似乎并非所有功能都在破坏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)
Run Code Online (Sandbox Code Playgroud)
以下函数调用将更改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)
Run Code Online (Sandbox Code Playgroud)
我已经发布了一个问题,对于这个问题,但认为我特地问了一下调用约定CDECL检索有关的ESP currupted的可能性更具体的信息,因为我认为这可能是问题的根源......因此原谅这个"双后".
源代码也可能位于被调用的错误函数中(可能是由于我使用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
Run Code Online (Sandbox Code Playgroud)
[更新2] 由于VC++和GCC都使用cdecl作为默认值,并且没有办法在GCC中强制执行另一个默认调用约定而没有在函数声明中明确说明它(对于有问题的函数没有这样做),我实际上确定cdecl随处可用.
我发现了这个链接,它说明了一些差异可以解释为什么某些功能有效而其他功能不起作用:
Visual C++/Win32
内存中返回大于8个字节的对象.
当在内存中进行返回时,调用者将指向内存位置的指针作为第一个参数(隐藏)传递.被调用者填充内存,并返回指针.该呼叫者与参数的休息一起弹出隐藏的指针.
MinGW g ++/Win32
内存中返回大于8个字节的对象.
当在内存中进行返回时,调用者将指向内存位置的指针作为第一个参数(隐藏)传递.被调用者填充内存,并返回指针.返回时,被调用者从堆栈弹出隐藏指针.
可能那是问题吗?有什么方法可以解决这个问题吗?或者我是否必须更改Clang的Index.h并切换到stdCall?
[更新3]
这是根据GCC-Bug.似乎在4.6(64位)和4.7(32位)中,您可以使用新的ms_abi函数属性来修复[Update 2]中描述的问题.
根据clang网站,在开始时,你可以用msvc构建它,所以为什么不只是省去一些麻烦并构建一个msvc libclang,这将确保使用正确的调用约定和ABI.或者,您可以使用makefile通过msvc使用gcc进行构建.