News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests
NB: Posting URL's See here: Posted URL Change

Main Menu

Call printf from 64-bit code

Started by Mark44, April 10, 2014, 01:36:29 PM

Previous topic - Next topic

Mark44

I have been trying to get up to speed with writing 64-bit routines, particularly those that call other routines. It took me a long while to figure out how to call standard library functions like printf from within some 64-bit code. Right after I figured the code below out, I found some interesting examples in, of all places, a site for art and music, by a fellow named Sydney Grew - http://artmusic.smfforfree.com/index.php/topic,249.0.html.

I've looked through the examples here in the 64-bit section (but not in the archived posts on the old masm site), and didn't find very much that pertained to calling external routines from 64-bit code. Hopefully what I have here will be helpful to someone else who is just getting started.

One important point is how the parameters are passed to the external function. In 32-bit code, I'm used to pushing the parameters on the stack in reverse order. In 64-bit programming (edit: in Windows), parameters are passed using the registers, with RCX, RDX, and R8 being used for the first three integer parameters, and XMM0, XMM1, and XMM2 being used for the first three floating point parameters. As I am passing a format string (address) and two integer parameters, I set RCX, RDX, and R8 with the appropriate values in the following code.

Another thing that's important is the use of a prologue to set up space on the stack for the function, and the epilogue at the end to put the stack back in its original state. I'm still somewhat vague on exactly how much to allocate. For my example, setting aside 64 bytes (40h bytes) seems to do the trick, although I could probably pare that down. I think it's important to use a number that is a multiple of 16 bytes.


extern printf:PROC

.data
fmtString db "test string: %lld and %lld", 0Dh, 0Ah, 0h
valueForPrintf dq 5
value2ForPrintf dq 6

.code
TestPrintf PROC C
  ; Prologue
  push        rdi 
  sub         rsp, 40h 
  mov         rdi, rsp

  ; Before calling printf, pass the format string in RCX, and int values in RDX and R8.
  ; Notice that the parameters are passed in registers rather than being pushed on the stack in reverse order.
  lea         rcx, [fmtString]
  mov   rdx, [valueForPrintf]
  mov   r8, [value2ForPrintf]
  call printf

  ; Epilogue
  add         rsp, 40h 
  pop         rdi 
  ret

TestPrintf ENDP
END


I call TestPrintf from some C code. The prototype for TestPrintf is void TestPrintf(void);
Output from the above is
test string: 5 and 6

Yuri

The 4th parameter is passed in R9/XMM3. You should allocate space on the stack for at least 4 parameters even if the function takes fewer or none. Also before the call the stack should be aligned on a 16-bit boundary, so, depending on the number of parameters, you may have to adjust the stack pointer accordingly before pushing parameters and allocating shadow space for the first 4.

Gunther

Hi Mark,

for calling some libc functions, you can also check this thread. You'll find a good overview about calling conventions and parameter passing here. I hope that helps.

Gunther
You have to know the facts before you can distort them.

sinsi


Mark44

Quote from: Yuri on April 10, 2014, 03:32:39 PM
The 4th parameter is passed in R9/XMM3. You should allocate space on the stack for at least 4 parameters even if the function takes fewer or none. Also before the call the stack should be aligned on a 16-bit boundary, so, depending on the number of parameters, you may have to adjust the stack pointer accordingly before pushing parameters and allocating shadow space for the first 4.
I have a better feel for this now, that you have to allocate space for at least 4 qword parameters, even if you don't use that many. In some circumstances, you need to allocate more space. For my simple function, which is essentially a wrapper around printf, allocating 32 bytes of space on the stack works. Also, per the MSDN docs (and I am using VS 10) in Overview of x64 Calling Conventions (http://msdn.microsoft.com/en-us/library/ms235286.aspx), the stack pointer is already aligned on a 16-byte boundary, so I don't think I need to be concerned about adjusting the stack pointer.

Quote from: Guntherfor calling some libc functions, you can also check this thread. You'll find a good overview about calling conventions and parameter passing here. I hope that helps.
I looked at the first link you posted, and will look at the second. I've also gotten some information on calling conventions from Agner Fog's posted articles, as well as in the MSDN docs.

Quote from: sinsiMore information - Overview of x64 Calling Conventions
Yes, this is one I've seen before, and now it's starting to make more sense to me.

Thanks, Yuri, Gunther, and sinsi, for your comments. Much appreciated.

habran

hey Mark44

for 64 bit programming use JWasm and you'll have a prosper life :biggrin:
Cod-Father

Mark44

Quote from: habran on April 11, 2014, 02:51:04 PM
hey Mark44

for 64 bit programming use JWasm and you'll have a prosper life :biggrin:
I've heard good things about Japheth's assembler, so I'll probably look into it sometime soon. For what I'm doing, though, VS 10 is working fine for me at the moment. Most of the code I write is a mix of C and assembly.