2015-04-04 42 views
2

我正在尝试创建输出相对虚拟地址的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 [email protected] # Outputs 0x3044 

所以我的问题是:我如何导致这个组件通过IR产生?

回答

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*)} 

最后,我需要调用llc用目标是Windows的三倍,因为它是支持图像相对重定位的唯一平台。例如,我可以在命令行中设置-mtriple=i386-pc-win32

出于某种原因,设置源文件中的目标三重像这样:

target triple = "i386-pc-win32" 

是不够的。如果不添加上面的命令行,则会导致LLVM抱怨未定义的常量___ImageBase

对于64位使用目标三元组x86_64-pc-win32和使用i32i64替换指针算术也可以达到相同的效果。