Index: test/tools/llvm-objcopy/strip-all.test =================================================================== --- test/tools/llvm-objcopy/strip-all.test +++ test/tools/llvm-objcopy/strip-all.test @@ -2,6 +2,14 @@ # RUN: llvm-objcopy --strip-all %t %t2 # RUN: llvm-readobj -file-headers -sections %t2 | FileCheck %s +# We run yaml2obj again rather than copy %t to avoid interfering +# with llvm-objcopy's test (which potentially could have corrupted/updated the binary). + +# RUN: yaml2obj %s > %t3 +# RUN: llvm-strip %t3 +# RUN: llvm-readobj -file-headers -sections %t3 | FileCheck %s +# RUN: cmp %t2 %t3 + !ELF FileHeader: Class: ELFCLASS64 Index: test/tools/llvm-objcopy/strip-debug.test =================================================================== --- test/tools/llvm-objcopy/strip-debug.test +++ test/tools/llvm-objcopy/strip-debug.test @@ -2,6 +2,16 @@ # RUN: llvm-objcopy -strip-debug %t %t2 # RUN: llvm-readobj -file-headers -sections -symbols %t2 | FileCheck %s +# We run yaml2obj again rather than copy %t to avoid interfering +# with llvm-objcopy's test (which potentially could have corrupted/updated the binary). + +# RUN: yaml2obj %s > %t +# RUN: llvm-strip -strip-debug %t3 +# RUN: llvm-readobj -file-headers -sections -symbols %t3 | FileCheck %s +# RUN: cmp %t2 %t3 + +# RUN: not llvm-strip -strip-debug 2>&1 | FileCheck %s --check-prefix=NO-INPUT-FILES + !ELF FileHeader: Class: ELFCLASS64 @@ -52,3 +62,5 @@ # CHECK-NEXT: Section: .text # CHECK-NEXT: } # CHECK-NEXT: ] + +# NO-INPUT-FILES: No input file specified Index: tools/llvm-objcopy/CMakeLists.txt =================================================================== --- tools/llvm-objcopy/CMakeLists.txt +++ tools/llvm-objcopy/CMakeLists.txt @@ -5,18 +5,25 @@ MC ) -set(LLVM_TARGET_DEFINITIONS Opts.td) +set(LLVM_TARGET_DEFINITIONS ObjcopyOpts.td) +tablegen(LLVM ObjcopyOpts.inc -gen-opt-parser-defs) +add_public_tablegen_target(ObjcopyOptsTableGen) -tablegen(LLVM Opts.inc -gen-opt-parser-defs) -add_public_tablegen_target(ObjcopyTableGen) +set(LLVM_TARGET_DEFINITIONS StripOpts.td) +tablegen(LLVM StripOpts.inc -gen-opt-parser-defs) +add_public_tablegen_target(StripOptsTableGen) add_llvm_tool(llvm-objcopy llvm-objcopy.cpp Object.cpp DEPENDS - ObjcopyTableGen + ObjcopyOptsTableGen + StripOptsTableGen ) +add_llvm_tool_symlink(llvm-strip llvm-objcopy) + if(LLVM_INSTALL_BINUTILS_SYMLINKS) add_llvm_tool_symlink(objcopy llvm-objcopy) + add_llvm_tool_symlink(strip llvm-objcopy) endif() Index: tools/llvm-objcopy/StripOpts.td =================================================================== --- /dev/null +++ tools/llvm-objcopy/StripOpts.td @@ -0,0 +1,12 @@ +include "llvm/Option/OptParser.td" + +multiclass Eq { + def NAME: Separate<["--", "-"], name>; + def NAME # _eq: Joined<["--", "-"], name # "=">, Alias(NAME)>; +} + +def help : Flag<["-", "--"], "help">; + +def strip_debug : Flag<["-", "--"], "strip-debug">, + HelpText<"Remove debugging symbols only">; + Index: tools/llvm-objcopy/llvm-objcopy.cpp =================================================================== --- tools/llvm-objcopy/llvm-objcopy.cpp +++ tools/llvm-objcopy/llvm-objcopy.cpp @@ -28,6 +28,7 @@ #include "llvm/Support/ErrorOr.h" #include "llvm/Support/FileOutputBuffer.h" #include "llvm/Support/InitLLVM.h" +#include "llvm/Support/Path.h" #include "llvm/Support/raw_ostream.h" #include #include @@ -45,17 +46,17 @@ namespace { -enum ID { +enum ObjcopyID { OBJCOPY_INVALID = 0, // This is not an option ID. #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ HELPTEXT, METAVAR, VALUES) \ OBJCOPY_##ID, -#include "Opts.inc" +#include "ObjcopyOpts.inc" #undef OPTION }; #define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; -#include "Opts.inc" +#include "ObjcopyOpts.inc" #undef PREFIX static const opt::OptTable::Info ObjcopyInfoTable[] = { @@ -65,7 +66,7 @@ METAVAR, OBJCOPY_##ID, opt::Option::KIND##Class, \ PARAM, FLAGS, OBJCOPY_##GROUP, \ OBJCOPY_##ALIAS, ALIASARGS, VALUES}, -#include "Opts.inc" +#include "ObjcopyOpts.inc" #undef OPTION }; @@ -74,6 +75,31 @@ ObjcopyOptTable() : OptTable(ObjcopyInfoTable, true) {} }; +enum StripID { + STRIP_INVALID = 0, // This is not an option ID. +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + STRIP_##ID, +#include "StripOpts.inc" +#undef OPTION +}; + +static const opt::OptTable::Info StripInfoTable[] = { +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + {PREFIX, NAME, HELPTEXT, \ + METAVAR, STRIP_##ID, opt::Option::KIND##Class, \ + PARAM, FLAGS, STRIP_##GROUP, \ + STRIP_##ALIAS, ALIASARGS, VALUES}, +#include "StripOpts.inc" +#undef OPTION +}; + +class StripOptTable : public opt::OptTable { +public: + StripOptTable() : OptTable(StripInfoTable, true) {} +}; + } // namespace // The name this program was invoked as. @@ -122,16 +148,16 @@ std::vector SymbolsToGlobalize; std::vector SymbolsToWeaken; StringMap SymbolsToRename; - bool StripAll; - bool StripAllGNU; - bool StripDebug; - bool StripSections; - bool StripNonAlloc; - bool StripDWO; - bool ExtractDWO; - bool LocalizeHidden; - bool Weaken; - bool DiscardAll; + bool StripAll = false; + bool StripAllGNU = false; + bool StripDebug = false; + bool StripSections = false; + bool StripNonAlloc = false; + bool StripDWO = false; + bool ExtractDWO = false; + bool LocalizeHidden = false; + bool Weaken = false; + bool DiscardAll = false; }; using SectionPred = std::function; @@ -449,10 +475,50 @@ return Config; } +// ParseStripOptions returns the config and sets the input arguments. If a +// help flag is set then ParseStripOptions will print the help messege and +// exit. +CopyConfig ParseStripOptions(ArrayRef ArgsArr) { + StripOptTable T; + unsigned MissingArgumentIndex, MissingArgumentCount; + llvm::opt::InputArgList InputArgs = + T.ParseArgs(ArgsArr, MissingArgumentIndex, MissingArgumentCount); + + if (InputArgs.size() == 0 || InputArgs.hasArg(STRIP_help)) { + T.PrintHelp(outs(), "llvm-strip [ ]", "strip tool"); + exit(0); + } + + SmallVector Positional; + for (auto Arg : InputArgs.filtered(STRIP_UNKNOWN)) + error("unknown argument '" + Arg->getAsString(InputArgs) + "'"); + for (auto Arg : InputArgs.filtered(STRIP_INPUT)) + Positional.push_back(Arg->getValue()); + + if (Positional.empty()) + error("No input file specified"); + + if (Positional.size() > 2) + error("Support for multiple input files is not implemented yet"); + + CopyConfig Config; + Config.InputFilename = Positional[0]; + Config.OutputFilename = Positional[0]; + + // Strip debug info only. + Config.StripDebug = InputArgs.hasArg(STRIP_strip_debug); + if (!Config.StripDebug) + Config.StripAll = true; + return Config; +} + int main(int argc, char **argv) { InitLLVM X(argc, argv); ToolName = argv[0]; - - CopyConfig Config = ParseObjcopyOptions(makeArrayRef(argv + 1, argc)); + CopyConfig Config; + if (sys::path::stem(ToolName).endswith_lower("strip")) + Config = ParseStripOptions(makeArrayRef(argv + 1, argc)); + else + Config = ParseObjcopyOptions(makeArrayRef(argv + 1, argc)); ExecuteElfObjcopy(Config); }