flat assembler
Message board for the users of flat assembler.

Index > Linux > Question about AMD64 function calling procedure

Author
Thread Post new topic Reply to topic
mattst88



Joined: 12 May 2006
Posts: 260
Location: South Carolina
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
Post 25 Apr 2007, 22:56
View user's profile Send private message Visit poster's website Reply with quote
LocoDelAssembly
Your code has a bug


Joined: 06 May 2005
Posts: 4624
Location: Argentina
LocoDelAssembly 26 Apr 2007, 02:24
Surely it is used for the variable parameters, printf can have from 1 to N parameters. The first one (the fmt string pointer) is always present, the others are optional. I'm not familiar with the x86-64 ABI but I think that the first fixed parameters goes to the registers (and if there is not enough registers some will go to stack), and the optional arguments go to the stack. This way is easy to handle the optional arguments since all can be sequentially accessed on the stack, but if you put some of them on registers then you need special code to handle the registers first and then handle the remainding parameters on the stack.

BTW, pop isn't really needed, a "add rsp, 8*N" (where N here is 1)should be equivalent.
Post 26 Apr 2007, 02:24
View user's profile Send private message Reply with quote
LocoDelAssembly
Your code has a bug


Joined: 06 May 2005
Posts: 4624
Location: Argentina
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.
Post 26 Apr 2007, 03:28
View user's profile Send private message Reply with quote
mattst88



Joined: 12 May 2006
Posts: 260
Location: South Carolina
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 Smile
Post 26 Apr 2007, 16:25
View user's profile Send private message Visit poster's website Reply with quote
LocoDelAssembly
Your code has a bug


Joined: 06 May 2005
Posts: 4624
Location: Argentina
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 Razz[/edit]
Post 26 Apr 2007, 16:44
View user's profile Send private message Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  


< Last Thread | Next Thread >
Forum Rules:
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Copyright © 1999-2025, Tomasz Grysztar. Also on GitHub, YouTube.

Website powered by rwasa.