Skip to content

Commit e6a9f8c

Browse files
committed
[ELF] Merge exportDynamic into versionId
For a Defined/Common symbol with a false `inDynamicList`, both `versionId==VER_NDX_LOCAL` and `!exportDynamic` indicate that the symbol should not be exported, which means that the two fields have overlapping purposes. We can merge them together by reserving `versionId==-1` to indicate a symbol that is not exported: * -1 (initial): not exported, binding unchanged * 0: VER_NDX_LOCAL, not exported, binding changed to STB_LOCAL * 1: VER_NDX_GLOBAL, exported, binding unchanged * others: verdef index, exported, binding unchanged -1 and 0 are similar, but -1 does not change the binding to STB_LOCAL. The saved bit can be use for another purpose, e.g. whether a symbol has a DSO definition (llvm#70130). --version-script is almost never used for executables. If used, a minor behavior change is that a version pattern that is not `local:` will export the matched symbols.
1 parent e6bd68c commit e6a9f8c

File tree

11 files changed

+55
-42
lines changed

11 files changed

+55
-42
lines changed

lld/ELF/Config.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@ struct Config {
225225
bool cref;
226226
llvm::SmallVector<std::pair<llvm::GlobPattern, uint64_t>, 0>
227227
deadRelocInNonAlloc;
228+
uint16_t defaultVersionId;
228229
bool demangle = true;
229230
bool dependentLibraries;
230231
bool disableVerify;

lld/ELF/Driver.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1683,6 +1683,9 @@ static void readConfigs(opt::InputArgList &args) {
16831683
}
16841684
}
16851685

1686+
config->defaultVersionId =
1687+
config->exportDynamic ? ELF::VER_NDX_GLOBAL : nonExported;
1688+
16861689
assert(config->versionDefinitions.empty());
16871690
config->versionDefinitions.push_back(
16881691
{"local", (uint16_t)VER_NDX_LOCAL, {}, {}});
@@ -2518,9 +2521,9 @@ static void combineVersionedSymbol(Symbol &sym,
25182521
sym.symbolKind = Symbol::PlaceholderKind;
25192522
sym.isUsedInRegularObj = false;
25202523
} else if (auto *sym1 = dyn_cast<Defined>(&sym)) {
2521-
if (sym2->versionId > VER_NDX_GLOBAL
2522-
? config->versionDefinitions[sym2->versionId].name == suffix1 + 1
2523-
: sym1->section == sym2->section && sym1->value == sym2->value) {
2524+
if (sym2->versionId == VER_NDX_GLOBAL || sym2->versionId == nonExported
2525+
? sym1->section == sym2->section && sym1->value == sym2->value
2526+
: config->versionDefinitions[sym2->versionId].name == suffix1 + 1) {
25242527
// Due to an assembler design flaw, if foo is defined, .symver foo,
25252528
// foo@v1 defines both foo and foo@v1. Unless foo is bound to a
25262529
// different version, GNU ld makes foo@v1 canonical and eliminates

lld/ELF/InputFiles.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1520,7 +1520,7 @@ template <class ELFT> void SharedFile::parse() {
15201520
}
15211521
Symbol *s = symtab.addSymbol(
15221522
Undefined{this, name, sym.getBinding(), sym.st_other, sym.getType()});
1523-
s->exportDynamic = true;
1523+
s->versionId = VER_NDX_GLOBAL;
15241524
if (s->isUndefined() && sym.getBinding() != STB_WEAK &&
15251525
config->unresolvedSymbolsInShlib != UnresolvedPolicy::Ignore)
15261526
requiredSymbols.push_back(s);
@@ -1698,7 +1698,7 @@ createBitcodeSymbol(Symbol *&sym, const std::vector<bool> &keptComdats,
16981698
} else {
16991699
Defined newSym(&f, StringRef(), binding, visibility, type, 0, 0, nullptr);
17001700
if (objSym.canBeOmittedFromSymbolTable())
1701-
newSym.exportDynamic = false;
1701+
newSym.versionId = nonExported;
17021702
sym->resolve(newSym);
17031703
}
17041704
}

lld/ELF/LTO.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -245,9 +245,9 @@ void BitcodeCompiler::add(BitcodeFile &f) {
245245
usedStartStop.count(objSym.getSectionName());
246246
// Identify symbols exported dynamically, and that therefore could be
247247
// referenced by a shared library not visible to the linker.
248-
r.ExportDynamic =
249-
sym->computeBinding() != STB_LOCAL &&
250-
(config->exportDynamic || sym->exportDynamic || sym->inDynamicList);
248+
r.ExportDynamic = sym->computeBinding() != STB_LOCAL &&
249+
(config->exportDynamic || sym->versionId != nonExported ||
250+
sym->inDynamicList);
251251
const auto *dr = dyn_cast<Defined>(sym);
252252
r.FinalDefinitionInLinkageUnit =
253253
(isExec || sym->visibility() != STV_DEFAULT) && dr &&

lld/ELF/Relocations.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@ static void replaceWithDefined(Symbol &sym, SectionBase &sec, uint64_t value,
310310
.overwrite(sym);
311311

312312
sym.verdefIndex = old.verdefIndex;
313-
sym.exportDynamic = true;
313+
sym.exportIfNonExported();
314314
sym.isUsedInRegularObj = true;
315315
// A copy relocated alias may need a GOT entry.
316316
sym.flags.store(old.flags.load(std::memory_order_relaxed) & NEEDS_GOT,
@@ -1068,7 +1068,7 @@ void RelocationScanner::processAux(RelExpr expr, RelType type, uint64_t offset,
10681068
// direct relocation on through.
10691069
if (LLVM_UNLIKELY(isIfunc) && config->zIfuncNoplt) {
10701070
std::lock_guard<std::mutex> lock(relocMutex);
1071-
sym.exportDynamic = true;
1071+
sym.exportIfNonExported();
10721072
mainPart->relaDyn->addSymbolReloc(type, *sec, offset, sym, addend, type);
10731073
return;
10741074
}

lld/ELF/SymbolTable.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ Symbol *SymbolTable::insert(StringRef name) {
9393
sym->setName(name);
9494
sym->partition = 1;
9595
sym->verdefIndex = -1;
96-
sym->versionId = VER_NDX_GLOBAL;
96+
sym->versionId = nonExported;
9797
if (pos != StringRef::npos)
9898
sym->hasVersionSuffix = true;
9999
return sym;

lld/ELF/Symbols.cpp

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ bool Symbol::includeInDynsym() const {
287287
// __pthread_initialize_minimal reference in csu/libc-start.c.
288288
return !(isUndefWeak() && config->noDynamicLinker);
289289

290-
return exportDynamic || inDynamicList;
290+
return versionId != nonExported || inDynamicList;
291291
}
292292

293293
// Print out a log message for --trace-symbol.
@@ -384,8 +384,8 @@ bool elf::computeIsPreemptible(const Symbol &sym) {
384384
// and that's the result of symbol resolution. However, symbols that
385385
// were not chosen still affect some symbol properties.
386386
void Symbol::mergeProperties(const Symbol &other) {
387-
if (other.exportDynamic)
388-
exportDynamic = true;
387+
if (versionId == nonExported)
388+
versionId = other.versionId;
389389

390390
// DSO symbols do not affect visibility in the output.
391391
if (!other.isShared() && other.visibility() != STV_DEFAULT) {
@@ -575,8 +575,8 @@ void Symbol::checkDuplicate(const Defined &other) const {
575575
}
576576

577577
void Symbol::resolve(const CommonSymbol &other) {
578-
if (other.exportDynamic)
579-
exportDynamic = true;
578+
if (versionId == nonExported)
579+
versionId = other.versionId;
580580
if (other.visibility() != STV_DEFAULT) {
581581
uint8_t v = visibility(), ov = other.visibility();
582582
setVisibility(v == STV_DEFAULT ? ov : std::min(v, ov));
@@ -613,8 +613,8 @@ void Symbol::resolve(const CommonSymbol &other) {
613613
}
614614

615615
void Symbol::resolve(const Defined &other) {
616-
if (other.exportDynamic)
617-
exportDynamic = true;
616+
if (versionId == nonExported)
617+
versionId = other.versionId;
618618
if (other.visibility() != STV_DEFAULT) {
619619
uint8_t v = visibility(), ov = other.visibility();
620620
setVisibility(v == STV_DEFAULT ? ov : std::min(v, ov));
@@ -663,7 +663,7 @@ void Symbol::resolve(const LazyObject &other) {
663663
}
664664

665665
void Symbol::resolve(const SharedSymbol &other) {
666-
exportDynamic = true;
666+
exportIfNonExported();
667667
if (isPlaceholder()) {
668668
other.overwrite(*this);
669669
return;

lld/ELF/Symbols.h

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ struct SymbolAux {
6767

6868
LLVM_LIBRARY_VISIBILITY extern SmallVector<SymbolAux, 0> symAux;
6969

70+
// A versionId value similar to VER_NDX_LOCAL, but the binding is not changed.
71+
constexpr uint16_t nonExported = uint16_t(-1);
72+
7073
// The base class for real symbol classes.
7174
class Symbol {
7275
public:
@@ -129,17 +132,6 @@ class Symbol {
129132
// which are referenced by relocations when -r or --emit-relocs is given.
130133
uint8_t used : 1;
131134

132-
// Used by a Defined symbol with protected or default visibility, to record
133-
// whether it is required to be exported into .dynsym. This is set when any of
134-
// the following conditions hold:
135-
//
136-
// - If there is an interposable symbol from a DSO. Note: We also do this for
137-
// STV_PROTECTED symbols which can't be interposed (to match BFD behavior).
138-
// - If -shared or --export-dynamic is specified, any symbol in an object
139-
// file/bitcode sets this property, unless suppressed by LTO
140-
// canBeOmittedFromSymbolTable().
141-
uint8_t exportDynamic : 1;
142-
143135
// True if the symbol is in the --dynamic-list file. A Defined symbol with
144136
// protected or default visibility with this property is required to be
145137
// exported into .dynsym.
@@ -254,7 +246,7 @@ class Symbol {
254246
Symbol(Kind k, InputFile *file, StringRef name, uint8_t binding,
255247
uint8_t stOther, uint8_t type)
256248
: file(file), nameData(name.data()), nameSize(name.size()), type(type),
257-
binding(binding), stOther(stOther), symbolKind(k), exportDynamic(false),
249+
binding(binding), stOther(stOther), symbolKind(k),
258250
archSpecificBit(false) {}
259251

260252
void overwrite(Symbol &sym, Kind k) const {
@@ -316,9 +308,25 @@ class Symbol {
316308
// This field is a index to the symbol's version definition.
317309
uint16_t verdefIndex;
318310

319-
// Version definition index.
320-
uint16_t versionId;
311+
// Used by a Defined symbol with protected or default visibility, to record
312+
// the verdef index and whether the symbol is exported into .dynsym.
313+
// * -1 (initial): not exported, binding unchanged
314+
// * 0: VER_NDX_LOCAL, not exported, binding changed to STB_LOCAL
315+
// * 1: VER_NDX_GLOBAL, exported, binding unchanged
316+
// * others: verdef index, exported, binding unchanged
317+
//
318+
// -1 transits to 1 if any of the following conditions hold:
319+
// * If there is an interposable symbol from a DSO. Note: We also do this for
320+
// STV_PROTECTED symbols which can't be interposed (to match BFD behavior).
321+
// * If -shared or --export-dynamic is specified, any symbol in an object
322+
// file/bitcode sets this property, unless suppressed by LTO
323+
// canBeOmittedFromSymbolTable().
324+
uint16_t versionId = nonExported;
321325

326+
void exportIfNonExported() {
327+
if (versionId == nonExported)
328+
versionId = llvm::ELF::VER_NDX_GLOBAL;
329+
}
322330
void setFlags(uint16_t bits) {
323331
flags.fetch_or(bits, std::memory_order_relaxed);
324332
}
@@ -353,7 +361,7 @@ class Defined : public Symbol {
353361
uint8_t type, uint64_t value, uint64_t size, SectionBase *section)
354362
: Symbol(DefinedKind, file, name, binding, stOther, type), value(value),
355363
size(size), section(section) {
356-
exportDynamic = config->exportDynamic;
364+
versionId = config->defaultVersionId;
357365
}
358366
void overwrite(Symbol &sym) const {
359367
Symbol::overwrite(sym, DefinedKind);
@@ -398,7 +406,7 @@ class CommonSymbol : public Symbol {
398406
uint8_t stOther, uint8_t type, uint64_t alignment, uint64_t size)
399407
: Symbol(CommonKind, file, name, binding, stOther, type),
400408
alignment(alignment), size(size) {
401-
exportDynamic = config->exportDynamic;
409+
versionId = config->defaultVersionId;
402410
}
403411
void overwrite(Symbol &sym) const {
404412
Symbol::overwrite(sym, CommonKind);
@@ -442,7 +450,7 @@ class SharedSymbol : public Symbol {
442450
uint32_t alignment)
443451
: Symbol(SharedKind, &file, name, binding, stOther, type), value(value),
444452
size(size), alignment(alignment) {
445-
exportDynamic = true;
453+
versionId = llvm::ELF::VER_NDX_GLOBAL;
446454
dsoProtected = visibility() == llvm::ELF::STV_PROTECTED;
447455
// GNU ifunc is a mechanism to allow user-supplied functions to
448456
// resolve PLT slot values at load-time. This is contrary to the

lld/ELF/SyntheticSections.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3128,7 +3128,8 @@ void VersionTableSection::writeTo(uint8_t *buf) {
31283128
// For an unextracted lazy symbol (undefined weak), it must have been
31293129
// converted to Undefined and have VER_NDX_GLOBAL version here.
31303130
assert(!s.sym->isLazy());
3131-
write16(buf, s.sym->versionId);
3131+
write16(buf, s.sym->versionId == nonExported ? VER_NDX_GLOBAL
3132+
: s.sym->versionId);
31323133
buf += 2;
31333134
}
31343135
}

lld/test/ELF/symver-non-default.s

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@
2020
# RUN: ld.lld -pie --version-script=%t/ver1 %t/def1.o %t/ref.o -o %t1
2121
# RUN: llvm-readelf -s %t1 | FileCheck %s --check-prefix=EXE1
2222

23-
# EXE1: Symbol table '.dynsym' contains 1 entries:
23+
# EXE1: Symbol table '.dynsym' contains 2 entries:
24+
# EXE: 1: {{.*}} NOTYPE GLOBAL DEFAULT [[#]] foo@v1
2425
# EXE1: Symbol table '.symtab' contains 3 entries:
2526
# EXE1: 2: {{.*}} NOTYPE GLOBAL DEFAULT [[#]] foo{{$}}
2627

@@ -49,7 +50,8 @@
4950
# RUN: ld.lld -pie --version-script=%t/ver1 %t/def2.o %t/def3.o %t/ref.o -o %t3
5051
# RUN: llvm-readelf -s %t3 | FileCheck %s --check-prefix=EXE3
5152

52-
# EXE3: Symbol table '.dynsym' contains 1 entries:
53+
# EXE3: Symbol table '.dynsym' contains 2 entries:
54+
# EXE3: 1: {{.*}} NOTYPE GLOBAL DEFAULT [[#]] foo@v1
5355
# EXE3: Symbol table '.symtab' contains 4 entries:
5456
# EXE3: 2: {{.*}} NOTYPE GLOBAL DEFAULT [[#SEC:]] foo{{$}}
5557
# EXE3-NEXT: 3: {{.*}} NOTYPE GLOBAL DEFAULT [[#SEC]] foo{{$}}

lld/test/ELF/version-script-symver.s

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,7 @@
4242
# RUN: ld.lld --version-script %t4.script -pie --export-dynamic %t.o -o %t4
4343
# RUN: llvm-readelf --dyn-syms %t4 | FileCheck --check-prefix=MIX2 %s
4444
# RUN: ld.lld --version-script %t4.script -pie %t.o -o %t4
45-
# RUN: llvm-readelf --dyn-syms %t4 | FileCheck --check-prefix=EXE %s
46-
47-
# EXE: Symbol table '.dynsym' contains 1 entries:
45+
# RUN: llvm-readelf --dyn-syms %t4 | FileCheck --check-prefix=MIX2 %s
4846

4947
# RUN: ld.lld --version-script %t4.script -shared %t.o %tref.o -o %t5.so
5048
# RUN: llvm-readelf -r %t5.so | FileCheck --check-prefix=RELOC %s

0 commit comments

Comments
 (0)