--- /dev/null
+# 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.
--- /dev/null
+# 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: ]
--- /dev/null
+# 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: ]
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;
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;
// 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;
}
!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");
}
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 =
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))
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
Optional<StringRef> BuildIdLinkOutput;
StringRef SplitDWO;
StringRef SymbolsPrefix;
+ DiscardType DiscardMode = DiscardType::None;
// Repeated options
std::vector<StringRef> AddSection;
// Boolean options
bool DeterministicArchives = true;
- bool DiscardAll = false;
bool ExtractDWO = false;
bool KeepFileSymbols = false;
bool LocalizeHidden = false;
(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)
def W : JoinedOrSeparate<["-"], "W">, Alias<weaken_symbol>;
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<discard_locals>;
+
def discard_all
: Flag<["-", "--"], "discard-all">,
HelpText<"Remove all local symbols except file and section symbols">;
MetaVarName<"symbol">;
def K : JoinedOrSeparate<["-"], "K">, Alias<keep_symbol>;
+def discard_locals : Flag<["-", "--"], "discard-locals">,
+ HelpText<"Remove compiler-generated local symbols, (e.g. "
+ "symbols starting with .L)">;
+def X : Flag<["-"], "X">, Alias<discard_locals>;
+
def discard_all
: Flag<["-", "--"], "discard-all">,
HelpText<"Remove all local symbols except file and section symbols">;