Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unable to link C runtime library (libcmt.lib) using lld-link.exe (Windows)

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
like image 963
John Doe Avatar asked Mar 04 '23 23:03

John Doe


1 Answers

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!

like image 60
John Doe Avatar answered Apr 28 '23 04:04

John Doe