Description
Over in JuliaLang/julia#53421, we are seeing an issue where a library linked with ld.bfd
is working, but lld
is not (when targeting win64/mingw). The code in question is a bit strange, but it's essentially a sanity check to make sure that there weren't any linking mistakes and that there aren't multiple copies of the runtime library floating around (e.g. a common mistake is to load both debug and release copies of the runtime library into the same address space).
In reduced terms, we have a source file that at the LLVM level looks like this (fully minimized):
# cat metadata.ll
; ModuleID = 'metadata_opt.bc'
source_filename = "metadata"
target datalayout = "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-w64-windows-gnu-coff"
@jl_RTLD_DEFAULT_handle = external dllimport constant ptr
@jl_RTLD_DEFAULT_handle_pointer = dllexport constant ptr @jl_RTLD_DEFAULT_handle
define x86_stdcallcc i32 @_DllMainCRTStartup(ptr %0, i32 %1, ptr %2) {
top:
ret i32 1
}
where jl_RTLD_DEFAULT_handle
is just some dllexported
global defined in libjulia-internal.dll
, but for the present purposes, we may just treat it as the following:
# cat fakelibjulia.c
__declspec(dllexport) void *jl_RTLD_DEFAULT_handle = 0;
# gcc -shared -o libfakejulia.dll fakelibjulia.c
Doing the following:
# llc.exe --filetype=obj -o metadata.o metadata.ll
# ld --disable-runtime-pseudo-reloc -shared -o metadata-bfd.dll --whole-archive metadata.o --no-whole-archive libfakejulia.dll
# lld.exe -flavor gnu -m i386pep -Bdynamic --disable-runtime-pseudo-reloc -shared -o metadata-lld.dll --whole-archive metadata.o --no-whole-archive -L./usr/bin -ljulia -ljulia-internal
lld: error: automatic dllimport of jl_RTLD_DEFAULT_handle in metadata.o requires pseudo relocations
# lld.exe -flavor gnu -m i386pep -Bdynamic --enable-runtime-pseudo-reloc -shared -o metadata-lld.dll --whole-archive metadata.o --no-whole-archive -L./usr/bin -ljulia -ljulia-internal
And then loading both libraries and comparing the pointers:
# cat print_pointers.c
#include <stdio.h>
extern __declspec(dllimport) void *jl_RTLD_DEFAULT_handle;
extern __declspec(dllimport) void *jl_RTLD_DEFAULT_handle_pointer;
int main(void) {
printf("Pointers: %p %p\n", &jl_RTLD_DEFAULT_handle, jl_RTLD_DEFAULT_handle_pointer);
return 0;
}
# gcc -o print_pointers-bfd.exe print_pointers.c metadata-bfd.dll libfakejulia.dll
# gcc -o print_pointers-lld.exe print_pointers.c metadata-lld.dll libfakejulia.dll
# ./print_pointers-bfd.exe
Pointers: 00007ffafef77020 00007ffafef77020
# ./print_pointers-lld.exe
Pointers: 00007ffafef77020 00007ffb0d1b20e0
The BFD result is expected. The LLD result is not (separate and apart from the fact that lld and bfd disagree over the necessity of runtime pseudo relocations - the output is the same for bfd if those are enabled, although it does require linking the mingw dllcrt in that case).
Finally, I want to note that the issue is the library, not the executable. The same behavior is observed via dlsym (as in the original issue).
julia> lib_lld = dlopen("metadata-lld.dll")
Ptr{Nothing} @0x00007ffb0d1b0000
julia> lib_bfd = dlopen("metadata-bfd.dll")
Ptr{Nothing} @0x00007ffb08ff0000
julia> unsafe_load(Ptr{Ptr{Cvoid}}(dlsym(lib_lld, "jl_RTLD_DEFAULT_handle_pointer")))
Ptr{Nothing} @0x00007ffb0d1b20e0
julia> unsafe_load(Ptr{Ptr{Cvoid}}(dlsym(lib_bfd, "jl_RTLD_DEFAULT_handle_pointer")))
Ptr{Nothing} @0x00007ffab81a9818
julia> cglobal(:jl_RTLD_DEFAULT_handle, Ptr{Cvoid})
Ptr{Ptr{Nothing}} @0x00007ffab81a9818
In particular, the value that lld gives is one extra level of indirection removed from that used by bfd:
julia> unsafe_load(unsafe_load(Ptr{Ptr{Ptr{Cvoid}}}(dlsym(lib_lld,` "jl_RTLD_DEFAULT_handle_pointer"))))
Ptr{Nothing} @0x00007ffb0d1b20e0
Versions:
# ld --version
GNU ld (GNU Binutils) 2.42
Copyright (C) 2024 Free Software Foundation, Inc.
This program is free software; you may redistribute it under the terms of
the GNU General Public License version 3 or (at your option) a later version.
This program has absolutely no warranty.
# lld.exe -flavor ld --version
LLD 16.0.6 (compatible with GNU linkers)