The following program compiles perfectly with no errors or warnings (even with -Wall) in g++, but crashes immediately.
#include <cstdio>
int stuff(void)
{
puts("hello there.");
return 0;
}
int (*main)(void) = stuff;
This is an (obviously horribly misguided) attempt at running a C++ program without explicitly declaring main as a function. It was my intention for the program to execute stuff by binding it to the symbol main. I was very surprised that this compiled, but why exactly does it fail, having compiled? I've looked at the generated assembly but I don't know enough to understand it at all.
I'm fully aware that there are plenty of restrictions on how main can be defined/used, but I'm unclear on how my program breaks any of them. I haven't overloaded main or called it within my program... so exactly what rule am I breaking by defining main this way?
Note: this was not something I was trying to do in actual code. It was actually the beginnings of an attempt to write Haskell in C++.
In the code that runs before main, there is something like:
extern "C" int main(int argc, char **argv);
The problem with your code is that if you have a function pointer called main, it is not a the same as a function (as opposed to Haskell where a function and a funciton pointer is pretty much interchangable - at least with my 0.1% knowledge of Haskell).
Whilst the compiler will happily accept:
int (*func)() = ...;
int x = func();
as a valid call to the function pointer func. However, when the compiler generates code to call func, it actually does this in a different way [although the standard doesn't say how this should be done, and it varies on different processor architectures, in practice it loads the value in the pointer variable, and then calls this content].
When you have:
int func() { ... }
int x = func();
the call to func just refers to the address of func itself, and calls that.
So, assuming your code actually does compile, the startup code before main will call the address of your variable main rather than indirectly reading the value in main and then calling that. In modern systems, this will cause a segfault because main lives in the data segment which is not executable, but in older OS's it would most likely crash due to main does not contain real code (but it may execute a few instructions before it falls over in this case - in the dim and distant past, I've accidentally run all sorts of "rubbish" with rather difficult to discover causes...)
But since main is a "special" function, it's also possible that the compiler says "No, you can't do this".
It used to work, many years ago to do this:
char main[] = { 0xXX, 0xYY, 0xZZ ... };
but again, this doesn't work in a modern OS, because main ends up in the data section, and it's not executable in that section.
Edit: After actually testing the posted code, at least on my 64-bit Linux, the code actually compiles, but crashes, unsurprisingly, when it tries to execute main.
Running in GDB gives this:
Program received signal SIGSEGV, Segmentation fault.
0x0000000000600950 in main ()
(gdb) bt
#0 0x0000000000600950 in main ()
(gdb) disass
Dump of assembler code for function main:
=> 0x0000000000600950 <+0>: and %al,0x40(%rip) # 0x600996
0x0000000000600956 <+6>: add %al,(%rax)
End of assembler dump.
(gdb) disass stuff
Dump of assembler code for function stuff():
0x0000000000400520 <+0>: push %rbp
0x0000000000400521 <+1>: mov %rsp,%rbp
0x0000000000400524 <+4>: sub $0x10,%rsp
0x0000000000400528 <+8>: lea 0x400648,%rdi
0x0000000000400530 <+16>: callq 0x400410 <puts#plt>
0x0000000000400535 <+21>: mov $0x0,%ecx
0x000000000040053a <+26>: mov %eax,-0x4(%rbp)
0x000000000040053d <+29>: mov %ecx,%eax
0x000000000040053f <+31>: add $0x10,%rsp
0x0000000000400543 <+35>: pop %rbp
0x0000000000400544 <+36>: retq
End of assembler dump.
(gdb) x main
0x400520 <stuff()>: 0xe5894855
(gdb) p main
$1 = (int (*)(void)) 0x400520 <stuff()>
(gdb)
So, we can see that main is not really a function, it's a variable which contains a pointer to stuff. The startup code calls main as if it was a function, but it fails to execute the instructions there (because it's data, and data has the "no execute" bit set - not that you can see that here, but I know it works that way).
Edit2:
Inspecting dmesg shows:
a.out[7035]: segfault at 600950 ip 0000000000600950 sp 00007fff4e7cb928 error 15 in a.out[600000+1000]
In other words, the segmentation fault happens immediately with the execution of main - because it's not executable.
Edit3:
Ok, so it's slightly more convoluted than that (at least in my C runtime library), as the code that calls main is a function that takes the pointer to main as an argument, and calls it through a pointer. This however doesn't change the fact that when the compiler builds the code, it produces a level of indirection less than it needs, and tries to execute the variable called main rather than the function that the variable is pointing at.
Listing __libc_start_main in GDB:
87 STATIC int
88 LIBC_START_MAIN (int (*main) (int, char **, char ** MAIN_AUXVEC_DECL),
89 int argc, char *__unbounded *__unbounded ubp_av,
90 #ifdef LIBC_START_MAIN_AUXVEC_ARG
91 ElfW(auxv_t) *__unbounded auxvec,
92 #endif
At this point, printing main gives us a function pointer that points at 0x600950, which is the variable called main (same as what I dissassembled above)
(gdb) p main
$1 = (int (*)(int, char **, char **)) 0x600950 <main>
Note that this is a different variable main than the one called main in the source posted in the question.
There's nothing special here about it being main(). The same will happen if you do this for any function. Consider this example:
file1.cpp:
#include <cstdio>
void stuff(void)
{
puts("hello there.");
}
void (*func)(void) = stuff;
file2.cpp:
extern "C" {void func(void);}
int main(int argc, char**argv)
{
func();
}
This will also compile, and then segfault. It is essentially doing the same thing for the function func, but because the coding is explicit it now more apparently looks wrong. main() is a plain C type function with no name mangling, and just appears as a name in the symbol table. If you make it something other than a function, you get a segfault when it executes a pointer.
I guess the interesting part is that the compiler will allow you to define a symbol called main when it is already implicitly declared with a different type.
Related
I learnt that constexpr functions are evaluated at compile time. But look at this example:
constexpr int fac(int n)
{
return (n>1) ? n*fac(n-1) : 1;
}
int main()
{
const int a = 500000;
cout << fac(a);
return 0;
}
Apparently this code would throw an error, but since constexpr functions are evaluated at compiling time, why I see no error when compile and link?
Further on, I disassembled this code, and it turned out this function isn't evaluated but rather called as a normal function:
(gdb) x/10i $pc
=> 0x80007ca <main()>: sub $0x8,%rsp
0x80007ce <main()+4>: mov $0x7a11f,%edi
0x80007d3 <main()+9>: callq 0x8000823 <fac(int)>
0x80007d8 <main()+14>: imul $0x7a120,%eax,%esi
0x80007de <main()+20>: lea 0x20083b(%rip),%rdi # 0x8201020 <_ZSt4cout##GLIBCXX_3.4>
0x80007e5 <main()+27>: callq 0x80006a0 <_ZNSolsEi#plt>
0x80007ea <main()+32>: mov $0x0,%eax
0x80007ef <main()+37>: add $0x8,%rsp
0x80007f3 <main()+41>: retq
However, if I call like fac(5):
constexpr int fac(int n)
{
return (n>1) ? n*fac(n-1) : 1;
}
int main()
{
const int a = 5;
cout << fac(a);
return 0;
}
The assemble code turned into:
(gdb) x/10i $pc
=> 0x80007ca <main()>: sub $0x8,%rsp
0x80007ce <main()+4>: mov $0x78,%esi
0x80007d3 <main()+9>: lea 0x200846(%rip),%rdi # 0x8201020 <_ZSt4cout##GLIBCXX_3.4>
0x80007da <main()+16>: callq 0x80006a0 <_ZNSolsEi#plt>
0x80007df <main()+21>: mov $0x0,%eax
0x80007e4 <main()+26>: add $0x8,%rsp
0x80007e8 <main()+30>: retq
The fac function is evaluated at compile time.
Can Anyone explain this?
Compiling command:
g++ -Wall test.cpp -g -O1 -o test
And with g++ version 7.4.0, gdb version 8.1.0
I learnt that constexpr functions are evaluated at compile time
No, constexpr can be evaluated at compile time, but also at runtime.
further reading:
Difference between `constexpr` and `const`
https://en.cppreference.com/w/cpp/language/constexpr
Purpose of constexpr
Apparently this code would throw an error
No, no errors thrown. For large input, the result will overflow which is undefined behavior. This doesn't mean an error will be thrown or displayed. It means anything can happen. And when I say anything, I do mean anything. The program can crash, hang, appear to work with a strange results, display weird characters, or literally anything.
further reading:
https://en.cppreference.com/w/cpp/language/ub
https://en.wikipedia.org/wiki/Undefined_behavior
http://blog.llvm.org/2011/05/what-every-c-programmer-should-know.html
And, as pointed out by nathanoliver
when invoked in a constant expression, a constexpr function must check and error out on UB http://coliru.stacked-crooked.com/a/43ccf2039dc511d5
In other words there can't be any UB at compile time. What at runtime would be UB, at compile time it's a hard error.
constexpr it means that it can be evaluated at compile time, not that it will be evaluated at compile time. The compiler will be forced to do the evaluation compile-time if you use it where a compile time constant is expected (e.g. the size of an array).
On the other hand for small values g++ is for example smart enough to compute the result compile time (even without constexpr).
For example with:
int fact(int n) {
return n < 2 ? 1 : n*fact(n-1);
}
int bar() {
return fact(5);
}
the code generated by g++ -O3 for bar is:
bar():
mov eax, 120
ret
Note that overflowing the call stack (e.g. infinite or excessive recursion) or even overflowing signed integer arithmetic is in C++ undefined behavior and anything can happen. It doesn't mean you'll get a nice "error" or even a segfault... but that ANYTHING can happen (including, unfortunately, nothing evident). Basically it means that the authors of compilers can just ignore to handle those cases because you're not supposed to do this kind of mistakes.
I have the following code. There is a function that takes two int32. Then I take a pointer to it and cast it to a function that takes three int8 and call it. I expected a runtime error but program works fine. Why this even possible?
main.cpp:
#include <iostream>
using namespace std;
void f(int32_t a, int32_t b) {
cout << a << " " << b << endl;
}
int main() {
cout << typeid(&f).name() << endl;
auto g = reinterpret_cast<void(*)(int8_t, int8_t, int8_t)>(&f);
cout << typeid(g).name() << endl;
g(10, 20, 30);
return 0;
}
Output:
PFviiE
PFvaaaE
10 20
As I can see the signature of the first function requires two ints and the second function requires three chars. Char is smaller than int and I wondered why a and b are still equals to 10 and 20.
As others have pointed out, this is undefined behavior, so all bets are off about what may in principle happen. But assuming that you're on an x86 machine, there's a plausible explanation as to why you're seeing this.
On x86, the g++ compiler doesn't always pass arguments by pushing them onto the stack. Instead, it stashes the first few arguments into registers. If we disassemble the f function, notice that the first few instructions move the arguments out of registers and explicitly onto the stack:
push rbp
mov rbp, rsp
sub rsp, 16
mov DWORD PTR [rbp-4], edi # <--- Here
mov DWORD PTR [rbp-8], esi # <--- Here
# (many lines skipped)
Similarly, notice how the call is generated in main. The arguments are placed into those registers:
mov rax, QWORD PTR [rbp-8]
mov edx, 30 # <--- Here
mov esi, 20 # <--- Here
mov edi, 10 # <--- Here
call rax
Since the entire register is being used to hold the arguments, the size of the arguments isn't relevant here.
Moreover, because these arguments are being passed via registers, there's no concern about resizing the stack in an incorrect way. Some calling conventions (cdecl) leave the caller to do cleanup, while others (stdcall) ask the callee to do cleanup. However, neither really matters here, because the stack isn't touched.
As others have pointed out, it's probably undefined behavior, but old school C programmers know this type of thing to work.
Also, because I can sense the language lawyers drafting their litigation documents and court petitions for what I'm about to say, I'm going to cast a spell of undefined behavior discussion. It's cast by saying undefined behavior three times while tapping my shoes together. And that makes the language lawyers disappear so I can explain why weird things just happen to work without getting sued.
Back to my answer:
Everything I discuss below is compiler specific behavior. All of my simulations are with Visual Studio compiled as 32-bit x86 code. I suspect it will work the same with gcc and g++ on a similar 32-bit architecture.
Here's why your code just happens to work and some caveats.
When function call arguments get pushed onto the stack, they get pushed in reverse order. When f is invoked normally, the compiler generates code to push the b argument onto the stack before the a argument. This helps facilitate variadic argument functions such as printf. So when your function, f is accessing a and b, it's just accessing arguments at the top of the stack. When invoked through g, there was an extra argument pushed to the stack (30), but it got pushed first. 20 was pushed next, followed by 10 which is at the top of the stack. f is only looking at the top two arguments on the stack.
IIRC, at least in classic ANSI C, chars and shorts, always get promoted to int before being placed on the stack. That's why, when you invoked it with g, the literals 10 and 20 get placed on the stack as full sized ints instead of 8-bit ints. However, the moment you redefine f to take 64-bit longs instead of 32-bit ints, the output of your program changes.
void f(int64_t a, int64_t b) {
cout << a << " " << b << endl;
}
Results in this getting output by your main (with my compiler)
85899345930 48435561672736798
And if you convert to hex:
140000000a effaf00000001e
14 is 20 and 0A is 10. And I suspect that 1e is your 30 getting pushed to the stack. So the arguments got pushed to the stack when invoked through g, but were munged up in some compiler specific way. (undefined behavior again, but you can see the arguments got pushed).
When you invoke a function, the usual behavior is that the calling code will fix up the stack pointer upon return from a called function. Again, this is for the sake of variadic functions and other legacy reasons for compat with K&R C. printf has no idea how many arguments you actually passed to it, and it relies on the caller to fix the stack when it returns. So when you invoked through g, the compiler generated code to push 3 integers to the stack, invoke the function, and then code to pop those same values off. The moment, you change your compiler option to have the callee clean up the stack (ala __stdcall on Visual Studio):
void __stdcall f(int32_t a, int32_t b) {
cout << a << " " << b << endl;
}
Now you are clearly in undefined behavior territory. Invoking through g pushed three int arguments onto the stack, but the compiler only generated code for f to pop two int arguments off the stack when it returns. The stack pointer is corrupted upon return.
As other have pointed out, it is entirely undefined behaviour, and what you get will depend on the compiler. It will work only if you have a specific call convention, that doesn't use the stack but registers to pass the parameters.
I used Godbolt to see the assembly generated, that you can check in full here
The relevant function call is here:
mov edi, 10
mov esi, 20
mov edx, 30
call f(int, int) #clang totally knows you're calling f by the way
It doesn't push parameters on the stack, it simply puts them in registers. What is most interesting is that the mov instruction doesn't change just the lower 8 bits of the register, but all of them as it is a 32-bit move. This also means that no matter what was in the register before, you will always get the right value when you read 32 bits back as f does.
If you wonder why the 32-bit move, it turns out that in almost every case, on a x86 or AMD64 architecture, compilers will always use either 32 bit literal moves or 64 bit literal moves (if and only if the value is too big for 32 bits). Moving a 8 bit value doesn't zero out the upper bits (8-31) of the register, and it can create problems if the value would end up being promoted. Using a 32-bit literal instruction is more simple than having one additional instruction to zero out the register first.
One thing you have to remember though is it is really trying to call f as if it had 8 bits parameters, so if you put a large value it will truncate the literal. For example, 1000 will become -24, as the lower bits of 1000 are E8, which is -24 when using signed integers. You will also get a warning
<source>:13:7: warning: implicit conversion from 'int' to 'signed char' changes value from 1000 to -24 [-Wconstant-conversion]
The first C compiler, as well as most compilers that preceded the publication of the C Standard, would process a function call by pushing arguments in right-to-left order, use the platform's "call subroutine" instruction to invoke the function, and then then after the function returned, pop whatever arguments were pushed. Functions would assign addresses to their arguments in sequential order starting just past whatever information had been pushed by the "call" instruction.
Even on platforms such as the Classic Macintosh where responsibility for popping arguments would normally lie with the called function (and where failing to push the right number of arguments would often corrupt the stack), C compilers typically used a calling convention that behaved like the first C compiler. A "pascal" qualifier was needed when calling, or on functions that were called by, code written in other languages (such as Pascal).
In most implementations of the language that existed before the Standard, one could write a function:
int foo(x,y) int x,y
{
printf("Hey\n");
if (x)
{ y+=x; printf("y=%d\n", y); }
}
and invoke it as e.g. foo(0) or foo(0,0), with the former being slightly faster. Attempting to call it as e.g. foo(1); would likely corrupt the stack, but if the function never used object y there was no need to pass it. Supporting such semantics would not have been practical on all platforms, however, and in most cases the benefits of argument validation outweigh the cost, so the Standard does not require that implementations be capable of supporting that pattern, but allows those that can support the pattern conveniently to extend the language by doing so.
So as you know in C and C++ if using Visual-C you can have in line assembly instructions such as:
int main() {
printf("Hello\n");
__asm int 3
printf("this will not be printed.\n");
return 0;
}
Which will make a breakpoint inside of the executable. So my question is, is there somekind of function I can use to call __asm using a variable such as a char array. I was thinking something like this:
char instruction[100] = "int 3";
__asm instruction
But that doesn't seem to really work since it gives 'Invalid OP code'. So can you help with this or it isn't possible at all.
Neither C nor C++ are interpreted languages, the compiler generates the int 3 machine instruction at compile time. The compiled program will not recognise the string as an instruction at run-time - unless your program is itself an interpreter.
You can of course use a macro:
#define BREAKPOINT __asm int 3
int main()
{
printf("Hello\n");
BREAKPOINT ;
printf("this will not be printed.\n");
return 0;
}
The code of your program is created by the compiler, during compilation.
You are trying to feed the compiler the input for that at run time - when the program is already executing. If you want ‘on-the-fly-compilation’, you will have to program an incremental compiler and linker that can modify the code while executing it.
Note that even if you would be successful, many OS would block such execution, as it violates security. It would be a great way to build viruses, and is therefore typically blocked.
I'm writing an RPC library for AVR and need to pass a function address to some inline assembler code and call the function from within the assembler code. However the assembler complains when I try to call the function directly.
This minimal example test.cpp illustrates the issue (in the actual case I'm passing args and the function is an instantiation of a static member of templated class):
void bar () {
return;
}
void foo() {
asm volatile (
"call %0" "\n"
:
: "p" (bar)
);
}
Compiling with avr-gcc -S test.cpp -o test.S -mmcu=atmega328p works fine but when I try to assemble with avr-gcc -c test.S -o test.o -mmcu=atmega328p avr-as complains:
test.c: Assembler messages:
test.c:38: Error: garbage at end of line
I have no idea why it writes "test.c", the file it is referring to is test.S, which contains this on line 38:
call gs(_Z3barv)
I have tried all even remotely sensible constraints on the paramter to the inline assembler that I could find here but none of those I tried worked.
I imagine if the gs() part was removed, everything should work, but all constraints seem to add it. I have no idea what it does.
The odd thing is that doing an indirect call like this assembles just fine:
void bar () {
return;
}
void foo() {
asm volatile (
"ldi r30, lo8(%0)" "\n"
"ldi r31, hi8(%0)" "\n"
"icall" "\n"
:
: "p" (bar)
);
}
The assembler produced looks like this:
ldi r30, lo8(gs(_Z3barv))
ldi r31, hi8(gs(_Z3barv))
icall
And avr-as doesn't complain about any garbage.
There are several issues with the code:
Issue 1: Wrong Constraint
The correct constraint for a call target is "i", thus known at link-time.
Issue 2: Wrong % print-modifier
In order to print an address suitable for a call, use %x which will print a plain symbol without gs(). Generating a linker stub at this place by means of gs() is not valid syntax, hence "garbage at end of line". Apart from that, as you are calling bar directly, there is no need for linker stub (at least not for this kind of symbol usage).
Issue 3: call instruction might not be available
To factor out whether a device supports call or just rcall, there is %~ which prints a single r if just rcall is available, and nothing if call is available.
Issue 4: The Call might clobber Registers or have other Side-Effects
It's unlikely that the call has no effects on registers or on memory whatsoever. If you description of the inline asm does not match some side-effects of the code, it's likely that you will get wrong code sooner or later.
Taking it all together
Let's assume you have a function bar written in assembly that takes two 16-bit operands in R22 and R26, and computes a result in R22. This function does not obey the avr-gcc C/C++ calling convention, so inline assembly is one way to interface to such a function. For bar we cannot write a correct prototype anyways, so we just provide a prototype so that we can use symbol bar. Register X has constraint "x", but R22 has no own register constraint, and therefore we have to use a local asm register:
extern "C" void bar (...);
int call_bar (int x, int y)
{
register int r22 __asm ("r22") = x;
__asm ("%~call %x2"
: "+r" (r22)
: "x" (y), "i" (bar));
return r22;
}
Generated code for ATmega32 + optimization:
_Z8call_barii:
movw r26,r22
movw r22,r24
call bar
movw r24,r22
ret
So what's that "generate stub" gs() thing?
Suppose the C/C++ code is taking the address of a function. The only sensible thing to do with it is to call that function, which will be an indirect call in general. Now an indirect call can target 64KiW = 128KiB at most, so that on devices with > 128KiB of code memory, special means must be taken to indirectly call a function beyond the 128KiB boundary. The AVR hardware features an SFR named EIND for that purpose, but problems using it are obvious. You'd have to set it prior to a call and then reset it somehow somewhere; all evil things would be necessary.
avr-gcc takes a different approach: For each such address taken, the compiler generates gs(func). This will just resolve to func if the address is in the 128KiB range. If not, gs() resolves to an address in section .trampolines which is located close to the beginning of flash, i.e. in the lower 128KiB. .trampolines containts a list of direct JMPs to targets beyond the 128KiB range.
Take for example the following C code:
extern int far_func (void);
int main (void)
{
int (*pfunc)(void) = far_func;
__asm ("" : "+r" (pfunc)); /* Forget content of pfunc. */
return pfunc();
}
The __asm is used to keep the compiler from optimizing the indirect call to a direct one. Then run
> avr-gcc main.c -o main.elf -mmcu=atmega2560 -save-temps -Os -Wl,--defsym,far_func=0x24680
> avr-objdump -d main.elf > main.lst
For the matter of brevity, we just define symbol far_func per command line.
The assembly dump in main.s shows that far_func might require a linker stub:
main:
ldi r30,lo8(gs(far_func))
ldi r31,hi8(gs(far_func))
eijmp
The final executable listing in main.lst then shows that the stub is actually generated and used:
main.elf: file format elf32-avr
Disassembly of section .text:
...
000000e4 <__trampolines_start>:
e4: 0d 94 40 23 jmp 0x24680 ; 0x24680 <far_func>
...
00000104 <main>:
104: e2 e7 ldi r30, 0x72 ; 114
106: f0 e0 ldi r31, 0x00 ; 0
108: 19 94 eijmp
main loads Z=0x0072 which is a word address for byte address 0x00e4, i.e. the code is indirectly jumping to 0x00e4, and from there it jumps directly to 0x24680.
Note that call requires a constant, known-at-link-time value. The "p" constraint does not include that semantics; it would also allow a pointer from a variable (e.g. char* x), which call cannot handle. (I seem to remember that sometimes gcc is clever enough to optimize in such a way that "p" does work here - but that's basically undocumented behavior and non-deterministic, so better not count on it.)
If the function you're calling actually is compile-time constant you can use "i" (bar). If it's not, then you have no other choice than using icall as you already figured out.
Btw, the AVR section of https://gcc.gnu.org/onlinedocs/gcc/Machine-Constraints.html#Machine-Constraints documents some more, AVR-specific constraints.
I've tries various ways of passing a C function name to inline ASM code without success. However I did find a workaround, which seems to provide the desired result.
Answer to the question:
As explained on https://www.nongnu.org/avr-libc/user-manual/inline_asm.html you can assign a ASM name to a C function in a prototype declaration:
void bar (void) asm ("ASM_BAR"); // any name possible here
void bar (void)
{
return;
}
Then you can call the function easily from your ASM code:
asm volatile("call ASM_BAR");
Use with library functions:
This approach does not work with library functions, because they have their own prototype declarations. To call a function like system_tick() of the time.h library more efficiently from an ISR, you can declare a helper function. Unfortunately GCC does not apply the inline setting to calls from ASM code.
inline void asm_system_tick(void) asm ("ASM_SYSTEM_TICK") __attribute__((always_inline));
void asm_system_tick(void)
{
system_tick();
}
In the following example GCC does only generate push/ pop instructions for the surrounding code, not for the function call! Note that system_tick() is specifically designed for ISR_NAKED and does all required stack operations on its own.
volatile uint8_t tick = 0;
ISR(TIMER2_OVF_vect)
{
tick++;
if (tick > 127)
{
tick = 0;
asm volatile ("call ASM_SYSTEM_TICK");
}
}
Because the inline attribute does not work, each function call takes 8 additional cpu cycles. Compared to 5632 CPU cycles required for push/ pull operations with a normal function call (44 CPU cycles for each run of the ISR) it is still a very impressive improvement.
I'm writing some inline functions for fun and it throws an exception I have never encountered before. The funny thing is, that if I continue, after the exception just stopped the flow of execution of my program, it will return the sum of two integers.
__declspec(dllexport) int addintegers(int one, int two)
{
int answer = 0;
__asm
{
mov eax, 0
push two
push one
call add
mov answer, eax
}
return answer;
} // Debugger stops here with exception message
Exception Message:
Run-Time Check Failure #0 - 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.
// add function definition
int add(int one, int two)
{
return one + two;
}
I don't know much about assembler, and you don't show us the declaration of add(), but if it adheres to C's calling convention you have to pop the arguments from the stack after the call returned to the caller.
Requiring the caller to clean up the stack, rather than the callee, is what allows C to have functions with a variable number of arguments, like printf().