[ELF] - Define linkerscript symbols early.
Currently symbols assigned or created by linkerscript are not processed early enough. As a result it is not possible to version them or assign any other flags/properties. Patch creates Defined symbols for -defsym and linkerscript symbols early, so that issue from above can be addressed. It is based on Rafael Espindola's version of D38239 patch. Fixes PR34121. Differential revision: https://reviews.llvm.org/D41987 llvm-svn: 323729
This commit is contained in:
parent
f5ad62d921
commit
c4ccfb5d93
|
@ -1063,6 +1063,10 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
|
|||
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();
|
||||
|
||||
|
|
|
@ -129,11 +129,6 @@ void BitcodeCompiler::add(BitcodeFile &F) {
|
|||
std::vector<Symbol *> Syms = F.getSymbols();
|
||||
std::vector<lto::SymbolResolution> Resols(Syms.size());
|
||||
|
||||
DenseSet<StringRef> ScriptSymbols;
|
||||
for (BaseCommand *Base : Script->SectionCommands)
|
||||
if (auto *Cmd = dyn_cast<SymbolAssignment>(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 @@ void BitcodeCompiler::add(BitcodeFile &F) {
|
|||
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));
|
||||
}
|
||||
|
|
|
@ -114,16 +114,28 @@ void LinkerScript::setDot(Expr E, const Twine &Loc, bool InSec) {
|
|||
Ctx->OutSec->Size = Dot - Ctx->OutSec->Addr;
|
||||
}
|
||||
|
||||
// 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 false;
|
||||
|
||||
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 (!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 (Cmd->Name == ".")
|
||||
return;
|
||||
|
||||
// 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 (!shouldDefineSym(Cmd))
|
||||
return;
|
||||
|
||||
// Define a symbol.
|
||||
|
@ -153,6 +165,30 @@ void LinkerScript::addSymbol(SymbolAssignment *Cmd) {
|
|||
Cmd->Sym = cast<Defined>(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<SymbolAssignment>(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<Defined>(Sym, nullptr, Cmd->Name, STB_GLOBAL, Visibility,
|
||||
STT_NOTYPE, 0, 0, nullptr);
|
||||
Cmd->Sym = cast<Defined>(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.
|
||||
|
|
|
@ -268,6 +268,7 @@ public:
|
|||
void assignAddresses();
|
||||
void allocateHeaders(std::vector<PhdrEntry *> &Phdrs);
|
||||
void processSectionCommands();
|
||||
void declareSymbols();
|
||||
|
||||
// SECTIONS command list.
|
||||
std::vector<BaseCommand *> SectionCommands;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
# REQUIRES: x86
|
||||
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
|
||||
|
||||
# RUN: echo "bar = foo; VERSION { V { global: foo; bar; local: *; }; }" > %t.script
|
||||
# RUN: ld.lld -T %t.script -shared --no-undefined-version %t.o -o %t.so
|
||||
# RUN: llvm-readobj -V %t.so | FileCheck %s
|
||||
|
||||
## Check that we are able to version symbols defined in script.
|
||||
# CHECK: Symbols [
|
||||
# CHECK-NEXT: Symbol {
|
||||
# CHECK-NEXT: Version: 0
|
||||
# CHECK-NEXT: Name: @
|
||||
# CHECK-NEXT: }
|
||||
# CHECK-NEXT: Symbol {
|
||||
# CHECK-NEXT: Version: 0
|
||||
# CHECK-NEXT: Name: und@
|
||||
# CHECK-NEXT: }
|
||||
# CHECK-NEXT: Symbol {
|
||||
# CHECK-NEXT: Version: 2
|
||||
# CHECK-NEXT: Name: foo@@V
|
||||
# CHECK-NEXT: }
|
||||
# CHECK-NEXT: Symbol {
|
||||
# CHECK-NEXT: Version: 2
|
||||
# CHECK-NEXT: Name: bar@@V
|
||||
# CHECK-NEXT: }
|
||||
# CHECK-NEXT: ]
|
||||
|
||||
# RUN: echo "bar = und; VERSION { V { global: foo; bar; local: *; }; }" > %t.script
|
||||
# RUN: not ld.lld -T %t.script -shared --no-undefined-version %t.o -o %t.so \
|
||||
# RUN: 2>&1 | FileCheck --check-prefix=ERR %s
|
||||
# ERR: symbol not found: und
|
||||
|
||||
# RUN: echo "und = 0x1; VERSION { V { global: und; local: *; }; }" > %t.script
|
||||
# RUN: ld.lld -T %t.script -shared --no-undefined-version %t.o -o %t.so
|
||||
# RUN: llvm-readobj -V %t.so | FileCheck %s --check-prefix=UNDEF
|
||||
# UNDEF: Symbols [
|
||||
# UNDEF-NEXT: Symbol {
|
||||
# UNDEF-NEXT: Version: 0
|
||||
# UNDEF-NEXT: Name: @
|
||||
# UNDEF-NEXT: }
|
||||
# UNDEF-NEXT: Symbol {
|
||||
# UNDEF-NEXT: Version: 2
|
||||
# UNDEF-NEXT: Name: und@@V
|
||||
# UNDEF-NEXT: }
|
||||
# UNDEF-NEXT: ]
|
||||
|
||||
.global und
|
||||
|
||||
.text
|
||||
.globl foo
|
||||
.type foo,@function
|
||||
foo:
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 <foo>
|
||||
; NOIPO-NEXT: {{.*}} jmp -21 <foo>
|
||||
|
||||
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
|
||||
target triple = "x86_64-unknown-linux-gnu"
|
||||
|
|
Loading…
Reference in New Issue