/* An explanation of how structs get passed by value in GCC This behavior is entirely compiler dependant since the C standard doesn't define how this should be done...so everybody just does it their own crazy way. This demo was done on linux/x86 with gcc 2.95.3 and gcc 3.3.5 See end of code for sample runs. satebackire */ #include struct foo { int a; unsigned int b; unsigned int c; } __attribute__((packed)); struct foo func(struct foo in); int main(){ struct foo struct1; struct foo struct2; //each of these is declared as normal 12 bytes //plus any padding for each printf("struct1 %p \n", &struct1); printf("struct2 %p \n", &struct2); struct1.a = 1; struct1.b = 2; struct1.c = 3; struct2 = func(struct1); //func() //when the parameters are pushed to func() a "hidden" //parameter is pushed on the stack as the last push before //call. This value is a pointer to the memory location of struct2 // //struct2's value assignment is never actually done in main() //the copying takes place during the return of //the called function func() printf("main] a=%d b=%d\n", struct2.a, struct2.b); return 0; } struct foo func(struct foo in){ struct foo out; int *p = (int *)&out; out.a = 0xa; out.b = 0xb; out.c = 0xc; while( p < (int *)&in ){ printf("%p = 0x%x\n", p, *p); p++;} out.a = out.b = out.c = 0; //clear all of out's struct members printf("func] a=%d b=%d\n", in.a, in.b); in.a = 69; //"in" was passed by value so "struct1" in main() in.b = 311; //will NOT be modifed by these assignments // &in is the actual address of where the struct members begin // this way we can copy structs w/o overwriting the pointer to the output struct memcpy( &out, &in, sizeof(out)); return out; //at this return something interesting happens, //the hidden parameter pointer is used as //a starting point for an inline memcpy (a sequence of 3 MOVs) //this copies "out" to the memory location of //"struct2" which was allocated in main's stack frame. //A parameter is passed to the RET instruction to clean up //the hidden parameter so that the calling function does not //have to do it...this looks like "RET 0x4" } /* gcc 3.3 $./struct struct1 0xbfffee70 struct2 0xbfffee60 0xbfffee20 = 0xa 0xbfffee24 = 0xb 0xbfffee28 = 0xc 0xbfffee2c = 0x8048681 //a couple padding values (gcc 3.x and above pads 0xbfffee30 = 0xbfffee48 //for alignment and to stop off-by-ones) 0xbfffee34 = 0x40137ff4 0xbfffee38 = 0xbfffee88 //sEBP 0xbfffee3c = 0x8048511 //sEIP 0xbfffee40 = 0xbfffee60 //hidden parameter i.e. location of struct2 func] a=1 b=2 main] a=69 b=311 */ /* gcc 2.9.5 $ ./struct struct1 0x5ffff3f0 struct2 0x5ffff3e4 0x5ffff3b0 = 0xa 0x5ffff3b4 = 0xb 0x5ffff3b8 = 0xc 0x5ffff3bc = 0x5ffff3fc 0x5ffff3c0 = 0x8048544 0x5ffff3c4 = 0x5ffff3e4 func] a=1 b=2 main] a=69 b=311 */