In a dll project, the function is like this:
extern "C" __declspec(dllexport) void foo(const wchar_t* a, const wchar_t* b, const wchar_t* c)
In a different project, I will use foo
function, but I declare foo
function in header file with
extern "C" __declspec(dllimport) void foo(const wchar_t* a, const wchar_t* b)
and I call it with only two parameters.
The result is success, I think it about __cdecl
call, but I would like to know how and why this works.
(1) The variable side is not given to the parameter, but directly accessed by the function which is possible in JavaScript. (2) The function does not return any value but prints the output to the browser console.
Arguments are passed by value; that is, when a function is called, the parameter receives a copy of the argument's value, not its address. This rule applies to all scalar values, structures, and unions passed as arguments. Modifying a parameter does not modify the corresponding argument passed by the function call.
Theoretically you can set these max stack size to 8192 bits. Each variable takes up 32 bits then you could pass 256 parameters. 8192/32 = 256. There is no maximum limit to pass parameters or arguments to a user defined function.
Passing arguments to function is a very important aspect of C++ programming. Arguments refer to values that can be passed to a function. Furthermore, this passing of arguments takes place for the purpose of being used as input information.
Default calling convention is __cdecl
, which means the caller pushes parameters onto the stack right-to-left then cleans up the stack after the call returns.
So in your case, the caller:
At this point the stack looks like this (assume 4 byte pointers for example, and remember the stack pointer travels backwards when you push things):
+-----+ <--- this is where esp is after pushing stuff
| ret | [esp]
+-----+
| a | [esp+4]
+-----+
| b | [esp+8]
+-----+ <--- this is where esp was before we started
| ??? | [esp+12 and beyond]
+-----+
Ok, great. Now the problem happens on the callee side. The callee is expecting parameters to be at certain locations on the stack, so:
a
is assumed to be at [esp+4]
b
is assumed to be at [esp+8]
c
is assumed to be at [esp+12]
And this is where the issue is: We have no idea what's at [esp+12]
. So the callee will see the correct values of a
and b
, but will interpret whatever unknown garbage happens to be at [esp+12]
as c
.
At that point it's pretty much undefined, and depends on what your function actually does with c
.
After all this is over and the callee returns, assuming your program didn't crash, the caller will restore esp
and the stack pointer will be back where it should be. So from the caller's POV everything is probably fine and the stack pointer ends up back where it's supposed to be, but the callee sees junk for c
.
The mechanics on 64-bit machines is different but the end result is roughly the same effect. Microsoft uses the following calling convention on 64-bit machines regardless of __cdecl
or whatever (any convention you specify is ignored and all are treated identically):
rcx
, rdx
, r8
, and r9
, in that order, left-to-right.xmm0
, xmm1
, xmm2
, and xmm3
, in that order, left-to-right.esp
as well as restoring the values of all volatile registers after the call.So in your case, the caller:
a
in rcx
.b
in rdx
.But the callee is expecting:
a
assumed to be in rcx
(check!)b
assumed to be in rdx
(check!)c
assumed to be in r8
(problem)And so, as with the 32-bit case, the callee interprets whatever happened to be in r8
as c
, and potential hijinks ensue, with the end effect depending on what the callee does with c
. When it returns, assuming the program did not crash, the caller restores all volatile registers (rcx
and rdx
, and also generally includes r8
and friends) and restores esp
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With