flat assembler
Message board for the users of flat assembler.
Index
> Linux > Question about AMD64 function calling procedure |
Author |
|
mattst88 25 Apr 2007, 22:56
When calling printf, I have to push something onto the stack before the call, and pop it off after the call otherwise the program will segfault. I've read the latest x86-64 ABI, but I didn't see any information related to the need to do this.
For instance Code: format ELF64 public main extrn printf section '.text' executable main: mov edi,msg mov esi,4 xor eax,eax push r15 call printf pop r15 ret section '.data' writable msg db "Testing 123%d",0xA,0 If the push and pop are removed the resulting program will segfault. When calling puts however, the push and pop are not required. How is this? The only thing I can figure is that is has something to do with the three dots in the declaration of printf. Code: int printf ( const char * format, ... ); Can anyone explain to me what the stack is used for here? Is there any specific register I should be pushing before the call? _________________ My x86 Instruction Reference -- includes SSE, SSE2, SSE3, SSSE3, SSE4 instructions. Assembly Programmer's Journal |
|||
25 Apr 2007, 22:56 |
|
LocoDelAssembly 26 Apr 2007, 03:28
I'm wrong and I can't believe that varagrs are handled this way :S
Here the C code tested Code: #include <stdio.h> int main(){ printf("testing 123%d\n", 4); return 0; } And here that code dissasembled: Code: 0000000000400478 <main>: 400478: 55 push %rbp 400479: 48 89 e5 mov %rsp,%rbp 40047c: be 04 00 00 00 mov $0x4,%esi 400481: bf 6c 05 40 00 mov $0x40056c,%edi 400486: b8 00 00 00 00 mov $0x0,%eax 40048b: e8 20 ff ff ff callq 4003b0 <printf@plt> 400490: b8 00 00 00 00 mov $0x0,%eax 400495: c9 leaveq 400496: c3 retq I can't remember well now but I'm pretty sure that the stack for the callee should be aligned to 16 bytes, since a PUSH/CALL does a "RSP+=16" and the caller of main surely aligned the stack before calling it, then that could be the reason of your sigfault, printf is relying on that convention but you are providing a stack that is just aligned to 8 bytes when it needs 16 bytes according to the ABI. |
|||
26 Apr 2007, 03:28 |
|
mattst88 26 Apr 2007, 16:25
Thanks for your responses. You seem to have found it.
1) printf (and fprintf, sprintf, scanf, etc) take a variable number of arguments 2) According to the x86-64 ABI arguments that cannot fit into registers are put on the stack 3) printf (and others) expect rsp to be aligned to a 16 byte boundary 4) Initially, rsp is only aligned to an 8 byte boundary Therefore, calling printf when rsp is not aligned to a 16 byte boundary will cause a segfault. Push will align to a 16 byte boundary so it will fix it. Better yet, just subtract 8 from rsp at the beginning of the program and add 8 to rsp right before the program ends and call printf as many times as needed in between. Glad I understand this now. Thanks |
|||
26 Apr 2007, 16:25 |
|
LocoDelAssembly 26 Apr 2007, 16:44
No, initially RSP is aligned to 16 bytes, the caller of main (added by the linker) do that. The problem is that CALL counts as a pseudo-push so, RSP+8 mod 16 <> 0. Note that the dissasembled code above doesn't need to adjust the stack before calling because it's implicity done by the "push rbp" which adds 8 to RSP, later the "call printf" adds 8 again making RSP multiple of 16 again. You have to assure that before the execution of any of the instructions of the callee RSP is multiple of 16.
Code: call function . . . function: ; At exactly this point RSP must be multiple of 16 . . . retq You know that at "main:" line the stack is dqword aligned, you only have to assure that the callees of main will enjoy this property too. PS: Look at figure 3.3 of the ABI doc. [edit]Here another example Code: #include <stdio.h> int main(){ printf("testing %d%d%d%d%d%d\n", 1, 2, 3, 4, 5, 6); return 0; } Code: 0000000000400478 <main>: 400478: 55 push %rbp 400479: 48 89 e5 mov %rsp,%rbp 40047c: 48 83 ec 10 sub $0x10,%rsp ; Reserved 16 bytes even when only 8 bytes is needed. 400480: c7 04 24 06 00 00 00 movl $0x6,(%rsp) 400487: 41 b9 05 00 00 00 mov $0x5,%r9d 40048d: 41 b8 04 00 00 00 mov $0x4,%r8d 400493: b9 03 00 00 00 mov $0x3,%ecx 400498: ba 02 00 00 00 mov $0x2,%edx 40049d: be 01 00 00 00 mov $0x1,%esi 4004a2: bf 8c 05 40 00 mov $0x40058c,%edi 4004a7: b8 00 00 00 00 mov $0x0,%eax 4004ac: e8 ff fe ff ff callq 4003b0 <printf@plt> 4004b1: b8 00 00 00 00 mov $0x0,%eax 4004b6: c9 leaveq 4004b7: c3 retq And if we add a 7th vararg parameter to prinft Code: 0000000000400478 <main>: 400478: 55 push %rbp 400479: 48 89 e5 mov %rsp,%rbp 40047c: 48 83 ec 10 sub $0x10,%rsp ; Now we really need 16 bytes 400480: c7 44 24 08 07 00 00 movl $0x7,0x8(%rsp) 400487: 00 400488: c7 04 24 06 00 00 00 movl $0x6,(%rsp) 40048f: 41 b9 05 00 00 00 mov $0x5,%r9d 400495: 41 b8 04 00 00 00 mov $0x4,%r8d 40049b: b9 03 00 00 00 mov $0x3,%ecx 4004a0: ba 02 00 00 00 mov $0x2,%edx 4004a5: be 01 00 00 00 mov $0x1,%esi 4004aa: bf 8c 05 40 00 mov $0x40058c,%edi 4004af: b8 00 00 00 00 mov $0x0,%eax 4004b4: e8 f7 fe ff ff callq 4003b0 <printf@plt> 4004b9: b8 00 00 00 00 mov $0x0,%eax 4004be: c9 leaveq 4004bf: c3 retq This is just to demostrate that 16 bytes aligment is required, I know that you understood already [/edit] |
|||
26 Apr 2007, 16:44 |
|
< Last Thread | Next Thread > |
Forum Rules:
|
Copyright © 1999-2025, Tomasz Grysztar. Also on GitHub, YouTube.
Website powered by rwasa.