2

If I have the following code then the second register read will always be 0.

So an example output would be:

Frame pointer 1560061208

Stack pointer 0

Character at location 1560061159

and if I swap the calls to rbp and rsp then the second one always outputs 0.

#include <stdio.h>

int main() {

    void *bp asm("rbp");
    printf("Frame pointer %u\n", bp);

    void *sp asm("rsp");
    printf("Stack pointer %u\n", sp);

    char c = 'A';
    printf("Character at location %u\n", &c);

    return 0;
}
Petr Skocik
  • 58,047
  • 6
  • 95
  • 142
Jamie Davenport
  • 351
  • 3
  • 10
  • 4
    why are you printing a pointer with `%u`? – Sourav Ghosh Oct 26 '17 at 12:32
  • Compile with all warnings and debug info: `gcc -Wall -Wextra -g` then use the `gdb` debugger to run your program step by step. – Basile Starynkevitch Oct 26 '17 at 12:34
  • 4
    [The *only* supported use of `type variable asm("regname");` is to control inputs to Extended-`asm` statements](https://gcc.gnu.org/onlinedocs/gcc/Local-Register-Variables.html). So your code has Undefined Behaviour in GNU C, and you shouldn't expect meaningful results from it. It's probably printing whatever garbage was left in `rsi` to printf, and not actually RSP or RBP. And local register variables don't work for `rsp` anyway. You can use inline asm to copy `%rsp` to another register, and there are some GNU C builtin functions to get rsp I think. – Peter Cordes Oct 26 '17 at 12:35
  • @PeterCordes This seems to be a valuable answer! You should totally post it! – anatolyg Oct 26 '17 at 12:42
  • @anatolyg: I noticed that, too :P Was half way through typing it. (But it turns out that wasn't the real answer. The OP forgot the `register` keyword so the `asm("rsp")` was doing nothing! TL:DR **always check for compiler warnings, especially when doing weird stuff**. – Peter Cordes Oct 26 '17 at 12:58

1 Answers1

3

The only supported use of type variable asm("regname"); is to control inputs to Extended-asm statements. So your code has Undefined Behaviour in GNU C, and you shouldn't expect meaningful results from it.

You can use inline asm to copy %rsp to another register, and there might be some GNU C builtin functions to get the stack or frame pointer; I forget. If you're going to do anything with the stack pointer, you should probably just write your whole function in asm.


It turns out you were just missing the register keyword.

Always check for compiler warnings, especially when doing "weird" stuff, which includes anything with the asm keyword. Or any time you don't get the expected output.

gcc7.2 -O3 -Wall says:

5 : <source>:5:11: warning: ignoring asm-specifier for non-static local variable 'bp'
     void *bp asm("rbp");
           ^~
  ... format-string warnings you should fix...

6 : <source>:6:5: warning: 'bp' is used uninitialized in this function [-Wuninitialized]
     printf("Frame pointer %u\n", bp);
     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

If you write register void *bp asm("rbp"); it appears to work, passing the register values to printf the way you were expecting.

gcc7.2 -O3 -Wall on the Godbolt compiler explorer, with the bugs fixed and using the register keyword for register asm locals. Remember this isn't supported, but does happen to do what you want in gcc7.2 for x86-64.

main:
    push    rbp         # save the old rbp (but it *isn't* making a "stack frame" with mov rbp, rsp)
    mov     rsi, rbp    # copy rbp (caller's value) as 2nd arg to printf
    mov     edi, OFFSET FLAT:.LC0   # format string
    xor     eax, eax
    sub     rsp, 16         # IDK why gcc allocates more stack space
    call    printf
    mov     rsi, rsp        # pass current rsp as 2nd arg to printf
    mov     edi, OFFSET FLAT:.LC1
    xor     eax, eax
    call    printf
    lea     rsi, [rsp+15]               # &c
    mov     edi, OFFSET FLAT:.LC2
    xor     eax, eax
    mov     BYTE PTR [rsp+15], 65
    call    printf
    add     rsp, 16
    xor     eax, eax
    pop     rbp                        # restore caller's RBP even though we didn't modify it
    ret

Probably using rbp as a register asm variable makes gcc act as if it was "used", thus saving & restoring.

If you want a stack frame, use -fno-omit-frame-pointer. (This is the default at -O0, but nobody wants to read gcc -O0 output.)

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847