Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 1b58d0d

Browse files
committedMar 8, 2024
[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. Closes #74771
1 parent 6a8e6c9 commit 1b58d0d

File tree

4 files changed

+99
-11
lines changed

4 files changed

+99
-11
lines changed
 

‎lld/ELF/Driver.cpp

Lines changed: 53 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2659,6 +2659,58 @@ static void postParseObjectFile(ELFFileBase *file) {
26592659
}
26602660
}
26612661

2662+
// Returns true if the provide symbol should be added to the link.
2663+
bool shouldAddProvideSym(StringRef symName) {
2664+
Symbol *b = symtab.find(symName);
2665+
return b && !b->isDefined() && !b->isCommon();
2666+
}
2667+
2668+
// Add symbols referred by the provide symbol to the symbol table.
2669+
// This function must only be called for provide symbols that should be added
2670+
// to the link.
2671+
static void
2672+
addProvideSymReferences(StringRef provideSym,
2673+
llvm::StringSet<> &addedRefsFromProvideSym) {
2674+
2675+
if (addedRefsFromProvideSym.count(provideSym))
2676+
return;
2677+
assert(shouldAddProvideSym(provideSym) &&
2678+
"This function must only be called for provide symbols that should be "
2679+
"added to the link.");
2680+
addedRefsFromProvideSym.insert(provideSym);
2681+
for (StringRef name : script->provideMap[provideSym].keys()) {
2682+
Symbol *sym = addUnusedUndefined(name);
2683+
sym->isUsedInRegularObj = true;
2684+
sym->referenced = true;
2685+
script->referencedSymbols.push_back(name);
2686+
if (script->provideMap.count(name) && shouldAddProvideSym(name) &&
2687+
!addedRefsFromProvideSym.count(name))
2688+
addProvideSymReferences(name, addedRefsFromProvideSym);
2689+
}
2690+
}
2691+
2692+
// Add symbols that are referenced in the linker script.
2693+
// Symbols referenced in a PROVIDE command are only added to the symbol table if
2694+
// the PROVIDE command actually provides the symbol.
2695+
static void addScriptReferencedSymbols() {
2696+
// Some symbols (such as __ehdr_start) are defined lazily only when there
2697+
// are undefined symbols for them, so we add these to trigger that logic.
2698+
for (StringRef name : script->referencedSymbols) {
2699+
Symbol *sym = addUnusedUndefined(name);
2700+
sym->isUsedInRegularObj = true;
2701+
sym->referenced = true;
2702+
}
2703+
2704+
// Keeps track of references from which PROVIDE symbols have been added to the
2705+
// symbol table.
2706+
llvm::StringSet<> addedRefsFromProvideSym;
2707+
for (StringRef provideSym : script->provideMap.keys()) {
2708+
if (!addedRefsFromProvideSym.count(provideSym) &&
2709+
shouldAddProvideSym(provideSym))
2710+
addProvideSymReferences(provideSym, addedRefsFromProvideSym);
2711+
}
2712+
}
2713+
26622714
// Do actual linking. Note that when this function is called,
26632715
// all linker scripts have already been parsed.
26642716
void LinkerDriver::link(opt::InputArgList &args) {
@@ -2735,13 +2787,7 @@ void LinkerDriver::link(opt::InputArgList &args) {
27352787
config->hasDynSymTab =
27362788
!ctx.sharedFiles.empty() || config->isPic || config->exportDynamic;
27372789

2738-
// Some symbols (such as __ehdr_start) are defined lazily only when there
2739-
// are undefined symbols for them, so we add these to trigger that logic.
2740-
for (StringRef name : script->referencedSymbols) {
2741-
Symbol *sym = addUnusedUndefined(name);
2742-
sym->isUsedInRegularObj = true;
2743-
sym->referenced = true;
2744-
}
2790+
addScriptReferencedSymbols();
27452791

27462792
// Prevent LTO from removing any definition referenced by -u.
27472793
for (StringRef name : config->undefined)

‎lld/ELF/LinkerScript.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616
#include "llvm/ADT/ArrayRef.h"
1717
#include "llvm/ADT/DenseMap.h"
1818
#include "llvm/ADT/MapVector.h"
19+
#include "llvm/ADT/StringMap.h"
1920
#include "llvm/ADT/StringRef.h"
21+
#include "llvm/ADT/StringSet.h"
2022
#include "llvm/Support/Compiler.h"
2123
#include <cstddef>
2224
#include <cstdint>
@@ -379,6 +381,14 @@ class LinkerScript final {
379381

380382
// Sections that will be warned/errored by --orphan-handling.
381383
SmallVector<const InputSectionBase *, 0> orphanSections;
384+
385+
// Stores the mapping: provide symbol -> symbols referred in the provide
386+
// expression. For example, if the PROVIDE command is:
387+
//
388+
// PROVIDE(v = a + b + c);
389+
//
390+
// then provideMap should contain the mapping: 'v' -> ['a', 'b', 'c']
391+
llvm::StringMap<llvm::StringSet<>> provideMap;
382392
};
383393

384394
LLVM_LIBRARY_VISIBILITY extern std::unique_ptr<LinkerScript> script;

‎lld/ELF/ScriptParser.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#include "llvm/Support/TimeProfiler.h"
3737
#include <cassert>
3838
#include <limits>
39+
#include <optional>
3940
#include <vector>
4041

4142
using namespace llvm;
@@ -138,6 +139,10 @@ class ScriptParser final : ScriptLexer {
138139

139140
// A set to detect an INCLUDE() cycle.
140141
StringSet<> seen;
142+
143+
// If we are currently parsing a PROVIDE|PROVIDE_HIDDEN command,
144+
// then this member is set to the provide symbol name.
145+
std::optional<llvm::StringRef> activeProvideSym;
141146
};
142147
} // namespace
143148

@@ -1055,6 +1060,9 @@ SymbolAssignment *ScriptParser::readProvideHidden(bool provide, bool hidden) {
10551060
;
10561061
return nullptr;
10571062
}
1063+
llvm::SaveAndRestore saveActiveProvideSym(activeProvideSym);
1064+
if (provide)
1065+
activeProvideSym = name;
10581066
SymbolAssignment *cmd = readSymbolAssignment(name);
10591067
cmd->provide = provide;
10601068
cmd->hidden = hidden;
@@ -1570,7 +1578,10 @@ Expr ScriptParser::readPrimary() {
15701578
tok = unquote(tok);
15711579
else if (!isValidSymbolName(tok))
15721580
setError("malformed number: " + tok);
1573-
script->referencedSymbols.push_back(tok);
1581+
if (activeProvideSym)
1582+
script->provideMap[activeProvideSym.value()].insert(tok);
1583+
else
1584+
script->referencedSymbols.push_back(tok);
15741585
return [=] { return script->getSymbolValue(tok, location); };
15751586
}
15761587

‎lld/test/ELF/linkerscript/symbolreferenced.s

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,20 @@
2323

2424
# CHECK: 0000000000001000 a f1
2525
# CHECK-NEXT: 0000000000001000 A f2
26-
# CHECK-NEXT: 0000000000001000 a g1
27-
# CHECK-NEXT: 0000000000001000 A g2
26+
# CHECK-NEXT: 0000000000001000 A f3
27+
# CHECK-NEXT: 0000000000001000 A f4
28+
# CHECK-NEXT: 0000000000001000 A f5
29+
# CHECK-NEXT: 0000000000001000 A f6
30+
# CHECK-NEXT: 0000000000001000 A f7
2831
# CHECK-NEXT: 0000000000001000 A newsym
32+
# CHECK: 0000000000002000 A u
33+
# CHECK-NEXT: 0000000000002000 A v
34+
# CHECK-NEXT: 0000000000002000 A w
35+
36+
# CHECK-NOT: g1
37+
# CHECK-NOT: g2
38+
# CHECK-NOT: unused
39+
# CHECK-NOT: another_unused
2940

3041
# RUN: not ld.lld -T chain2.t a.o 2>&1 | FileCheck %s --check-prefix=ERR --implicit-check-not=error:
3142
# ERR-COUNT-3: error: chain2.t:1: symbol not found: undef
@@ -40,13 +51,23 @@ patatino:
4051
movl newsym, %eax
4152

4253
#--- chain.t
43-
PROVIDE(f2 = 0x1000);
54+
PROVIDE(f7 = 0x1000);
55+
PROVIDE(f5 = f6);
56+
PROVIDE(f6 = f7);
57+
PROVIDE(f4 = f5);
58+
PROVIDE(f3 = f4);
59+
PROVIDE(f2 = f3);
4460
PROVIDE_HIDDEN(f1 = f2);
4561
PROVIDE(newsym = f1);
4662

63+
u = v;
64+
PROVIDE(w = 0x2000);
65+
PROVIDE(v = w);
66+
4767
PROVIDE(g2 = 0x1000);
4868
PROVIDE_HIDDEN(g1 = g2);
4969
PROVIDE(unused = g1);
70+
PROVIDE_HIDDEN(another_unused = g1);
5071

5172
#--- chain2.t
5273
PROVIDE(f2 = undef);

0 commit comments

Comments
 (0)
Please sign in to comment.