1

I am fairly new to assembly and architectures so I was playing around with the GCC to assemble my assembler file. I am running Windows 10, with AMD 3750H (IDK if this helps)

It is a fairly simple program, that does the following:

  1. Creates a stack frame
  2. Pushes two numbers to the stack
  3. Pops them one at a time, calling printf once for each. (So the last pop is after the first call)
  4. exits

Here is the code I wrote:

.data
form:
  .ascii "%d\n\0";

.text
.globl main

main:
  pushq %rbp
  movq %rsp, %rbp
  subq $32, %rsp;

  pushq $420;
  pushq $69;

  lea form(%rip) , %rcx;
  xor %eax, %eax
  popq %rdx
  call printf

  lea form(%rip) , %rcx;
  xor %eax, %eax
  popq %rdx
  call printf

  mov %rbp, %rsp
  popq %rbp
  ret

But the output I get is (rather strangely):

69
4199744

I read about shadow space in the Windows x64 calling convention but I couldn't find the proper way to work with it when using push/pop:

This is what I tried (thanks to Jester) and it worked

# subtract 32 when I push
  pushq $420;
  subq $32, %rsp

# Add 32 when I pop

  addq $32, %rsp
  popq %rdx

But for some reason I feel there maybe a more elegant way to go about this

Do I have to leave 32 bytes after every push? That seems like a lot of space is being wasted.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
user2519656
  • 63
  • 1
  • 7
  • maybe try debugging the value before and after the operation? – jspcal Oct 20 '21 at 23:19
  • 2
    Your pushes end up in the shadow space which is 32 bytes above the return address. You did try to allocate that with the `sub $32, %rsp` but subsequent `push` will put stuff under that. – Jester Oct 20 '21 at 23:19
  • 2
    Long story short, allocate more space (make sure 16 byte aligned) and use `mov` instructions to the top slots leaving the bottom 32 bytes untouched. – Jester Oct 20 '21 at 23:25
  • 1
    In Windows x64, a `call` can clobber the 32 bytes above the current RSP; those bytes are owned by the callee. – Peter Cordes Oct 21 '21 at 00:42
  • I didn't know it was called "shadow space" so I couldn't find anything useful on the internet. @Jester Do I have to leave 32 bytes after every push?? Something like `pushq $420; subq $32, %rsp; pushq $69;` I tried this and this indeed was working. But seems like a lot of space is being wasted – user2519656 Oct 21 '21 at 01:32
  • 1
    No, you only have to leave 32 bytes of unused stack for a `call`. As Jester suggested, you might prefer to just use `mov`, like `mov %eax, 32(%rsp)` to save something in memory before a `call`, if you were using stack-space instead of a call-preserved register. – Peter Cordes Oct 21 '21 at 02:19
  • 1
    Look at compiler output for examples of how to use it for local vars in a non-leaf function. If you pop again *before* a call, you don't need to do anything special, but in that case you normally wouldn't use push/pop at all. – Peter Cordes Oct 21 '21 at 03:43
  • FYI, `xor %eax, %eax` is unnecessary even before variadic functions in Windows x64. Passing AL = number of FP args in XMM regs is only a thing in x86-64 System V (used on all non-Windows systems). – Peter Cordes Oct 21 '21 at 04:12

0 Answers0