Reputation: 33
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
Upvotes: 3
Views: 1117
Reputation: 22308
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.
Upvotes: 2
Reputation: 3335
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());
}
Upvotes: 0