我正在尝试创建输出相对虚拟地址的LLVM IR。但是,在编译和链接之后,我看到它根据可执行文件的首选图像基地址输出地址,而不是相对地址。
例如,如果我使用如下代码:
@.myconstant = private constant [12 x i8] c"My constant\00"
@.myglobal = global {i8*} {i8* bitcast([12 x i8]* @.myconstant to i8*)}
在匹配的可执行文件部分中,我看到一个十六进制值,如:
44 30 40 00
或简单地说0x403044,这比我的整个可执行文件大小要大得多,即使在部分对齐后也是如此。
如果我手动减去0x400000,就像这样:
@.myconstant = private constant [12 x i8] c"My constant\00"
@.myglobal = global {i8*} {i8* inttoptr (i32 sub(i32 ptrtoint([12 x i8]* @.myconstant to i32), i32 u0x400000) to i8*)}
我在可执行文件中获取了正确的地址。但是这个解决方案是不可维护的,因为图像基地址不能保证为0x400000。
同时,我必须使用指向全局的指针,因为我不知道全局会在相关部分内的哪个部分结束(因为这取决于同一部分中的其他全局变量),或者将为该部分分配什么相对内存地址(因为这取决于与前面部分的对齐)。
所以我的问题是,如何将基地址作为常量,或者获取相对于程序加载地址的地址?
更新:显然,lld的开发人员已经遇到了这个问题,并添加了对AT& T汇编语言的扩展来解释这个问题:
.regular_global:
.long .L.myconstant # Outputs 0x403044
.rva_global:
.long .L.myconstant@imgrel # Outputs 0x3044
所以我的问题变成了:我如何通过IR生成这个程序集?
答案 0 :(得分:0)
好吧,我找到了解决方案。我需要定义一个名为@__ImageBase
的外部全局,如下所示:
@__ImageBase = external global i8
然后,我执行相对于此全局地址的指针减法:
@.myconstant = private constant [12 x i8] c"My constant\00"
@.myglobal = global {i8*} {i8* inttoptr (i32 sub(i32 ptrtoint([12 x i8]* @.myconstant to i32), i32 ptrtoint(i8* @__ImageBase to i32)) to i8*)}
最后,我需要使用Windows的目标三元组调用llc
,因为它是唯一支持图像相对重定位的平台。例如,我可以在命令行中设置-mtriple=i386-pc-win32
。
出于某种原因,在源文件中设置目标三元组如下:
target triple = "i386-pc-win32"
还不够。在不添加上述命令行的情况下执行此操作将导致LLVM抱怨未定义的常量___ImageBase
。
对于使用目标三元组x86_64-pc-win32
的64位并将指针算术替换为使用i32
到i64
,也可以实现相同目的。