读取C中的寄存器在第一次读取后始终为0

时间:2017-10-26 12:31:49

标签: c x86 x86-64

如果我有以下代码,那么第二个寄存器读取将始终为0。

因此示例输出将是:

帧指针1560061208

堆栈指针0

位置1560061159处的字符

如果我将调用交换到rbp和rsp,那么第二个调用总是输出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;
}

1 个答案:

答案 0 :(得分:3)

The only supported use of type variable asm("regname"); is to control inputs to Extended-asm statements。因此,您的代码在GNU C中具有未定义的行为,并且您不应期望从中获得有意义的结果。

您可以使用内联asm将%rsp复制到另一个寄存器,并且可能有一些GNU C内置函数来获取堆栈或帧指针;我忘了。如果您要对堆栈指针做任何事情,您应该只是在asm中编写整个函数。

事实证明你错过了register关键字。

始终检查编译器警告,特别是在执行&#34;奇怪的&#34;东西,其中包含带有asm关键字的任何。或者任何时候你没有得到预期的输出。

gcc7.2 -O3 -Wall说:

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);
     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

如果你写register void *bp asm("rbp");它似乎有效,将寄存器值按照你期望的方式传递给printf。

gcc7.2 -O3 -Wall on the Godbolt compiler explorer,修复了错误并使用register关键字注册asm locals。请记住,这不是支持的,但确实碰巧在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

可能使用rbp作为寄存器asm变量使得gcc就好像它是&#34;使用&#34;,因此保存&amp;恢复。

如果您想要堆叠帧,请使用-fno-omit-frame-pointer。 (这是-O0的默认值,but nobody wants to read gcc -O0 output。)