Skip to content

Commit b3452f8

Browse files
committed
[ELF] redirectSymbols: skip versioned symbol combine if config->versionDefinitions.size() == 2
1 parent 6c9f681 commit b3452f8

File tree

1 file changed

+50
-44
lines changed

1 file changed

+50
-44
lines changed

lld/ELF/Driver.cpp

Lines changed: 50 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -2245,6 +2245,49 @@ static std::vector<WrappedSymbol> addWrappedSymbols(opt::InputArgList &args) {
22452245
return v;
22462246
}
22472247

2248+
static void combineVersionedSymbol(Symbol &sym,
2249+
DenseMap<Symbol *, Symbol *> &map) {
2250+
const char *suffix1 = sym.getVersionSuffix();
2251+
if (suffix1[0] != '@' || suffix1[1] == '@')
2252+
return;
2253+
2254+
// Check the existing symbol foo. We have two special cases to handle:
2255+
//
2256+
// * There is a definition of foo@v1 and foo@@v1.
2257+
// * There is a definition of foo@v1 and foo.
2258+
Defined *sym2 = dyn_cast_or_null<Defined>(symtab->find(sym.getName()));
2259+
if (!sym2)
2260+
return;
2261+
const char *suffix2 = sym2->getVersionSuffix();
2262+
if (suffix2[0] == '@' && suffix2[1] == '@' &&
2263+
strcmp(suffix1 + 1, suffix2 + 2) == 0) {
2264+
// foo@v1 and foo@@v1 should be merged, so redirect foo@v1 to foo@@v1.
2265+
map.try_emplace(&sym, sym2);
2266+
// If both foo@v1 and foo@@v1 are defined and non-weak, report a
2267+
// duplicate definition error.
2268+
if (sym.isDefined())
2269+
sym2->checkDuplicate(cast<Defined>(sym));
2270+
sym2->resolve(sym);
2271+
// Eliminate foo@v1 from the symbol table.
2272+
sym.symbolKind = Symbol::PlaceholderKind;
2273+
sym.isUsedInRegularObj = false;
2274+
} else if (auto *sym1 = dyn_cast<Defined>(&sym)) {
2275+
if (sym2->versionId > VER_NDX_GLOBAL
2276+
? config->versionDefinitions[sym2->versionId].name == suffix1 + 1
2277+
: sym1->section == sym2->section && sym1->value == sym2->value) {
2278+
// Due to an assembler design flaw, if foo is defined, .symver foo,
2279+
// foo@v1 defines both foo and foo@v1. Unless foo is bound to a
2280+
// different version, GNU ld makes foo@v1 canonical and eliminates
2281+
// foo. Emulate its behavior, otherwise we would have foo or foo@@v1
2282+
// beside foo@v1. foo@v1 and foo combining does not apply if they are
2283+
// not defined in the same place.
2284+
map.try_emplace(sym2, &sym);
2285+
sym2->symbolKind = Symbol::PlaceholderKind;
2286+
sym2->isUsedInRegularObj = false;
2287+
}
2288+
}
2289+
}
2290+
22482291
// Do renaming for --wrap and foo@v1 by updating pointers to symbols.
22492292
//
22502293
// When this function is executed, only InputFiles and symbol table
@@ -2257,51 +2300,14 @@ static void redirectSymbols(ArrayRef<WrappedSymbol> wrapped) {
22572300
map[w.sym] = w.wrap;
22582301
map[w.real] = w.sym;
22592302
}
2260-
for (Symbol *sym : symtab->symbols()) {
2261-
// Enumerate symbols with a non-default version (foo@v1). hasVersionSuffix
2262-
// filters out most symbols but is not sufficient.
2263-
if (!sym->hasVersionSuffix)
2264-
continue;
2265-
const char *suffix1 = sym->getVersionSuffix();
2266-
if (suffix1[0] != '@' || suffix1[1] == '@')
2267-
continue;
22682303

2269-
// Check the existing symbol foo. We have two special cases to handle:
2270-
//
2271-
// * There is a definition of foo@v1 and foo@@v1.
2272-
// * There is a definition of foo@v1 and foo.
2273-
Defined *sym2 = dyn_cast_or_null<Defined>(symtab->find(sym->getName()));
2274-
if (!sym2)
2275-
continue;
2276-
const char *suffix2 = sym2->getVersionSuffix();
2277-
if (suffix2[0] == '@' && suffix2[1] == '@' &&
2278-
strcmp(suffix1 + 1, suffix2 + 2) == 0) {
2279-
// foo@v1 and foo@@v1 should be merged, so redirect foo@v1 to foo@@v1.
2280-
map.try_emplace(sym, sym2);
2281-
// If both foo@v1 and foo@@v1 are defined and non-weak, report a duplicate
2282-
// definition error.
2283-
if (sym->isDefined())
2284-
sym2->checkDuplicate(cast<Defined>(*sym));
2285-
sym2->resolve(*sym);
2286-
// Eliminate foo@v1 from the symbol table.
2287-
sym->symbolKind = Symbol::PlaceholderKind;
2288-
sym->isUsedInRegularObj = false;
2289-
} else if (auto *sym1 = dyn_cast<Defined>(sym)) {
2290-
if (sym2->versionId > VER_NDX_GLOBAL
2291-
? config->versionDefinitions[sym2->versionId].name == suffix1 + 1
2292-
: sym1->section == sym2->section && sym1->value == sym2->value) {
2293-
// Due to an assembler design flaw, if foo is defined, .symver foo,
2294-
// foo@v1 defines both foo and foo@v1. Unless foo is bound to a
2295-
// different version, GNU ld makes foo@v1 canonical and eliminates foo.
2296-
// Emulate its behavior, otherwise we would have foo or foo@@v1 beside
2297-
// foo@v1. foo@v1 and foo combining does not apply if they are not
2298-
// defined in the same place.
2299-
map.try_emplace(sym2, sym);
2300-
sym2->symbolKind = Symbol::PlaceholderKind;
2301-
sym2->isUsedInRegularObj = false;
2302-
}
2303-
}
2304-
}
2304+
// If there are version definitions (versionDefinitions.size() > 2), enumerate
2305+
// symbols with a non-default version (foo@v1) and check whether it should be
2306+
// combined with foo or foo@@v1.
2307+
if (config->versionDefinitions.size() > 2)
2308+
for (Symbol *sym : symtab->symbols())
2309+
if (sym->hasVersionSuffix)
2310+
combineVersionedSymbol(*sym, map);
23052311

23062312
if (map.empty())
23072313
return;

0 commit comments

Comments
 (0)