$ cat bin.cc int main() {} $ cat 1.cc __attribute__((constructor(0))) void ctor() {} $ ./bin/clang++ -flto bin.cc -c $ ./bin/clang++ -flto 1.cc -c $ ./bin/clang++ -fuse-ld=gold -flto bin.o -Wl,--start-lib 1.o -Wl,--end-lib /tmp/lto-llvm-2af7ce.o(.init_array.0+0x0): error: undefined reference to 'ctor()' clang-3.9: error: linker command failed with exit code 1 (use -v to see invocation) With -Wl,-plugin-opt,save-temps the merged IR (before link time optimizations) looks like this: [...] @llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 0, void ()* @_Z4ctorv, i8* null }] ; Function Attrs: norecurse nounwind uwtable define i32 @main() #0 { entry: ret i32 0 } ; Function Attrs: nounwind uwtable declare void @_Z4ctorv() #1 [...] $ /usr/bin/ld.gold --version GNU gold (GNU Binutils for Ubuntu 2.24) 1.11
Gold says that ctor() is LDPR_PREEMPTED_REG. I think we hit this code in get_symbol_resolution_info(): // We never decided to include this object. We mark all symbols as // preempted. gold_assert(this->symbols_.size() == 0); for (int i = 0; i < nsyms; i++) syms[i].resolution = LDPR_PREEMPTED_REG; Why are we still trying to add it then?
This reduces to just @llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void \ ()*, i8* } { i32 0, void ()* @foo, i8* null }] define void @foo() { ret void } link with $ ld -plugin LLVMgold.so -shared --start-lib 1.bc --end-lib -o t.so t.so will have an undefined reference to foo. What is going on is We see @foo is preemepted_reg and we *do not* include it. The problem is that llvm.global_ctors is one of the magical llvm append variables and in handled by llvm directly. Gold doesn't know it exists. We then get an undefined reference to foo from it. This would still be an issue even with a more direct representation of static constructors: @ctor1 = private constant void()* @foo, section "llvm.metadata.ctros" since it would be a private variable, it would still be unknown to gold. The fundamental problem is that there is no way for the plugin api to distinguish two different cases * The file was not included in the link. * The file was included in the link, but every linker visible symbol is preemepted_reg A possible way to extend it would be to add a LDPT_GET_SYMBOLS_V3 that returns a new LDPS_NOT_INCLUDED_IN_LINK for the first case.
Fixed in http://reviews.llvm.org/rL262676 with ToT gold.