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

How to get the image base address of a program in LLVM IR

我正在尝试创建输出相对虚拟地址的 LLVM IR。但是,在编译和链接之后,我发现它输出的地址是基于可执行文件的首选图像基地址,而不是相对地址。

例如,如果我使用如下代码:

@.myconstant = private constant [12 x i8] c"My constant[=10=]"
@.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[=12=]"
@.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 生成?

好吧,我找到了解决方案。我需要定义一个名为 @__ImageBase 的外部全局变量,如下所示:

@__ImageBase = external global i8

然后,我执行相对于这个全局地址的指针减法:

@.myconstant = private constant [12 x i8] c"My constant[=11=]"
@.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.

对于 64 位,使用目标三元组 x86_64-pc-win32 并将指针算法从使用 i32 替换为 i64.

也可以实现同样的效果