Index: llvm/test/tools/llvm-objcopy/ELF/discard-locals-rel.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-objcopy/ELF/discard-locals-rel.test @@ -0,0 +1,27 @@ +# RUN: yaml2obj %s > %t +# RUN: not llvm-objcopy --discard-locals %t %t2 2>&1 | FileCheck %s + +!ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + - Name: .rel.text + Type: SHT_REL + Link: .symtab + Info: .text + Relocations: + - Offset: 0x1000 + Symbol: .L.rel + Type: R_X86_64_PC32 +Symbols: + Local: + - Name: .L.rel + Type: STT_FUNC + Section: .text + +# CHECK: not stripping symbol '.L.rel' because it is named in a relocation. Index: llvm/test/tools/llvm-objcopy/ELF/discard-locals.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-objcopy/ELF/discard-locals.test @@ -0,0 +1,121 @@ +# RUN: yaml2obj %s > %t +# RUN: cp %t %t1 +# RUN: llvm-objcopy --discard-locals %t %t2 +# Verify that llvm-objcopy has not modified the input. +# RUN: cmp %t %t1 +# RUN: llvm-readobj --symbols %t2 | FileCheck %s + +# RUN: llvm-objcopy -X %t %t3 +# Verify that llvm-objcopy has not modified the input. +# RUN: cmp %t %t1 +# RUN: cmp %t2 %t3 + +# Verify that llvm-strip modifies the symbol table the same way. + +# RUN: cp %t %t4 +# RUN: llvm-strip --discard-locals %t4 +# RUN: cmp %t2 %t4 + +# RUN: cp %t %t5 +# RUN: llvm-strip -X %t5 +# RUN: cmp %t2 %t5 + +!ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + - Name: .LLVM.Custom.Section + Type: SHT_PROGBITS +Symbols: + Local: + - Name: Local + Type: STT_FUNC + Section: .text + - Name: .L.LocalSection + Type: STT_SECTION + Section: .text + - Type: STT_SECTION + Section: .LLVM.Custom.Section + - Name: .L.LocalFile + Type: STT_FILE + - Name: .L.str + Type: STT_OBJECT + Section: .text + - Name: .L.undefined + - Name: .L.abs + Index: SHN_ABS + Global: + - Name: .L.Global + Type: STT_FUNC + Section: .text + +# CHECK: Symbols [ +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: +# CHECK-NEXT: Value: 0x0 +# CHECK-NEXT: Size: 0 +# CHECK-NEXT: Binding: Local +# CHECK-NEXT: Type: None +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: Undefined +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: Local +# CHECK-NEXT: Value: +# CHECK-NEXT: Size: +# CHECK-NEXT: Binding: Local +# CHECK-NEXT: Type: Function +# CHECK-NEXT: Other: +# CHECK-NEXT: Section: .text +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: .L.LocalSection +# CHECK-NEXT: Value: +# CHECK-NEXT: Size: +# CHECK-NEXT: Binding: Local +# CHECK-NEXT: Type: Section +# CHECK-NEXT: Other: +# CHECK-NEXT: Section: .text +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: +# CHECK-NEXT: Value: +# CHECK-NEXT: Size: +# CHECK-NEXT: Binding: Local +# CHECK-NEXT: Type: Section +# CHECK-NEXT: Other: +# CHECK-NEXT: Section: .LLVM.Custom.Section +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: .L.LocalFile +# CHECK-NEXT: Value: +# CHECK-NEXT: Size: +# CHECK-NEXT: Binding: Local +# CHECK-NEXT: Type: File +# CHECK-NEXT: Other: +# CHECK-NEXT: Section: Undefined +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: .L.undefined +# CHECK-NEXT: Value: +# CHECK-NEXT: Size: +# CHECK-NEXT: Binding: Local +# CHECK-NEXT: Type: None +# CHECK-NEXT: Other: +# CHECK-NEXT: Section: Undefined +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: .L.Global +# CHECK-NEXT: Value: +# CHECK-NEXT: Size: +# CHECK-NEXT: Binding: Global +# CHECK-NEXT: Type: Function +# CHECK-NEXT: Other: +# CHECK-NEXT: Section: .text +# CHECK-NEXT: } +# CHECK-NEXT: ] Index: llvm/test/tools/llvm-objcopy/ELF/discard-mix-local-and-all.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-objcopy/ELF/discard-mix-local-and-all.test @@ -0,0 +1,107 @@ +# RUN: yaml2obj %s > %t +# Establish baseline objects for further checks. --discard-locals only discards +# compiler-generated local symbols (starting with .L), --discard-all discards +# all regular local symbols. +# RUN: llvm-objcopy %t %t-discard-none +# RUN: llvm-readobj --symbols %t-discard-none | FileCheck %s --check-prefixes=CHECK,LOCAL,COMPILER-LOCAL +# RUN: llvm-objcopy --discard-all %t %t-discard-all +# RUN: llvm-readobj --symbols %t-discard-all | FileCheck %s +# RUN: llvm-objcopy --discard-locals %t %t-discard-locals +# RUN: llvm-readobj --symbols %t-discard-locals | FileCheck %s --check-prefixes=CHECK,LOCAL + +# When mixing --discard-all and --discard-locals, the last one wins. +# RUN: llvm-objcopy --discard-all --discard-locals %t %t.1.o +# RUN: cmp %t.1.o %t-discard-locals +# RUN: llvm-objcopy --discard-locals --discard-all %t %t.2.o +# RUN: cmp %t.2.o %t-discard-all +# RUN: llvm-objcopy -x -X %t %t.3.o +# RUN: cmp %t.3.o %t-discard-locals +# RUN: llvm-objcopy -X -x %t %t.4.o +# RUN: cmp %t.4.o %t-discard-all +# RUN: llvm-objcopy -x -X -x -X %t %t.5.o +# RUN: cmp %t.5.o %t-discard-locals +# RUN: llvm-objcopy -X -x -X -x %t %t.6.o +# RUN: cmp %t.6.o %t-discard-all +# RUN: llvm-objcopy -X -x -X -x --discard-locals %t %t.7.o +# RUN: cmp %t.7.o %t-discard-locals +# RUN: llvm-objcopy -X -x -X -x --discard-all %t %t.8.o +# RUN: cmp %t.8.o %t-discard-all + +# llvm-strip works in the same way. +# RUN: llvm-strip --discard-all --discard-locals %t -o %t.9.o +# RUN: cmp %t.9.o %t-discard-locals +# RUN: llvm-strip --discard-locals --discard-all %t -o %t.10.o +# RUN: cmp %t.10.o %t-discard-all +# RUN: llvm-strip -x -X %t -o %t.11.o +# RUN: cmp %t.11.o %t-discard-locals +# RUN: llvm-strip -X -x %t -o %t.12.o +# RUN: cmp %t.12.o %t-discard-all +# RUN: llvm-strip -x -X -x -X %t -o %t.13.o +# RUN: cmp %t.13.o %t-discard-locals +# RUN: llvm-strip -X -x -X -x %t -o %t.14.o +# RUN: cmp %t.14.o %t-discard-all +# RUN: llvm-strip -X -x -X -x --discard-locals %t -o %t.15.o +# RUN: cmp %t.15.o %t-discard-locals +# RUN: llvm-strip -X -x -X -x --discard-all %t -o %t.16.o +# RUN: cmp %t.16.o %t-discard-all + +!ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS +Symbols: + Local: + - Name: Local + Type: STT_FUNC + Section: .text + - Name: .L.str + Type: STT_OBJECT + Section: .text + Global: + - Name: Global + Type: STT_FUNC + Section: .text + +# CHECK: Symbols [ +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: +# CHECK-NEXT: Value: 0x0 +# CHECK-NEXT: Size: 0 +# CHECK-NEXT: Binding: Local +# CHECK-NEXT: Type: None +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: Undefined +# CHECK-NEXT: } +# LOCAL-NEXT: Symbol { +# LOCAL-NEXT: Name: Local +# LOCAL-NEXT: Value: +# LOCAL-NEXT: Size: +# LOCAL-NEXT: Binding: Local +# LOCAL-NEXT: Type: Function +# LOCAL-NEXT: Other: +# LOCAL-NEXT: Section: .text +# LOCAL-NEXT: } +# COMPILER-LOCAL-NEXT: Symbol { +# COMPILER-LOCAL-NEXT: Name: .L.str +# COMPILER-LOCAL-NEXT: Value: +# COMPILER-LOCAL-NEXT: Size: +# COMPILER-LOCAL-NEXT: Binding: Local +# COMPILER-LOCAL-NEXT: Type: Object +# COMPILER-LOCAL-NEXT: Other: +# COMPILER-LOCAL-NEXT: Section: .text +# COMPILER-LOCAL-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: Global +# CHECK-NEXT: Value: +# CHECK-NEXT: Size: +# CHECK-NEXT: Binding: Global +# CHECK-NEXT: Type: Function +# CHECK-NEXT: Other: +# CHECK-NEXT: Section: .text +# CHECK-NEXT: } +# CHECK-NEXT: ] Index: llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp =================================================================== --- llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp +++ llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp @@ -97,7 +97,7 @@ return true; if (Config.StripDebug || Config.StripAll || Config.StripAllGNU || - Config.DiscardAll || Config.StripUnneeded) { + Config.DiscardMode == DiscardType::All || Config.StripUnneeded) { if (isDebugSection(Sec) && (Sec.Header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE) != 0) return true; @@ -125,7 +125,7 @@ Sec.Relocs.clear(); // If we need to do per-symbol removals, initialize the Referenced field. - if (Config.StripUnneeded || Config.DiscardAll || + if (Config.StripUnneeded || Config.DiscardMode == DiscardType::All || !Config.SymbolsToRemove.empty()) if (Error E = Obj.markSymbols()) return E; @@ -159,7 +159,8 @@ // GNU objcopy keeps referenced local symbols and external symbols // if --discard-all is set, similar to what --strip-unneeded does, // but undefined local symbols are kept when --discard-all is set. - if (Config.DiscardAll && Sym.Sym.StorageClass == IMAGE_SYM_CLASS_STATIC && + if (Config.DiscardMode == DiscardType::All && + Sym.Sym.StorageClass == IMAGE_SYM_CLASS_STATIC && Sym.Sym.SectionNumber != 0) return true; } @@ -180,7 +181,8 @@ !Config.SetSectionFlags.empty() || !Config.SymbolsToRename.empty() || Config.ExtractDWO || Config.KeepFileSymbols || Config.LocalizeHidden || Config.PreserveDates || Config.StripDWO || Config.StripNonAlloc || - Config.StripSections || Config.Weaken || Config.DecompressDebugSections) { + Config.StripSections || Config.Weaken || Config.DecompressDebugSections || + Config.DiscardMode == DiscardType::Locals) { return createStringError(llvm::errc::invalid_argument, "Option not supported by llvm-objcopy for COFF"); } Index: llvm/tools/llvm-objcopy/CopyConfig.h =================================================================== --- llvm/tools/llvm-objcopy/CopyConfig.h +++ llvm/tools/llvm-objcopy/CopyConfig.h @@ -42,6 +42,12 @@ uint64_t NewFlags; }; +enum class DiscardType { + None, // Default + All, // --discard-all (-x) + Locals, // --discard-locals (-X) +}; + // Configuration for copying/stripping a single file. struct CopyConfig { // Main input/output options @@ -62,6 +68,7 @@ Optional BuildIdLinkOutput; StringRef SplitDWO; StringRef SymbolsPrefix; + DiscardType DiscardMode = DiscardType::None; // Repeated options std::vector AddSection; @@ -83,7 +90,6 @@ // Boolean options bool DeterministicArchives = true; - bool DiscardAll = false; bool ExtractDWO = false; bool KeepFileSymbols = false; bool LocalizeHidden = false; Index: llvm/tools/llvm-objcopy/CopyConfig.cpp =================================================================== --- llvm/tools/llvm-objcopy/CopyConfig.cpp +++ llvm/tools/llvm-objcopy/CopyConfig.cpp @@ -387,7 +387,11 @@ Config.ExtractDWO = InputArgs.hasArg(OBJCOPY_extract_dwo); Config.LocalizeHidden = InputArgs.hasArg(OBJCOPY_localize_hidden); Config.Weaken = InputArgs.hasArg(OBJCOPY_weaken); - Config.DiscardAll = InputArgs.hasArg(OBJCOPY_discard_all); + if (InputArgs.hasArg(OBJCOPY_discard_all, OBJCOPY_discard_locals)) + Config.DiscardMode = + InputArgs.hasFlag(OBJCOPY_discard_all, OBJCOPY_discard_locals) + ? DiscardType::All + : DiscardType::Locals; Config.OnlyKeepDebug = InputArgs.hasArg(OBJCOPY_only_keep_debug); Config.KeepFileSymbols = InputArgs.hasArg(OBJCOPY_keep_file_symbols); Config.DecompressDebugSections = @@ -467,13 +471,17 @@ CopyConfig Config; Config.StripDebug = InputArgs.hasArg(STRIP_strip_debug); - Config.DiscardAll = InputArgs.hasArg(STRIP_discard_all); + if (InputArgs.hasArg(STRIP_discard_all, STRIP_discard_locals)) + Config.DiscardMode = + InputArgs.hasFlag(STRIP_discard_all, STRIP_discard_locals) + ? DiscardType::All + : DiscardType::Locals; Config.StripUnneeded = InputArgs.hasArg(STRIP_strip_unneeded); Config.StripAll = InputArgs.hasArg(STRIP_strip_all); Config.StripAllGNU = InputArgs.hasArg(STRIP_strip_all_gnu); - if (!Config.StripDebug && !Config.StripUnneeded && !Config.DiscardAll && - !Config.StripAllGNU) + if (!Config.StripDebug && !Config.StripUnneeded && + Config.DiscardMode == DiscardType::None && !Config.StripAllGNU) Config.StripAll = true; for (auto Arg : InputArgs.filtered(STRIP_keep_section)) Index: llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp =================================================================== --- llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp +++ llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp @@ -336,9 +336,11 @@ (Config.KeepFileSymbols && Sym.Type == STT_FILE)) return false; - if (Config.DiscardAll && Sym.Binding == STB_LOCAL && - Sym.getShndx() != SHN_UNDEF && Sym.Type != STT_FILE && - Sym.Type != STT_SECTION) + if ((Config.DiscardMode == DiscardType::All || + (Config.DiscardMode == DiscardType::Locals && + StringRef(Sym.Name).startswith(".L"))) && + Sym.Binding == STB_LOCAL && Sym.getShndx() != SHN_UNDEF && + Sym.Type != STT_FILE && Sym.Type != STT_SECTION) return true; if (Config.StripAll || Config.StripAllGNU) Index: llvm/tools/llvm-objcopy/ObjcopyOpts.td =================================================================== --- llvm/tools/llvm-objcopy/ObjcopyOpts.td +++ llvm/tools/llvm-objcopy/ObjcopyOpts.td @@ -149,6 +149,12 @@ def W : JoinedOrSeparate<["-"], "W">, Alias; def weaken : Flag<["-", "--"], "weaken">, HelpText<"Mark all global symbols as weak">; + +def discard_locals : Flag<["-", "--"], "discard-locals">, + HelpText<"Remove compiler-generated local symbols, (e.g. " + "symbols starting with .L)">; +def X : Flag<["-"], "X">, Alias; + def discard_all : Flag<["-", "--"], "discard-all">, HelpText<"Remove all local symbols except file and section symbols">; Index: llvm/tools/llvm-objcopy/StripOpts.td =================================================================== --- llvm/tools/llvm-objcopy/StripOpts.td +++ llvm/tools/llvm-objcopy/StripOpts.td @@ -57,6 +57,11 @@ MetaVarName<"symbol">; def K : JoinedOrSeparate<["-"], "K">, Alias; +def discard_locals : Flag<["-", "--"], "discard-locals">, + HelpText<"Remove compiler-generated local symbols, (e.g. " + "symbols starting with .L)">; +def X : Flag<["-"], "X">, Alias; + def discard_all : Flag<["-", "--"], "discard-all">, HelpText<"Remove all local symbols except file and section symbols">;