mucka
mucka

Reputation: 1335

Where LLVM interpreter is looking for external functions (libraries?)

I am playing with LLVM IR a little and I cannot resolve (with help of google and doc) where LLVM interpreter lli is looking for external (not explicidly defined functions. I mean basic system functions). For example if I want to write simple program with no dependencies, on linux which will write something on screen I can do something like that:

@message = private constant [12 x i8] c"hello world\0A"
define i32 @puts(i8* %s) {
    call i32 asm sideeffect "movl $$0x2, %edi\0Amovl $$0xC, %edx\0Amovl$$1, %eax\0Asyscall\0A", "=A,{si}"(i8* %s) #1
    ret i32 %1
}
define void @exit(i32 %c) {
    call i32 asm sideeffect "movl $$60, %eax\0Asyscall\0A", "=A,{di}"(i32 %c) #1
    ret void
}
define void @main() {
    getelementptr [12 x i8], [12 x i8]* @message, i64 0, i64 0
    call i32 @puts(i8* %1)
    call void @exit(i32 0)
    ret void
}
define void @_start() {
    call void @main()
    ret void
}

main and _start are for cross compatibility with ld and lli. So above code works same in both. I can lli this, and code will start from main, or llc then ld it and it also will work, thus code will start from _start as expected. Now if I write code:

@formatString = private constant [4 x i8] c"%d\0A\00" 
declare i32 @printf(i8*, ...)
define i32 @main() {
    %d = shl i32 2, 3
    %s = getelementptr [4 x i8], [4 x i8]* @formatString, i64 0, i64 0
    %call = call i32 (i8*, ...) @printf(i8* %s, i32 %d)
    ret i32 0
}

It also works in lli but I cannot ld it because ld do not know reference to printf which is also expected. I can include prepare ld parameters to make that code work also, or simply use gcc file.o -o file, but this is not my point. My point is how to make lli to not include any external libraries (like libc) and just run only my code, and maybe define my own entry point, so I can include any prepared libc or any other library at will, I know I can overwrite names of functions and that should work, but then I cannot be sure what was rewritten, so I would be happy if lli throws an error if printf was used but not defined. Or maybe I got things wrong and lli cannot perform in such bare environment.

Upvotes: 3

Views: 759

Answers (1)

AlexDenisov
AlexDenisov

Reputation: 4117

TL;DR: use lli without JIT and will probably work:

lli -force-interpreter main.bc

More info:

In general, it depends on a type of JIT engine you use under the hood (if any).

I can't speak about MCJIT (-jit-kind=mcjit) because I am not familiar with it, but I can assure you that this is not possible if you use ORC JIT (-jit-kind=orc-mcjit or -jit-kind=orc-lazy). However, this is true only for lli, if you decide to use ORC on your own, then you can control this behavior.

In the context of lli ORC loads not only your module/external objects but also the whole program's address space as well. It means that you get all the libc and the whole LLVM itself.

Depending on your needs you may try to use lli truly as an interpreter, though it will be slower since no JIT is involved anymore.

Just add -force-interpreter option, and you will be good to go, with few exceptions. These functions still will be executed normally:

void Interpreter::initializeExternalFunctions() {
  sys::ScopedLock Writer(*FunctionsLock);
  (*FuncNames)["lle_X_atexit"]       = lle_X_atexit;
  (*FuncNames)["lle_X_exit"]         = lle_X_exit;
  (*FuncNames)["lle_X_abort"]        = lle_X_abort;

  (*FuncNames)["lle_X_printf"]       = lle_X_printf;
  (*FuncNames)["lle_X_sprintf"]      = lle_X_sprintf;
  (*FuncNames)["lle_X_sscanf"]       = lle_X_sscanf;
  (*FuncNames)["lle_X_scanf"]        = lle_X_scanf;
  (*FuncNames)["lle_X_fprintf"]      = lle_X_fprintf;
  (*FuncNames)["lle_X_memset"]       = lle_X_memset;
  (*FuncNames)["lle_X_memcpy"]       = lle_X_memcpy;
}

Upvotes: 3

Related Questions