diff --git a/lld/ELF/Symbols.h b/lld/ELF/Symbols.h --- a/lld/ELF/Symbols.h +++ b/lld/ELF/Symbols.h @@ -258,6 +258,9 @@ 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 diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -637,13 +637,48 @@ error("failed to write to the output file: " + toString(std::move(e))); } +template +static void markUsedLocalSymbolsImpl(ObjFile *file, + llvm::ArrayRef 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 static void markUsedLocalSymbols() { + // With --gc-sections, the field is already filled. + // See MarkLive::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 *f = cast>(file); + for (Symbol *b : f->getLocalSymbols()) + b->used = false; + for (InputSectionBase *s : f->getSections()) { + InputSection *isec = dyn_cast_or_null(s); + if (!isec) + continue; + if (isec->type == SHT_REL) + markUsedLocalSymbolsImpl(f, isec->getDataAs()); + else if (isec->type == SHT_RELA) + markUsedLocalSymbolsImpl(f, isec->getDataAs()); + } + } +} + 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 @@ template void Writer::copyLocalSymbols() { if (!in.symTab) return; + if (config->copyRelocs && config->discard != DiscardPolicy::None) + markUsedLocalSymbols(); for (InputFile *file : objectFiles) { ObjFile *f = cast>(file); for (Symbol *b : f->getLocalSymbols()) { diff --git a/lld/test/ELF/emit-relocs-discard-locals.s b/lld/test/ELF/emit-relocs-discard-locals.s --- a/lld/test/ELF/emit-relocs-discard-locals.s +++ b/lld/test/ELF/emit-relocs-discard-locals.s @@ -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 diff --git a/lld/test/ELF/relocatable-discard-locals.s b/lld/test/ELF/relocatable-discard-locals.s --- a/lld/test/ELF/relocatable-discard-locals.s +++ b/lld/test/ELF/relocatable-discard-locals.s @@ -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