Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does using the wrong calling convention sometimes work?

I used "StartServiceCtrlDispatcher" function to register a callback function (called ServiceMain) in windows, but the callback function I declared got compiled with the wrong calling convention.

The thing is that on some computers, when the application returned from the callback function, the application crashed, but on other computers the application did not crash.

Now, once I found the bug everything worked, but I just don't understand why on some computers it worked correctly without crashing ?

Thanks! :-)

like image 912
TCS Avatar asked Aug 10 '11 22:08

TCS


People also ask

Why are calling conventions important?

The differences in calling conventions is very important to understand because mismatches can be disastrous. If you have a callee that is cleaning up the stack, and a caller that is also cleaning up the stack, then you've stomped the stack by cleaning it up twice!

What are different calling conventions?

Microsoft x64 calling convention That means RCX, RDX, R8, R9 for integer, struct or pointer arguments (in that order), and XMM0, XMM1, XMM2, XMM3 for floating point arguments. Additional arguments are pushed onto the stack (right to left). Integer return values (similar to x86) are returned in RAX if 64 bits or less.

What is the default calling convention?

__cdecl is the default calling convention for C and C++ programs. Because the stack is cleaned up by the caller, it can do vararg functions. The __cdecl calling convention creates larger executables than __stdcall, because it requires each function call to include stack cleanup code.

Which calling convention does c follow?

stdcall - The stdcall[4] calling convention is a variation on the Pascal calling convention in which the callee is responsible for cleaning up the stack, but the parameters are pushed onto the stack in right-to-left order, as in the _cdecl calling convention.


2 Answers

This is all very Windows-specific, we're not talking standard C++ here.

Checking out the documentation of StartServiceDispatcher it has only one argument, and is declared as WINAPI which in turn means __stcall calling convention.

For freestanding functions, __stdcall is one of two main calling conventions. The other one is __cdecl. The machine code level difference is simply who restores the stack pointer: with __stdcall it is the function itself, while with __cdecl it is the calling code.

When the function actually is __stdcall but is invoked as if it was __cdecl, the situation is that there are two attempts to restore the stack pointer: one at the exit from the function, and one in the calling code. The one in the function will succeed. Depending on how the attempt in the calling code is done, it can mess things up thoroughly (e.g. if just adding the required offset, treating the stack pointer as relative), or it may have no harmful effect. But it's very likely to create a mess, since the assumption about the stack pointer value on return from the function, is incorrect.

When the function actually is __cdecl it will not itself restore the stack pointer, since that is the calling code's responsibility. And if the calling code is treating it as __stdcall then the calling code won't restore it either, since from the calling code's view the function is doing that. The result, if you don't get an early crash (because of broken assumptions), should then be that repeated calls, say in a loop, will eat stack space.

It's all very much Undefined Behavior.

And one property of Undefined Behavior is that it can do anything, including apparently working…

Cheers & hth.,

like image 124
Cheers and hth. - Alf Avatar answered Nov 15 '22 00:11

Cheers and hth. - Alf


Calling conventions differ in the details, like which registers are preserved. If you happened to not store anything you still needed in those registers, then it didn't matter that they were erased when they didn't have to be. Similarly, if your calling convention differs about how it deals with return values, if you don't return anything, then it doesn't matter.

Fortunately, x64 only has one calling convention and this whole mess will be in the past.

like image 35
Puppy Avatar answered Nov 15 '22 00:11

Puppy