Skip to content

Commit 70c23e2

Browse files
committed
[LLD] Improve reporting unresolved symbols in shared libraries
Currently, when reporting unresolved symbols in shared libraries, if an undefined symbol is firstly seen in a regular object file that shadows the reference for the same symbol in a shared object. As a result, the error for the unresolved symbol in the shared library is not reported. If referencing sections in regular object files are discarded because of '--gc-sections', no reports about such symbols are generated, and the linker finishes successfully, generating an output image that fails on the run. The patch fixes the issue by keeping symbols, which should be checked, for each shared library separately. Differential Revision: https://reviews.llvm.org/D101996
1 parent 2b09a89 commit 70c23e2

File tree

4 files changed

+33
-17
lines changed

4 files changed

+33
-17
lines changed

lld/ELF/InputFiles.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1567,6 +1567,9 @@ template <class ELFT> void SharedFile::parse() {
15671567
Symbol *s = symtab->addSymbol(
15681568
Undefined{this, name, sym.getBinding(), sym.st_other, sym.getType()});
15691569
s->exportDynamic = true;
1570+
if (s->isUndefined() && !s->isWeak() &&
1571+
config->unresolvedSymbolsInShlib != UnresolvedPolicy::Ignore)
1572+
requiredSymbols.push_back(s);
15701573
continue;
15711574
}
15721575

lld/ELF/InputFiles.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -381,12 +381,13 @@ class SharedFile : public ELFFileBase {
381381

382382
template <typename ELFT> void parse();
383383

384-
// Used for --no-allow-shlib-undefined.
385-
bool allNeededIsKnown;
386-
387384
// Used for --as-needed
388385
bool isNeeded;
389386

387+
// Non-weak undefined symbols which are not yet resolved when the SO is
388+
// parsed. Only filled for `--no-allow-shlib-undefined`.
389+
std::vector<Symbol *> requiredSymbols;
390+
390391
private:
391392
template <typename ELFT>
392393
std::vector<uint32_t> parseVerneed(const llvm::object::ELFFile<ELFT> &obj,

lld/ELF/Writer.cpp

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2006,30 +2006,29 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() {
20062006
in.iplt->addSymbols();
20072007

20082008
if (config->unresolvedSymbolsInShlib != UnresolvedPolicy::Ignore) {
2009+
auto diagnose =
2010+
config->unresolvedSymbolsInShlib == UnresolvedPolicy::ReportError
2011+
? errorOrWarn
2012+
: warn;
20092013
// Error on undefined symbols in a shared object, if all of its DT_NEEDED
20102014
// entries are seen. These cases would otherwise lead to runtime errors
20112015
// reported by the dynamic linker.
20122016
//
20132017
// ld.bfd traces all DT_NEEDED to emulate the logic of the dynamic linker to
20142018
// catch more cases. That is too much for us. Our approach resembles the one
20152019
// used in ld.gold, achieves a good balance to be useful but not too smart.
2016-
for (SharedFile *file : sharedFiles)
2017-
file->allNeededIsKnown =
2020+
for (SharedFile *file : sharedFiles) {
2021+
bool allNeededIsKnown =
20182022
llvm::all_of(file->dtNeeded, [&](StringRef needed) {
20192023
return symtab->soNames.count(needed);
20202024
});
2021-
2022-
for (Symbol *sym : symtab->symbols())
2023-
if (sym->isUndefined() && !sym->isWeak())
2024-
if (auto *f = dyn_cast_or_null<SharedFile>(sym->file))
2025-
if (f->allNeededIsKnown) {
2026-
auto diagnose = config->unresolvedSymbolsInShlib ==
2027-
UnresolvedPolicy::ReportError
2028-
? errorOrWarn
2029-
: warn;
2030-
diagnose(toString(f) + ": undefined reference to " +
2031-
toString(*sym) + " [--no-allow-shlib-undefined]");
2032-
}
2025+
if (!allNeededIsKnown)
2026+
continue;
2027+
for (Symbol *sym : file->requiredSymbols)
2028+
if (sym->isUndefined() && !sym->isWeak())
2029+
diagnose(toString(file) + ": undefined reference to " +
2030+
toString(*sym) + " [--no-allow-shlib-undefined]");
2031+
}
20332032
}
20342033

20352034
{

lld/test/ELF/allow-shlib-undefined.s

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,22 @@
2525
# RUN: ld.lld -shared --allow-shlib-undefined %t1.o -o /dev/null
2626
# RUN: ld.lld -shared --no-allow-shlib-undefined %t1.o -o /dev/null
2727

28+
## Check that the error is reported if an unresolved symbol is first seen in a
29+
## regular object file.
30+
# RUN: echo 'callq _unresolved@PLT' | \
31+
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux - -o %tref.o
32+
# RUN: not ld.lld --gc-sections %t.o %tref.o %t.so -o /dev/null 2>&1 | FileCheck %s
33+
34+
## Check that the error is reported for each shared library where the symbol
35+
## is referenced.
36+
# RUN: cp %t.so %t2.so
37+
# RUN: not ld.lld %t.o %t.so %t2.so -o /dev/null 2>&1 | \
38+
# RUN: FileCheck %s --check-prefixes=CHECK,CHECK2
39+
2840
.globl _start
2941
_start:
3042
callq _shared@PLT
3143

3244
# CHECK: error: {{.*}}.so: undefined reference to _unresolved [--no-allow-shlib-undefined]
45+
# CHECK2: error: {{.*}}2.so: undefined reference to _unresolved [--no-allow-shlib-undefined]
3346
# WARN: warning: {{.*}}.so: undefined reference to _unresolved [--no-allow-shlib-undefined]

0 commit comments

Comments
 (0)