0

I'm writing some simple x64 assembly code and am having a bit of trouble understanding some fundamentals about how the stack frames are set up.

.section .data
    infmt: .asciz "%d"
    outfmt: .asciz "%d\n"

.section .text
    .extern scanf
    .extern printf
    .global main

print_number:
    ... # Omitted for space purposes.   

print_rax:
    ... # Omitted for space purposes.

main:
    subq $16, %rsp
    movq %rsp, %rsi
    leaq infmt, %rdi
    movq $0, %rax
    call scanf
    movq (%rsp), %rdi
    call print_number
    addq $16, %rsp
    ret

For some reason, when I allocate sixteen bytes (or, really, any multiple of 16), the program segmentation faults. Though, if I allocate any other multiple of 8, e.g., 8, 24, ..., it does not segfault. If I save the base pointer and move the stack pointer over to the base pointer, e.g.,

main:
    pushq %rbp
    movq %rsp, %rbp
    subq $16, %rsp
    movq %rsp, %rsi
    leaq infmt, %rdi
    movq $0, %rax
    call scanf
    movq (%rsp), %rdi
    call print_number
    popq %rbp
    addq $16, %rsp
    ret

The code does not segfault. I want to allocate exactly 16 bytes because main will declare two local variables. Allocating more wouldn't necessarily hurt anything, but I want to know what is causing the segfault.

  • 4
    `call` puts an extra 8 bytes on the stack and according to the ABI the callee expects the stack to be aligned. – Jester Nov 23 '22 at 22:17
  • @Jester Does moving the current stack pointer into the base pointer realign the stack? – TheSinisterStone Nov 23 '22 at 22:17
  • That does not change the stack pointer. Maybe you meant something else than what you wrote. – Jester Nov 23 '22 at 22:18
  • @Jester Yes, that's correct, but I'm not sure what is realigning the stack in this instance. The only thing that fixes the issue is pushing rbp and moving rsp into rbp, but I'm not sure how that causes the stack to be aligned. – TheSinisterStone Nov 23 '22 at 22:22
  • 4
    `pushq %rbp` puts 8 bytes on the stack. Combine that with the 16 bytes that you've set aside with `subq $16, %rsp`, and you've adjusted the stack pointer by 24 bytes. – user3386109 Nov 23 '22 at 22:25
  • 3
    You just have to make sure to move the stack pointer by an odd multiple of 8. How you do that is not relevant. You can `sub $24, %rsp` if you do not need a frame pointer to allocate the extra 8 bytes of padding together with your 16 bytes of locals. – Jester Nov 23 '22 at 22:29
  • @user3386109 Aha, that'll do it... Well, let's say I want to allocate space for 3 eight-byte variables, meaning I do `subq $24, %rsp`. How do I realign the stack in this instance? Do I just need to allocate $32 instead to realign it on a 16-byte boundary? – TheSinisterStone Nov 23 '22 at 22:30
  • 2
    Yes, that is exactly what you need to do. The stack alignment requirement forces you to waste a few bytes sometimes. Keep in mind that you're starting with 8 bytes for the return address, and another 8 bytes for the frame pointer (if you choose to use it). Any other adjustments to the stack pointer must bring the total usage to a multiple of 16, before calling another function. – user3386109 Nov 23 '22 at 22:32
  • 4
    If you do not need the frame pointer you can allocate 24 bytes and use all of those for your locals. Only allocate 32 bytes if you also used a `push %rbp`. – Jester Nov 23 '22 at 22:39
  • One of you can post an answer and I'll accept it. – TheSinisterStone Nov 23 '22 at 22:53

0 Answers0