//===- ObjcopyOptions.cpp -------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "ObjcopyOptions.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSet.h" #include "llvm/BinaryFormat/COFF.h" #include "llvm/ObjCopy/CommonConfig.h" #include "llvm/ObjCopy/ConfigManager.h" #include "llvm/ObjCopy/MachO/MachOConfig.h" #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" #include "llvm/Support/CRC.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Compression.h" #include "llvm/Support/Errc.h" #include "llvm/Support/Error.h" #include "llvm/Support/MemoryBuffer.h" using namespace llvm; using namespace llvm::objcopy; namespace { 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 "ObjcopyOpts.inc" #undef OPTION }; namespace objcopy_opt { #define PREFIX(NAME, VALUE) \ static constexpr StringLiteral NAME##_init[] = VALUE; \ static constexpr ArrayRef NAME(NAME##_init, \ std::size(NAME##_init) - 1); #include "ObjcopyOpts.inc" #undef PREFIX static constexpr opt::OptTable::Info ObjcopyInfoTable[] = { #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ HELPTEXT, METAVAR, VALUES) \ {PREFIX, NAME, HELPTEXT, \ METAVAR, OBJCOPY_##ID, opt::Option::KIND##Class, \ PARAM, FLAGS, OBJCOPY_##GROUP, \ OBJCOPY_##ALIAS, ALIASARGS, VALUES}, #include "ObjcopyOpts.inc" #undef OPTION }; } // namespace objcopy_opt class ObjcopyOptTable : public opt::GenericOptTable { public: ObjcopyOptTable() : opt::GenericOptTable(objcopy_opt::ObjcopyInfoTable) { setGroupedShortOptions(true); } }; enum InstallNameToolID { INSTALL_NAME_TOOL_INVALID = 0, // This is not an option ID. #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ HELPTEXT, METAVAR, VALUES) \ INSTALL_NAME_TOOL_##ID, #include "InstallNameToolOpts.inc" #undef OPTION }; namespace install_name_tool { #define PREFIX(NAME, VALUE) \ static constexpr StringLiteral NAME##_init[] = VALUE; \ static constexpr ArrayRef NAME(NAME##_init, \ std::size(NAME##_init) - 1); #include "InstallNameToolOpts.inc" #undef PREFIX static constexpr opt::OptTable::Info InstallNameToolInfoTable[] = { #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ HELPTEXT, METAVAR, VALUES) \ {PREFIX, \ NAME, \ HELPTEXT, \ METAVAR, \ INSTALL_NAME_TOOL_##ID, \ opt::Option::KIND##Class, \ PARAM, \ FLAGS, \ INSTALL_NAME_TOOL_##GROUP, \ INSTALL_NAME_TOOL_##ALIAS, \ ALIASARGS, \ VALUES}, #include "InstallNameToolOpts.inc" #undef OPTION }; } // namespace install_name_tool class InstallNameToolOptTable : public opt::GenericOptTable { public: InstallNameToolOptTable() : GenericOptTable(install_name_tool::InstallNameToolInfoTable) {} }; enum BitcodeStripID { BITCODE_STRIP_INVALID = 0, // This is not an option ID. #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ HELPTEXT, METAVAR, VALUES) \ BITCODE_STRIP_##ID, #include "BitcodeStripOpts.inc" #undef OPTION }; namespace bitcode_strip { #define PREFIX(NAME, VALUE) \ static constexpr StringLiteral NAME##_init[] = VALUE; \ static constexpr ArrayRef NAME(NAME##_init, \ std::size(NAME##_init) - 1); #include "BitcodeStripOpts.inc" #undef PREFIX static constexpr opt::OptTable::Info BitcodeStripInfoTable[] = { #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ HELPTEXT, METAVAR, VALUES) \ {PREFIX, \ NAME, \ HELPTEXT, \ METAVAR, \ BITCODE_STRIP_##ID, \ opt::Option::KIND##Class, \ PARAM, \ FLAGS, \ BITCODE_STRIP_##GROUP, \ BITCODE_STRIP_##ALIAS, \ ALIASARGS, \ VALUES}, #include "BitcodeStripOpts.inc" #undef OPTION }; } // namespace bitcode_strip class BitcodeStripOptTable : public opt::GenericOptTable { public: BitcodeStripOptTable() : opt::GenericOptTable(bitcode_strip::BitcodeStripInfoTable) {} }; 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 }; namespace strip { #define PREFIX(NAME, VALUE) \ static constexpr StringLiteral NAME##_init[] = VALUE; \ static constexpr ArrayRef NAME(NAME##_init, \ std::size(NAME##_init) - 1); #include "StripOpts.inc" #undef PREFIX static constexpr 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 }; } // namespace strip class StripOptTable : public opt::GenericOptTable { public: StripOptTable() : GenericOptTable(strip::StripInfoTable) { setGroupedShortOptions(true); } }; } // namespace static SectionFlag parseSectionRenameFlag(StringRef SectionName) { return llvm::StringSwitch(SectionName) .CaseLower("alloc", SectionFlag::SecAlloc) .CaseLower("load", SectionFlag::SecLoad) .CaseLower("noload", SectionFlag::SecNoload) .CaseLower("readonly", SectionFlag::SecReadonly) .CaseLower("debug", SectionFlag::SecDebug) .CaseLower("code", SectionFlag::SecCode) .CaseLower("data", SectionFlag::SecData) .CaseLower("rom", SectionFlag::SecRom) .CaseLower("merge", SectionFlag::SecMerge) .CaseLower("strings", SectionFlag::SecStrings) .CaseLower("contents", SectionFlag::SecContents) .CaseLower("share", SectionFlag::SecShare) .CaseLower("exclude", SectionFlag::SecExclude) .Default(SectionFlag::SecNone); } static Expected parseSectionFlagSet(ArrayRef SectionFlags) { SectionFlag ParsedFlags = SectionFlag::SecNone; for (StringRef Flag : SectionFlags) { SectionFlag ParsedFlag = parseSectionRenameFlag(Flag); if (ParsedFlag == SectionFlag::SecNone) return createStringError( errc::invalid_argument, "unrecognized section flag '%s'. Flags supported for GNU " "compatibility: alloc, load, noload, readonly, exclude, debug, " "code, data, rom, share, contents, merge, strings", Flag.str().c_str()); ParsedFlags |= ParsedFlag; } return ParsedFlags; } static Expected parseRenameSectionValue(StringRef FlagValue) { if (!FlagValue.contains('=')) return createStringError(errc::invalid_argument, "bad format for --rename-section: missing '='"); // Initial split: ".foo" = ".bar,f1,f2,..." auto Old2New = FlagValue.split('='); SectionRename SR; SR.OriginalName = Old2New.first; // Flags split: ".bar" "f1" "f2" ... SmallVector NameAndFlags; Old2New.second.split(NameAndFlags, ','); SR.NewName = NameAndFlags[0]; if (NameAndFlags.size() > 1) { Expected ParsedFlagSet = parseSectionFlagSet(ArrayRef(NameAndFlags).drop_front()); if (!ParsedFlagSet) return ParsedFlagSet.takeError(); SR.NewFlags = *ParsedFlagSet; } return SR; } static Expected> parseSetSectionAttribute(StringRef Option, StringRef FlagValue) { if (!FlagValue.contains('=')) return make_error("bad format for " + Option + ": missing '='", errc::invalid_argument); auto Split = StringRef(FlagValue).split('='); if (Split.first.empty()) return make_error("bad format for " + Option + ": missing section name", errc::invalid_argument); uint64_t Value; if (Split.second.getAsInteger(0, Value)) return make_error("invalid value for " + Option + ": '" + Split.second + "'", errc::invalid_argument); return std::make_pair(Split.first, Value); } static Expected parseSetSectionFlagValue(StringRef FlagValue) { if (!StringRef(FlagValue).contains('=')) return createStringError(errc::invalid_argument, "bad format for --set-section-flags: missing '='"); // Initial split: ".foo" = "f1,f2,..." auto Section2Flags = StringRef(FlagValue).split('='); SectionFlagsUpdate SFU; SFU.Name = Section2Flags.first; // Flags split: "f1" "f2" ... SmallVector SectionFlags; Section2Flags.second.split(SectionFlags, ','); Expected ParsedFlagSet = parseSectionFlagSet(SectionFlags); if (!ParsedFlagSet) return ParsedFlagSet.takeError(); SFU.NewFlags = *ParsedFlagSet; return SFU; } namespace { struct TargetInfo { FileFormat Format; MachineInfo Machine; }; } // namespace // FIXME: consolidate with the bfd parsing used by lld. static const StringMap TargetMap{ // Name, {EMachine, 64bit, LittleEndian} // x86 {"elf32-i386", {ELF::EM_386, false, true}}, {"elf32-x86-64", {ELF::EM_X86_64, false, true}}, {"elf64-x86-64", {ELF::EM_X86_64, true, true}}, // Intel MCU {"elf32-iamcu", {ELF::EM_IAMCU, false, true}}, // ARM {"elf32-littlearm", {ELF::EM_ARM, false, true}}, // ARM AArch64 {"elf64-aarch64", {ELF::EM_AARCH64, true, true}}, {"elf64-littleaarch64", {ELF::EM_AARCH64, true, true}}, // RISC-V {"elf32-littleriscv", {ELF::EM_RISCV, false, true}}, {"elf64-littleriscv", {ELF::EM_RISCV, true, true}}, // PowerPC {"elf32-powerpc", {ELF::EM_PPC, false, false}}, {"elf32-powerpcle", {ELF::EM_PPC, false, true}}, {"elf64-powerpc", {ELF::EM_PPC64, true, false}}, {"elf64-powerpcle", {ELF::EM_PPC64, true, true}}, // MIPS {"elf32-bigmips", {ELF::EM_MIPS, false, false}}, {"elf32-ntradbigmips", {ELF::EM_MIPS, false, false}}, {"elf32-ntradlittlemips", {ELF::EM_MIPS, false, true}}, {"elf32-tradbigmips", {ELF::EM_MIPS, false, false}}, {"elf32-tradlittlemips", {ELF::EM_MIPS, false, true}}, {"elf64-tradbigmips", {ELF::EM_MIPS, true, false}}, {"elf64-tradlittlemips", {ELF::EM_MIPS, true, true}}, // SPARC {"elf32-sparc", {ELF::EM_SPARC, false, false}}, {"elf32-sparcel", {ELF::EM_SPARC, false, true}}, {"elf32-hexagon", {ELF::EM_HEXAGON, false, true}}, }; static Expected getOutputTargetInfoByTargetName(StringRef TargetName) { StringRef OriginalTargetName = TargetName; bool IsFreeBSD = TargetName.consume_back("-freebsd"); auto Iter = TargetMap.find(TargetName); if (Iter == std::end(TargetMap)) return createStringError(errc::invalid_argument, "invalid output format: '%s'", OriginalTargetName.str().c_str()); MachineInfo MI = Iter->getValue(); if (IsFreeBSD) MI.OSABI = ELF::ELFOSABI_FREEBSD; FileFormat Format; if (TargetName.startswith("elf")) Format = FileFormat::ELF; else // This should never happen because `TargetName` is valid (it certainly // exists in the TargetMap). llvm_unreachable("unknown target prefix"); return {TargetInfo{Format, MI}}; } static Error addSymbolsFromFile(NameMatcher &Symbols, BumpPtrAllocator &Alloc, StringRef Filename, MatchStyle MS, function_ref ErrorCallback) { StringSaver Saver(Alloc); SmallVector Lines; auto BufOrErr = MemoryBuffer::getFile(Filename); if (!BufOrErr) return createFileError(Filename, BufOrErr.getError()); BufOrErr.get()->getBuffer().split(Lines, '\n'); for (StringRef Line : Lines) { // Ignore everything after '#', trim whitespace, and only add the symbol if // it's not empty. auto TrimmedLine = Line.split('#').first.trim(); if (!TrimmedLine.empty()) if (Error E = Symbols.addMatcher(NameOrPattern::create( Saver.save(TrimmedLine), MS, ErrorCallback))) return E; } return Error::success(); } static Error addSymbolsToRenameFromFile(StringMap &SymbolsToRename, BumpPtrAllocator &Alloc, StringRef Filename) { StringSaver Saver(Alloc); SmallVector Lines; auto BufOrErr = MemoryBuffer::getFile(Filename); if (!BufOrErr) return createFileError(Filename, BufOrErr.getError()); BufOrErr.get()->getBuffer().split(Lines, '\n'); size_t NumLines = Lines.size(); for (size_t LineNo = 0; LineNo < NumLines; ++LineNo) { StringRef TrimmedLine = Lines[LineNo].split('#').first.trim(); if (TrimmedLine.empty()) continue; std::pair Pair = Saver.save(TrimmedLine).split(' '); StringRef NewName = Pair.second.trim(); if (NewName.empty()) return createStringError(errc::invalid_argument, "%s:%zu: missing new symbol name", Filename.str().c_str(), LineNo + 1); SymbolsToRename.insert({Pair.first, NewName}); } return Error::success(); } template static ErrorOr getAsInteger(StringRef Val) { T Result; if (Val.getAsInteger(0, Result)) return errc::invalid_argument; return Result; } namespace { enum class ToolType { Objcopy, Strip, InstallNameTool, BitcodeStrip }; } // anonymous namespace static void printHelp(const opt::OptTable &OptTable, raw_ostream &OS, ToolType Tool) { StringRef HelpText, ToolName; switch (Tool) { case ToolType::Objcopy: ToolName = "llvm-objcopy"; HelpText = " [options] input [output]"; break; case ToolType::Strip: ToolName = "llvm-strip"; HelpText = " [options] inputs..."; break; case ToolType::InstallNameTool: ToolName = "llvm-install-name-tool"; HelpText = " [options] input"; break; case ToolType::BitcodeStrip: ToolName = "llvm-bitcode-strip"; HelpText = " [options] input"; break; } OptTable.printHelp(OS, (ToolName + HelpText).str().c_str(), (ToolName + " tool").str().c_str()); // TODO: Replace this with libOption call once it adds extrahelp support. // The CommandLine library has a cl::extrahelp class to support this, // but libOption does not have that yet. OS << "\nPass @FILE as argument to read options from FILE.\n"; } static Expected parseNewSymbolInfo(StringRef FlagValue) { // Parse value given with --add-symbol option and create the // new symbol if possible. The value format for --add-symbol is: // // =[
:][,] // // where: // - symbol name, can be empty string //
- optional section name. If not given ABS symbol is created // - symbol value, can be decimal or hexadecimal number prefixed // with 0x. // - optional flags affecting symbol type, binding or visibility. NewSymbolInfo SI; StringRef Value; std::tie(SI.SymbolName, Value) = FlagValue.split('='); if (Value.empty()) return createStringError( errc::invalid_argument, "bad format for --add-symbol, missing '=' after '%s'", SI.SymbolName.str().c_str()); if (Value.contains(':')) { std::tie(SI.SectionName, Value) = Value.split(':'); if (SI.SectionName.empty() || Value.empty()) return createStringError( errc::invalid_argument, "bad format for --add-symbol, missing section name or symbol value"); } SmallVector Flags; Value.split(Flags, ','); if (Flags[0].getAsInteger(0, SI.Value)) return createStringError(errc::invalid_argument, "bad symbol value: '%s'", Flags[0].str().c_str()); using Functor = std::function; SmallVector UnsupportedFlags; for (size_t I = 1, NumFlags = Flags.size(); I < NumFlags; ++I) static_cast( StringSwitch(Flags[I]) .CaseLower("global", [&] { SI.Flags.push_back(SymbolFlag::Global); }) .CaseLower("local", [&] { SI.Flags.push_back(SymbolFlag::Local); }) .CaseLower("weak", [&] { SI.Flags.push_back(SymbolFlag::Weak); }) .CaseLower("default", [&] { SI.Flags.push_back(SymbolFlag::Default); }) .CaseLower("hidden", [&] { SI.Flags.push_back(SymbolFlag::Hidden); }) .CaseLower("protected", [&] { SI.Flags.push_back(SymbolFlag::Protected); }) .CaseLower("file", [&] { SI.Flags.push_back(SymbolFlag::File); }) .CaseLower("section", [&] { SI.Flags.push_back(SymbolFlag::Section); }) .CaseLower("object", [&] { SI.Flags.push_back(SymbolFlag::Object); }) .CaseLower("function", [&] { SI.Flags.push_back(SymbolFlag::Function); }) .CaseLower( "indirect-function", [&] { SI.Flags.push_back(SymbolFlag::IndirectFunction); }) .CaseLower("debug", [&] { SI.Flags.push_back(SymbolFlag::Debug); }) .CaseLower("constructor", [&] { SI.Flags.push_back(SymbolFlag::Constructor); }) .CaseLower("warning", [&] { SI.Flags.push_back(SymbolFlag::Warning); }) .CaseLower("indirect", [&] { SI.Flags.push_back(SymbolFlag::Indirect); }) .CaseLower("synthetic", [&] { SI.Flags.push_back(SymbolFlag::Synthetic); }) .CaseLower("unique-object", [&] { SI.Flags.push_back(SymbolFlag::UniqueObject); }) .StartsWithLower("before=", [&] { StringRef SymNamePart = Flags[I].split('=').second; if (!SymNamePart.empty()) SI.BeforeSyms.push_back(SymNamePart); }) .Default([&] { UnsupportedFlags.push_back(Flags[I]); }))(); if (!UnsupportedFlags.empty()) return createStringError(errc::invalid_argument, "unsupported flag%s for --add-symbol: '%s'", UnsupportedFlags.size() > 1 ? "s" : "", join(UnsupportedFlags, "', '").c_str()); return SI; } // Parse input option \p ArgValue and load section data. This function // extracts section name and name of the file keeping section data from // ArgValue, loads data from the file, and stores section name and data // into the vector of new sections \p NewSections. static Error loadNewSectionData(StringRef ArgValue, StringRef OptionName, std::vector &NewSections) { if (!ArgValue.contains('=')) return createStringError(errc::invalid_argument, "bad format for " + OptionName + ": missing '='"); std::pair SecPair = ArgValue.split("="); if (SecPair.second.empty()) return createStringError(errc::invalid_argument, "bad format for " + OptionName + ": missing file name"); ErrorOr> BufOrErr = MemoryBuffer::getFile(SecPair.second); if (!BufOrErr) return createFileError(SecPair.second, errorCodeToError(BufOrErr.getError())); NewSections.push_back({SecPair.first, std::move(*BufOrErr)}); return Error::success(); } // parseObjcopyOptions returns the config and sets the input arguments. If a // help flag is set then parseObjcopyOptions will print the help messege and // exit. Expected objcopy::parseObjcopyOptions(ArrayRef RawArgsArr, function_ref ErrorCallback) { DriverConfig DC; ObjcopyOptTable T; const char *const *DashDash = llvm::find_if(RawArgsArr, [](StringRef Str) { return Str == "--"; }); ArrayRef ArgsArr = ArrayRef(RawArgsArr.begin(), DashDash); if (DashDash != RawArgsArr.end()) DashDash = std::next(DashDash); unsigned MissingArgumentIndex, MissingArgumentCount; llvm::opt::InputArgList InputArgs = T.ParseArgs(ArgsArr, MissingArgumentIndex, MissingArgumentCount); if (InputArgs.size() == 0 && DashDash == RawArgsArr.end()) { printHelp(T, errs(), ToolType::Objcopy); exit(1); } if (InputArgs.hasArg(OBJCOPY_help)) { printHelp(T, outs(), ToolType::Objcopy); exit(0); } if (InputArgs.hasArg(OBJCOPY_version)) { outs() << "llvm-objcopy, compatible with GNU objcopy\n"; cl::PrintVersionMessage(); exit(0); } SmallVector Positional; for (auto *Arg : InputArgs.filtered(OBJCOPY_UNKNOWN)) return createStringError(errc::invalid_argument, "unknown argument '%s'", Arg->getAsString(InputArgs).c_str()); for (auto *Arg : InputArgs.filtered(OBJCOPY_INPUT)) Positional.push_back(Arg->getValue()); std::copy(DashDash, RawArgsArr.end(), std::back_inserter(Positional)); if (Positional.empty()) return createStringError(errc::invalid_argument, "no input file specified"); if (Positional.size() > 2) return createStringError(errc::invalid_argument, "too many positional arguments"); ConfigManager ConfigMgr; CommonConfig &Config = ConfigMgr.Common; COFFConfig &COFFConfig = ConfigMgr.COFF; ELFConfig &ELFConfig = ConfigMgr.ELF; MachOConfig &MachOConfig = ConfigMgr.MachO; Config.InputFilename = Positional[0]; Config.OutputFilename = Positional[Positional.size() == 1 ? 0 : 1]; if (InputArgs.hasArg(OBJCOPY_target) && (InputArgs.hasArg(OBJCOPY_input_target) || InputArgs.hasArg(OBJCOPY_output_target))) return createStringError( errc::invalid_argument, "--target cannot be used with --input-target or --output-target"); if (InputArgs.hasArg(OBJCOPY_regex) && InputArgs.hasArg(OBJCOPY_wildcard)) return createStringError(errc::invalid_argument, "--regex and --wildcard are incompatible"); MatchStyle SectionMatchStyle = InputArgs.hasArg(OBJCOPY_regex) ? MatchStyle::Regex : MatchStyle::Wildcard; MatchStyle SymbolMatchStyle = InputArgs.hasArg(OBJCOPY_regex) ? MatchStyle::Regex : InputArgs.hasArg(OBJCOPY_wildcard) ? MatchStyle::Wildcard : MatchStyle::Literal; StringRef InputFormat, OutputFormat; if (InputArgs.hasArg(OBJCOPY_target)) { InputFormat = InputArgs.getLastArgValue(OBJCOPY_target); OutputFormat = InputArgs.getLastArgValue(OBJCOPY_target); } else { InputFormat = InputArgs.getLastArgValue(OBJCOPY_input_target); OutputFormat = InputArgs.getLastArgValue(OBJCOPY_output_target); } // FIXME: Currently, we ignore the target for non-binary/ihex formats // explicitly specified by -I option (e.g. -Ielf32-x86-64) and guess the // format by llvm::object::createBinary regardless of the option value. Config.InputFormat = StringSwitch(InputFormat) .Case("binary", FileFormat::Binary) .Case("ihex", FileFormat::IHex) .Default(FileFormat::Unspecified); if (InputArgs.hasArg(OBJCOPY_new_symbol_visibility)) { const uint8_t Invalid = 0xff; StringRef VisibilityStr = InputArgs.getLastArgValue(OBJCOPY_new_symbol_visibility); ELFConfig.NewSymbolVisibility = StringSwitch(VisibilityStr) .Case("default", ELF::STV_DEFAULT) .Case("hidden", ELF::STV_HIDDEN) .Case("internal", ELF::STV_INTERNAL) .Case("protected", ELF::STV_PROTECTED) .Default(Invalid); if (ELFConfig.NewSymbolVisibility == Invalid) return createStringError(errc::invalid_argument, "'%s' is not a valid symbol visibility", VisibilityStr.str().c_str()); } for (const auto *Arg : InputArgs.filtered(OBJCOPY_subsystem)) { StringRef Subsystem, Version; std::tie(Subsystem, Version) = StringRef(Arg->getValue()).split(':'); COFFConfig.Subsystem = StringSwitch(Subsystem.lower()) .Case("boot_application", COFF::IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION) .Case("console", COFF::IMAGE_SUBSYSTEM_WINDOWS_CUI) .Case("efi_application", COFF::IMAGE_SUBSYSTEM_EFI_APPLICATION) .Case("efi_boot_service_driver", COFF::IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER) .Case("efi_rom", COFF::IMAGE_SUBSYSTEM_EFI_ROM) .Case("efi_runtime_driver", COFF::IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER) .Case("native", COFF::IMAGE_SUBSYSTEM_NATIVE) .Case("posix", COFF::IMAGE_SUBSYSTEM_POSIX_CUI) .Case("windows", COFF::IMAGE_SUBSYSTEM_WINDOWS_GUI) .Default(COFF::IMAGE_SUBSYSTEM_UNKNOWN); if (*COFFConfig.Subsystem == COFF::IMAGE_SUBSYSTEM_UNKNOWN) return createStringError(errc::invalid_argument, "'%s' is not a valid subsystem", Subsystem.str().c_str()); if (!Version.empty()) { StringRef Major, Minor; std::tie(Major, Minor) = Version.split('.'); unsigned Number; if (Major.getAsInteger(10, Number)) return createStringError(errc::invalid_argument, "'%s' is not a valid subsystem major version", Major.str().c_str()); COFFConfig.MajorSubsystemVersion = Number; Number = 0; if (!Minor.empty() && Minor.getAsInteger(10, Number)) return createStringError(errc::invalid_argument, "'%s' is not a valid subsystem minor version", Minor.str().c_str()); COFFConfig.MinorSubsystemVersion = Number; } } Config.OutputFormat = StringSwitch(OutputFormat) .Case("binary", FileFormat::Binary) .Case("ihex", FileFormat::IHex) .Default(FileFormat::Unspecified); if (Config.OutputFormat == FileFormat::Unspecified) { if (OutputFormat.empty()) { Config.OutputFormat = Config.InputFormat; } else { Expected Target = getOutputTargetInfoByTargetName(OutputFormat); if (!Target) return Target.takeError(); Config.OutputFormat = Target->Format; Config.OutputArch = Target->Machine; } } if (const auto *A = InputArgs.getLastArg(OBJCOPY_compress_debug_sections)) { Config.CompressionType = StringSwitch(A->getValue()) .Case("zlib", DebugCompressionType::Zlib) .Case("zstd", DebugCompressionType::Zstd) .Default(DebugCompressionType::None); if (Config.CompressionType == DebugCompressionType::None) { return createStringError( errc::invalid_argument, "invalid or unsupported --compress-debug-sections format: %s", A->getValue()); } if (const char *Reason = compression::getReasonIfUnsupported( compression::formatFor(Config.CompressionType))) return createStringError(errc::invalid_argument, Reason); } Config.AddGnuDebugLink = InputArgs.getLastArgValue(OBJCOPY_add_gnu_debuglink); // The gnu_debuglink's target is expected to not change or else its CRC would // become invalidated and get rejected. We can avoid recalculating the // checksum for every target file inside an archive by precomputing the CRC // here. This prevents a significant amount of I/O. if (!Config.AddGnuDebugLink.empty()) { auto DebugOrErr = MemoryBuffer::getFile(Config.AddGnuDebugLink); if (!DebugOrErr) return createFileError(Config.AddGnuDebugLink, DebugOrErr.getError()); auto Debug = std::move(*DebugOrErr); Config.GnuDebugLinkCRC32 = llvm::crc32(arrayRefFromStringRef(Debug->getBuffer())); } Config.SplitDWO = InputArgs.getLastArgValue(OBJCOPY_split_dwo); Config.SymbolsPrefix = InputArgs.getLastArgValue(OBJCOPY_prefix_symbols); Config.AllocSectionsPrefix = InputArgs.getLastArgValue(OBJCOPY_prefix_alloc_sections); if (auto Arg = InputArgs.getLastArg(OBJCOPY_extract_partition)) Config.ExtractPartition = Arg->getValue(); for (auto *Arg : InputArgs.filtered(OBJCOPY_redefine_symbol)) { if (!StringRef(Arg->getValue()).contains('=')) return createStringError(errc::invalid_argument, "bad format for --redefine-sym"); auto Old2New = StringRef(Arg->getValue()).split('='); if (!Config.SymbolsToRename.insert(Old2New).second) return createStringError(errc::invalid_argument, "multiple redefinition of symbol '%s'", Old2New.first.str().c_str()); } for (auto *Arg : InputArgs.filtered(OBJCOPY_redefine_symbols)) if (Error E = addSymbolsToRenameFromFile(Config.SymbolsToRename, DC.Alloc, Arg->getValue())) return std::move(E); for (auto *Arg : InputArgs.filtered(OBJCOPY_rename_section)) { Expected SR = parseRenameSectionValue(StringRef(Arg->getValue())); if (!SR) return SR.takeError(); if (!Config.SectionsToRename.try_emplace(SR->OriginalName, *SR).second) return createStringError(errc::invalid_argument, "multiple renames of section '%s'", SR->OriginalName.str().c_str()); } for (auto *Arg : InputArgs.filtered(OBJCOPY_set_section_alignment)) { Expected> NameAndAlign = parseSetSectionAttribute("--set-section-alignment", Arg->getValue()); if (!NameAndAlign) return NameAndAlign.takeError(); Config.SetSectionAlignment[NameAndAlign->first] = NameAndAlign->second; } for (auto *Arg : InputArgs.filtered(OBJCOPY_set_section_flags)) { Expected SFU = parseSetSectionFlagValue(Arg->getValue()); if (!SFU) return SFU.takeError(); if (!Config.SetSectionFlags.try_emplace(SFU->Name, *SFU).second) return createStringError( errc::invalid_argument, "--set-section-flags set multiple times for section '%s'", SFU->Name.str().c_str()); } for (auto *Arg : InputArgs.filtered(OBJCOPY_set_section_type)) { Expected> NameAndType = parseSetSectionAttribute("--set-section-type", Arg->getValue()); if (!NameAndType) return NameAndType.takeError(); Config.SetSectionType[NameAndType->first] = NameAndType->second; } // Prohibit combinations of --set-section-{flags,type} when the section name // is used as the destination of a --rename-section. for (const auto &E : Config.SectionsToRename) { const SectionRename &SR = E.second; auto Err = [&](const char *Option) { return createStringError( errc::invalid_argument, "--set-section-%s=%s conflicts with --rename-section=%s=%s", Option, SR.NewName.str().c_str(), SR.OriginalName.str().c_str(), SR.NewName.str().c_str()); }; if (Config.SetSectionFlags.count(SR.NewName)) return Err("flags"); if (Config.SetSectionType.count(SR.NewName)) return Err("type"); } for (auto *Arg : InputArgs.filtered(OBJCOPY_remove_section)) if (Error E = Config.ToRemove.addMatcher(NameOrPattern::create( Arg->getValue(), SectionMatchStyle, ErrorCallback))) return std::move(E); for (auto *Arg : InputArgs.filtered(OBJCOPY_keep_section)) if (Error E = Config.KeepSection.addMatcher(NameOrPattern::create( Arg->getValue(), SectionMatchStyle, ErrorCallback))) return std::move(E); for (auto *Arg : InputArgs.filtered(OBJCOPY_only_section)) if (Error E = Config.OnlySection.addMatcher(NameOrPattern::create( Arg->getValue(), SectionMatchStyle, ErrorCallback))) return std::move(E); for (auto *Arg : InputArgs.filtered(OBJCOPY_add_section)) { if (Error Err = loadNewSectionData(Arg->getValue(), "--add-section", Config.AddSection)) return std::move(Err); } for (auto *Arg : InputArgs.filtered(OBJCOPY_update_section)) { if (Error Err = loadNewSectionData(Arg->getValue(), "--update-section", Config.UpdateSection)) return std::move(Err); } for (auto *Arg : InputArgs.filtered(OBJCOPY_dump_section)) { StringRef Value(Arg->getValue()); if (Value.split('=').second.empty()) return createStringError( errc::invalid_argument, "bad format for --dump-section, expected section=file"); Config.DumpSection.push_back(Value); } Config.StripAll = InputArgs.hasArg(OBJCOPY_strip_all); Config.StripAllGNU = InputArgs.hasArg(OBJCOPY_strip_all_gnu); Config.StripDebug = InputArgs.hasArg(OBJCOPY_strip_debug); Config.StripDWO = InputArgs.hasArg(OBJCOPY_strip_dwo); Config.StripSections = InputArgs.hasArg(OBJCOPY_strip_sections); Config.StripNonAlloc = InputArgs.hasArg(OBJCOPY_strip_non_alloc); Config.StripUnneeded = InputArgs.hasArg(OBJCOPY_strip_unneeded); Config.ExtractDWO = InputArgs.hasArg(OBJCOPY_extract_dwo); Config.ExtractMainPartition = InputArgs.hasArg(OBJCOPY_extract_main_partition); ELFConfig.LocalizeHidden = InputArgs.hasArg(OBJCOPY_localize_hidden); Config.Weaken = InputArgs.hasArg(OBJCOPY_weaken); if (auto *Arg = InputArgs.getLastArg(OBJCOPY_discard_all, OBJCOPY_discard_locals)) { Config.DiscardMode = Arg->getOption().matches(OBJCOPY_discard_all) ? DiscardType::All : DiscardType::Locals; } Config.OnlyKeepDebug = InputArgs.hasArg(OBJCOPY_only_keep_debug); ELFConfig.KeepFileSymbols = InputArgs.hasArg(OBJCOPY_keep_file_symbols); MachOConfig.KeepUndefined = InputArgs.hasArg(OBJCOPY_keep_undefined); Config.DecompressDebugSections = InputArgs.hasArg(OBJCOPY_decompress_debug_sections); if (Config.DiscardMode == DiscardType::All) { Config.StripDebug = true; ELFConfig.KeepFileSymbols = true; } for (auto *Arg : InputArgs.filtered(OBJCOPY_localize_symbol)) if (Error E = Config.SymbolsToLocalize.addMatcher(NameOrPattern::create( Arg->getValue(), SymbolMatchStyle, ErrorCallback))) return std::move(E); for (auto *Arg : InputArgs.filtered(OBJCOPY_localize_symbols)) if (Error E = addSymbolsFromFile(Config.SymbolsToLocalize, DC.Alloc, Arg->getValue(), SymbolMatchStyle, ErrorCallback)) return std::move(E); for (auto *Arg : InputArgs.filtered(OBJCOPY_keep_global_symbol)) if (Error E = Config.SymbolsToKeepGlobal.addMatcher(NameOrPattern::create( Arg->getValue(), SymbolMatchStyle, ErrorCallback))) return std::move(E); for (auto *Arg : InputArgs.filtered(OBJCOPY_keep_global_symbols)) if (Error E = addSymbolsFromFile(Config.SymbolsToKeepGlobal, DC.Alloc, Arg->getValue(), SymbolMatchStyle, ErrorCallback)) return std::move(E); for (auto *Arg : InputArgs.filtered(OBJCOPY_globalize_symbol)) if (Error E = Config.SymbolsToGlobalize.addMatcher(NameOrPattern::create( Arg->getValue(), SymbolMatchStyle, ErrorCallback))) return std::move(E); for (auto *Arg : InputArgs.filtered(OBJCOPY_globalize_symbols)) if (Error E = addSymbolsFromFile(Config.SymbolsToGlobalize, DC.Alloc, Arg->getValue(), SymbolMatchStyle, ErrorCallback)) return std::move(E); for (auto *Arg : InputArgs.filtered(OBJCOPY_weaken_symbol)) if (Error E = Config.SymbolsToWeaken.addMatcher(NameOrPattern::create( Arg->getValue(), SymbolMatchStyle, ErrorCallback))) return std::move(E); for (auto *Arg : InputArgs.filtered(OBJCOPY_weaken_symbols)) if (Error E = addSymbolsFromFile(Config.SymbolsToWeaken, DC.Alloc, Arg->getValue(), SymbolMatchStyle, ErrorCallback)) return std::move(E); for (auto *Arg : InputArgs.filtered(OBJCOPY_strip_symbol)) if (Error E = Config.SymbolsToRemove.addMatcher(NameOrPattern::create( Arg->getValue(), SymbolMatchStyle, ErrorCallback))) return std::move(E); for (auto *Arg : InputArgs.filtered(OBJCOPY_strip_symbols)) if (Error E = addSymbolsFromFile(Config.SymbolsToRemove, DC.Alloc, Arg->getValue(), SymbolMatchStyle, ErrorCallback)) return std::move(E); for (auto *Arg : InputArgs.filtered(OBJCOPY_strip_unneeded_symbol)) if (Error E = Config.UnneededSymbolsToRemove.addMatcher(NameOrPattern::create( Arg->getValue(), SymbolMatchStyle, ErrorCallback))) return std::move(E); for (auto *Arg : InputArgs.filtered(OBJCOPY_strip_unneeded_symbols)) if (Error E = addSymbolsFromFile(Config.UnneededSymbolsToRemove, DC.Alloc, Arg->getValue(), SymbolMatchStyle, ErrorCallback)) return std::move(E); for (auto *Arg : InputArgs.filtered(OBJCOPY_keep_symbol)) if (Error E = Config.SymbolsToKeep.addMatcher(NameOrPattern::create( Arg->getValue(), SymbolMatchStyle, ErrorCallback))) return std::move(E); for (auto *Arg : InputArgs.filtered(OBJCOPY_keep_symbols)) if (Error E = addSymbolsFromFile(Config.SymbolsToKeep, DC.Alloc, Arg->getValue(), SymbolMatchStyle, ErrorCallback)) return std::move(E); for (auto *Arg : InputArgs.filtered(OBJCOPY_add_symbol)) { Expected SymInfo = parseNewSymbolInfo(Arg->getValue()); if (!SymInfo) return SymInfo.takeError(); Config.SymbolsToAdd.push_back(*SymInfo); } ELFConfig.AllowBrokenLinks = InputArgs.hasArg(OBJCOPY_allow_broken_links); Config.DeterministicArchives = InputArgs.hasFlag( OBJCOPY_enable_deterministic_archives, OBJCOPY_disable_deterministic_archives, /*default=*/true); Config.PreserveDates = InputArgs.hasArg(OBJCOPY_preserve_dates); if (Config.PreserveDates && (Config.OutputFilename == "-" || Config.InputFilename == "-")) return createStringError(errc::invalid_argument, "--preserve-dates requires a file"); for (auto *Arg : InputArgs) if (Arg->getOption().matches(OBJCOPY_set_start)) { auto EAddr = getAsInteger(Arg->getValue()); if (!EAddr) return createStringError( EAddr.getError(), "bad entry point address: '%s'", Arg->getValue()); ELFConfig.EntryExpr = [EAddr](uint64_t) { return *EAddr; }; } else if (Arg->getOption().matches(OBJCOPY_change_start)) { auto EIncr = getAsInteger(Arg->getValue()); if (!EIncr) return createStringError(EIncr.getError(), "bad entry point increment: '%s'", Arg->getValue()); auto Expr = ELFConfig.EntryExpr ? std::move(ELFConfig.EntryExpr) : [](uint64_t A) { return A; }; ELFConfig.EntryExpr = [Expr, EIncr](uint64_t EAddr) { return Expr(EAddr) + *EIncr; }; } if (Config.DecompressDebugSections && Config.CompressionType != DebugCompressionType::None) { return createStringError( errc::invalid_argument, "cannot specify both --compress-debug-sections and " "--decompress-debug-sections"); } if (Config.ExtractPartition && Config.ExtractMainPartition) return createStringError(errc::invalid_argument, "cannot specify --extract-partition together with " "--extract-main-partition"); DC.CopyConfigs.push_back(std::move(ConfigMgr)); return std::move(DC); } // parseInstallNameToolOptions returns the config and sets the input arguments. // If a help flag is set then parseInstallNameToolOptions will print the help // messege and exit. Expected objcopy::parseInstallNameToolOptions(ArrayRef ArgsArr) { DriverConfig DC; ConfigManager ConfigMgr; CommonConfig &Config = ConfigMgr.Common; MachOConfig &MachOConfig = ConfigMgr.MachO; InstallNameToolOptTable T; unsigned MissingArgumentIndex, MissingArgumentCount; llvm::opt::InputArgList InputArgs = T.ParseArgs(ArgsArr, MissingArgumentIndex, MissingArgumentCount); if (MissingArgumentCount) return createStringError( errc::invalid_argument, "missing argument to " + StringRef(InputArgs.getArgString(MissingArgumentIndex)) + " option"); if (InputArgs.size() == 0) { printHelp(T, errs(), ToolType::InstallNameTool); exit(1); } if (InputArgs.hasArg(INSTALL_NAME_TOOL_help)) { printHelp(T, outs(), ToolType::InstallNameTool); exit(0); } if (InputArgs.hasArg(INSTALL_NAME_TOOL_version)) { outs() << "llvm-install-name-tool, compatible with cctools " "install_name_tool\n"; cl::PrintVersionMessage(); exit(0); } for (auto *Arg : InputArgs.filtered(INSTALL_NAME_TOOL_add_rpath)) MachOConfig.RPathToAdd.push_back(Arg->getValue()); for (auto *Arg : InputArgs.filtered(INSTALL_NAME_TOOL_prepend_rpath)) MachOConfig.RPathToPrepend.push_back(Arg->getValue()); for (auto *Arg : InputArgs.filtered(INSTALL_NAME_TOOL_delete_rpath)) { StringRef RPath = Arg->getValue(); // Cannot add and delete the same rpath at the same time. if (is_contained(MachOConfig.RPathToAdd, RPath)) return createStringError( errc::invalid_argument, "cannot specify both -add_rpath '%s' and -delete_rpath '%s'", RPath.str().c_str(), RPath.str().c_str()); if (is_contained(MachOConfig.RPathToPrepend, RPath)) return createStringError( errc::invalid_argument, "cannot specify both -prepend_rpath '%s' and -delete_rpath '%s'", RPath.str().c_str(), RPath.str().c_str()); MachOConfig.RPathsToRemove.insert(RPath); } for (auto *Arg : InputArgs.filtered(INSTALL_NAME_TOOL_rpath)) { StringRef Old = Arg->getValue(0); StringRef New = Arg->getValue(1); auto Match = [=](StringRef RPath) { return RPath == Old || RPath == New; }; // Cannot specify duplicate -rpath entries auto It1 = find_if( MachOConfig.RPathsToUpdate, [&Match](const DenseMap::value_type &OldNew) { return Match(OldNew.getFirst()) || Match(OldNew.getSecond()); }); if (It1 != MachOConfig.RPathsToUpdate.end()) return createStringError(errc::invalid_argument, "cannot specify both -rpath '" + It1->getFirst() + "' '" + It1->getSecond() + "' and -rpath '" + Old + "' '" + New + "'"); // Cannot specify the same rpath under both -delete_rpath and -rpath auto It2 = find_if(MachOConfig.RPathsToRemove, Match); if (It2 != MachOConfig.RPathsToRemove.end()) return createStringError(errc::invalid_argument, "cannot specify both -delete_rpath '" + *It2 + "' and -rpath '" + Old + "' '" + New + "'"); // Cannot specify the same rpath under both -add_rpath and -rpath auto It3 = find_if(MachOConfig.RPathToAdd, Match); if (It3 != MachOConfig.RPathToAdd.end()) return createStringError(errc::invalid_argument, "cannot specify both -add_rpath '" + *It3 + "' and -rpath '" + Old + "' '" + New + "'"); // Cannot specify the same rpath under both -prepend_rpath and -rpath. auto It4 = find_if(MachOConfig.RPathToPrepend, Match); if (It4 != MachOConfig.RPathToPrepend.end()) return createStringError(errc::invalid_argument, "cannot specify both -prepend_rpath '" + *It4 + "' and -rpath '" + Old + "' '" + New + "'"); MachOConfig.RPathsToUpdate.insert({Old, New}); } if (auto *Arg = InputArgs.getLastArg(INSTALL_NAME_TOOL_id)) { MachOConfig.SharedLibId = Arg->getValue(); if (MachOConfig.SharedLibId->empty()) return createStringError(errc::invalid_argument, "cannot specify an empty id"); } for (auto *Arg : InputArgs.filtered(INSTALL_NAME_TOOL_change)) MachOConfig.InstallNamesToUpdate.insert( {Arg->getValue(0), Arg->getValue(1)}); MachOConfig.RemoveAllRpaths = InputArgs.hasArg(INSTALL_NAME_TOOL_delete_all_rpaths); SmallVector Positional; for (auto *Arg : InputArgs.filtered(INSTALL_NAME_TOOL_UNKNOWN)) return createStringError(errc::invalid_argument, "unknown argument '%s'", Arg->getAsString(InputArgs).c_str()); for (auto *Arg : InputArgs.filtered(INSTALL_NAME_TOOL_INPUT)) Positional.push_back(Arg->getValue()); if (Positional.empty()) return createStringError(errc::invalid_argument, "no input file specified"); if (Positional.size() > 1) return createStringError( errc::invalid_argument, "llvm-install-name-tool expects a single input file"); Config.InputFilename = Positional[0]; Config.OutputFilename = Positional[0]; DC.CopyConfigs.push_back(std::move(ConfigMgr)); return std::move(DC); } Expected objcopy::parseBitcodeStripOptions(ArrayRef ArgsArr, function_ref ErrorCallback) { DriverConfig DC; ConfigManager ConfigMgr; CommonConfig &Config = ConfigMgr.Common; MachOConfig &MachOConfig = ConfigMgr.MachO; BitcodeStripOptTable T; unsigned MissingArgumentIndex, MissingArgumentCount; opt::InputArgList InputArgs = T.ParseArgs(ArgsArr, MissingArgumentIndex, MissingArgumentCount); if (InputArgs.size() == 0) { printHelp(T, errs(), ToolType::BitcodeStrip); exit(1); } if (InputArgs.hasArg(BITCODE_STRIP_help)) { printHelp(T, outs(), ToolType::BitcodeStrip); exit(0); } if (InputArgs.hasArg(BITCODE_STRIP_version)) { outs() << "llvm-bitcode-strip, compatible with cctools " "bitcode_strip\n"; cl::PrintVersionMessage(); exit(0); } for (auto *Arg : InputArgs.filtered(BITCODE_STRIP_UNKNOWN)) return createStringError(errc::invalid_argument, "unknown argument '%s'", Arg->getAsString(InputArgs).c_str()); SmallVector Positional; for (auto *Arg : InputArgs.filtered(BITCODE_STRIP_INPUT)) Positional.push_back(Arg->getValue()); if (Positional.size() > 1) return createStringError(errc::invalid_argument, "llvm-bitcode-strip expects a single input file"); assert(!Positional.empty()); Config.InputFilename = Positional[0]; if (!InputArgs.hasArg(BITCODE_STRIP_output)) { return createStringError(errc::invalid_argument, "-o is a required argument"); } Config.OutputFilename = InputArgs.getLastArgValue(BITCODE_STRIP_output); if (!InputArgs.hasArg(BITCODE_STRIP_remove)) return createStringError(errc::invalid_argument, "no action specified"); // We only support -r for now, which removes all bitcode sections and // the __LLVM segment if it's now empty. cantFail(Config.ToRemove.addMatcher(NameOrPattern::create( "__LLVM,__asm", MatchStyle::Literal, ErrorCallback))); cantFail(Config.ToRemove.addMatcher(NameOrPattern::create( "__LLVM,__bitcode", MatchStyle::Literal, ErrorCallback))); cantFail(Config.ToRemove.addMatcher(NameOrPattern::create( "__LLVM,__bundle", MatchStyle::Literal, ErrorCallback))); cantFail(Config.ToRemove.addMatcher(NameOrPattern::create( "__LLVM,__cmdline", MatchStyle::Literal, ErrorCallback))); cantFail(Config.ToRemove.addMatcher(NameOrPattern::create( "__LLVM,__swift_cmdline", MatchStyle::Literal, ErrorCallback))); MachOConfig.EmptySegmentsToRemove.insert("__LLVM"); DC.CopyConfigs.push_back(std::move(ConfigMgr)); return std::move(DC); } // parseStripOptions returns the config and sets the input arguments. If a // help flag is set then parseStripOptions will print the help messege and // exit. Expected objcopy::parseStripOptions(ArrayRef RawArgsArr, function_ref ErrorCallback) { const char *const *DashDash = llvm::find_if(RawArgsArr, [](StringRef Str) { return Str == "--"; }); ArrayRef ArgsArr = ArrayRef(RawArgsArr.begin(), DashDash); if (DashDash != RawArgsArr.end()) DashDash = std::next(DashDash); StripOptTable T; unsigned MissingArgumentIndex, MissingArgumentCount; llvm::opt::InputArgList InputArgs = T.ParseArgs(ArgsArr, MissingArgumentIndex, MissingArgumentCount); if (InputArgs.size() == 0 && DashDash == RawArgsArr.end()) { printHelp(T, errs(), ToolType::Strip); exit(1); } if (InputArgs.hasArg(STRIP_help)) { printHelp(T, outs(), ToolType::Strip); exit(0); } if (InputArgs.hasArg(STRIP_version)) { outs() << "llvm-strip, compatible with GNU strip\n"; cl::PrintVersionMessage(); exit(0); } SmallVector Positional; for (auto *Arg : InputArgs.filtered(STRIP_UNKNOWN)) return createStringError(errc::invalid_argument, "unknown argument '%s'", Arg->getAsString(InputArgs).c_str()); for (auto *Arg : InputArgs.filtered(STRIP_INPUT)) Positional.push_back(Arg->getValue()); std::copy(DashDash, RawArgsArr.end(), std::back_inserter(Positional)); if (Positional.empty()) return createStringError(errc::invalid_argument, "no input file specified"); if (Positional.size() > 1 && InputArgs.hasArg(STRIP_output)) return createStringError( errc::invalid_argument, "multiple input files cannot be used in combination with -o"); ConfigManager ConfigMgr; CommonConfig &Config = ConfigMgr.Common; ELFConfig &ELFConfig = ConfigMgr.ELF; MachOConfig &MachOConfig = ConfigMgr.MachO; if (InputArgs.hasArg(STRIP_regex) && InputArgs.hasArg(STRIP_wildcard)) return createStringError(errc::invalid_argument, "--regex and --wildcard are incompatible"); MatchStyle SectionMatchStyle = InputArgs.hasArg(STRIP_regex) ? MatchStyle::Regex : MatchStyle::Wildcard; MatchStyle SymbolMatchStyle = InputArgs.hasArg(STRIP_regex) ? MatchStyle::Regex : InputArgs.hasArg(STRIP_wildcard) ? MatchStyle::Wildcard : MatchStyle::Literal; ELFConfig.AllowBrokenLinks = InputArgs.hasArg(STRIP_allow_broken_links); Config.StripDebug = InputArgs.hasArg(STRIP_strip_debug); if (auto *Arg = InputArgs.getLastArg(STRIP_discard_all, STRIP_discard_locals)) Config.DiscardMode = Arg->getOption().matches(STRIP_discard_all) ? DiscardType::All : DiscardType::Locals; Config.StripSections = InputArgs.hasArg(STRIP_strip_sections); Config.StripUnneeded = InputArgs.hasArg(STRIP_strip_unneeded); if (auto Arg = InputArgs.getLastArg(STRIP_strip_all, STRIP_no_strip_all)) Config.StripAll = Arg->getOption().getID() == STRIP_strip_all; Config.StripAllGNU = InputArgs.hasArg(STRIP_strip_all_gnu); MachOConfig.StripSwiftSymbols = InputArgs.hasArg(STRIP_strip_swift_symbols); Config.OnlyKeepDebug = InputArgs.hasArg(STRIP_only_keep_debug); ELFConfig.KeepFileSymbols = InputArgs.hasArg(STRIP_keep_file_symbols); MachOConfig.KeepUndefined = InputArgs.hasArg(STRIP_keep_undefined); for (auto *Arg : InputArgs.filtered(STRIP_keep_section)) if (Error E = Config.KeepSection.addMatcher(NameOrPattern::create( Arg->getValue(), SectionMatchStyle, ErrorCallback))) return std::move(E); for (auto *Arg : InputArgs.filtered(STRIP_remove_section)) if (Error E = Config.ToRemove.addMatcher(NameOrPattern::create( Arg->getValue(), SectionMatchStyle, ErrorCallback))) return std::move(E); for (auto *Arg : InputArgs.filtered(STRIP_strip_symbol)) if (Error E = Config.SymbolsToRemove.addMatcher(NameOrPattern::create( Arg->getValue(), SymbolMatchStyle, ErrorCallback))) return std::move(E); for (auto *Arg : InputArgs.filtered(STRIP_keep_symbol)) if (Error E = Config.SymbolsToKeep.addMatcher(NameOrPattern::create( Arg->getValue(), SymbolMatchStyle, ErrorCallback))) return std::move(E); if (!InputArgs.hasArg(STRIP_no_strip_all) && !Config.StripDebug && !Config.OnlyKeepDebug && !Config.StripUnneeded && Config.DiscardMode == DiscardType::None && !Config.StripAllGNU && Config.SymbolsToRemove.empty()) Config.StripAll = true; if (Config.DiscardMode == DiscardType::All) { Config.StripDebug = true; ELFConfig.KeepFileSymbols = true; } Config.DeterministicArchives = InputArgs.hasFlag(STRIP_enable_deterministic_archives, STRIP_disable_deterministic_archives, /*default=*/true); Config.PreserveDates = InputArgs.hasArg(STRIP_preserve_dates); Config.InputFormat = FileFormat::Unspecified; Config.OutputFormat = FileFormat::Unspecified; DriverConfig DC; if (Positional.size() == 1) { Config.InputFilename = Positional[0]; Config.OutputFilename = InputArgs.getLastArgValue(STRIP_output, Positional[0]); DC.CopyConfigs.push_back(std::move(ConfigMgr)); } else { StringMap InputFiles; for (StringRef Filename : Positional) { if (InputFiles[Filename]++ == 1) { if (Filename == "-") return createStringError( errc::invalid_argument, "cannot specify '-' as an input file more than once"); if (Error E = ErrorCallback(createStringError( errc::invalid_argument, "'%s' was already specified", Filename.str().c_str()))) return std::move(E); } Config.InputFilename = Filename; Config.OutputFilename = Filename; DC.CopyConfigs.push_back(ConfigMgr); } } if (Config.PreserveDates && (is_contained(Positional, "-") || InputArgs.getLastArgValue(STRIP_output) == "-")) return createStringError(errc::invalid_argument, "--preserve-dates requires a file"); return std::move(DC); }