[LLD][ELF] Implement --discard-* for cases when -r or --emit-relocs are used.
authorIgor Kudrin <ikudrin@accesssoftek.com>
Sat, 25 Apr 2020 11:58:00 +0000 (18:58 +0700)
committerIgor Kudrin <ikudrin@accesssoftek.com>
Sat, 25 Apr 2020 11:59:41 +0000 (18:59 +0700)
When discarding local symbols with --discard-all or --discard-locals,
the ones which are used in relocations should be preserved. LLD used
the simplest approach and just ignored those switches when -r or
--emit-relocs was specified.

The patch implements handling the --discard-* switches for the cases
when relocations are kept by identifying used local symbols and allowing
removing only unused ones. This makes the behavior of LLD compatible
with GNU linkers.

Differential Revision: https://reviews.llvm.org/D77807

lld/ELF/Symbols.h
lld/ELF/Writer.cpp
lld/test/ELF/emit-relocs-discard-locals.s
lld/test/ELF/relocatable-discard-locals.s

index a36669e..b69d263 100644 (file)
@@ -258,6 +258,9 @@ public:
   uint8_t isPreemptible : 1;
 
   // True if an undefined or shared symbol is used from a live section.
+  //
+  // NOTE: In Writer.cpp the field is used to mark local defined symbols
+  // which are referenced by relocations when -r or --emit-relocs is given.
   uint8_t used : 1;
 
   // True if a call to this symbol needs to be followed by a restore of the
index a9dd643..03fe0fe 100644 (file)
@@ -637,13 +637,48 @@ template <class ELFT> void Writer<ELFT>::run() {
     error("failed to write to the output file: " + toString(std::move(e)));
 }
 
+template <class ELFT, class RelTy>
+static void markUsedLocalSymbolsImpl(ObjFile<ELFT> *file,
+                                     llvm::ArrayRef<RelTy> rels) {
+  for (const RelTy &rel : rels) {
+    Symbol &sym = file->getRelocTargetSym(rel);
+    if (sym.isLocal())
+      sym.used = true;
+  }
+}
+
+// The function ensures that the "used" field of local symbols reflects the fact
+// that the symbol is used in a relocation from a live section.
+template <class ELFT> static void markUsedLocalSymbols() {
+  // With --gc-sections, the field is already filled.
+  // See MarkLive<ELFT>::resolveReloc().
+  if (config->gcSections)
+    return;
+  // Without --gc-sections, the field is initialized with "true".
+  // Drop the flag first and then rise for symbols referenced in relocations.
+  for (InputFile *file : objectFiles) {
+    ObjFile<ELFT> *f = cast<ObjFile<ELFT>>(file);
+    for (Symbol *b : f->getLocalSymbols())
+      b->used = false;
+    for (InputSectionBase *s : f->getSections()) {
+      InputSection *isec = dyn_cast_or_null<InputSection>(s);
+      if (!isec)
+        continue;
+      if (isec->type == SHT_REL)
+        markUsedLocalSymbolsImpl(f, isec->getDataAs<typename ELFT::Rel>());
+      else if (isec->type == SHT_RELA)
+        markUsedLocalSymbolsImpl(f, isec->getDataAs<typename ELFT::Rela>());
+    }
+  }
+}
+
 static bool shouldKeepInSymtab(const Defined &sym) {
   if (sym.isSection())
     return false;
 
-  // If --emit-reloc or -r is given, all symbols including local ones need to be
-  // copied because they may be referenced by relocations.
-  if (config->copyRelocs)
+  // If --emit-reloc or -r is given, preserve symbols referenced by relocations
+  // from live sections.
+  if (config->copyRelocs && sym.used)
     return true;
 
   if (config->discard == DiscardPolicy::None)
@@ -696,6 +731,8 @@ static bool includeInSymtab(const Symbol &b) {
 template <class ELFT> void Writer<ELFT>::copyLocalSymbols() {
   if (!in.symTab)
     return;
+  if (config->copyRelocs && config->discard != DiscardPolicy::None)
+    markUsedLocalSymbols<ELFT>();
   for (InputFile *file : objectFiles) {
     ObjFile<ELFT> *f = cast<ObjFile<ELFT>>(file);
     for (Symbol *b : f->getLocalSymbols()) {
index 39d1aa5..baf3150 100644 (file)
@@ -1,31 +1,50 @@
 # REQUIRES: x86
-## Test that --emit-relocs keeps local symbols and overrides --discard-{locals,all}.
+## Test that --emit-relocs keeps local symbols which are used in relocations
+## even when --discard-{locals,all} is given.
+
+## There are two separate code paths that mark used local symbols when
+## --gc-sections is specified and when is not. The test checks both.
 
 # RUN: llvm-mc -filetype=obj -triple=x86_64 -save-temp-labels %s -o %t.o
 
 # RUN: ld.lld --emit-relocs --discard-locals %t.o -o %tlocal
-# RUN: llvm-readelf -s %tlocal | FileCheck --check-prefixes=SYM,SYM-NOGC %s
+# RUN: llvm-readelf -s %tlocal | FileCheck --check-prefixes=DISCARD-LOCALS,DISCARD-LOCALS-NOGC %s
 # RUN: llvm-readobj -r %tlocal | FileCheck --check-prefix=REL %s
+
 ## --gc-sections can discard symbols relative to GCed sections (including STT_SECTION).
 # RUN: ld.lld --emit-relocs --discard-locals --gc-sections %t.o -o %tlocal.gc
-# RUN: llvm-readelf -s %tlocal.gc | FileCheck --check-prefix=SYM %s
+# RUN: llvm-readelf -s %tlocal.gc | FileCheck --check-prefix=DISCARD-LOCALS %s
 # RUN: llvm-readobj -r %tlocal | FileCheck --check-prefix=REL %s
 
 # RUN: ld.lld --emit-relocs --discard-all %t.o -o %tall
-# RUN: llvm-readelf -s %tall | FileCheck --check-prefixes=SYM,SYM-NOGC %s
+# RUN: llvm-readelf -s %tall | FileCheck --check-prefixes=DISCARD-ALL,DISCARD-ALL-NOGC %s
 # RUN: llvm-readobj -r %tall | FileCheck --check-prefix=REL %s
 
-# SYM:           NOTYPE  LOCAL  DEFAULT {{.*}} .Lunused
-# SYM-NOGC-NEXT: NOTYPE  LOCAL  DEFAULT {{.*}} .Lunused_gc
-# SYM-NEXT:      NOTYPE  LOCAL  DEFAULT {{.*}} .Lused
-# SYM-NEXT:      NOTYPE  LOCAL  DEFAULT {{.*}} unused
-# SYM-NOGC-NEXT: NOTYPE  LOCAL  DEFAULT {{.*}} unused_gc
-# SYM-NEXT:      NOTYPE  LOCAL  DEFAULT {{.*}} used
-# SYM-NEXT:      SECTION LOCAL  DEFAULT {{.*}} .text
-# SYM-NEXT:      SECTION LOCAL  DEFAULT {{.*}} text
-# SYM-NOGC-NEXT: SECTION LOCAL  DEFAULT {{.*}} gc
-# SYM-NEXT:      SECTION LOCAL  DEFAULT {{.*}} .comment
-# SYM-NEXT:      NOTYPE  GLOBAL DEFAULT {{.*}} _start
+# RUN: ld.lld --emit-relocs --discard-all --gc-sections %t.o -o %tall.gc
+# RUN: llvm-readelf -s %tall.gc | FileCheck --check-prefix=DISCARD-ALL %s
+# RUN: llvm-readobj -r %tall.gc | FileCheck --check-prefix=REL %s
+
+## --discard-locals removes unused local symbols which start with ".L"
+# DISCARD-LOCALS:    0: {{0+}} 0 NOTYPE  LOCAL  DEFAULT UND
+# DISCARD-LOCALS-NEXT:           NOTYPE  LOCAL  DEFAULT {{.*}} .Lused
+# DISCARD-LOCALS-NEXT:           NOTYPE  LOCAL  DEFAULT {{.*}} unused
+# DISCARD-LOCALS-NOGC-NEXT:      NOTYPE  LOCAL  DEFAULT {{.*}} unused_gc
+# DISCARD-LOCALS-NEXT:           NOTYPE  LOCAL  DEFAULT {{.*}} used
+# DISCARD-LOCALS-NEXT:           SECTION LOCAL  DEFAULT {{.*}} .text
+# DISCARD-LOCALS-NEXT:           SECTION LOCAL  DEFAULT {{.*}} text
+# DISCARD-LOCALS-NOGC-NEXT:      SECTION LOCAL  DEFAULT {{.*}} gc
+# DISCARD-LOCALS-NEXT:           SECTION LOCAL  DEFAULT {{.*}} .comment
+# DISCARD-LOCALS-NEXT:           NOTYPE  GLOBAL DEFAULT {{.*}} _start
+
+## --discard-all removes all unused regular local symbols.
+# DISCARD-ALL:       0: {{0+}} 0 NOTYPE  LOCAL  DEFAULT UND
+# DISCARD-ALL-NEXT:              NOTYPE  LOCAL  DEFAULT {{.*}} .Lused
+# DISCARD-ALL-NEXT:              NOTYPE  LOCAL  DEFAULT {{.*}} used
+# DISCARD-ALL-NEXT:              SECTION LOCAL  DEFAULT {{.*}} .text
+# DISCARD-ALL-NEXT:              SECTION LOCAL  DEFAULT {{.*}} text
+# DISCARD-ALL-NOGC-NEXT:         SECTION LOCAL  DEFAULT {{.*}} gc
+# DISCARD-ALL-NEXT:              SECTION LOCAL  DEFAULT {{.*}} .comment
+# DISCARD-ALL-NEXT:              NOTYPE  GLOBAL DEFAULT {{.*}} _start
 
 # REL:      .rela.text {
 # REL-NEXT:   R_X86_64_PLT32 text 0xFFFFFFFFFFFFFFFC
index c1e06bd..298590d 100644 (file)
@@ -1,27 +1,36 @@
 # REQUIRES: x86
-## Test that -r keeps local symbols and overrides --discard-{locals,all}.
-## Also see emit-relocs-discard-locals.s
+## Test that -r keeps local symbols which are used in relocations even when
+## --discard-{locals,all} is given.
 
 # RUN: llvm-mc -filetype=obj -triple=x86_64 -save-temp-labels %s -o %t.o
 
 # RUN: ld.lld -r --discard-locals %t.o -o %tlocal.ro
-# RUN: llvm-readelf -s %tlocal.ro | FileCheck --check-prefixes=SYM,SYM-NOGC %s
+# RUN: llvm-readelf -s %tlocal.ro | FileCheck --check-prefix=DISCARD-LOCALS %s
 # RUN: llvm-readobj -r %tlocal.ro | FileCheck --check-prefix=REL %s
 
 # RUN: ld.lld -r --discard-all %t.o -o %tall.ro
-# RUN: llvm-readelf -s %tall.ro | FileCheck --check-prefixes=SYM,SYM-NOGC %s
+# RUN: llvm-readelf -s %tall.ro | FileCheck --check-prefix=DISCARD-ALL %s
 # RUN: llvm-readobj -r %tall.ro | FileCheck --check-prefix=REL %s
 
-# SYM:           NOTYPE  LOCAL  DEFAULT {{.*}} .Lunused
-# SYM-NOGC-NEXT: NOTYPE  LOCAL  DEFAULT {{.*}} .Lunused_gc
-# SYM-NEXT:      NOTYPE  LOCAL  DEFAULT {{.*}} .Lused
-# SYM-NEXT:      NOTYPE  LOCAL  DEFAULT {{.*}} unused
-# SYM-NOGC-NEXT: NOTYPE  LOCAL  DEFAULT {{.*}} unused_gc
-# SYM-NEXT:      NOTYPE  LOCAL  DEFAULT {{.*}} used
-# SYM-NEXT:      SECTION LOCAL  DEFAULT {{.*}} .text
-# SYM-NEXT:      SECTION LOCAL  DEFAULT {{.*}} text
-# SYM-NOGC-NEXT: SECTION LOCAL  DEFAULT {{.*}} gc
-# SYM-NEXT:      NOTYPE  GLOBAL DEFAULT {{.*}} _start
+## --discard-locals removes unused local symbols which start with ".L"
+# DISCARD-LOCALS:    0: {{0+}} 0 NOTYPE  LOCAL  DEFAULT UND
+# DISCARD-LOCALS-NEXT:           NOTYPE  LOCAL  DEFAULT {{.*}} .Lused
+# DISCARD-LOCALS-NEXT:           NOTYPE  LOCAL  DEFAULT {{.*}} unused
+# DISCARD-LOCALS-NEXT:           NOTYPE  LOCAL  DEFAULT {{.*}} unused_gc
+# DISCARD-LOCALS-NEXT:           NOTYPE  LOCAL  DEFAULT {{.*}} used
+# DISCARD-LOCALS-NEXT:           SECTION LOCAL  DEFAULT {{.*}} .text
+# DISCARD-LOCALS-NEXT:           SECTION LOCAL  DEFAULT {{.*}} text
+# DISCARD-LOCALS-NEXT:           SECTION LOCAL  DEFAULT {{.*}} gc
+# DISCARD-LOCALS-NEXT:           NOTYPE  GLOBAL DEFAULT {{.*}} _start
+
+## --discard-all removes all unused regular local symbols.
+# DISCARD-ALL:    0: {{0+}} 0 NOTYPE  LOCAL  DEFAULT UND
+# DISCARD-ALL-NEXT:           NOTYPE  LOCAL  DEFAULT {{.*}} .Lused
+# DISCARD-ALL-NEXT:           NOTYPE  LOCAL  DEFAULT {{.*}} used
+# DISCARD-ALL-NEXT:           SECTION LOCAL  DEFAULT {{.*}} .text
+# DISCARD-ALL-NEXT:           SECTION LOCAL  DEFAULT {{.*}} text
+# DISCARD-ALL-NEXT:           SECTION LOCAL  DEFAULT {{.*}} gc
+# DISCARD-ALL-NEXT:           NOTYPE  GLOBAL DEFAULT {{.*}} _start
 
 # REL:      .rela.text {
 # REL-NEXT:   R_X86_64_PLT32 text 0xFFFFFFFFFFFFFFFC