0

I meet a strange phenomenon, I record the code in following. My test bed is x86_64 and gcc is 5.3.0 When I reserve some space in the stack for local value, sometimes it would crash.

                   | AS and LD | gcc    |
--------------------------------------------
 40 bytes in stack |  crash    | ok     |
--------------------------------------------
 32 bytes in stack |   ok      | crash  |
--------------------------------------------


.section .data

fmt:
    .ascii "0x%lx\n\0"

.section .text
.global _start

_start:
    subq $40, %rsp   # subq $32, %rsp is OK
                     # I want to reserve some place for local value.

    movq $8, %rsi
    movq $fmt, %rdi
    call printf      #print something

    addq $40, %rsp
    movq $1, %rax
    int $0x80

    as tsp.s -o tsp.o 
    ld -lc -I /lib64/ld-linux-x86-64.so.2 tsp.o -o tsp
    ./tsp
    Segmentation fault (core dumped)

This time I use gcc to compile and link. It is ok, when I reserve 40 bytes in the stack. It crash, when I reserve 32 bytes in the stack.

.section .data

fmt:
    .ascii "0x%lx\n\0"

.section .text
.global main 

main:
    subq $40, %rsp   # if subq $32, %rsp, it would crash.

    movq $8, %rsi
    movq $fmt, %rdi
    call printf

    addq $40, %rsp   
    movq $1, %rax
    int $0x80

    gcc tsp.s -o tsp
    ./tsp
    0x8
Forward
  • 855
  • 7
  • 12
  • 1
    Possibly a duplicate: [Calling printf in x86_64 using GNU assembler](https://stackoverflow.com/questions/38335212/calling-printf-in-x86-64-using-gnu-assembler) – user5329483 Feb 12 '18 at 10:31
  • related: [What happens if you use the 32-bit int 0x80 Linux ABI in 64-bit code?](https://stackoverflow.com/questions/46087730/what-happens-if-you-use-the-32-bit-int-0x80-linux-abi-in-64-bit-code), and note that `sys_exit` won't flush C stdio buffers, so you should use `exit(3)` or return from `main` if you're using `printf`, so your program still works if you pipe the output into something. – Peter Cordes Feb 12 '18 at 17:24

1 Answers1

4

When I tested your code printf crashed when accessing xmm registers. There are two reasons for it. When you let gcc do the compilation and linking it will actually have additional code before main. That code will correctly align the stack and then call main.

Since main was called like a normal function the stack will be aligned at 8 mod 16 because of the call instruction, but when calling a function the stack has to be correctly aligned (0 mod 16). The reason for the alignment requirement is because of xmm registers (among others).

Now, why did printf touch xmm registers in the first place? Because you called printf incorrectly. The ABI for amd64 says:

When a function taking variable-arguments is called, %rax must be set to the total number of floating point parameters passed to the function in SSE registers.

Your rax probably has some non-zero value in it.

So, two things to fix your problems. xorl %eax, %eax to zero %rax before the call to printf. And be aware of how you have been called and how to align the stack. If you've been called as a normal function, you need to subtract 8+n*16 (n can be 0) from your stack pointer before doing a call. If you've been called as an entry point to be safe you need to properly align your stack pointer because I'm not sure if the kernel always guarantees that your stack pointer will be aligned.

Art
  • 19,807
  • 1
  • 34
  • 60
  • Or to put it more simply, `main` *is* a normal function (as far as the ABI is concerned), but `_start` (the ELF entry point) isn't. – Peter Cordes Feb 12 '18 at 17:17
  • And yes, `rax` will hold garbage even at `_start` in a dynamically linked executable because the dynamic linker code runs first in your process. But in a static executable, registers are zeroed by Linux (to avoid info leaks; the ABI doesn't specify values but Linux chooses zero.) This had me scratching my head for a while; normally if I write `_start` it's for a static executable so I was wondering why `printf` would still be seeing non-zero `al`. But the OP is strangely still dynamically linking with`ld` instead of `gcc -nostartfiles`. – Peter Cordes Feb 12 '18 at 17:19
  • The x86-64 System V ABI guarantees 16-byte alignment for RSP at `_start`. (So do recent version of the i386 SysV ABI). So no, you don't need to `and rsp, -16`, not even in 32-bit mode. (Although gcc still aligns the stack manually in 32-bit mode, I guess for compat for old kernels, or just because nobody's removed that code yet.) Anyway, it's always correct to assume 16-byte stack alignment at _start in 64-bit mode, because the ABI has always included that guarantee. – Peter Cordes Feb 12 '18 at 17:20
  • @Art, yes, if I zero rax before the call to printf,it would not crash.while I miss your words "align the stack", I don't find the requirement about the stack alignment in the document http://refspecs.linuxfoundation.org/elf/x86_64-abi-0.99.pdf, and why do I have to subtract 8+n*16, if I subtract 16 not 8+n*16, there is still no crash. – Forward Feb 13 '18 at 07:59
  • @Forward In that document, 3.2.2: "The end of the input argument area shall be aligned on a 16 (32, if __m256 is passed on stack) byte boundary. In other words, the value (%rsp + 8) is always a multiple of 16 (32) when control is transferred to the function entry point." – Art Feb 13 '18 at 08:05
  • @Forward There is no crash because you happen to get away with not aligning the stack because you fixed the other problem that exposed this problem. You can either do it correctly and make sure that your stack is aligned or hope for the best and get bitten by it later. – Art Feb 13 '18 at 08:06
  • @Art, thanks very much, yes, I get it, if the stack is broken, it may not crash immediately, but when the bomb is exploded, that would be very difficult to find out where the bomb is buried. – Forward Feb 13 '18 at 08:55
  • @PeterCordes, my situation is very similar with the question "https://stackoverflow.com/questions/2352048/to-learn-assembly-should-i-start-with-32-bit-or-64-bit ", I want to understand better for programs and then I can optimize them deeply with the cpu architecture. I begin with the book "programing groundup" and try to transfer the 32bit examples to 64bit, then I meet this issue. – Forward Feb 13 '18 at 09:10