Pay attention to the calling convention!

Published Monday, December 11, 2006 8:25 PM by mithuns
Filed under: Windows Programming
Ever seen this run-time failure? - " The value of ESP was not properly saved across a function call. This is usually a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention"
Whenever I get sloppy, I tend to write awful code as shown below -

 

FARPROC pDllFunc = NULL;
HRESULT (* pDllGetReqRuntimeVer) (LPWSTR, LPWSTR, DWORD, DWORD*);
HMODULE hModuleMscoree = LoadLibrary(L"mscoree.dll");
 
if (NULL != hModuleMscoree)
{
    pDllFunc = GetProcAddress(hModuleMscoree, "GetRequestedRuntimeVersion");
    if(NULL != pDllFunc)
    {
        pDllGetReqRuntimeVer = (HRESULT(*)(LPWSTR, LPWSTR, DWORD, DWORD*))pDllFunc;
        hr = pDllGetReqRuntimeVer(
                    thrParam->cmdLineSw.pwszImage,
                    wszDebuggeeVer,
                    sizeof(wszDebuggeeVer)/sizeof(wszDebuggeeVer[0]),
                    & dws);
         :
        :
        :
    }
}
When sloppy, I forget to use the correct calling convention while declaring function pointers. Then I run into the above run-time error while calling into various functions exported by dlls (mscoree!GetRequestedRuntimeVersion in above example). Most Win32 APIs are declared as __stdcall which expects the " callee" to cleanup the stack. I compile with /Gd (uses __cdecl as default convention). Because I forgot of mark the function pointer with __stdcall, the run-time is now expecting the " caller" to cleanup the stack. Fortunately I'm bailed out by the above run time check. To correct this the, function pointer must be declared and used as follows -

FARPROC pDllFunc = NULL;
HRESULT (__stdcall * pDllGetReqRuntimeVer) (LPWSTR, LPWSTR, DWORD, DWORD*);
HMODULE hModuleMscoree = LoadLibrary(L"mscoree.dll");
 
if (NULL != hModuleMscoree)
{
    pDllFunc = GetProcAddress(hModuleMscoree, "GetRequestedRuntimeVersion");
    if(NULL != pDllFunc)
    {
        pDllGetReqRuntimeVer = (HRESULT(__stdcall *)(LPWSTR, LPWSTR, DWORD, DWORD*))pDllFunc;
        hr = pDllGetReqRuntimeVer(
                    thrParam->cmdLineSw.pwszImage,
                    wszDebuggeeVer,
                    sizeof(wszDebuggeeVer)/sizeof(wszDebuggeeVer[0]),
                    & dws);
        :
        :
        :
    }
}

 
Given below are the native disassemblies (function prolog and epilog) when you use __cdecl and __stdcall respectively. As you can see, for the function marked as __cdecl, the " caller" is cleaning up the stack. While the " callee" does the needful when __stdcall is used.
When using __cdecl 
00412DB1 mov         esi,esp
00412DB3 lea         eax,[dws]
00412DB9 push        eax 
00412DBA push        80h 
00412DBF lea         ecx,[ebp-168h]
00412DC5 push        ecx 
00412DC6 mov         edx,dword ptr [ebp-48h]
00412DC9 mov         eax,dword ptr [edx+10h]
00412DCC push        eax 
00412DCD call        dword ptr [ebp-61Ch]
00412DD3 add         esp,10h ß----------- stack is cleaned by caller!
00412DD6 cmp         esi,esp
00412DD8 call        @ILT+840(__RTC_CheckEsp) (41134Dh) ß------ The run time check
00412DDD mov         dword ptr [ebp-18h],eax

 

 
When using __stdcall 
00412DB1 mov         esi,esp
00412DB3 lea         eax,[dws]
00412DB9 push        eax 
00412DBA push        80h 
00412DBF lea         ecx,[ebp-168h]
00412DC5 push        ecx 
00412DC6 mov         edx,dword ptr [ebp-48h]
00412DC9 mov         eax,dword ptr [edx+10h]
00412DCC push        eax 
00412DCD call        dword ptr [ebp-61Ch]
00412DD3 cmp         esi,esp ß--------- At this stage callee has already cleaned up stack
00412DD5 call        @ILT+840(__RTC_CheckEsp) (41134Dh) ß------ The run time check
00412DDA mov         dword ptr [ebp-18h],eax
 
I should be paying more attention to the calling conventions and instead of relying on this run-time check. But it is nice to have this safety net.
 
Comments
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值