OS X asm C调用返回值

时间:2014-03-16 19:12:04

标签: c macos assembly llvm llvm-gcc

我一直在玩C中的asm宏来直接调用OS X Mavericks上的一些汇编指令来获取堆栈指针地址(来自%rsp)并且我发现了真的尝试将汇编程序代码的返回值分配到%rax寄存器(按惯例应该保存函数返回值的那个)时,奇怪的行为(至少对我来说)。 C代码非常简单:

#include <stdio.h>

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

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

如果我编译并运行代码,则会打印%rax寄存器中的值(实际堆栈指针),这很奇怪,因为我希望%#xx寄存器被&#34覆盖;返回0;&# 34; 但是,如果我删除return 0;一个字符串&#34; 0x0&#34;得到打印也很奇怪,因为我希望读取和打印%rax寄存器的返回值。

我也尝试在Ubuntu Linux上运行此代码(使用%esp和%eax寄存器的唯一区别),它实际上可以正常工作(使用gcc编译器)。

这可能是llvm-gcc编译器(Apple LLVM版本5.1)中的错误吗?

//修改 这是没有&#34;返回0;&#34;

的版本

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

2 个答案:

答案 0 :(得分:2)

这不是错误。这是不正确使用内联汇编的结果。在包含return语句的情况下,编译器不检查asm语句。如果在asm块之前%rax已经设置为零,则指令将覆盖该值。 asm块之前,编译器可以自由地执行此操作,因为您还没有通知任何寄存器输出,clobbers等。

如果不包含return语句,则不能依赖返回值。这就是为什么clang(这就是llvm-gcc与Xcode 5.1的关系 - 它不是gcc前端)发出警告。 gcc-4.8.2似乎适用于OS X - 但由于代码在两种情况下都不正确,所以它只是“运气”。通过优化:-O2,它不再有效。默认情况下,gcc不会发出警告,这是至少使用-Wall的一个很好的理由。

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

始终有效。 volatile不是必需的,因为编译器正在使用输出,因此它不能丢弃asm语句。即使将第一行更改为unsigned long long ret = 0; - 编译器显然也无法重新排序。

答案 1 :(得分:0)

这适用于我在小牛队[编辑:在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());
}