繁体   English   中英

与gcc相比,为什么clang与寄存器变量表现得很奇怪?

[英]Why does clang behave weirdly with register variables compared to gcc?

背景:

我需要在c代码中获取寄存器值,所以我在gcc用法中找到了它。 我使用以下代码获取ebp值。

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

在我将程序的编译器改为clang之前,一切似乎都没问题。 在gcc中,它通常打印类似于0x7f1284978的东西,绝对是一个像值一样的指针。

但是当使用clang时,输出变得怪异,它打印出一个像0x9这样的值。 Ebp必须不能有这样的值。

QS:

  • 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; } 

在Gcc中,register关键字执行以下操作(如下所述: 使用Gcc - 本地寄存器变量 ):

如果在Inline Assembly中使用变量,gcc将尝试将其放入您指定的寄存器中。 在任何其他上下文中,register关键字没有效果,并且如第一个链接的底部所示,在内联汇编的输入中指定变量是没有其他选择。

如果与clang一起使用,该关键字的作用我不知道,大多数可能只是被忽略(请参阅注册表关键字是否仍在使用?

TL; DR:

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

如果我用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

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

解:

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

作为@ThePatrickStar和@Boden_Units的答案的补充:在LLVM IR生成期间,Clang Driver会删除显式寄存器初始化。 以下是运行clang -emit-llvm -S inline_asm.c -o inline_asm.ll (clang-7)时inline_asm.ll的内容。

; ModuleID = 'inline_asm.c'
source_filename = "inline_asm.c"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-pc-linux-gnu"

@vfp = internal global i64 563, align 8
@.str = private unnamed_addr constant [20 x i8] c"vfp value is 0x%lx\0A\00", align 1

; Function Attrs: noinline nounwind optnone uwtable
define dso_local i32 @main() #0 {
  %1 = alloca i32, align 4
  %2 = alloca i32, align 4
  store i32 0, i32* %1, align 4
  %3 = load i32, i32* %2, align 4
  %4 = sext i32 %3 to i64
  store i64 %4, i64* @vfp, align 8
  %5 = load i64, i64* @vfp, align 8
  %6 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([20 x i8], [20 x i8]* @.str, i32 0, i32 0), i64 %5)
  ret i32 0
}

declare dso_local i32 @printf(i8*, ...) #1

attributes #0 = { noinline nounwind optnone uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }

!llvm.module.flags = !{!0}
!llvm.ident = !{!1}

!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{!"clang version 7.0.1-svn348686-1~exp1~20190113235231.54 (branches/release_70)"}

实际上,生成的IR用于register int ebp asm("ebp"); register int ebp;没有什么不同register int ebp; ,好像ebp从未初始化或绑定到ebp寄存器。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM