Index: ELF/Driver.cpp =================================================================== --- ELF/Driver.cpp +++ ELF/Driver.cpp @@ -1063,6 +1063,10 @@ if (!Config->Relocatable) addReservedSymbols(); + // We want to declare linker script's symbols early, + // so that we can version them. + Script->declareSymbols(); + // Apply version scripts. Symtab->scanVersionScript(); Index: ELF/LTO.cpp =================================================================== --- ELF/LTO.cpp +++ ELF/LTO.cpp @@ -129,11 +129,6 @@ std::vector Syms = F.getSymbols(); std::vector Resols(Syms.size()); - DenseSet ScriptSymbols; - for (BaseCommand *Base : Script->SectionCommands) - if (auto *Cmd = dyn_cast(Base)) - ScriptSymbols.insert(Cmd->Name); - bool IsExecutable = !Config->Shared && !Config->Relocatable; // Provide a resolution to the LTO API for each symbol. @@ -164,12 +159,10 @@ if (R.Prevailing) undefine(Sym); - // We tell LTO to not apply interprocedural optimization for following - // symbols because otherwise LTO would inline them while their values are - // still not final: - // 1) Aliased (with --defsym) or wrapped (with --wrap) symbols. - // 2) Symbols redefined in linker script. - R.LinkerRedefined = !Sym->CanInline || ScriptSymbols.count(Sym->getName()); + // We tell LTO to not apply interprocedural optimization for wrapped + // (with --wrap) symbols because otherwise LTO would inline them while + // their values are still not final. + R.LinkerRedefined = !Sym->CanInline; } checkError(LTOObj->add(std::move(F.Obj), Resols)); } Index: ELF/LinkerScript.h =================================================================== --- ELF/LinkerScript.h +++ ELF/LinkerScript.h @@ -268,6 +268,7 @@ void assignAddresses(); void allocateHeaders(std::vector &Phdrs); void processSectionCommands(); + void declareSymbols(); // SECTIONS command list. std::vector SectionCommands; Index: ELF/LinkerScript.cpp =================================================================== --- ELF/LinkerScript.cpp +++ ELF/LinkerScript.cpp @@ -114,16 +114,28 @@ Ctx->OutSec->Size = Dot - Ctx->OutSec->Addr; } -// This function is called from processSectionCommands, -// while we are fixing the output section layout. -void LinkerScript::addSymbol(SymbolAssignment *Cmd) { +// Used for handling linker symbol assignments, for both finalizing +// their values and doing early declarations. Returns true if symbol +// should be defined from linker script. +static bool shouldDefineSym(SymbolAssignment *Cmd) { if (Cmd->Name == ".") - return; + return false; - // If a symbol was in PROVIDE(), we need to define it only when - // it is a referenced undefined symbol. + if (!Cmd->Provide) + return true; + + // If a symbol was in PROVIDE(), we need to define it only + // when it is a referenced undefined symbol. Symbol *B = Symtab->find(Cmd->Name); - if (Cmd->Provide && (!B || B->isDefined())) + if (!B || B->isDefined()) + return false; + return true; +} + +// This function is called from processSectionCommands, +// while we are fixing the output section layout. +void LinkerScript::addSymbol(SymbolAssignment *Cmd) { + if (!shouldDefineSym(Cmd)) return; // Define a symbol. @@ -153,6 +165,30 @@ Cmd->Sym = cast(Sym); } +// Symbols defined in script should not be inlined by LTO. At the same time +// we don't know their final values until late stages of link. Here we scan +// over symbol assignment commands and create placeholder symbols if needed. +void LinkerScript::declareSymbols() { + assert(!Ctx); + for (BaseCommand *Base : SectionCommands) { + auto *Cmd = dyn_cast(Base); + if (!Cmd || !shouldDefineSym(Cmd)) + continue; + + // We can't calculate final value right now. + Symbol *Sym; + uint8_t Visibility = Cmd->Hidden ? STV_HIDDEN : STV_DEFAULT; + std::tie(Sym, std::ignore) = + Symtab->insert(Cmd->Name, /*Type*/ 0, Visibility, + /*CanOmitFromDynSym*/ false, + /*File*/ nullptr); + replaceSymbol(Sym, nullptr, Cmd->Name, STB_GLOBAL, Visibility, + STT_NOTYPE, 0, 0, nullptr); + Cmd->Sym = cast(Sym); + Cmd->Provide = false; + } +} + // This function is called from assignAddresses, while we are // fixing the output section addresses. This function is supposed // to set the final value for a given symbol assignment. Index: test/ELF/linkerscript/symbols-synthetic.s =================================================================== --- test/ELF/linkerscript/symbols-synthetic.s +++ test/ELF/linkerscript/symbols-synthetic.s @@ -61,6 +61,7 @@ # SIMPLE-NEXT: 0000000000000120 .foo 00000000 _begin_sec # SIMPLE-NEXT: 0000000000000128 *ABS* 00000000 _end_sec_abs # SIMPLE-NEXT: 0000000000001048 .text 00000000 _start +# SIMPLE-NEXT: 0000000000000ee4 *ABS* 00000000 size_foo_3 # SIMPLE-NEXT: 0000000000000120 .foo 00000000 begin_foo # SIMPLE-NEXT: 0000000000000128 .foo 00000000 end_foo # SIMPLE-NEXT: 0000000000000008 *ABS* 00000000 size_foo_1 @@ -68,7 +69,6 @@ # SIMPLE-NEXT: 0000000000001000 .foo 00000000 begin_bar # SIMPLE-NEXT: 0000000000001004 .foo 00000000 end_bar # SIMPLE-NEXT: 0000000000000ee4 *ABS* 00000000 size_foo_2 -# SIMPLE-NEXT: 0000000000000ee4 *ABS* 00000000 size_foo_3 # SIMPLE-NEXT: 0000000000001004 .eh_frame_hdr 00000000 __eh_frame_hdr_start # SIMPLE-NEXT: 0000000000001010 *ABS* 00000000 __eh_frame_hdr_start2 # SIMPLE-NEXT: 0000000000001018 .eh_frame_hdr 00000000 __eh_frame_hdr_end Index: test/ELF/lto/defsym.ll =================================================================== --- test/ELF/lto/defsym.ll +++ test/ELF/lto/defsym.ll @@ -2,14 +2,18 @@ ; LTO ; RUN: llvm-as %s -o %t.o ; RUN: llvm-as %S/Inputs/defsym-bar.ll -o %t1.o -; RUN: ld.lld %t.o %t1.o -shared -o %t.so -defsym=bar2=bar3 +; RUN: ld.lld %t.o %t1.o -shared -o %t.so -defsym=bar2=bar3 -save-temps +; RUN: llvm-readelf -t %t.so.lto.o | FileCheck --check-prefix=OBJ %s ; RUN: llvm-objdump -d %t.so | FileCheck %s ; ThinLTO ; RUN: opt -module-summary %s -o %t.o ; RUN: opt -module-summary %S/Inputs/defsym-bar.ll -o %t1.o -; RUN: ld.lld %t.o %t1.o -shared -o %t.so -defsym=bar2=bar3 -; RUN: llvm-objdump -d %t.so | FileCheck %s --check-prefix=THIN +; RUN: ld.lld %t.o %t1.o -shared -o %t2.so -defsym=bar2=bar3 -save-temps +; RUN: llvm-readelf -t %t2.so1.lto.o | FileCheck --check-prefix=OBJ %s +; RUN: llvm-objdump -d %t2.so | FileCheck %s --check-prefix=THIN + +; OBJ: UND bar2 ; Call to bar2() should not be inlined and should be routed to bar3() ; Symbol bar3 should not be eliminated Index: test/ELF/lto/linker-script-symbols-assign.ll =================================================================== --- test/ELF/lto/linker-script-symbols-assign.ll +++ test/ELF/lto/linker-script-symbols-assign.ll @@ -6,16 +6,7 @@ ; RUN: llvm-readobj -symbols %t2.lto.o | FileCheck %s ; CHECK-NOT: bar -; CHECK: Symbol { -; CHECK: Name: foo -; CHECK-NEXT: Value: 0x0 -; CHECK-NEXT: Size: 4 -; CHECK-NEXT: Binding: Weak -; CHECK-NEXT: Type: Object -; CHECK-NEXT: Other: 0 -; CHECK-NEXT: Section: .bss.foo -; CHECK-NEXT: } -; CHECK-NEXT:] +; CHECK-NOT: foo ; RUN: llvm-readobj -symbols %t2 | FileCheck %s --check-prefix=VAL ; VAL: Symbol { Index: test/ELF/lto/linker-script-symbols-ipo.ll =================================================================== --- test/ELF/lto/linker-script-symbols-ipo.ll +++ test/ELF/lto/linker-script-symbols-ipo.ll @@ -16,9 +16,9 @@ ; RUN: llvm-objdump -d %t4 | FileCheck %s --check-prefix=NOIPO ; NOIPO: Disassembly of section .text: ; NOIPO: foo: -; NOIPO-NEXT: 201010: {{.*}} movl $2, %eax +; NOIPO-NEXT: {{.*}} movl $2, %eax ; NOIPO: _start: -; NOIPO-NEXT: 201020: {{.*}} jmp -21 +; NOIPO-NEXT: {{.*}} jmp -21 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-unknown-linux-gnu"