Index: lld/ELF/Driver.cpp =================================================================== --- lld/ELF/Driver.cpp +++ lld/ELF/Driver.cpp @@ -608,9 +608,6 @@ } static DiscardPolicy getDiscard(opt::InputArgList &args) { - if (args.hasArg(OPT_relocatable)) - return DiscardPolicy::None; - auto *arg = args.getLastArg(OPT_discard_all, OPT_discard_locals, OPT_discard_none); if (!arg) Index: lld/ELF/Symbols.h =================================================================== --- lld/ELF/Symbols.h +++ lld/ELF/Symbols.h @@ -238,8 +238,8 @@ exportDynamic(isExportDynamic(k, visibility)), inDynamicList(false), canInline(false), referenced(false), traced(false), needsPltAddr(false), isInIplt(false), gotInIgot(false), isPreemptible(false), - used(!config->gcSections), needsTocRestore(false), - scriptDefined(false) {} + used(!config->gcSections && binding != llvm::ELF::STB_LOCAL), + needsTocRestore(false), scriptDefined(false) {} public: // True the symbol should point to its PLT entry. @@ -258,6 +258,7 @@ uint8_t isPreemptible : 1; // True if an undefined or shared symbol is used from a live section. + // True if a local symbol is referenced by a relocation. uint8_t used : 1; // True if a call to this symbol needs to be followed by a restore of the Index: lld/ELF/Writer.cpp =================================================================== --- lld/ELF/Writer.cpp +++ lld/ELF/Writer.cpp @@ -560,8 +560,7 @@ // The main function of the writer. template void Writer::run() { - if (config->discard != DiscardPolicy::All) - copyLocalSymbols(); + copyLocalSymbols(); if (config->copyRelocs) addSectionSymbols(); @@ -638,6 +637,31 @@ 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; + } +} + +template static void markUsedLocalSymbols() { + for (InputFile *file : objectFiles) { + ObjFile *f = cast>(file); + 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; @@ -645,11 +669,13 @@ if (config->discard == DiscardPolicy::None) return true; - // If -emit-reloc is given, all symbols including local ones need to be - // copied because they may be referenced by relocations. - if (config->emitRelocs) + // If --emit-reloc or -r is given, preserve symbols referenced by relocations. + if (config->copyRelocs && sym.used) return true; + if (config->discard == DiscardPolicy::All) + return false; + // In ELF assembly .L symbols are normally discarded by the assembler. // If the assembler fails to do so, the linker discards them if // * --discard-locals is used. @@ -695,6 +721,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()) { Index: lld/test/ELF/discard-locals-preserve-used.s =================================================================== --- /dev/null +++ lld/test/ELF/discard-locals-preserve-used.s @@ -0,0 +1,48 @@ +# REQUIRES: x86 + +## This test checks that --discard-all and --discard-locals, when used with -r +## or --emit-relocs, preserve local symbols which are used in relocations and +## remove only unreferenced ones. + +# RUN: llvm-mc -filetype=obj -triple=x86_64 -save-temp-labels %s -o %t + +# RUN: ld.lld --discard-none -shared --emit-relocs %t -o - | \ +# RUN: llvm-nm - | FileCheck %s --check-prefix=DISCARD-NONE + +# RUN: ld.lld --discard-locals -shared --emit-relocs %t -o - | \ +# RUN: llvm-nm - | FileCheck %s --check-prefix=DISCARD-LOCALS + +# RUN: ld.lld --discard-all -shared --emit-relocs %t -o - | \ +# RUN: llvm-nm - | FileCheck %s --check-prefix=DISCARD-ALL + +# RUN: ld.lld --discard-none -r %t -o - | \ +# RUN: llvm-nm - | FileCheck %s --check-prefix=DISCARD-NONE + +# RUN: ld.lld --discard-locals -r %t -o - | \ +# RUN: llvm-nm - | FileCheck %s --check-prefix=DISCARD-LOCALS + +# RUN: ld.lld --discard-all -r %t -o - | \ +# RUN: llvm-nm - | FileCheck %s --check-prefix=DISCARD-ALL + +# DISCARD-NONE: .LUnused +# DISCARD-NONE-NEXT: .LUsed +# DISCARD-NONE-NEXT: Unused +# DISCARD-NONE-NEXT: Used + +# DISCARD-LOCALS-NOT: .LUnused +# DISCARD-LOCALS: .LUsed +# DISCARD-LOCALS-NEXT: Unused +# DISCARD-LOCALS-NEXT: Used + +# DISCARD-ALL-NOT: .LUnused +# DISCARD-ALL: .LUsed +# DISCARD-ALL-NOT: Unused +# DISCARD-ALL-NEXT: Used + +.text + jmp .LUsed@PLT + call Used@PLT +.LUnused: +Unused: +.LUsed: +Used: