如何在LLVM IR中获取程序的映像基址

时间:2015-04-04 14:26:50

标签: llvm llvm-ir

我正在尝试创建输出相对虚拟地址的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生成这个程序集?

1 个答案:

答案 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位并将指针算术替换为使用i32i64,也可以实现相同目的。