Question

I've been playing around with the asm macro in C to directly call some assembly instructions on OS X Mavericks to get a stack pointer address (from %rsp) and I've found really strange behaviour (at least to me) while trying to assign a return value from the assembler code into the %rax register (the one that should by convention hold the function return value). The C code is very simple:

#include <stdio.h>

unsigned long long get_sp(void) {
    asm ("mov %rsp, %rax");
    return 0;
}   

int main(void) {
    printf("0x%llx\n", get_sp());
}

If I compile and run the code, the value from %rax register gets printed(the actual stack pointer), which is strange as I would expect the %rax register to be overwritten by "return 0;" However if I remove the return 0; a string "0x0" gets printed which is also strange as I would expect the return value from %rax register to be read and printed.

I've tried to run this code(with the only difference using %esp and %eax registers) also on the Ubuntu Linux also and it actually works as I would expect(using the gcc compiler).

Could this be a bug in the llvm-gcc compiler(Apple LLVM version 5.1)?

//EDIT This is the version without the "return 0;"

otool -tV sp.out

sp.out:
(__TEXT,__text) section
_get_sp:
0000000100000f30    pushq   %rbp
0000000100000f31    movq    %rsp, %rbp
0000000100000f34    movq    %rsp, %rax
0000000100000f37    movq    -0x8(%rbp), %rax
0000000100000f3b    popq    %rbp
0000000100000f3c    ret
0000000100000f3d    nopl    (%rax)
_main:
0000000100000f40    pushq   %rbp
0000000100000f41    movq    %rsp, %rbp
0000000100000f44    subq    $0x10, %rsp
0000000100000f48    callq   _get_sp
0000000100000f4d    leaq    0x3a(%rip), %rdi ## literal pool for: "0x%llx
"
0000000100000f54    movq    %rax, %rsi
0000000100000f57    movb    $0x0, %al
0000000100000f59    callq   0x100000f6e ## symbol stub for: _printf
0000000100000f5e    movl    $0x0, %ecx
0000000100000f63    movl    %eax, -0x4(%rbp)
0000000100000f66    movl    %ecx, %eax
0000000100000f68    addq    $0x10, %rsp
0000000100000f6c    popq    %rbp
0000000100000f6d    ret
Was it helpful?

Solution

This is not a bug. It's the result of incorrect use of inline assembly. In the case where the return statement is included, the compiler does not inspect the asm statement. If %rax has already been set to zero before the asm block, the instruction overwrites that value. The compiler is free to do this before the asm block, since you haven't informed it of any register outputs, clobbers, etc.

In the case where no return statement is included, you can't rely on the return value. Which is why clang (that's what llvm-gcc is with Xcode 5.1 - it's not the gcc front end) issues a warning. gcc-4.8.2 appears to work on OS X - but because the code is incorrect in both cases, it's just 'luck'. With optimization: -O2, it no longer works. gcc doesn't issue a warning by default, which is a good reason to at least use -Wall.

{
    unsigned long long ret;
    __asm__ ("movq %rsp, %0" : "=r" (ret));
    return ret;
}

always works. volatile is not necessary, as the compiler is using an output, so it cannot discard the asm statement. Even changing the first line to unsigned long long ret = 0; - the compiler is obviously not free to reorder.

OTHER TIPS

this works for me on Mavericks [edit: and without a single change on Ubuntu Saucy x86_64]:

#include <stdio.h>

unsigned long long get_sp(void) {
    long _sp = 0x0L;

    __asm__ __volatile__(
        "mov %%rsp, %[value] \n\t"
            : [value] "=r" (_sp)
            :
            :);

    return _sp;
}       

int main(void) {
    printf("0x%llx\n", get_sp());
}           
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top