Skip to content
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

[ELF] Merge copyLocalSymbols and demoteLocalSymbolsInDiscardedSections #69425

Merged
merged 1 commit into from
Oct 18, 2023

Conversation

MaskRay
Copy link
Member

@MaskRay MaskRay commented Oct 18, 2023

Follow-up to #69295: In Writer<ELFT>::run, the symbol passes are flexible:
they can be placed almost everywhere before scanRelocations, with a constraint
that the computeIsPreemptible pass must be invoked for linker-defined
non-local symbols.

Merge copyLocalSymbols and demoteLocalSymbolsInDiscardedSections to simplify
code:

  • Demoting local symbols can be made unconditional, not constrainted to /DISCARD/ uses due to performance concerns
  • includeInSymtab can be made faster
  • Make symbol passes close to each other
  • Decrease data cache misses due to saving an iteration over local symbols

There is no speedup, likely due to the unconditional dr->section access in demoteAndCopyLocalSymbols.

gc-sections-tls.s no longer reports an error because the TLS symbol is
converted to an Undefined.

In `Writer<ELFT>::run`, the symbol passes are flexible: they can be placed
almost everywhere before `scanRelocations`, with a constraint that the
`computeIsPreemptible` pass must be invoked for linker-defined non-local
symbols.

Merge copyLocalSymbols and demoteLocalSymbolsInDiscardedSections to simplify
code:

* Demoting local symbols can be made unconditional, not constrainted to /DISCARD/ uses due to performance concerns
* `includeInSymtab` can be made faster
* Make symbol passes close to each other
* Decrease data cache misses due to saving an iteration over local symbols

There is no speedup, likely due to the unconditional `dr->section` access in `demoteAndCopyLocalSymbols`.

`gc-sections-tls.s` no longer reports an error because the TLS symbol is
converted to an Undefined.
@llvmbot
Copy link
Collaborator

llvmbot commented Oct 18, 2023

@llvm/pr-subscribers-lld-elf

@llvm/pr-subscribers-lld

Author: Fangrui Song (MaskRay)

Changes

In Writer&lt;ELFT&gt;::run, the symbol passes are flexible: they can be placed
almost everywhere before scanRelocations, with a constraint that the
computeIsPreemptible pass must be invoked for linker-defined non-local
symbols.

Merge copyLocalSymbols and demoteLocalSymbolsInDiscardedSections to simplify
code:

  • Demoting local symbols can be made unconditional, not constrainted to /DISCARD/ uses due to performance concerns
  • includeInSymtab can be made faster
  • Make symbol passes close to each other
  • Decrease data cache misses due to saving an iteration over local symbols

There is no speedup, likely due to the unconditional dr-&gt;section access in demoteAndCopyLocalSymbols.

gc-sections-tls.s no longer reports an error because the TLS symbol is
converted to an Undefined.


Full diff: https://github.com/llvm/llvm-project/pull/69425.diff

4 Files Affected:

  • (modified) lld/ELF/LinkerScript.cpp (-1)
  • (modified) lld/ELF/LinkerScript.h (-1)
  • (modified) lld/ELF/Writer.cpp (+20-35)
  • (modified) lld/test/ELF/gc-sections-tls.s (+7-13)
diff --git a/lld/ELF/LinkerScript.cpp b/lld/ELF/LinkerScript.cpp
index 00e583903f1b455..df091613dc0a144 100644
--- a/lld/ELF/LinkerScript.cpp
+++ b/lld/ELF/LinkerScript.cpp
@@ -613,7 +613,6 @@ void LinkerScript::processSectionCommands() {
         discard(*s);
       discardSynthetic(*osec);
       osec->commands.clear();
-      seenDiscard = true;
       return false;
     }
 
diff --git a/lld/ELF/LinkerScript.h b/lld/ELF/LinkerScript.h
index c97fdfab1d2f21c..18eaf58b785e370 100644
--- a/lld/ELF/LinkerScript.h
+++ b/lld/ELF/LinkerScript.h
@@ -356,7 +356,6 @@ class LinkerScript final {
 
   bool hasSectionsCommand = false;
   bool seenDataAlign = false;
-  bool seenDiscard = false;
   bool seenRelroEnd = false;
   bool errorOnMissingSection = false;
   std::string backwardDotErr;
diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp
index 6f00c7ff8c0d1a8..57e1aa06c6aa873 100644
--- a/lld/ELF/Writer.cpp
+++ b/lld/ELF/Writer.cpp
@@ -53,7 +53,6 @@ template <class ELFT> class Writer {
   void run();
 
 private:
-  void copyLocalSymbols();
   void addSectionSymbols();
   void sortSections();
   void resolveShfLinkOrder();
@@ -292,18 +291,6 @@ static void demoteSymbolsAndComputeIsPreemptible() {
   }
 }
 
-static void demoteLocalSymbolsInDiscardedSections() {
-  llvm::TimeTraceScope timeScope("Demote local symbols");
-  parallelForEach(ctx.objectFiles, [&](ELFFileBase *file) {
-    DenseMap<SectionBase *, size_t> sectionIndexMap;
-    for (Symbol *sym : file->getLocalSymbols()) {
-      Defined *d = dyn_cast<Defined>(sym);
-      if (d && d->section && !d->section->isLive())
-        demoteDefined(*d, sectionIndexMap);
-    }
-  });
-}
-
 // Fully static executables don't support MTE globals at this point in time, as
 // we currently rely on:
 //   - A dynamic loader to process relocations, and
@@ -598,11 +585,6 @@ template <class ELFT> void elf::createSyntheticSections() {
 
 // The main function of the writer.
 template <class ELFT> void Writer<ELFT>::run() {
-  copyLocalSymbols();
-
-  if (config->copyRelocs)
-    addSectionSymbols();
-
   // Now that we have a complete set of output sections. This function
   // completes section contents. For example, we need to add strings
   // to the string table, and add entries to .got and .plt.
@@ -751,31 +733,33 @@ bool lld::elf::includeInSymtab(const Symbol &b) {
     SectionBase *sec = d->section;
     if (!sec)
       return true;
+    assert(sec->isLive());
 
     if (auto *s = dyn_cast<MergeInputSection>(sec))
       return s->getSectionPiece(d->value).live;
-    return sec->isLive();
+    return true;
   }
   return b.used || !config->gcSections;
 }
 
-// Local symbols are not in the linker's symbol table. This function scans
-// each object file's symbol table to copy local symbols to the output.
-template <class ELFT> void Writer<ELFT>::copyLocalSymbols() {
-  if (!in.symTab)
-    return;
+// Scan local symbols to:
+//
+// - demote symbols defined relative to /DISCARD/ discarded input sections so
+//   that relocations referencing them will lead to errors.
+// - copy eligible symbols to .symTab
+static void demoteAndCopyLocalSymbols() {
   llvm::TimeTraceScope timeScope("Add local symbols");
-  if (config->copyRelocs && config->discard != DiscardPolicy::None)
-    markUsedLocalSymbols<ELFT>();
   for (ELFFileBase *file : ctx.objectFiles) {
+    DenseMap<SectionBase *, size_t> sectionIndexMap;
     for (Symbol *b : file->getLocalSymbols()) {
       assert(b->isLocal() && "should have been caught in initializeSymbols()");
       auto *dr = dyn_cast<Defined>(b);
-
-      // No reason to keep local undefined symbol in symtab.
       if (!dr)
         continue;
-      if (includeInSymtab(*b) && shouldKeepInSymtab(*dr))
+
+      if (dr->section && !dr->section->isLive())
+        demoteDefined(*dr, sectionIndexMap);
+      else if (in.symTab && includeInSymtab(*b) && shouldKeepInSymtab(*dr))
         in.symTab->addSymbol(b);
     }
   }
@@ -1991,12 +1975,13 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() {
   }
 
   demoteSymbolsAndComputeIsPreemptible();
-  // Also demote local symbols defined relative to discarded input sections so
-  // that relocations referencing them will lead to errors. To avoid unneeded
-  // work, we only do this when /DISCARD/ is seen, but this demotation also
-  // applies to --gc-sections discarded sections.
-  if (script->seenDiscard)
-    demoteLocalSymbolsInDiscardedSections();
+
+  if (config->copyRelocs && config->discard != DiscardPolicy::None)
+    markUsedLocalSymbols<ELFT>();
+  demoteAndCopyLocalSymbols();
+
+  if (config->copyRelocs)
+    addSectionSymbols();
 
   // Change values of linker-script-defined symbols from placeholders (assigned
   // by declareSymbols) to actual definitions.
diff --git a/lld/test/ELF/gc-sections-tls.s b/lld/test/ELF/gc-sections-tls.s
index edcf30e264909e0..3036a676dde1235 100644
--- a/lld/test/ELF/gc-sections-tls.s
+++ b/lld/test/ELF/gc-sections-tls.s
@@ -1,31 +1,25 @@
 # REQUIRES: x86
 # RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o
 
-## Relocation in a non .debug_* referencing a discarded TLS symbol is invalid.
-## If we happen to have no PT_TLS, we will emit an error.
-# RUN: not ld.lld %t.o --gc-sections -o /dev/null 2>&1 | FileCheck %s --check-prefix=ERR
-
-# ERR: error: {{.*}}.o has an STT_TLS symbol but doesn't have an SHF_TLS section
-
-## TODO As a corner case, when /DISCARD/ is present, demoteLocalSymbolsInDiscardedSections
-## demotes tls and the error is not triggered.
-# RUN: echo 'SECTIONS { /DISCARD/ : {} }' > %t.lds
-# RUN: ld.lld %t.o --gc-sections -T %t.lds -o /dev/null
+## When a TLS section is discarded, we will resolve the relocation in a non-SHF_ALLOC
+## section to the addend. Technically, we can emit an error in this case as the
+## relocation type is not TLS.
+# RUN: ld.lld %t.o --gc-sections -o %t
+# RUN: llvm-readelf -x .noalloc %t | FileCheck %s
 
-## If we happen to have a PT_TLS, we will resolve the relocation to
-## an arbitrary value (current implementation uses a negative value).
 # RUN: echo '.section .tbss,"awT"; .globl root; root: .long 0' | \
 # RUN:   llvm-mc -filetype=obj -triple=x86_64 - -o %t1.o
 # RUN: ld.lld --gc-sections -u root %t.o %t1.o -o %t
 # RUN: llvm-readelf -x .noalloc %t | FileCheck %s
 
 # CHECK:      Hex dump of section '.noalloc':
-# CHECK-NEXT: 0x00000000 {{[0-9a-f]+}} ffffffff
+# CHECK-NEXT: 0x00000000 00800000 00000000
 
 .globl _start
 _start:
 
 .section .tbss,"awT",@nobits
+  .long 0
 tls:
   .long 0
 

Copy link
Collaborator

@smithp35 smithp35 left a comment

Choose a reason for hiding this comment

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

LGTM I think that this is a useful cleanup. I can't see any obvious problems at the moment.

Copy link
Contributor

@bevin-hansson bevin-hansson left a comment

Choose a reason for hiding this comment

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

Seems good to me as well.

@MaskRay MaskRay merged commit ec0e556 into llvm:main Oct 18, 2023
5 checks passed
@MaskRay MaskRay deleted the lld-local-symbols branch October 18, 2023 15:56
@ilovepi
Copy link
Contributor

ilovepi commented Oct 18, 2023

I think we're seeing an issue with this patch when building Fuchsia. I haven't bisected precisely yet, but given that this is the only ELF/LLD change in the blame it seems highly likely.

Can you take a look?

Bot: https://luci-milo.appspot.com/ui/p/fuchsia/builders/ci/clang_toolchain.ci.core.x64-release/b8766941698462381393/overview

136211/280027](64) LINK kernel_x64/zircon.elf
FAILED: kernel_x64/zircon.elf kernel_x64/zircon.elf.map kernel_x64/zircon.elf.build-id.stamp
../../prebuilt/third_party/clang/custom/bin/clang++ -o kernel_x64/zircon.elf -Wl,-T,../../zircon/kernel/kernel.ld -Wl,--emit-relocs -g3 -gdwarf-4 -gz=zstd -fdata-sections -ffunction-sections -Wl,--gc-sections -O2 -fno-exceptions -fno-rtti -ffile-compilation-dir=. -no-canonical-prefixes -fuse-ld=lld -Wl,-z,relro --target=x86_64-fuchsia -fcolor-diagnostics -Wl,--color-diagnostics -fcrash-diagnost...
ld.lld: lld/ELF/InputSection.cpp:438: void lld::elf::InputSection::copyRelocations(uint8_t *, llvm::iterator_range<RelIt>) [ELFT = llvm::object::ELFType<llvm::endianness::little, true>, RelTy = llvm::object::Elf_Rel_Impl<llvm::object::ELFType<llvm::endianness::little, true>, true>, RelIt = llvm::mapped_iterator<const llvm::object::Elf_Rel_Impl<llvm::object::ELFType<llvm::endianness::little, tru...
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace.
ld.lld: lld/ELF/InputSection.cpp:438: void lld::elf::InputSection::copyRelocations(uint8_t *, llvm::iterator_range<RelIt>) [ELFT = llvm::object::ELFType<llvm::endianness::little, true>, RelTy = llvm::object::Elf_Rel_Impl<llvm::object::ELFType<llvm::endianness::little, true>, true>, RelIt = llvm::mapped_iterator<const llvm::object::Elf_Rel_Impl<llvm::object::ELFType<llvm::endianness::little, tru...
clang++: error: unable to execute command: Aborted
clang++: error: ld.lld command failed due to signal (use -v to see invocation)
Fuchsia clang version 18.0.0 (https://llvm.googlesource.com/llvm-project 6971081cefdc498ed474182cc06aec68b007955c)
Target: x86_64-unknown-fuchsia
Thread model: posix
InstalledDir: ../../prebuilt/third_party/clang/custom/bin
clang++: note: diagnostic msg:

I had to split the reproducer w/ zip. You should be able to recombine them in the following way:

mv linker-reproducer.z01.zip linker-reproducer.z01 # rename file to work around github upload limitations

zip -F linker-reproducer.zip --out combined-reproducer.zip

The following page has more details if needed: https://superuser.com/questions/336219/how-do-i-split-a-zip-file-into-multiple-segments

linker-reproducer.zip

linker-reproducer.z01.zip

@ilovepi
Copy link
Contributor

ilovepi commented Oct 18, 2023

hmm, seems like I copied a truncated log message from the bot diagnostic. This should be the full output.

FAILED: kernel_x64/zircon.elf kernel_x64/zircon.elf.map kernel_x64/zircon.elf.build-id.stamp 
../../prebuilt/third_party/clang/custom/bin/clang++ -o kernel_x64/zircon.elf -Wl,-T,../../zircon/kernel/kernel.ld -Wl,--emit-relocs -g3 -gdwarf-4 -gz=zstd -fdata-sections -ffunction-sections -Wl,--gc-sections -O2 -fno-exceptions -fno-rtti -ffile-compilation-dir=. -no-canonical-prefixes -fuse-ld=lld -Wl,-z,relro --target=x86_64-fuchsia -fcolor-diagnostics -Wl,--color-diagnostics -fcrash-diagnostics-dir=clang-crashreports -fcrash-diagnostics=all -gen-reproducer=error -Wl,--icf=all -nostdlib -static -Wl,--no-pie -fno-exceptions -fdata-sections -fno-omit-frame-pointer -fno-omit-frame-pointer -fdata-sections -ffunction-sections -Wl,--gc-sections -Wl,-defsym,KERNEL_BASE=0xffffffff80100000 -Wl,-defsym,SMP_MAX_CPUS=32 -Wl,-defsym,BOOT_HEADER_SIZE=0x50 -Wl,--dependency-file=kernel_x64/zircon.elf.d -Wl,-Map,kernel_x64/zircon.elf.map -Wl,--start-group '@kernel_x64/zircon.elf.rsp' ../../src/lib/elfldltl/self-base.ld -Wl,--end-group  && ../../prebuilt/tools/buildidtool/linux-x64/buildidtool -build-id-dir ".build-id" -stamp "kernel_x64/zircon.elf.build-id.stamp" -entry "=kernel_x64/zircon.elf" -entry ".debug=kernel_x64/zircon.elf"
ld.lld: lld/ELF/InputSection.cpp:438: void lld::elf::InputSection::copyRelocations(uint8_t *, llvm::iterator_range<RelIt>) [ELFT = llvm::object::ELFType<llvm::endianness::little, true>, RelTy = llvm::object::Elf_Rel_Impl<llvm::object::ELFType<llvm::endianness::little, true>, true>, RelIt = llvm::mapped_iterator<const llvm::object::Elf_Rel_Impl<llvm::object::ELFType<llvm::endianness::little, true>, true> *, MapRel>]: Assertion `section->isLive()' failed.
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace.
ld.lld: lld/ELF/InputSection.cpp:438: void lld::elf::InputSection::copyRelocations(uint8_t *, llvm::iterator_range<RelIt>) [ELFT = llvm::object::ELFType<llvm::endianness::little, true>, RelTy = llvm::object::Elf_Rel_Impl<llvm::object::ELFType<llvm::endianness::little, true>, true>, RelIt = llvm::mapped_iterator<const llvm::object::Elf_Rel_Impl<llvm::object::ELFType<llvm::endianness::little, true>, true> *, MapRel>]: Assertion `section->isLive()' failed.
clang++: error: unable to execute command: Aborted
clang++: error: ld.lld command failed due to signal (use -v to see invocation)
Fuchsia clang version 18.0.0 (https://llvm.googlesource.com/llvm-project 6971081cefdc498ed474182cc06aec68b007955c)
Target: x86_64-unknown-fuchsia
Thread model: posix
InstalledDir: ../../prebuilt/third_party/clang/custom/bin
clang++: note: diagnostic msg: 

@ilovepi
Copy link
Contributor

ilovepi commented Oct 18, 2023

After bisecting it seems like bbf7b9d is when the issue crops up.

Sorry for pinging the wrong PR. Our CI infra hasn't adapted to linkify github PR numbers yet, and I must have copied the wrong PR number. At least I pinged the right person, but still not great. I'll file a separate issue and CC you directly.

@MaskRay
Copy link
Member Author

MaskRay commented Oct 18, 2023

After bisecting it seems like bbf7b9d is when the issue crops up.

Sorry for pinging the wrong PR. Our CI infra hasn't adapted to linkify github PR numbers yet, and I must have copied the wrong PR number. At least I pinged the right person, but still not great. I'll file a separate issue and CC you directly.

I saw your internal message as well. Do you mean that

bbf7b9d ("[ELF] Remove unused setSymbolAndType after #69295. NFC") introduced a regression which is fixed by this patch?

This ensures for every Defined symbol, d->section->isLive is true. The "NFC" part from the commit message bbf7b9d is incorrect.

Thank you for the zipped reproduce files. I think we miss some coverage for --emit-relocs or -r.

@MaskRay
Copy link
Member Author

MaskRay commented Oct 19, 2023


The following page has more details if needed: superuser.com/questions/336219/how-do-i-split-a-zip-file-into-multiple-segments

linker-reproducer.zip

linker-reproducer.z01.zip

I have confirmed that this patch has actually fixed this regression (--emit-relocs --gc-sections, ALLOC sections discarded by --gc-sections and referenced by non-ALLOC) caused by a previous commit.

Adding a test ab301d8 to prevent regression.

MaskRay added a commit that referenced this pull request Oct 19, 2023
…ions and referenced by non-ALLOC

bbf7b9d accidentally caused a regression that
is fixed by #69425. Add test to prevent regression.
@ilovepi
Copy link
Contributor

ilovepi commented Oct 19, 2023

After bisecting it seems like bbf7b9d is when the issue crops up.
Sorry for pinging the wrong PR. Our CI infra hasn't adapted to linkify github PR numbers yet, and I must have copied the wrong PR number. At least I pinged the right person, but still not great. I'll file a separate issue and CC you directly.

I saw your internal message as well. Do you mean that

bbf7b9d ("[ELF] Remove unused setSymbolAndType after #69295. NFC") introduced a regression which is fixed by this patch?

Sorry, I missed this reply. Yes, that was what I meant.

This ensures for every Defined symbol, d->section->isLive is true. The "NFC" part from the commit message bbf7b9d is incorrect.

Thank you for the zipped reproduce files. I think we miss some coverage for --emit-relocs or -r.

NP. It's what I would want, so I'm happy to do the minimum :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants