[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:
George Rimar 2018-01-30 09:04:27 +00:00
parent f5ad62d921
commit c4ccfb5d93
9 changed files with 157 additions and 76 deletions

View File

@ -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();

View File

@ -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));
}

View File

@ -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.

View File

@ -265,12 +265,13 @@ public:
bool needsInterpSection();
bool shouldKeep(InputSectionBase *S);
void assignAddresses();
void allocateHeaders(std::vector<PhdrEntry *> &Phdrs);
void processSectionCommands();
// SECTIONS command list.
std::vector<BaseCommand *> SectionCommands;
void assignAddresses();
void allocateHeaders(std::vector<PhdrEntry *> &Phdrs);
void processSectionCommands();
void declareSymbols();
// SECTIONS command list.
std::vector<BaseCommand *> SectionCommands;
// PHDRS command list.
std::vector<PhdrsCommand> PhdrsCommands;

View File

@ -58,20 +58,20 @@
# RUN: llvm-objdump -t %t1 | FileCheck --check-prefix=IN-SEC %s
# SIMPLE: 0000000000000128 .foo 00000000 .hidden _end_sec
# SIMPLE-NEXT: 0000000000000120 .foo 00000000 _begin_sec
# SIMPLE-NEXT: 0000000000000128 *ABS* 00000000 _end_sec_abs
# SIMPLE-NEXT: 0000000000001048 .text 00000000 _start
# SIMPLE-NEXT: 0000000000000120 .foo 00000000 begin_foo
# SIMPLE-NEXT: 0000000000000128 .foo 00000000 end_foo
# SIMPLE-NEXT: 0000000000000008 *ABS* 00000000 size_foo_1
# 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
# SIMPLE-NEXT: 0000000000000008 *ABS* 00000000 size_foo_1_abs
# 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
# SIMPLE-NEXT: 0000000000001000 .foo 00000000 begin_bar
# SIMPLE-NEXT: 0000000000001004 .foo 00000000 end_bar
# SIMPLE-NEXT: 0000000000000ee4 *ABS* 00000000 size_foo_2
# 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
# SIMPLE-NEXT: 0000000000001020 *ABS* 00000000 __eh_frame_hdr_end2
# NO-SEC: 0000000000201000 .text 00000000 .hidden _begin_sec

View File

@ -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:

View File

@ -1,18 +1,22 @@
; REQUIRES: x86
; 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: 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
; Call to bar2() should not be inlined and should be routed to bar3()
; Symbol bar3 should not be eliminated
; 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 -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 %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
; CHECK: foo:
; CHECK-NEXT: pushq %rax

View File

@ -3,22 +3,13 @@
; RUN: echo "foo = 1;" > %t.script
; RUN: ld.lld -m elf_x86_64 %t.o -o %t2 --script %t.script -save-temps
; 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:]
; RUN: llvm-readobj -symbols %t2 | FileCheck %s --check-prefix=VAL
; VAL: Symbol {
; RUN: llvm-readobj -symbols %t2.lto.o | FileCheck %s
; CHECK-NOT: bar
; CHECK-NOT: foo
; RUN: llvm-readobj -symbols %t2 | FileCheck %s --check-prefix=VAL
; VAL: Symbol {
; VAL: Name: foo
; VAL-NEXT: Value: 0x1
; VAL-NEXT: Size:

View File

@ -13,15 +13,15 @@
;; Check that LTO does not do IPO for symbols assigned by script.
; RUN: ld.lld -m elf_x86_64 %t1.o %t2.o -o %t4 --script %t.script -save-temps
; RUN: llvm-objdump -d %t4 | FileCheck %s --check-prefix=NOIPO
; NOIPO: Disassembly of section .text:
; NOIPO: foo:
; NOIPO-NEXT: 201010: {{.*}} movl $2, %eax
; NOIPO: _start:
; NOIPO-NEXT: 201020: {{.*}} jmp -21 <foo>
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
; RUN: llvm-objdump -d %t4 | FileCheck %s --check-prefix=NOIPO
; NOIPO: Disassembly of section .text:
; NOIPO: foo:
; NOIPO-NEXT: {{.*}} movl $2, %eax
; NOIPO: _start:
; 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"
define i32 @bar() {
ret i32 1