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/Writer.cpp =================================================================== --- lld/ELF/Writer.cpp +++ lld/ELF/Writer.cpp @@ -53,6 +53,7 @@ void run(); private: + void markUsedLocalSymbols(); void copyLocalSymbols(); void addSectionSymbols(); void forEachRelSec(llvm::function_ref fn); @@ -560,8 +561,9 @@ // The main function of the writer. template void Writer::run() { - if (config->discard != DiscardPolicy::All) - copyLocalSymbols(); + if (config->copyRelocs && config->discard != DiscardPolicy::None) + markUsedLocalSymbols(); + copyLocalSymbols(); if (config->copyRelocs) addSectionSymbols(); @@ -638,6 +640,33 @@ 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 void Writer::markUsedLocalSymbols() { + 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; @@ -645,11 +674,12 @@ 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 (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. Index: lld/test/ELF/discard-locals-preserve-used.s =================================================================== --- /dev/null +++ lld/test/ELF/discard-locals-preserve-used.s @@ -0,0 +1,47 @@ +## 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. + +# REQUIRES: x86 +# 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: