Can I use the JIT interface to create trampolines? (For functions and global values)

Dear LLVM-Mailing list (again),

I still have another beginners question in my mind - thank you for your patience.

Again I was playing around, but this time with llvm::Function. In an older question I learned, that I can replace a llvm::Function directly with an address.

llvm::Function *function = mainModue->getFunction(“puts”);

function->replaceAllUsesWith(

llvm::ConstantExpr::getIntToPtr(

llvm::ConstantInt::get(llvm::IntegerType::get(context, 64), 0xF),

function->getType()

)

);

With this code I ‘overloaded’ the function “puts” with the address 0xF - that is what I was taught and it is functional. I also noticed that you can do the same with a llvm::GlobalValue - as said I’m still a beginner.

Now I wonder can I replace the use of a specific function with the address of an address? (I think this is called a trampoline)

In C++ I know that I could use a function pointer instead of a function call to achieve this.

So… Deriving from that knowledge I would have to create a function pointer with the same signature as the function I want to replace.

Is this possible with the llvm JIT interface? Can I simply use that value in the “replaceAllUsesWith” function?

And would the same technique also work with a llvm::GlobalValue?

Kind greetings and thank you for the help in advance

Björn

Hi Bjoern,

Now I wonder can I replace the use of a specific function with the address of an address? (I think this is called a trampoline)

There are two ways to do this, if I understand properly. Taking puts
as the primitive example again:

    call void @puts(i8* ...)

It would be called a trampoline if you replaced this just as you did
before with a call to 0xf:

    call void inttoptr(i64 15 to void(i8*)*)(i8* ...)

and then at 0xf there's a sequence that loads the real function from
somewhere and jumps to it. Maybe (AArch64 code here, just because it's
what I'm most familiar with):

    adr x16, #12 // x16 now has the address of the .xword below
    ldr x16, [x16]
    br x16
    .xword real_puts_address

In reality you'd probably load from a GOT-like table you'd put
elsewhere in memory. The alternative (closer to what you actually
described with "address of an address"), would be if 0xf instead
contained a bare pointer to the real function. In that case you'd have
to iterate through all users of the function, and replace a call with
something like:

    %fn.i8 = load i8*, i8** inttoptr(i64 15 to i8**)
    %fn = bitcast i8* %fn.i8 to void(i8*)*
    call void %fn(i8* ...)

You'd probably use an IRBuilder to help you here, and eventually
ReplaceAllUses of the original "call @puts" with that final indirected
call. (There are no uses here because it returns void, but in general
there may be).

The main difference between the two can be seen as whether the
trampoline is inlined or not.

Can I simply use that value in the "replaceAllUsesWith" function?

Not in the second case, but mostly yes if you use an actual trampoline.

And would the same technique also work with a llvm::GlobalValue?

The trampoline wouldn't because there's no call involved for normal
globals. The second technique of introducing an extra load explicitly
would work for uses inside a function.

Another issue you might have to deal with is if one global variable
references another:

    @my_functions = global [3 x i8*] [i8* bitcast(void(i8*)* @puts to i8*), ...]

Obviously you can't introduce a load there. The trampoline might work
still if you only expect functions to be called, but may or may not
have issues if people start comparing pointers for equality (depending
on what you want to happen). It's likely to be an even bigger problem
for non-functions.

Cheers.

Tim.

Hi Björn,

No sure it exactly matches your issue, but we had a similar problem.

TL;DR: you can achieve this by manipulating IR and a little help from JIT.

Here is a high-level algorithm of what we did:

Created an external global variable (GlobalFunctionPointer) with the same type as the function (basically a function pointer).
Created a copy of the function, e.g. foobar -> foobar_copy.
Replace the body of the original function with something like this (pseudocode):

define foobar(a, b, c) {
  ptr = load @GlobalFunctionPointer
  call ptr(a, b, c)
}

Then, when you add your program into JIT you will hit the unresolved external symbol (GlobalFunctionPointer) that you should hook up to an address under your control:
Something like this:

SymbolInfo Resolver::findSymbol(const std::string &name) {
  if (name == "GlobalFunctionPointer") {
    return SymbolInfo((uint64_t)trampoline, JITSymbolFlags::Exported);
  }
  return SymbolInfo(nullptr);
}

Where the trampoline is a pointer, which can hold the address of a function you want to redirect your call to, e.g.:

uint64_t *trampoline = new uint64_t;
....
*trampoline = jit.getFunctionAddess("foobar_copy");

Hey Alex and Tim,

Seems like both ideas are way more complex and over my current abilities then I thought...
The basic idea behind this was to deal with references between two llvm::Modules. Normally I could link those cross references simply by adding both modules to the same ExecutionEngine. But I wondered if they could run in there own ExecutionEngines.

When the object file is generated both modules would miss the references to each other, but at this state I'm in a deadlock. Because none of the modules is resolved, I can't look up any address from the other module.
By using trampolines I could had provide the address for the trampoline and could had written the address the trampoline is pointing to, when both modules are done.

Seems like both ideas are way more complex and over my current abilities then I thought...

I don't know the second idea, but in case of my example there is no issues with your abilities to understand/implement, but rather with my abilities to explain the idea.
Given what you have explained I would suggest you drawing the modules and connections between the functions and finding a way to connect them via trampolines and global variables.