Skip to content

Commit 00809c8

Browse files
committed
[ELF] Apply version script patterns to non-default version symbols
Currently version script patterns are ignored for .symver produced non-default version (single @) symbols. This makes such symbols not localizable by `local:`, e.g. ``` .symver foo3_v1,foo3@v1 .globl foo_v1 foo3_v1: ld.lld --version-script=a.ver -shared a.o ``` This patch adds the support: * Move `config->versionDefinitions[VER_NDX_LOCAL].patterns` to `config->versionDefinitions[versionId].localPatterns` * Rename `config->versionDefinitions[versionId].patterns` to `config->versionDefinitions[versionId].nonLocalPatterns` * Allow `findAllByVersion` to find non-default version symbols when `includeNonDefault` is true. (Note: `symtab` keys do not have `@@`) * Make each pattern check both the unversioned `pat.name` and the versioned `${pat.name}@${v.name}` * `localPatterns` can localize `${pat.name}@${v.name}`. `nonLocalPatterns` can prevent localization by assigning `verdefIndex` (before `parseSymbolVersion`). --- If a user notices new `undefined symbol` errors with a version script containing `local: *;`, the issue is likely due to a missing `global:` pattern. Reviewed By: peter.smith Differential Revision: https://reviews.llvm.org/D107234
1 parent a533eb7 commit 00809c8

13 files changed

+154
-229
lines changed

lld/ELF/Config.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,8 @@ struct SymbolVersion {
8686
struct VersionDefinition {
8787
llvm::StringRef name;
8888
uint16_t id;
89-
std::vector<SymbolVersion> patterns;
89+
std::vector<SymbolVersion> nonLocalPatterns;
90+
std::vector<SymbolVersion> localPatterns;
9091
};
9192

9293
// This struct contains the global configuration for the linker.

lld/ELF/Driver.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1351,18 +1351,19 @@ static void readConfigs(opt::InputArgList &args) {
13511351
}
13521352

13531353
assert(config->versionDefinitions.empty());
1354-
config->versionDefinitions.push_back({"local", (uint16_t)VER_NDX_LOCAL, {}});
13551354
config->versionDefinitions.push_back(
1356-
{"global", (uint16_t)VER_NDX_GLOBAL, {}});
1355+
{"local", (uint16_t)VER_NDX_LOCAL, {}, {}});
1356+
config->versionDefinitions.push_back(
1357+
{"global", (uint16_t)VER_NDX_GLOBAL, {}, {}});
13571358

13581359
// If --retain-symbol-file is used, we'll keep only the symbols listed in
13591360
// the file and discard all others.
13601361
if (auto *arg = args.getLastArg(OPT_retain_symbols_file)) {
1361-
config->versionDefinitions[VER_NDX_LOCAL].patterns.push_back(
1362+
config->versionDefinitions[VER_NDX_LOCAL].nonLocalPatterns.push_back(
13621363
{"*", /*isExternCpp=*/false, /*hasWildcard=*/true});
13631364
if (Optional<MemoryBufferRef> buffer = readFile(arg->getValue()))
13641365
for (StringRef s : args::getLines(*buffer))
1365-
config->versionDefinitions[VER_NDX_GLOBAL].patterns.push_back(
1366+
config->versionDefinitions[VER_NDX_GLOBAL].nonLocalPatterns.push_back(
13661367
{s, /*isExternCpp=*/false, /*hasWildcard=*/false});
13671368
}
13681369

lld/ELF/ScriptParser.cpp

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1496,9 +1496,9 @@ void ScriptParser::readAnonymousDeclaration() {
14961496
std::vector<SymbolVersion> globals;
14971497
std::tie(locals, globals) = readSymbols();
14981498
for (const SymbolVersion &pat : locals)
1499-
config->versionDefinitions[VER_NDX_LOCAL].patterns.push_back(pat);
1499+
config->versionDefinitions[VER_NDX_LOCAL].localPatterns.push_back(pat);
15001500
for (const SymbolVersion &pat : globals)
1501-
config->versionDefinitions[VER_NDX_GLOBAL].patterns.push_back(pat);
1501+
config->versionDefinitions[VER_NDX_GLOBAL].nonLocalPatterns.push_back(pat);
15021502

15031503
expect(";");
15041504
}
@@ -1510,13 +1510,12 @@ void ScriptParser::readVersionDeclaration(StringRef verStr) {
15101510
std::vector<SymbolVersion> locals;
15111511
std::vector<SymbolVersion> globals;
15121512
std::tie(locals, globals) = readSymbols();
1513-
for (const SymbolVersion &pat : locals)
1514-
config->versionDefinitions[VER_NDX_LOCAL].patterns.push_back(pat);
15151513

15161514
// Create a new version definition and add that to the global symbols.
15171515
VersionDefinition ver;
15181516
ver.name = verStr;
1519-
ver.patterns = globals;
1517+
ver.nonLocalPatterns = std::move(globals);
1518+
ver.localPatterns = std::move(locals);
15201519
ver.id = config->versionDefinitions.size();
15211520
config->versionDefinitions.push_back(ver);
15221521

lld/ELF/SymbolTable.cpp

Lines changed: 84 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -134,9 +134,20 @@ static bool canBeVersioned(const Symbol &sym) {
134134
StringMap<std::vector<Symbol *>> &SymbolTable::getDemangledSyms() {
135135
if (!demangledSyms) {
136136
demangledSyms.emplace();
137+
std::string demangled;
137138
for (Symbol *sym : symVector)
138-
if (canBeVersioned(*sym))
139-
(*demangledSyms)[demangleItanium(sym->getName())].push_back(sym);
139+
if (canBeVersioned(*sym)) {
140+
StringRef name = sym->getName();
141+
size_t pos = name.find('@');
142+
if (pos == std::string::npos)
143+
demangled = demangleItanium(name);
144+
else if (pos + 1 == name.size() || name[pos + 1] == '@')
145+
demangled = demangleItanium(name.substr(0, pos));
146+
else
147+
demangled =
148+
(demangleItanium(name.substr(0, pos)) + name.substr(pos)).str();
149+
(*demangledSyms)[demangled].push_back(sym);
150+
}
140151
}
141152
return *demangledSyms;
142153
}
@@ -150,19 +161,29 @@ std::vector<Symbol *> SymbolTable::findByVersion(SymbolVersion ver) {
150161
return {};
151162
}
152163

153-
std::vector<Symbol *> SymbolTable::findAllByVersion(SymbolVersion ver) {
164+
std::vector<Symbol *> SymbolTable::findAllByVersion(SymbolVersion ver,
165+
bool includeNonDefault) {
154166
std::vector<Symbol *> res;
155167
SingleStringMatcher m(ver.name);
168+
auto check = [&](StringRef name) {
169+
size_t pos = name.find('@');
170+
if (!includeNonDefault)
171+
return pos == StringRef::npos;
172+
return !(pos + 1 < name.size() && name[pos + 1] == '@');
173+
};
156174

157175
if (ver.isExternCpp) {
158176
for (auto &p : getDemangledSyms())
159177
if (m.match(p.first()))
160-
res.insert(res.end(), p.second.begin(), p.second.end());
178+
for (Symbol *sym : p.second)
179+
if (check(sym->getName()))
180+
res.push_back(sym);
161181
return res;
162182
}
163183

164184
for (Symbol *sym : symVector)
165-
if (canBeVersioned(*sym) && m.match(sym->getName()))
185+
if (canBeVersioned(*sym) && check(sym->getName()) &&
186+
m.match(sym->getName()))
166187
res.push_back(sym);
167188
return res;
168189
}
@@ -172,7 +193,7 @@ void SymbolTable::handleDynamicList() {
172193
for (SymbolVersion &ver : config->dynamicList) {
173194
std::vector<Symbol *> syms;
174195
if (ver.hasWildcard)
175-
syms = findAllByVersion(ver);
196+
syms = findAllByVersion(ver, /*includeNonDefault=*/true);
176197
else
177198
syms = findByVersion(ver);
178199

@@ -181,21 +202,13 @@ void SymbolTable::handleDynamicList() {
181202
}
182203
}
183204

184-
// Set symbol versions to symbols. This function handles patterns
185-
// containing no wildcard characters.
186-
void SymbolTable::assignExactVersion(SymbolVersion ver, uint16_t versionId,
187-
StringRef versionName) {
188-
if (ver.hasWildcard)
189-
return;
190-
205+
// Set symbol versions to symbols. This function handles patterns containing no
206+
// wildcard characters. Return false if no symbol definition matches ver.
207+
bool SymbolTable::assignExactVersion(SymbolVersion ver, uint16_t versionId,
208+
StringRef versionName,
209+
bool includeNonDefault) {
191210
// Get a list of symbols which we need to assign the version to.
192211
std::vector<Symbol *> syms = findByVersion(ver);
193-
if (syms.empty()) {
194-
if (!config->undefinedVersion)
195-
error("version script assignment of '" + versionName + "' to symbol '" +
196-
ver.name + "' failed: symbol not defined");
197-
return;
198-
}
199212

200213
auto getName = [](uint16_t ver) -> std::string {
201214
if (ver == VER_NDX_LOCAL)
@@ -207,10 +220,11 @@ void SymbolTable::assignExactVersion(SymbolVersion ver, uint16_t versionId,
207220

208221
// Assign the version.
209222
for (Symbol *sym : syms) {
210-
// Skip symbols containing version info because symbol versions
211-
// specified by symbol names take precedence over version scripts.
212-
// See parseSymbolVersion().
213-
if (sym->getName().contains('@'))
223+
// For a non-local versionId, skip symbols containing version info because
224+
// symbol versions specified by symbol names take precedence over version
225+
// scripts. See parseSymbolVersion().
226+
if (!includeNonDefault && versionId != VER_NDX_LOCAL &&
227+
sym->getName().contains('@'))
214228
continue;
215229

216230
// If the version has not been assigned, verdefIndex is -1. Use an arbitrary
@@ -225,13 +239,15 @@ void SymbolTable::assignExactVersion(SymbolVersion ver, uint16_t versionId,
225239
warn("attempt to reassign symbol '" + ver.name + "' of " +
226240
getName(sym->versionId) + " to " + getName(versionId));
227241
}
242+
return !syms.empty();
228243
}
229244

230-
void SymbolTable::assignWildcardVersion(SymbolVersion ver, uint16_t versionId) {
245+
void SymbolTable::assignWildcardVersion(SymbolVersion ver, uint16_t versionId,
246+
bool includeNonDefault) {
231247
// Exact matching takes precedence over fuzzy matching,
232248
// so we set a version to a symbol only if no version has been assigned
233249
// to the symbol. This behavior is compatible with GNU.
234-
for (Symbol *sym : findAllByVersion(ver))
250+
for (Symbol *sym : findAllByVersion(ver, includeNonDefault))
235251
if (sym->verdefIndex == UINT32_C(-1)) {
236252
sym->verdefIndex = 0;
237253
sym->versionId = versionId;
@@ -244,26 +260,60 @@ void SymbolTable::assignWildcardVersion(SymbolVersion ver, uint16_t versionId) {
244260
// script file, the script does not actually define any symbol version,
245261
// but just specifies symbols visibilities.
246262
void SymbolTable::scanVersionScript() {
263+
SmallString<128> buf;
247264
// First, we assign versions to exact matching symbols,
248265
// i.e. version definitions not containing any glob meta-characters.
249-
for (VersionDefinition &v : config->versionDefinitions)
250-
for (SymbolVersion &pat : v.patterns)
251-
assignExactVersion(pat, v.id, v.name);
266+
std::vector<Symbol *> syms;
267+
for (VersionDefinition &v : config->versionDefinitions) {
268+
auto assignExact = [&](SymbolVersion pat, uint16_t id, StringRef ver) {
269+
bool found =
270+
assignExactVersion(pat, id, ver, /*includeNonDefault=*/false);
271+
buf.clear();
272+
found |= assignExactVersion({(pat.name + "@" + v.name).toStringRef(buf),
273+
pat.isExternCpp, /*hasWildCard=*/false},
274+
id, ver, /*includeNonDefault=*/true);
275+
if (!found && !config->undefinedVersion)
276+
errorOrWarn("version script assignment of '" + ver + "' to symbol '" +
277+
pat.name + "' failed: symbol not defined");
278+
};
279+
for (SymbolVersion &pat : v.nonLocalPatterns)
280+
if (!pat.hasWildcard)
281+
assignExact(pat, v.id, v.name);
282+
for (SymbolVersion pat : v.localPatterns)
283+
if (!pat.hasWildcard)
284+
assignExact(pat, VER_NDX_LOCAL, "local");
285+
}
252286

253287
// Next, assign versions to wildcards that are not "*". Note that because the
254288
// last match takes precedence over previous matches, we iterate over the
255289
// definitions in the reverse order.
256-
for (VersionDefinition &v : llvm::reverse(config->versionDefinitions))
257-
for (SymbolVersion &pat : v.patterns)
290+
auto assignWildcard = [&](SymbolVersion pat, uint16_t id, StringRef ver) {
291+
assignWildcardVersion(pat, id, /*includeNonDefault=*/false);
292+
buf.clear();
293+
assignWildcardVersion({(pat.name + "@" + ver).toStringRef(buf),
294+
pat.isExternCpp, /*hasWildCard=*/true},
295+
id,
296+
/*includeNonDefault=*/true);
297+
};
298+
for (VersionDefinition &v : llvm::reverse(config->versionDefinitions)) {
299+
for (SymbolVersion &pat : v.nonLocalPatterns)
258300
if (pat.hasWildcard && pat.name != "*")
259-
assignWildcardVersion(pat, v.id);
301+
assignWildcard(pat, v.id, v.name);
302+
for (SymbolVersion &pat : v.localPatterns)
303+
if (pat.hasWildcard && pat.name != "*")
304+
assignWildcard(pat, VER_NDX_LOCAL, v.name);
305+
}
260306

261307
// Then, assign versions to "*". In GNU linkers they have lower priority than
262308
// other wildcards.
263-
for (VersionDefinition &v : config->versionDefinitions)
264-
for (SymbolVersion &pat : v.patterns)
309+
for (VersionDefinition &v : config->versionDefinitions) {
310+
for (SymbolVersion &pat : v.nonLocalPatterns)
265311
if (pat.hasWildcard && pat.name == "*")
266-
assignWildcardVersion(pat, v.id);
312+
assignWildcard(pat, v.id, v.name);
313+
for (SymbolVersion &pat : v.localPatterns)
314+
if (pat.hasWildcard && pat.name == "*")
315+
assignWildcard(pat, VER_NDX_LOCAL, v.name);
316+
}
267317

268318
// Symbol themselves might know their versions because symbols
269319
// can contain versions in the form of <name>@<version>.

lld/ELF/SymbolTable.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,14 @@ class SymbolTable {
6565

6666
private:
6767
std::vector<Symbol *> findByVersion(SymbolVersion ver);
68-
std::vector<Symbol *> findAllByVersion(SymbolVersion ver);
68+
std::vector<Symbol *> findAllByVersion(SymbolVersion ver,
69+
bool includeNonDefault);
6970

7071
llvm::StringMap<std::vector<Symbol *>> &getDemangledSyms();
71-
void assignExactVersion(SymbolVersion ver, uint16_t versionId,
72-
StringRef versionName);
73-
void assignWildcardVersion(SymbolVersion ver, uint16_t versionId);
72+
bool assignExactVersion(SymbolVersion ver, uint16_t versionId,
73+
StringRef versionName, bool includeNonDefault);
74+
void assignWildcardVersion(SymbolVersion ver, uint16_t versionId,
75+
bool includeNonDefault);
7476

7577
// The order the global symbols are in is not defined. We can use an arbitrary
7678
// order, but it has to be reproducible. That is true even when cross linking.

lld/ELF/Symbols.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,9 @@ OutputSection *Symbol::getOutputSection() const {
208208
// If a symbol name contains '@', the characters after that is
209209
// a symbol version name. This function parses that.
210210
void Symbol::parseSymbolVersion() {
211+
// Return if localized by a local: pattern in a version script.
212+
if (versionId == VER_NDX_LOCAL)
213+
return;
211214
StringRef s = getName();
212215
size_t pos = s.find('@');
213216
if (pos == 0 || pos == StringRef::npos)

lld/test/ELF/verdef-defaultver.s

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# REQUIRES: x86
22

33
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/verdef-defaultver.s -o %t1
4-
# RUN: echo "V1 { global: a; local: *; };" > %t.script
4+
# RUN: echo "V1 { global: a; b; local: *; };" > %t.script
55
# RUN: echo "V2 { global: b; c; } V1;" >> %t.script
66
# RUN: ld.lld --hash-style=sysv -shared -soname shared %t1 --version-script %t.script -o %t.so
77
# RUN: llvm-readobj -V --dyn-syms %t.so | FileCheck --check-prefix=DSO %s

lld/test/ELF/version-script-extern-exact.s

Lines changed: 0 additions & 30 deletions
This file was deleted.

lld/test/ELF/version-script-extern-wildcards.s

Lines changed: 0 additions & 28 deletions
This file was deleted.

0 commit comments

Comments
 (0)