与gcc相比,为什么clang在寄存器变量中表现怪异?

occ*_*cia 5 c assembly clang

背景:

我需要在C代码中获取寄存器值,因此我在gcc用法中发现了这一点。我使用以下代码获得ebp值。

   register int ebp asm("ebp");
   printf("currently ebp is %08x\n", ebp);
   // then some code use the value
Run Code Online (Sandbox Code Playgroud)

一切正常,直到我将程序的编译器更改为clang为止。在gcc中,它通常会打印类似于0x7f1284978的内容,绝对是类似value的指针。

但是当使用clang时,输出变得很奇怪,它打印出一个0x9之类的值。Ebp不能具有这样的值。

问:

  • clang不支持此寄存器变量用法吗?
  • 如果它不支持此功能,为什么不抱怨警告或错误(使用以下代码进行编译)?

    #include <stdio.h>
    
    static size_t vfp = 0x233;
    
    int main(void) {
        register int ebp asm("ebp");
        vfp = (size_t) ebp;
        printf("vfp value is 0x%lx\n", vfp);
        return 0;
    }
    
    Run Code Online (Sandbox Code Playgroud)

The*_*tar 4

长话短说:

Clang 目前不支持显式注册变量。

细节:

请参阅clang 文档

当指定的寄存器不可分配时(例如堆栈指针),clang 仅支持全局寄存器变量。对通用全局寄存器变量的支持不太可能很快实现,因为它需要额外的 LLVM 后端支持。

在我的机器(x86_64 ubuntu 16.04)上,如果我使用 Clang-5.0 编译,我得到的程序集是:

 08048410 <main>:
 8048410:       55                      push   %ebp
 8048411:       89 e5                   mov    %esp,%ebp
 8048413:       83 ec 18                sub    $0x18,%esp
 8048416:       8d 05 c0 84 04 08       lea    0x80484c0,%eax
 804841c:       8b 4d fc                mov    -0x4(%ebp),%ecx ;this line is wrong, the behavior is meaningless
 804841f:       89 0d 1c a0 04 08       mov    %ecx,0x804a01c
 8048425:       8b 0d 1c a0 04 08       mov    0x804a01c,%ecx
 804842b:       89 04 24                mov    %eax,(%esp)
 804842e:       89 4c 24 04             mov    %ecx,0x4(%esp)
 8048432:       e8 89 fe ff ff          call   80482c0 <printf@plt>
 8048437:       89 45 f8                mov    %eax,-0x8(%ebp)
 804843a:       83 c4 18                add    $0x18,%esp
 804843d:       5d                      pop    %ebp
 804843e:       c3                      ret
 804843f:       90                      nop
Run Code Online (Sandbox Code Playgroud)

如果我使用 GCC-5.5.0 进行编译,这就是我得到的程序集:

0000051d <main>:


 51d:   8d 4c 24 04             lea    0x4(%esp),%ecx
 521:   83 e4 f0                and    $0xfffffff0,%esp
 524:   ff 71 fc                pushl  -0x4(%ecx)
 527:   55                      push   %ebp
 528:   89 e5                   mov    %esp,%ebp
 52a:   53                      push   %ebx
 52b:   51                      push   %ecx
 52c:   e8 33 00 00 00          call   564 <__x86.get_pc_thunk.ax>
 531:   05 a7 1a 00 00          add    $0x1aa7,%eax
 536:   89 ea                   mov    %ebp,%edx ; this is the correct location to get the value of ebp
 538:   89 90 30 00 00 00       mov    %edx,0x30(%eax)
 53e:   8b 90 30 00 00 00       mov    0x30(%eax),%edx
 544:   83 ec 08                sub    $0x8,%esp
 547:   52                      push   %edx
 548:   8d 90 18 e6 ff ff       lea    -0x19e8(%eax),%edx
 54e:   52                      push   %edx
 54f:   89 c3                   mov    %eax,%ebx
 551:   e8 5a fe ff ff          call   3b0 <printf@plt>
 556:   83 c4 10                add    $0x10,%esp
 559:   90                      nop
 55a:   8d 65 f8                lea    -0x8(%ebp),%esp
 55d:   59                      pop    %ecx
 55e:   5b                      pop    %ebx
 55f:   5d                      pop    %ebp
 560:   8d 61 fc                lea    -0x4(%ecx),%esp
 563:   c3                      ret
Run Code Online (Sandbox Code Playgroud)

我们可以看到,GCC 一般支持显式寄存器值访问,而 Clang 不支持。

解决方案:

如果你想使用 Clang 访问 ebp 值,你可以使用内联汇编,如下所示:asm("\t movl %%ebp,%0" : "=r"(vfp));

  • “*我们可以看到 GCC 通常支持显式寄存器值访问*” - 除非文档表明明确不支持这种用法。如果它看起来有效,那也是偶然的。 (4认同)
  • @occia:**GCC 有一个内置函数**:[`void * __builtin_frame_address(0)` 获取当前函数的帧指针](https://gcc.gnu.org/onlinedocs/gcc/Return-Address.html )。使用它总是(?)安全,并且不需要使用“-fno-omit-frame-pointer”编译整个文件。 (3认同)