[llvm-dev] Can I use the JIT interface to create trampolines? (For functions and global values)

Tim Northover via llvm-dev llvm-dev at lists.llvm.org
Thu May 9 03:21:20 PDT 2019


Hi Bjoern,

On Thu, 9 May 2019 at 10:05, Gaier, Bjoern via llvm-dev
<llvm-dev at lists.llvm.org> wrote:
> 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.


More information about the llvm-dev mailing list