Reputation: 1335
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
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