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

如何在LLVM IR中获取程序的映像基址,llvm,llvm-ir,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

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

好吧,我找到了解决方案。我需要定义一个名为
@\uuu 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会抱怨未定义的常量
\uuuuuu ImageBase

使用目标三元组
x86\u 64-pc-win32
并将指针算术从使用
i32
替换为使用
i64
,64位也可以实现同样的效果