I'm writing a language using LLVM. I'd like to avoid having to package clang and simply use the LLVM tools (ex. lld, lld-link). I've been trying to invoke the printf
function from my simple IR code (testinput.ll
):
; ModuleID = 'Test2'
source_filename = "entry"
@str_0 = private unnamed_addr constant [13 x i8] c"Hello world!\00"
declare i32 @printf(i8*, ...)
define i32 @main() {
entry:
%anonymous_10 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([13 x i8], [13 x i8]* @str_0, i32 0, i32 0))
ret i32 1234
}
But I keep receiving errors no matter what I try:
$ clang-cl -fuse-ld=lld-link testinput.ll "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.16.27023\lib\spectre\x64\libcmt.lib"
Note: I've chosen the link randomly "... spectre\x64\libcmt.lib ..." by simply searching for libcmt.lib
on the system.
Error:
C:\Program Files\LLVM\bin\lld-link: warning: libcmt.lib(loadcfg.obj): undefined symbol: __enclave_config
error: link failed
clang-cl.exe: error: linker command failed with exit code 1 (use -v to see invocation)
I'm using Windows 10 (x64) with LLVM 5.0. Interestingly, using link.exe
(Windows' VS tools' linker) everything works fine (which is what clang uses under the hood in my case).
I've read in this article:
... As I wrote earlier, __enclave_config is a variable that is filled in by the linker, but you have to use the VC linker, and a linker new enough to be able to automatically fill it in. ...
I believe the problem here is libcmt.lib
and the lld-link
linker. Is the lld-link
version (LLVM 5.0) incompatible with the libcmt.lib
that I'm using, is that the problem?
Edit: I've managed to track down what clang does behind the scenes, and have found it using the following command:
lld-link -out:a.exe -defaultlib:libcmt "-libpath:C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Community\\VC\\Tools\\MSVC\\14.16.27023\\lib\\x64" "-libpath:C:\\Program Files (x86)\\Windows Kits\\10\\Lib\\10.0.17763.0\\ucrt\\x64" "-libpath:C:\\Program Files (x86)\\Windows Kits\\10\\Lib\\10.0.17763.0\\um\\x64" -nologo "test.obj"
Clearly it's using lld-link, and it's working. However, strangely enough, it only compiles without errors if the input object file was compiled to .LL (LLVM IR) using clang (maybe using -fuse-ld=lld -v
options?).
What's weird about this, is that upon inspection of the output .LL file from clang (test.ll
) the full, source code (in IR) definitions of printf (and some other *printf functions used by it) is present (in the output .LL file).
So, somehow it's getting the definitions of printf
itself inside the output .LL, IR code file.
As far as I know, you can't just $ llc libcmt.lib testinput.ll
? That'd be the linker's job... (llc accepts only one positional argument)
The error that I'm getting once I try the same lld-link command and arguments with my testinput.ll
file (not outputted from clang) is the following:
lld-link: error: <root>: undefined symbol: _mainCRTStartup
lld-link: error: undefined symbol: _printf
Turns out, it was much much more simple than I anticipated. Maybe if the errors were at least somewhat helpful, I could have avoided all this confusion...
I've figured it out by comparing clang's output LL file, and noticed a curious line at the beginning:
target triple = "x86_64-pc-windows-msvc"
Once I added it to my testinput.ll
file, everything worked flawlessly with lld-link
. Hurray!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With