[英]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:
如果不支持此功能,为什么它没有抱怨警告或错误(使用以下代码编译)?
#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.