[ELF] Fix unnecessary inclusion of unreferenced provide symbols

Previously, linker was unnecessarily including a PROVIDE symbol which
was referenced by another unused PROVIDE symbol. For example, if a
linker script contained the below code and 'not_used_sym' provide symbol
is not included, then linker was still unnecessarily including 'foo' PROVIDE
symbol because it was referenced by 'not_used_sym'. This commit fixes
this behavior.

PROVIDE(not_used_sym = foo)
PROVIDE(foo = 0x1000)

This commit fixes this behavior by using dfs-like algorithm to find
all the symbols referenced in provide expressions of included provide
symbols.

This commit also fixes the issue of unused section not being garbage-collected
if a symbol of the section is referenced by an unused PROVIDE symbol.

Closes #74771
Closes #84730

Co-authored-by: Fangrui Song <i@maskray.me>
This commit is contained in:
Parth Arora
2024-03-25 16:11:21 -07:00
committed by Fangrui Song
parent b6dfaf4c29
commit ebb326a51f
8 changed files with 188 additions and 34 deletions

View File

@@ -198,15 +198,7 @@ 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() && !b->isCommon())
return true;
return false;
return !cmd->provide || LinkerScript::shouldAddProvideSym(cmd->name);
}
// Called by processSymbolAssignments() to assign definitions to
@@ -1517,3 +1509,41 @@ void LinkerScript::checkFinalScriptConditions() const {
checkMemoryRegion(lmaRegion, sec, sec->getLMA());
}
}
void LinkerScript::addScriptReferencedSymbolsToSymTable() {
// Some symbols (such as __ehdr_start) are defined lazily only when there
// are undefined symbols for them, so we add these to trigger that logic.
auto reference = [](StringRef name) {
Symbol *sym = symtab.addUnusedUndefined(name);
sym->isUsedInRegularObj = true;
sym->referenced = true;
};
for (StringRef name : referencedSymbols)
reference(name);
// Keeps track of references from which PROVIDE symbols have been added to the
// symbol table.
DenseSet<StringRef> added;
SmallVector<const SmallVector<StringRef, 0> *, 0> symRefsVec;
for (const auto &[name, symRefs] : provideMap)
if (LinkerScript::shouldAddProvideSym(name) && added.insert(name).second)
symRefsVec.push_back(&symRefs);
while (symRefsVec.size()) {
for (StringRef name : *symRefsVec.pop_back_val()) {
reference(name);
// Prevent the symbol from being discarded by --gc-sections.
script->referencedSymbols.push_back(name);
auto it = script->provideMap.find(name);
if (it != script->provideMap.end() &&
LinkerScript::shouldAddProvideSym(name) &&
added.insert(name).second) {
symRefsVec.push_back(&it->second);
}
}
}
}
bool LinkerScript::shouldAddProvideSym(StringRef symName) {
Symbol *sym = symtab.find(symName);
return sym && !sym->isDefined() && !sym->isCommon();
}