[英]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.