18 changes: 6 additions & 12 deletions lld/ELF/Symbols.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class BitcodeFile;
class InputFile;
class LazyObjectFile;
class SymbolBody;
struct Version;
template <class ELFT> class ObjectFile;
template <class ELFT> class OutputSection;
template <class ELFT> class OutputSectionBase;
Expand Down Expand Up @@ -302,13 +303,8 @@ template <class ELFT> class SharedSymbol : public Defined {
SharedFile<ELFT> *File;
const Elf_Sym &Sym;

// This field is initially a pointer to the symbol's version definition. As
// symbols are added to the version table, this field is replaced with the
// version identifier to be stored in .gnu.version in the output file.
union {
const Elf_Verdef *Verdef;
uint16_t VersionId;
};
// This field is a pointer to the symbol's version definition.
const Elf_Verdef *Verdef;

// OffsetInBss is significant only when needsCopy() is true.
uintX_t OffsetInBss = 0;
Expand Down Expand Up @@ -407,6 +403,9 @@ struct Symbol {
// it is weak.
uint8_t Binding;

// Version definition index.
uint16_t VersionId;

// Symbol visibility. This is the computed minimum visibility of all
// observed non-DSO symbols.
unsigned Visibility : 2;
Expand All @@ -423,11 +422,6 @@ struct Symbol {
// --export-dynamic, and by dynamic lists.
unsigned ExportDynamic : 1;

// This flag acts as an additional filter on the dynamic symbol list. It is
// set if there is no version script, or if the symbol appears in the global
// section of the version script.
unsigned VersionScriptGlobal : 1;

bool includeInDynsym() const;
bool isWeak() const { return Binding == llvm::ELF::STB_WEAK; }

Expand Down
13 changes: 11 additions & 2 deletions lld/ELF/Writer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ template <class ELFT> void elf::writeResult(SymbolTable<ELFT> *Symtab) {
std::unique_ptr<StringTableSection<ELFT>> StrTab;
std::unique_ptr<SymbolTableSection<ELFT>> SymTabSec;
std::unique_ptr<OutputSection<ELFT>> MipsRldMap;
std::unique_ptr<VersionDefinitionSection<ELFT>> VerDef;

if (Config->BuildId == BuildIdKind::Fnv1)
BuildId.reset(new BuildIdFnv1<ELFT>);
Expand Down Expand Up @@ -170,6 +171,8 @@ template <class ELFT> void elf::writeResult(SymbolTable<ELFT> *Symtab) {
MipsRldMap->setSize(sizeof(uintX_t));
MipsRldMap->updateAlignment(sizeof(uintX_t));
}
if (!Config->SymbolVersions.empty())
VerDef.reset(new VersionDefinitionSection<ELFT>());

Out<ELFT>::Bss = &Bss;
Out<ELFT>::BuildId = BuildId.get();
Expand All @@ -189,6 +192,7 @@ template <class ELFT> void elf::writeResult(SymbolTable<ELFT> *Symtab) {
Out<ELFT>::ShStrTab = &ShStrTab;
Out<ELFT>::StrTab = StrTab.get();
Out<ELFT>::SymTab = SymTabSec.get();
Out<ELFT>::VerDef = VerDef.get();
Out<ELFT>::VerSym = &VerSym;
Out<ELFT>::VerNeed = &VerNeed;
Out<ELFT>::MipsRldMap = MipsRldMap.get();
Expand Down Expand Up @@ -904,10 +908,15 @@ template <class ELFT> void Writer<ELFT>::addPredefinedSections() {
Add(Out<ELFT>::StrTab);
if (isOutputDynamic()) {
Add(Out<ELFT>::DynSymTab);
if (Out<ELFT>::VerNeed->getNeedNum() != 0) {

bool HasVerNeed = Out<ELFT>::VerNeed->getNeedNum() != 0;
if (Out<ELFT>::VerDef || HasVerNeed)
Add(Out<ELFT>::VerSym);
if (Out<ELFT>::VerDef)
Add(Out<ELFT>::VerDef);
if (HasVerNeed)
Add(Out<ELFT>::VerNeed);
}

Add(Out<ELFT>::GnuHashTab);
Add(Out<ELFT>::HashTab);
Add(Out<ELFT>::Dynamic);
Expand Down
6 changes: 6 additions & 0 deletions lld/test/ELF/Inputs/verdef.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.text
.globl _start
_start:
callq a
callq b
callq c
50 changes: 50 additions & 0 deletions lld/test/ELF/lto/version-script.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
; REQUIRES: x86
; RUN: llvm-as %s -o %t.o
; RUN: echo "VERSION_1.0{ global: foo; local: *; }; VERSION_2.0{ global: bar; local: *; };" > %t.script
; RUN: ld.lld -m elf_x86_64 %t.o -o %t2 -shared --version-script %t.script -save-temps
; RUN: llvm-dis < %t2.lto.bc | FileCheck %s
; RUN: llvm-readobj -V -dyn-symbols %t2 | FileCheck --check-prefix=DSO %s

target triple = "x86_64-unknown-linux-gnu"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"

define void @foo() {
ret void
}

define void @bar() {
ret void
}

; CHECK: define void @foo()
; CHECK: define void @bar()

; DSO: DynamicSymbols [
; DSO: Symbol {
; DSO: Name: @ (0)
; DSO: Value: 0x0
; DSO: Size: 0
; DSO: Binding: Local
; DSO: Type: None
; DSO: Other: 0
; DSO: Section: Undefined
; DSO: }
; DSO: Symbol {
; DSO: Name: foo@@VERSION_1.0
; DSO: Value: 0x1000
; DSO: Size: 1
; DSO: Binding: Global
; DSO: Type: Function
; DSO: Other: 0
; DSO: Section: .text
; DSO: }
; DSO: Symbol {
; DSO: Name: bar@@VERSION_2.0
; DSO: Value: 0x1010
; DSO: Size: 1
; DSO: Binding: Global
; DSO: Type: Function
; DSO: Other: 0
; DSO: Section: .text
; DSO: }
; DSO: ]
131 changes: 131 additions & 0 deletions lld/test/ELF/verdef.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
# RUN: echo "LIBSAMPLE_1.0{ \
# RUN: global: a; \
# RUN: local: *; }; \
# RUN: LIBSAMPLE_2.0{ \
# RUN: global: b; \
# RUN: local: *; }; \
# RUN: LIBSAMPLE_3.0{ \
# RUN: global: c; \
# RUN: local: *; };" > %t.script
# RUN: ld.lld --version-script %t.script -shared %t.o -o %t.so
# RUN: llvm-readobj -V -dyn-symbols %t.so | FileCheck --check-prefix=DSO %s

# DSO: Version symbols {
# DSO-NEXT: Section Name: .gnu.version
# DSO-NEXT: Address: 0x228
# DSO-NEXT: Offset: 0x228
# DSO-NEXT: Link: 1
# DSO-NEXT: Symbols [
# DSO-NEXT: Symbol {
# DSO-NEXT: Version: 0
# DSO-NEXT: Name: @
# DSO-NEXT: }
# DSO-NEXT: Symbol {
# DSO-NEXT: Version: 2
# DSO-NEXT: Name: a@@LIBSAMPLE_1.0
# DSO-NEXT: }
# DSO-NEXT: Symbol {
# DSO-NEXT: Version: 3
# DSO-NEXT: Name: b@@LIBSAMPLE_2.0
# DSO-NEXT: }
# DSO-NEXT: Symbol {
# DSO-NEXT: Version: 4
# DSO-NEXT: Name: c@@LIBSAMPLE_3.0
# DSO-NEXT: }
# DSO-NEXT: ]
# DSO-NEXT: }
# DSO-NEXT: Version definition {
# DSO-NEXT: Section Name: .gnu.version_d
# DSO-NEXT: Address: 0x230
# DSO-NEXT: Offset: 0x230
# DSO-NEXT: Link: 5
# DSO-NEXT: Entries [
# DSO-NEXT: Entry {
# DSO-NEXT: Offset: 0x0
# DSO-NEXT: Rev: 1
# DSO-NEXT: Flags: 1
# DSO-NEXT: Index: 1
# DSO-NEXT: Cnt: 1
# DSO-NEXT: Hash: 146942095
# DSO-NEXT: Name:
# DSO-NEXT: }
# DSO-NEXT: Entry {
# DSO-NEXT: Offset: 0x14
# DSO-NEXT: Rev: 1
# DSO-NEXT: Flags: 0
# DSO-NEXT: Index: 2
# DSO-NEXT: Cnt: 1
# DSO-NEXT: Hash: 98457184
# DSO-NEXT: Name: LIBSAMPLE_1.0
# DSO-NEXT: }
# DSO-NEXT: Entry {
# DSO-NEXT: Offset: 0x28
# DSO-NEXT: Rev: 1
# DSO-NEXT: Flags: 0
# DSO-NEXT: Index: 3
# DSO-NEXT: Cnt: 1
# DSO-NEXT: Hash: 98456416
# DSO-NEXT: Name: LIBSAMPLE_2.0
# DSO-NEXT: }
# DSO-NEXT: Entry {
# DSO-NEXT: Offset: 0x3C
# DSO-NEXT: Rev: 1
# DSO-NEXT: Flags: 0
# DSO-NEXT: Index: 4
# DSO-NEXT: Cnt: 1
# DSO-NEXT: Hash: 98456672
# DSO-NEXT: Name: LIBSAMPLE_3.0
# DSO-NEXT: }
# DSO-NEXT: ]
# DSO-NEXT: }
# DSO-NEXT: SHT_GNU_verneed {
# DSO-NEXT: }

## Check that we can link agains DSO we produced.
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %S/Inputs/verdef.s -o %tmain.o
# RUN: ld.lld %tmain.o %t.so -o %tout
# RUN: llvm-readobj -V %tout | FileCheck --check-prefix=MAIN %s

# MAIN: Version symbols {
# MAIN-NEXT: Section Name: .gnu.version
# MAIN-NEXT: Address: 0x10228
# MAIN-NEXT: Offset: 0x228
# MAIN-NEXT: Link: 1
# MAIN-NEXT: Symbols [
# MAIN-NEXT: Symbol {
# MAIN-NEXT: Version: 0
# MAIN-NEXT: Name: @
# MAIN-NEXT: }
# MAIN-NEXT: Symbol {
# MAIN-NEXT: Version: 2
# MAIN-NEXT: Name: a@LIBSAMPLE_1.0
# MAIN-NEXT: }
# MAIN-NEXT: Symbol {
# MAIN-NEXT: Version: 3
# MAIN-NEXT: Name: b@LIBSAMPLE_2.0
# MAIN-NEXT: }
# MAIN-NEXT: Symbol {
# MAIN-NEXT: Version: 4
# MAIN-NEXT: Name: c@LIBSAMPLE_3.0
# MAIN-NEXT: }
# MAIN-NEXT: ]
# MAIN-NEXT: }
# MAIN-NEXT: Version definition {
# MAIN-NEXT: }

.globl a
.type a,@function
a:
retq

.globl b
.type b,@function
b:
retq

.globl c
.type c,@function
c:
retq
40 changes: 39 additions & 1 deletion lld/test/ELF/version-script.s
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
# RUN: global: foo3; \
# RUN: local: *; }; " > %t4.script
# RUN: ld.lld --version-script %t4.script -shared %t.o %t2.so -o %t4.so
# RUN: llvm-readobj -dyn-symbols %t4.so | FileCheck --check-prefix=DSO %s
# RUN: llvm-readobj -dyn-symbols %t4.so | FileCheck --check-prefix=VERDSO %s

# RUN: echo "VERSION_1.0{ \
# RUN: global: foo1; \
Expand Down Expand Up @@ -140,6 +140,44 @@
# EXE-NEXT: }
# EXE-NEXT: ]

# VERDSO: DynamicSymbols [
# VERDSO-NEXT: Symbol {
# VERDSO-NEXT: Name: @
# VERDSO-NEXT: Value: 0x0
# VERDSO-NEXT: Size: 0
# VERDSO-NEXT: Binding: Local
# VERDSO-NEXT: Type: None
# VERDSO-NEXT: Other: 0
# VERDSO-NEXT: Section: Undefined
# VERDSO-NEXT: }
# VERDSO-NEXT: Symbol {
# VERDSO-NEXT: Name: bar@
# VERDSO-NEXT: Value: 0x0
# VERDSO-NEXT: Size: 0
# VERDSO-NEXT: Binding: Global
# VERDSO-NEXT: Type: Function
# VERDSO-NEXT: Other: 0
# VERDSO-NEXT: Section: Undefined
# VERDSO-NEXT: }
# VERDSO-NEXT: Symbol {
# VERDSO-NEXT: Name: foo1@@VERSION_1.0
# VERDSO-NEXT: Value: 0x1000
# VERDSO-NEXT: Size: 0
# VERDSO-NEXT: Binding: Global
# VERDSO-NEXT: Type: None
# VERDSO-NEXT: Other: 0
# VERDSO-NEXT: Section: .text
# VERDSO-NEXT: }
# VERDSO-NEXT: Symbol {
# VERDSO-NEXT: Name: foo3@@VERSION_2.0
# VERDSO-NEXT: Value: 0x1007
# VERDSO-NEXT: Size: 0
# VERDSO-NEXT: Binding: Global
# VERDSO-NEXT: Type: None
# VERDSO-NEXT: Other: 0
# VERDSO-NEXT: Section: .text
# VERDSO-NEXT: }
# VERDSO-NEXT: ]

# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
# RUN: ld.lld -shared %t.o %t2.so -o %t.so
Expand Down