Skip to content

[lld][ELF] Demote symbols in discarded sections to Undefined. #68777

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions lld/ELF/Driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3061,6 +3061,37 @@ void LinkerDriver::link(opt::InputArgList &args) {
script->addOrphanSections();
}

// Demote symbols defined relative to input sections that are discarded by
// /DISCARD/ so that relocations referencing them will get reported.
if (script->seenDiscard) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a corner case but I think is worth testing. The demotion logic applies --gc-sections as well but we only run the code when /DISCARD/ is seen, so there will be some behavior differences.

I have updated lld/test/ELF/gc-sections-tls.s in https://github.com/MaskRay/llvm-project/tree/lld-symbols-in-discard to test this.

Actually, we probably should do #69295 so that all non-local symbols in discarded sections are demoted, regardless of whether /DISCARD/ is seen? @smithp35

llvm::TimeTraceScope timeScope("Demote symbols in discarded sections");
parallelForEach(symtab.getSymbols(), [](Symbol *sym) {
Defined *d = dyn_cast<Defined>(sym);
if (!d)
return;
if (d->section && !d->section->isLive()) {
uint32_t secIdx = 0;
if (d->file) {
uint32_t idx = 0;
for (SectionBase *s : d->file->getSections()) {
if (s == d->section) {
secIdx = idx;
break;
}
idx++;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed the section index code in both places. It was not giving the right index. I'm not sure why I didn't notice it earlier. I'm pretty sure it worked before.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a performance issue. If an object file has many discarded symbols, the quadratic time complexity will not be acceptable. I have fixed this in https://github.com/maskray/llvm-project/tree/lld-symbols-in-discard

}
}
// If we don't change the binding from WEAK to GLOBAL here, the
// undefined symbol reporting will think this is undefined weak and
// not give a warning.
Undefined(d->file, sym->getName(),
sym->isWeak() ? (uint8_t)STB_GLOBAL : sym->binding,
sym->stOther, sym->type, secIdx)
.overwrite(*sym);
}
});
}

{
llvm::TimeTraceScope timeScope("Merge/finalize input sections");

Expand Down
1 change: 1 addition & 0 deletions lld/ELF/LinkerScript.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -613,6 +613,7 @@ void LinkerScript::processSectionCommands() {
discard(*s);
discardSynthetic(*osec);
osec->commands.clear();
seenDiscard = true;
return false;
}

Expand Down
1 change: 1 addition & 0 deletions lld/ELF/LinkerScript.h
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,7 @@ class LinkerScript final {

bool hasSectionsCommand = false;
bool seenDataAlign = false;
bool seenDiscard = false;
bool seenRelroEnd = false;
bool errorOnMissingSection = false;
std::string backwardDotErr;
Expand Down
6 changes: 3 additions & 3 deletions lld/ELF/Relocations.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -507,8 +507,7 @@ int64_t RelocationScanner::computeMipsAddend(const RelTy &rel, RelExpr expr,
template <class ELFT>
static std::string maybeReportDiscarded(Undefined &sym) {
auto *file = dyn_cast_or_null<ObjFile<ELFT>>(sym.file);
if (!file || !sym.discardedSecIdx ||
file->getSections()[sym.discardedSecIdx] != &InputSection::discarded)
if (!file || !sym.discardedSecIdx)
return "";
ArrayRef<typename ELFT::Shdr> objSections =
file->template getELFShdrs<ELFT>();
Expand Down Expand Up @@ -1575,7 +1574,8 @@ template <class ELFT> void elf::scanRelocations() {
scanner.template scanSection<ELFT>(*sec);
if (part.armExidx && part.armExidx->isLive())
for (InputSection *sec : part.armExidx->exidxSections)
scanner.template scanSection<ELFT>(*sec);
if (sec->isLive())
scanner.template scanSection<ELFT>(*sec);
}
});
}
Expand Down
11 changes: 7 additions & 4 deletions lld/ELF/Symbols.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ void elf::maybeWarnUnorderableSymbol(const Symbol *sym) {
//
// Note, ld.bfd --symbol-ordering-file= does not warn on undefined symbols,
// but we don't have to be compatible here.
if (sym->isUndefined() &&
if (sym->isUndefined() && !cast<Undefined>(sym)->discardedSecIdx &&
config->unresolvedSymbols == UnresolvedPolicy::Ignore)
return;

Expand All @@ -330,9 +330,12 @@ void elf::maybeWarnUnorderableSymbol(const Symbol *sym) {

auto report = [&](StringRef s) { warn(toString(file) + s + sym->getName()); };

if (sym->isUndefined())
report(": unable to order undefined symbol: ");
else if (sym->isShared())
if (sym->isUndefined()) {
if (cast<Undefined>(sym)->discardedSecIdx)
report(": unable to order discarded symbol: ");
else
report(": unable to order undefined symbol: ");
} else if (sym->isShared())
report(": unable to order shared symbol: ");
else if (d && !d->section)
report(": unable to order absolute symbol: ");
Expand Down
21 changes: 21 additions & 0 deletions lld/ELF/Writer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -722,6 +722,27 @@ template <class ELFT> void Writer<ELFT>::copyLocalSymbols() {
// No reason to keep local undefined symbol in symtab.
if (!dr)
continue;

// Demote locals which did not end up in any partition. This is similar
// to what we do in Driver.cpp, but that only works on globals.
if (script->seenDiscard && dr->section && !dr->section->isLive()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do not need this copy. This can be unified with Driver.cpp. See https://github.com/maskray/llvm-project/tree/lld-symbols-in-discard

uint32_t secIdx = 0;
if (dr->file) {
uint32_t idx = 0;
for (SectionBase *s : dr->file->getSections()) {
if (s == dr->section) {
secIdx = idx;
break;
}
idx++;
}
}
Undefined(dr->file, b->getName(), b->binding, b->stOther, b->type,
secIdx)
.overwrite(*b);
continue;
}

if (includeInSymtab(*b) && shouldKeepInSymtab(*dr))
in.symTab->addSymbol(b);
}
Expand Down
29 changes: 26 additions & 3 deletions lld/test/ELF/linkerscript/discard-section.s
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,32 @@
# RUN: rm -rf %t && split-file %s %t && cd %t
# RUN: llvm-mc -filetype=obj -triple=x86_64 a.s -o a.o
# RUN: llvm-mc -filetype=obj -triple=x86_64 b.s -o b.o
# RUN: ld.lld -T a.lds a.o b.o -z undefs -o /dev/null 2>&1 | count 0
# RUN: ld.lld -T a.lds a.o b.o -o /dev/null 2>&1 | count 0
# RUN: ld.lld -r -T a.lds a.o b.o -o /dev/null 2>&1 | count 0
# RUN: not ld.lld --threads=1 -T a.lds a.o b.o -z undefs -o /dev/null 2>&1 | FileCheck %s --check-prefix=SECTION --implicit-check-not=error:
# RUN: not ld.lld --threads=1 -T a.lds a.o b.o -o /dev/null 2>&1 | FileCheck %s --check-prefixes=SECTION,SYMBOL --implicit-check-not=error:
# RUN: ld.lld -r -T a.lds a.o b.o -o /dev/null 2>&1 | FileCheck %s --check-prefix=WARNING --implicit-check-not=error:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

--implicit-check-not=warning:

exit code 0 means that there cannot be any error.


# SECTION: error: relocation refers to a discarded section: .aaa
# SECTION-NEXT: >>> defined in a.o
# SECTION-NEXT: >>> referenced by a.o:(.bbb+0x0)

# SYMBOL: error: relocation refers to a symbol in a discarded section: global
# SYMBOL-NEXT: >>> defined in a.o
# SYMBOL-NEXT: >>> referenced by b.o:(.data+0x0)

# SYMBOL: error: relocation refers to a symbol in a discarded section: weak
# SYMBOL-NEXT: >>> defined in a.o
# SYMBOL-NEXT: >>> referenced by b.o:(.data+0x8)

# SYMBOL: error: relocation refers to a symbol in a discarded section: weakref1
# SYMBOL-NEXT: >>> defined in a.o
# SYMBOL-NEXT: >>> referenced by b.o:(.data+0x10)

# SYMBOL: error: relocation refers to a symbol in a discarded section: weakref2
# SYMBOL-NEXT: >>> defined in a.o
# SYMBOL-NEXT: >>> referenced by b.o:(.data+0x18)

# WARNING: warning: relocation refers to a discarded section: .aaa
# WARNING-NEXT: >>> referenced by a.o:(.rela.bbb+0x0)

#--- a.s
.globl _start
Expand Down