diff --git a/llvm/test/tools/llvm-objcopy/wasm/basic-keep.test b/llvm/test/tools/llvm-objcopy/wasm/basic-keep.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-objcopy/wasm/basic-keep.test @@ -0,0 +1,26 @@ +## Test that --keep-section keeps a debug section when stripping. +# RUN: yaml2obj %s -o %t +# RUN: llvm-objcopy --strip-all --keep-section=.debug_info %t %t2 +# RUN: obj2yaml %t2 | FileCheck --implicit-check-not linking %s + +# CHECK: Sections: +# CHECK: Name: .debug_info +# CHECK-NEXT: Payload: DEADBEEF + +## Test that keep overrides an explicit removal. +# RUN: llvm-objcopy --remove-section=.debug_info --keep-section=.debug_info %t %t2 +# RUN: obj2yaml %t2 | FileCheck %s --check-prefix=KEEP + +# KEEP: Sections: +# KEEP: Name: .debug_info + +--- !WASM +FileHeader: + Version: 0x00000001 +Sections: + - Type: CUSTOM + Name: linking + Version: 2 + - Type: CUSTOM + Name: .debug_info + Payload: DEADBEEF diff --git a/llvm/test/tools/llvm-objcopy/wasm/basic-only-section.test b/llvm/test/tools/llvm-objcopy/wasm/basic-only-section.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-objcopy/wasm/basic-only-section.test @@ -0,0 +1,44 @@ +## Test --only-section. +# RUN: yaml2obj %s -o %t +# RUN: llvm-objcopy --only-section=producers %t %t2 +# RUN: obj2yaml %t2 | FileCheck --implicit-check-not TYPE --implicit-check-not linking %s + +## Test that it's the same with only-section + keep-section (for the same section). +# RUN: llvm-objcopy --only-section=producers --keep-section=producers %t %t2 +# RUN: obj2yaml %t2 | FileCheck --implicit-check-not TYPE --implicit-check-not linking %s + +## Also test that only-section overrides remove-section. +# RUN: llvm-objcopy --only-section=producers --remove-section=producers %t %t2 +# RUN: obj2yaml %t2 | FileCheck --implicit-check-not linking %s + +## This file has both known and custom sections. Check that only the producers section is left. +# CHECK: Sections: +# CHECK-NEXT: - Type: CUSTOM +# CHECK-NEXT: Name: producers +# CHECK-NEXT: Tools: + +## Test that only-section + keep-section keeps both sections. +# RUN: llvm-objcopy --only-section=producers --keep-section=linking %t %t2 +# RUN: obj2yaml %t2 | FileCheck --implicit-check-not=TYPE --check-prefix=KEEP %s +# KEEP: Name: linking +# KEEP: Name: producers + +--- !WASM +FileHeader: + Version: 0x00000001 +Sections: + - Type: TYPE + Signatures: + - Index: 0 + ParamTypes: + - I32 + ReturnTypes: + - F32 + - Type: CUSTOM + Name: linking + Version: 2 + - Type: CUSTOM + Name: producers + Tools: + - Name: clang + Version: 9.0.0 diff --git a/llvm/test/tools/llvm-objcopy/wasm/basic-strip.test b/llvm/test/tools/llvm-objcopy/wasm/basic-strip.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-objcopy/wasm/basic-strip.test @@ -0,0 +1,46 @@ +## Test that debug, name, and producers sections are stripped with --strip-all. +## Other sections (unknown to LLVM, such as foo) are not stripped by --strip-all. +# RUN: yaml2obj %s -o %t +# RUN: llvm-strip --strip-all %t +# RUN: obj2yaml %t | FileCheck --implicit-check-not producers --implicit-check-not .debug --implicit-check-not name %s + +## The default no-arg behavior is the same as --strip-all. +# RUN: llvm-strip %t +# RUN: obj2yaml %t | FileCheck --implicit-check-not producers --implicit-check-not .debug --implicit-check-not name %s + +# CHECK: Sections: +# CHECK-NEXT: - Type: TYPE +# CHECK: Name: foo + +--- !WASM +FileHeader: + Version: 0x00000001 +Sections: + - Type: TYPE + Signatures: + - Index: 0 + ParamTypes: [] + ReturnTypes: [] + - Type: FUNCTION + FunctionTypes: [ 0 ] + - Type: CODE + Functions: + - Index: 0 + Locals: [] + Body: 0B + - Type: CUSTOM + Name: name + FunctionNames: + - Index: 0 + Name: foo + - Type: CUSTOM + Name: producers + Tools: + - Name: clang + Version: 9.0.0 + - Type: CUSTOM + Name: .debug_info + Payload: DEADBEEF + - Type: CUSTOM + Name: foo + Payload: CAFE diff --git a/llvm/test/tools/llvm-objcopy/wasm/only-keep-debug.test b/llvm/test/tools/llvm-objcopy/wasm/only-keep-debug.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-objcopy/wasm/only-keep-debug.test @@ -0,0 +1,52 @@ +## Test that only debug sections are kept with --only-keep-debug. +# RUN: yaml2obj %s -o %t +# RUN: llvm-strip --only-keep-debug %t +# RUN: obj2yaml %t | FileCheck %s + +## Test that keep-section overrides only-keep-debug. +# RUN: yaml2obj %s -o %t +# RUN: llvm-strip --only-keep-debug --keep-section=foo %t +# RUN: obj2yaml %t | FileCheck --implicit-check-not=Name --check-prefix=CHECK --check-prefix=KEEP %s + +# CHECK: Sections: +# CHECK: - Type: CUSTOM +# CHECK-NEXT: Name: .debug_info +# CHECK: - Type: CUSTOM +# CHECK-NEXT: Name: .debug_line +# KEEP: Name: foo + +## Test that remove-section overrides only-keep-debug. +# RUN: yaml2obj %s -o %t +# RUN: llvm-strip --only-keep-debug --remove-section=.debug_info %t +# RUN: obj2yaml %t | FileCheck %s --check-prefix=NOINFO --implicit-check-not=.debug_info + +# NOINFO: Name: .debug_line + +--- !WASM +FileHeader: + Version: 0x00000001 +Sections: + - Type: TYPE + Signatures: + - Index: 0 + ParamTypes: + - I32 + ReturnTypes: + - F32 + - Type: CUSTOM + Name: .debug_info + Payload: CAFE1234 + - Type: CUSTOM + Name: linking + Version: 2 + - Type: CUSTOM + Name: producers + Tools: + - Name: clang + Version: 9.0.0 + - Type: CUSTOM + Name: .debug_line + Payload: DEADBEEF + - Type: CUSTOM + Name: foo + Payload: CAFE diff --git a/llvm/test/tools/llvm-objcopy/wasm/strip-all.test b/llvm/test/tools/llvm-objcopy/wasm/strip-all.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-objcopy/wasm/strip-all.test @@ -0,0 +1,34 @@ +## Test that --strip-all removes debug, linking, and producers sections, but not +## known or unknown-custom sections. +# RUN: yaml2obj %s -o %t +# RUN: llvm-objcopy --strip-all %t %t2 +# RUN: obj2yaml %t2 | FileCheck --implicit-check-not=Type: %s + +# CHECK: Sections: +# CHECK-NEXT: - Type: TYPE +# CHECK: - Type: CUSTOM +# CHECK-NEXT: Name: foo + +--- !WASM +FileHeader: + Version: 0x00000001 +Sections: + - Type: TYPE + Signatures: + - Index: 0 + ParamTypes: [] + ReturnTypes: [] + - Type: CUSTOM + Name: linking + Version: 2 + - Type: CUSTOM + Name: producers + Tools: + - Name: clang + Version: 9.0.0 + - Type: CUSTOM + Name: .debug_info + Payload: DEADBEEF + - Type: CUSTOM + Name: foo + Payload: CAFE diff --git a/llvm/test/tools/llvm-objcopy/wasm/strip-debug.test b/llvm/test/tools/llvm-objcopy/wasm/strip-debug.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-objcopy/wasm/strip-debug.test @@ -0,0 +1,53 @@ +## Test that debug sections (but not linking or names) are stripped with --strip-debug +# RUN: yaml2obj %s -o %t +# RUN: llvm-strip --strip-debug %t +# RUN: obj2yaml %t | FileCheck --implicit-check-not=.debug %s + +# CHECK: Sections: +# CHECK-NEXT: - Type: TYPE +# CHECK: Name: linking +# CHECK: Name: name +# CHECK-NEXT: FunctionNames: +# CHECK: Name: producers + +--- !WASM +FileHeader: + Version: 0x00000001 +Sections: + - Type: TYPE + Signatures: + - Index: 0 + ParamTypes: [] + ReturnTypes: [] + - Type: FUNCTION + FunctionTypes: [ 0 ] + - Type: CODE + Functions: + - Index: 0 + Locals: [] + Body: 0B + - Type: CUSTOM + Name: .debug_info + Payload: CAFE1234 + - Type: CUSTOM + Name: linking + Version: 2 + SymbolTable: + - Index: 0 + Kind: FUNCTION + Name: foo + Flags: [ BINDING_LOCAL ] + Function: 0 + - Type: CUSTOM + Name: name + FunctionNames: + - Index: 0 + Name: foo + - Type: CUSTOM + Name: producers + Tools: + - Name: clang + Version: 9.0.0 + - Type: CUSTOM + Name: .debug_line + Payload: DEADBEEF diff --git a/llvm/test/tools/llvm-objcopy/wasm/strip-reloc.test b/llvm/test/tools/llvm-objcopy/wasm/strip-reloc.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-objcopy/wasm/strip-reloc.test @@ -0,0 +1,51 @@ +## Test that linking, reloc, and name sections are stripped by --strip-all. + +## These get a separate test because ObjectYaml understands relocs and names, +## so the test needs to be a valid object with relocs and names. + +# RUN: yaml2obj %s -o %t +# RUN: llvm-objcopy --strip-all %t %t2 +# RUN: obj2yaml %t2 | FileCheck --implicit-check-not=Type: %s + +## Check that the known sections are still present. +# CHECK: Sections: +# CHECK: - Type: TYPE +# CHECK: - Type: FUNCTION +# CHECK: - Type: CODE +## Check that there are still functions in the code section. +# CHECK: Functions: + +--- !WASM +FileHeader: + Version: 0x00000001 +Sections: + - Type: TYPE + Signatures: + - Index: 0 + ParamTypes: [] + ReturnTypes: [] + - Type: FUNCTION + FunctionTypes: [ 0 ] + - Type: CODE + Relocations: + - Type: R_WASM_FUNCTION_INDEX_LEB + Index: 0 + Offset: 0x4 + Functions: + - Index: 0 + Locals: [] + Body: 1080808080000B + - Type: CUSTOM + Name: linking + Version: 2 + SymbolTable: + - Index: 0 + Kind: FUNCTION + Name: foo + Flags: [ BINDING_LOCAL ] + Function: 0 + - Type: CUSTOM + Name: name + FunctionNames: + - Index: 0 + Name: foo diff --git a/llvm/tools/llvm-objcopy/ConfigManager.cpp b/llvm/tools/llvm-objcopy/ConfigManager.cpp --- a/llvm/tools/llvm-objcopy/ConfigManager.cpp +++ b/llvm/tools/llvm-objcopy/ConfigManager.cpp @@ -614,16 +614,15 @@ !Common.AllocSectionsPrefix.empty() || Common.DiscardMode != DiscardType::None || ELF.NewSymbolVisibility || !Common.SymbolsToAdd.empty() || !Common.RPathToAdd.empty() || - !Common.OnlySection.empty() || !Common.SymbolsToGlobalize.empty() || - !Common.SymbolsToKeep.empty() || !Common.SymbolsToLocalize.empty() || - !Common.SymbolsToRemove.empty() || + !Common.SymbolsToGlobalize.empty() || !Common.SymbolsToLocalize.empty() || + !Common.SymbolsToKeep.empty() || !Common.SymbolsToRemove.empty() || !Common.UnneededSymbolsToRemove.empty() || !Common.SymbolsToWeaken.empty() || !Common.SymbolsToKeepGlobal.empty() || !Common.SectionsToRename.empty() || !Common.SetSectionAlignment.empty() || !Common.SetSectionFlags.empty() || !Common.SymbolsToRename.empty()) { return createStringError( llvm::errc::invalid_argument, - "only add-section, dump-section, and remove-section are supported"); + "only flags for section dumping, removal, and addition are supported"); } return Wasm; diff --git a/llvm/tools/llvm-objcopy/wasm/WasmObjcopy.cpp b/llvm/tools/llvm-objcopy/wasm/WasmObjcopy.cpp --- a/llvm/tools/llvm-objcopy/wasm/WasmObjcopy.cpp +++ b/llvm/tools/llvm-objcopy/wasm/WasmObjcopy.cpp @@ -19,6 +19,23 @@ namespace wasm { using namespace object; +using SectionPred = std::function; + +static bool isDebugSection(const Section &Sec) { + return Sec.Name.startswith(".debug"); +} + +static bool isLinkerSection(const Section &Sec) { + return Sec.Name.startswith("reloc.") || Sec.Name == "linking"; +} + +static bool isNameSection(const Section &Sec) { return Sec.Name == "name"; } + +// Sections which are known to be "comments" or informational and do not affect +// program semantics. +static bool isCommentSection(const Section &Sec) { + return Sec.Name == "producers"; +} static Error dumpSectionToFile(StringRef SecName, StringRef Filename, Object &Obj) { @@ -39,6 +56,59 @@ return createStringError(errc::invalid_argument, "section '%s' not found", SecName.str().c_str()); } + +static void removeSections(const CommonConfig &Config, Object &Obj) { + SectionPred RemovePred = [](const Section &) { return false; }; + + // Explicitly-requested sections. + if (!Config.ToRemove.empty()) { + RemovePred = [&Config](const Section &Sec) { + return Config.ToRemove.matches(Sec.Name); + }; + } + + if (Config.StripDebug) { + RemovePred = [RemovePred](const Section &Sec) { + return RemovePred(Sec) || isDebugSection(Sec); + }; + } + + if (Config.StripAll) { + RemovePred = [RemovePred](const Section &Sec) { + return RemovePred(Sec) || isDebugSection(Sec) || isLinkerSection(Sec) || + isNameSection(Sec) || isCommentSection(Sec); + }; + } + + if (Config.OnlyKeepDebug) { + RemovePred = [&Config](const Section &Sec) { + // Keep debug sections, unless explicitly requested to remove. + // Remove everything else, including known sections. + return Config.ToRemove.matches(Sec.Name) || !isDebugSection(Sec); + }; + } + + if (!Config.OnlySection.empty()) { + RemovePred = [&Config](const Section &Sec) { + // Explicitly keep these sections regardless of previous removes. + // Remove everything else, inluding known sections. + return !Config.OnlySection.matches(Sec.Name); + }; + } + + if (!Config.KeepSection.empty()) { + RemovePred = [&Config, RemovePred](const Section &Sec) { + // Explicitly keep these sections regardless of previous removes. + if (Config.KeepSection.matches(Sec.Name)) + return false; + // Otherwise defer to RemovePred. + return RemovePred(Sec); + }; + } + + Obj.removeSections(RemovePred); +} + static Error handleArgs(const CommonConfig &Config, Object &Obj) { // Only support AddSection, DumpSection, RemoveSection for now. for (StringRef Flag : Config.DumpSection) { @@ -49,11 +119,7 @@ return createFileError(FileName, std::move(E)); } - Obj.removeSections([&Config](const Section &Sec) { - if (Config.ToRemove.matches(Sec.Name)) - return true; - return false; - }); + removeSections(Config, Obj); for (StringRef Flag : Config.AddSection) { StringRef SecName, FileName;