I am trying to analyze what happening to the extra or less parameters that are supplied to the function pointers which are not of compatible size (Less or more arguments).
Consider the below example
#include <stdio.h>
#include <conio.h>
typedef void (*fp)(int a, int b, int c);
void function1(int a, int b) {
printf("\n Function1: a: %d, b: %d (%d)", a, b, __LINE__);
}
void function2(int a, int b, int c) {
printf("\n Function1: a: %d, b: %d, c: %d (%d)", a, b, c, __LINE__);
}
void function3(int a, int b, int c, int d) {
printf("\n Function1: a: %d, b: %d, c: %d, d: %d (%d)", a, b, c, d, __LINE__);
}
int main() {
fp fp1 = (fp)function1;
fp fp2 = (fp)function2;
fp fp3 = (fp)function3;
fp1(1, 2, 3);
fp2(4, 5, 6);
fp3(7, 8, 9);
getch();
return 0;
}
It can be observed that fp1 & fp3 take different parameter sizes (arguments) than the function pointer. But still i didn't observe any undefined behavior while executing the program.
Output of the program is
Function1: a: 1, b: 2 (7)
Function1: a: 4, b: 5, c: 6 (11)
Function1: a: 7, b: 8, c: 9, d: 4200926 (15)
So, when are calling a function we push the address and the parameters to the stack according to the calling convention and then are popped out. In the above case when we push parameters to the stack and popping out them ,we are popping out one less element which actual has to corrupt the return address (Same in case of fp3), but all the code is executing properly.
In this stack overflow question there is no mismatch in the function arguments. So, i am not able to find answer for my question.
From the wikipedia , it has been mentioned that callee/caller need to cleanup the stack like this
Callee clean-up[edit] When the callee cleans the arguments from the stack it needs to be known at compile time how many bytes the stack needs to be adjusted. Therefore, these calling conventions are not compatible with variable argument lists, e.g. printf(). They may be, however, more space efficient, as the code needed to unwind the stack does not need to be generated for each call. Functions which utilize these conventions are easy to recognize in ASM code because they will unwind the stack prior to returning. The x86 ret instruction allows an optional 16-bit parameter that specifies the number of stack bytes to unwind before returning to the caller. Such code looks like this:
ret 12
But i didn't observe anything like that in the dis-assembly code of the above program. (pasted below for function1)
--------------------------------------------------------------------------------
18 int main() {
0x004013C7 push %ebp
0x004013C8 mov %esp,%ebp
0x004013CA and $0xfffffff0,%esp
0x004013CD sub $0x20,%esp
0x004013D0 call 0x401a00 <__main>
19 fp fp1 = (fp)function1;
0x004013D5 movl $0x401334,0x1c(%esp)
20 fp fp2 = (fp)function2;
0x004013DD movl $0x40135e,0x18(%esp)
21 fp fp3 = (fp)function3;
0x004013E5 movl $0x40138f,0x14(%esp)
23 fp1(1, 2, 3);
0x004013ED movl $0x3,0x8(%esp)
0x004013F5 movl $0x2,0x4(%esp)
0x004013FD movl $0x1,(%esp)
0x00401404 mov 0x1c(%esp),%eax
0x00401408 call *%eax
24 fp2(4, 5, 6);
0x0040140A movl $0x6,0x8(%esp)
0x00401412 movl $0x5,0x4(%esp)
0x0040141A movl $0x4,(%esp)
0x00401421 mov 0x18(%esp),%eax
0x00401425 call *%eax
25 fp3(7, 8, 9);
0x00401427 movl $0x9,0x8(%esp)
0x0040142F movl $0x8,0x4(%esp)
0x00401437 movl $0x7,(%esp)
0x0040143E mov 0x14(%esp),%eax
0x00401442 call *%eax
27 getch();
0x00401444 call 0x401c40 <getch>
29 return 0;
0x00401449 mov $0x0,%eax
30 }
0x0040144E leave
0x0040144F ret
--------------------------------------------------------------------------------
6 void function1(int a, int b) {
0x00401334 push %ebp
0x00401335 mov %esp,%ebp
0x00401337 sub $0x18,%esp
7 printf("\n Function1: a: %d, b: %d (%d)", a, b, __LINE__);
0x0040133A movl $0x7,0xc(%esp)
0x00401342 mov 0xc(%ebp),%eax
0x00401345 mov %eax,0x8(%esp)
0x00401349 mov 0x8(%ebp),%eax
0x0040134C mov %eax,0x4(%esp)
0x00401350 movl $0x403024,(%esp)
0x00401357 call 0x401c78 <printf>
8 }
0x0040135C leave
0x0040135D ret
So, will anyone please guide/help me in finding the answer.
Thanks.