2

I'm trying to create LLVM IR which outputs Relative Virtual Addresses. However, after compiling and linking, I see that it outputs addresses based on the preferred image base address of the executable, rather than relative addresses.

For instance, if I use code like:

@.myconstant = private constant [12 x i8] c"My constant\00"
@.myglobal = global {i8*} {i8* bitcast([12 x i8]* @.myconstant to i8*)}

In the matching executable section, I see a hex value like:

44 30 40 00

Or simply 0x403044, which is much larger than my entire executable size, even after section alignment.

If I manually subtract 0x400000, Like so:

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

I get the correct address in the executable. But this solution is not maintainable, because the image base address isn't guaranteed to be 0x400000.

At the same time, I have to use a pointer to a global, because I don't know where that global would end up inside the relevant section (since that depends on other globals in the same section), or what relative memory address would be assigned for that section (since that depends on alignment with previous sections).

So my question is, how do I either get the base address as a constant, or get an address relative to the loading address of the program?

Update: Apparently, the developers of lld already encountered this problem, and added an extention to the AT&T assembly language to account for this:

.regular_global:
    .long .L.myconstant # Outputs 0x403044
.rva_global:
    .long .L.myconstant@imgrel # Outputs 0x3044

So my question becomes: How do I cause this assembly to be produced through the IR?

1 Answer 1

0

Well, I've found the solution. I need to define an external global called @__ImageBase, as so:

@__ImageBase = external global i8

Then, I perform pointer subtraction relative to this global's address as so:

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

Finally, I need to call llc with a target triple for Windows, as it is the only platform that supports image-relative relocations. For instance, I can set -mtriple=i386-pc-win32 in the command line.

For some reason, setting the target triple inside the source file like so:

target triple = "i386-pc-win32"

is not sufficient. Doing this without adding the command line above would result in LLVM complaining about an undefined constant ___ImageBase.

The same can also be achieved for 64-bit using the target triple x86_64-pc-win32 and replacing pointer arithmetics from using i32 to i64.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.