//===- CopyConfig.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 "CopyConfig.h" #include "llvm/ADT/BitmaskEnum.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Compression.h" #include "llvm/Support/Errc.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/StringSaver.h" #include namespace llvm { namespace 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 }; #define PREFIX(NAME, VALUE) const char *const OBJCOPY_##NAME[] = VALUE; #include "ObjcopyOpts.inc" #undef PREFIX static const opt::OptTable::Info ObjcopyInfoTable[] = { #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ HELPTEXT, METAVAR, VALUES) \ {OBJCOPY_##PREFIX, \ NAME, \ HELPTEXT, \ METAVAR, \ OBJCOPY_##ID, \ opt::Option::KIND##Class, \ PARAM, \ FLAGS, \ OBJCOPY_##GROUP, \ OBJCOPY_##ALIAS, \ ALIASARGS, \ VALUES}, #include "ObjcopyOpts.inc" #undef OPTION }; class ObjcopyOptTable : public opt::OptTable { public: ObjcopyOptTable() : OptTable(ObjcopyInfoTable) {} }; 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 }; #define PREFIX(NAME, VALUE) const char *const STRIP_##NAME[] = VALUE; #include "StripOpts.inc" #undef PREFIX static const opt::OptTable::Info StripInfoTable[] = { #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ HELPTEXT, METAVAR, VALUES) \ {STRIP_##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) {} }; enum SectionFlag { SecNone = 0, SecAlloc = 1 << 0, SecLoad = 1 << 1, SecNoload = 1 << 2, SecReadonly = 1 << 3, SecDebug = 1 << 4, SecCode = 1 << 5, SecData = 1 << 6, SecRom = 1 << 7, SecMerge = 1 << 8, SecStrings = 1 << 9, SecContents = 1 << 10, SecShare = 1 << 11, LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ SecShare) }; } // namespace static SectionFlag parseSectionRenameFlag(StringRef SectionName) { return llvm::StringSwitch(SectionName) .Case("alloc", SectionFlag::SecAlloc) .Case("load", SectionFlag::SecLoad) .Case("noload", SectionFlag::SecNoload) .Case("readonly", SectionFlag::SecReadonly) .Case("debug", SectionFlag::SecDebug) .Case("code", SectionFlag::SecCode) .Case("data", SectionFlag::SecData) .Case("rom", SectionFlag::SecRom) .Case("merge", SectionFlag::SecMerge) .Case("strings", SectionFlag::SecStrings) .Case("contents", SectionFlag::SecContents) .Case("share", SectionFlag::SecShare) .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, debug, code, data, " "rom, share, contents, merge, strings", Flag.str().c_str()); ParsedFlags |= ParsedFlag; } uint64_t NewFlags = 0; if (ParsedFlags & SectionFlag::SecAlloc) NewFlags |= ELF::SHF_ALLOC; if (!(ParsedFlags & SectionFlag::SecReadonly)) NewFlags |= ELF::SHF_WRITE; if (ParsedFlags & SectionFlag::SecCode) NewFlags |= ELF::SHF_EXECINSTR; if (ParsedFlags & SectionFlag::SecMerge) NewFlags |= ELF::SHF_MERGE; if (ParsedFlags & SectionFlag::SecStrings) NewFlags |= ELF::SHF_STRINGS; return NewFlags; } 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(makeArrayRef(NameAndFlags).drop_front()); if (!ParsedFlagSet) return ParsedFlagSet.takeError(); SR.NewFlags = *ParsedFlagSet; } return SR; } 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; } 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: // The following are currently supported: // // global, local, weak, default, hidden, file, section, object, // indirect-function. // // The following flags are ignored and provided for GNU // compatibility only: // // warning, debug, constructor, indirect, synthetic, // unique-object, before=. 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] { SI.Bind = ELF::STB_GLOBAL; }) .CaseLower("local", [&SI] { SI.Bind = ELF::STB_LOCAL; }) .CaseLower("weak", [&SI] { SI.Bind = ELF::STB_WEAK; }) .CaseLower("default", [&SI] { SI.Visibility = ELF::STV_DEFAULT; }) .CaseLower("hidden", [&SI] { SI.Visibility = ELF::STV_HIDDEN; }) .CaseLower("file", [&SI] { SI.Type = ELF::STT_FILE; }) .CaseLower("section", [&SI] { SI.Type = ELF::STT_SECTION; }) .CaseLower("object", [&SI] { SI.Type = ELF::STT_OBJECT; }) .CaseLower("function", [&SI] { SI.Type = ELF::STT_FUNC; }) .CaseLower("indirect-function", [&SI] { SI.Type = ELF::STT_GNU_IFUNC; }) .CaseLower("debug", [] {}) .CaseLower("constructor", [] {}) .CaseLower("warning", [] {}) .CaseLower("indirect", [] {}) .CaseLower("synthetic", [] {}) .CaseLower("unique-object", [] {}) .StartsWithLower("before", [] {}) .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; } static const StringMap ArchMap{ // Name, {EMachine, OS/ABI, 64bit, LittleEndian} {"aarch64", {ELF::EM_AARCH64, ELF::ELFOSABI_NONE, true, true}}, {"arm", {ELF::EM_ARM, ELF::ELFOSABI_NONE, false, true}}, {"i386", {ELF::EM_386, ELF::ELFOSABI_NONE, false, true}}, {"i386:x86-64", {ELF::EM_X86_64, ELF::ELFOSABI_NONE, true, true}}, {"powerpc:common64", {ELF::EM_PPC64, ELF::ELFOSABI_NONE, true, true}}, {"sparc", {ELF::EM_SPARC, ELF::ELFOSABI_NONE, false, true}}, {"x86-64", {ELF::EM_X86_64, ELF::ELFOSABI_NONE, true, true}}, }; static Expected getMachineInfo(StringRef Arch) { auto Iter = ArchMap.find(Arch); if (Iter == std::end(ArchMap)) return createStringError(errc::invalid_argument, "Invalid architecture: '%s'", Arch.str().c_str()); return Iter->getValue(); } static const StringMap OutputFormatMap{ // Name, {EMachine, OSABI, 64bit, LittleEndian} {"elf32-i386", {ELF::EM_386, ELF::ELFOSABI_NONE, false, true}}, {"elf32-i386-freebsd", {ELF::EM_386, ELF::ELFOSABI_FREEBSD, false, true}}, {"elf32-powerpcle", {ELF::EM_PPC, ELF::ELFOSABI_NONE, false, true}}, {"elf32-x86-64", {ELF::EM_X86_64, ELF::ELFOSABI_NONE, false, true}}, {"elf64-powerpcle", {ELF::EM_PPC64, ELF::ELFOSABI_NONE, true, true}}, {"elf64-x86-64", {ELF::EM_X86_64, ELF::ELFOSABI_NONE, true, true}}, {"elf64-x86-64-freebsd", {ELF::EM_X86_64, ELF::ELFOSABI_FREEBSD, true, true}}, }; static Expected getOutputFormatMachineInfo(StringRef Format) { auto Iter = OutputFormatMap.find(Format); if (Iter == std::end(OutputFormatMap)) return createStringError(errc::invalid_argument, "Invalid output format: '%s'", Format.str().c_str()); return Iter->getValue(); } static Error addSymbolsFromFile(std::vector &Symbols, BumpPtrAllocator &Alloc, StringRef Filename, bool UseRegex) { 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()) Symbols.emplace_back(Saver.save(TrimmedLine), UseRegex); } return Error::success(); } NameOrRegex::NameOrRegex(StringRef Pattern, bool IsRegex) { if (!IsRegex) { Name = Pattern; return; } SmallVector Data; R = std::make_shared( ("^" + Pattern.ltrim('^').rtrim('$') + "$").toStringRef(Data)); } 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; } // 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 parseObjcopyOptions(ArrayRef ArgsArr) { DriverConfig DC; ObjcopyOptTable T; unsigned MissingArgumentIndex, MissingArgumentCount; llvm::opt::InputArgList InputArgs = T.ParseArgs(ArgsArr, MissingArgumentIndex, MissingArgumentCount); if (InputArgs.size() == 0) { T.PrintHelp(errs(), "llvm-objcopy input [output]", "objcopy tool"); exit(1); } if (InputArgs.hasArg(OBJCOPY_help)) { T.PrintHelp(outs(), "llvm-objcopy input [output]", "objcopy tool"); 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()); 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"); CopyConfig Config; 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"); bool UseRegex = InputArgs.hasArg(OBJCOPY_regex); if (InputArgs.hasArg(OBJCOPY_target)) { Config.InputFormat = InputArgs.getLastArgValue(OBJCOPY_target); Config.OutputFormat = InputArgs.getLastArgValue(OBJCOPY_target); } else { Config.InputFormat = InputArgs.getLastArgValue(OBJCOPY_input_target); Config.OutputFormat = InputArgs.getLastArgValue(OBJCOPY_output_target); } if (Config.InputFormat == "binary") { auto BinaryArch = InputArgs.getLastArgValue(OBJCOPY_binary_architecture); if (BinaryArch.empty()) return createStringError( errc::invalid_argument, "Specified binary input without specifiying an architecture"); Expected MI = getMachineInfo(BinaryArch); if (!MI) return MI.takeError(); Config.BinaryArch = *MI; } if (!Config.OutputFormat.empty() && Config.OutputFormat != "binary") { Expected MI = getOutputFormatMachineInfo(Config.OutputFormat); if (!MI) return MI.takeError(); Config.OutputArch = *MI; } if (auto Arg = InputArgs.getLastArg(OBJCOPY_compress_debug_sections, OBJCOPY_compress_debug_sections_eq)) { Config.CompressionType = DebugCompressionType::Z; if (Arg->getOption().getID() == OBJCOPY_compress_debug_sections_eq) { Config.CompressionType = StringSwitch( InputArgs.getLastArgValue(OBJCOPY_compress_debug_sections_eq)) .Case("zlib-gnu", DebugCompressionType::GNU) .Case("zlib", DebugCompressionType::Z) .Default(DebugCompressionType::None); if (Config.CompressionType == DebugCompressionType::None) return createStringError( errc::invalid_argument, "Invalid or unsupported --compress-debug-sections format: %s", InputArgs.getLastArgValue(OBJCOPY_compress_debug_sections_eq) .str() .c_str()); } if (!zlib::isAvailable()) return createStringError( errc::invalid_argument, "LLVM was not compiled with LLVM_ENABLE_ZLIB: can not compress"); } Config.AddGnuDebugLink = InputArgs.getLastArgValue(OBJCOPY_add_gnu_debuglink); Config.BuildIdLinkDir = InputArgs.getLastArgValue(OBJCOPY_build_id_link_dir); if (InputArgs.hasArg(OBJCOPY_build_id_link_input)) Config.BuildIdLinkInput = InputArgs.getLastArgValue(OBJCOPY_build_id_link_input); if (InputArgs.hasArg(OBJCOPY_build_id_link_output)) Config.BuildIdLinkOutput = InputArgs.getLastArgValue(OBJCOPY_build_id_link_output); Config.SplitDWO = InputArgs.getLastArgValue(OBJCOPY_split_dwo); Config.SymbolsPrefix = InputArgs.getLastArgValue(OBJCOPY_prefix_symbols); 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_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()); } // Prohibit combinations of --set-section-flags when the section name is used // by --rename-section, either as a source or a destination. for (const auto &E : Config.SectionsToRename) { const SectionRename &SR = E.second; if (Config.SetSectionFlags.count(SR.OriginalName)) return createStringError( errc::invalid_argument, "--set-section-flags=%s conflicts with --rename-section=%s=%s", SR.OriginalName.str().c_str(), SR.OriginalName.str().c_str(), SR.NewName.str().c_str()); if (Config.SetSectionFlags.count(SR.NewName)) return createStringError( errc::invalid_argument, "--set-section-flags=%s conflicts with --rename-section=%s=%s", SR.NewName.str().c_str(), SR.OriginalName.str().c_str(), SR.NewName.str().c_str()); } for (auto Arg : InputArgs.filtered(OBJCOPY_remove_section)) Config.ToRemove.emplace_back(Arg->getValue(), UseRegex); for (auto Arg : InputArgs.filtered(OBJCOPY_keep_section)) Config.KeepSection.emplace_back(Arg->getValue(), UseRegex); for (auto Arg : InputArgs.filtered(OBJCOPY_only_section)) Config.OnlySection.emplace_back(Arg->getValue(), UseRegex); for (auto Arg : InputArgs.filtered(OBJCOPY_add_section)) Config.AddSection.push_back(Arg->getValue()); for (auto Arg : InputArgs.filtered(OBJCOPY_dump_section)) Config.DumpSection.push_back(Arg->getValue()); 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.LocalizeHidden = InputArgs.hasArg(OBJCOPY_localize_hidden); Config.Weaken = InputArgs.hasArg(OBJCOPY_weaken); 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 = InputArgs.hasArg(OBJCOPY_decompress_debug_sections); for (auto Arg : InputArgs.filtered(OBJCOPY_localize_symbol)) Config.SymbolsToLocalize.emplace_back(Arg->getValue(), UseRegex); for (auto Arg : InputArgs.filtered(OBJCOPY_localize_symbols)) if (Error E = addSymbolsFromFile(Config.SymbolsToLocalize, DC.Alloc, Arg->getValue(), UseRegex)) return std::move(E); for (auto Arg : InputArgs.filtered(OBJCOPY_keep_global_symbol)) Config.SymbolsToKeepGlobal.emplace_back(Arg->getValue(), UseRegex); for (auto Arg : InputArgs.filtered(OBJCOPY_keep_global_symbols)) if (Error E = addSymbolsFromFile(Config.SymbolsToKeepGlobal, DC.Alloc, Arg->getValue(), UseRegex)) return std::move(E); for (auto Arg : InputArgs.filtered(OBJCOPY_globalize_symbol)) Config.SymbolsToGlobalize.emplace_back(Arg->getValue(), UseRegex); for (auto Arg : InputArgs.filtered(OBJCOPY_globalize_symbols)) if (Error E = addSymbolsFromFile(Config.SymbolsToGlobalize, DC.Alloc, Arg->getValue(), UseRegex)) return std::move(E); for (auto Arg : InputArgs.filtered(OBJCOPY_weaken_symbol)) Config.SymbolsToWeaken.emplace_back(Arg->getValue(), UseRegex); for (auto Arg : InputArgs.filtered(OBJCOPY_weaken_symbols)) if (Error E = addSymbolsFromFile(Config.SymbolsToWeaken, DC.Alloc, Arg->getValue(), UseRegex)) return std::move(E); for (auto Arg : InputArgs.filtered(OBJCOPY_strip_symbol)) Config.SymbolsToRemove.emplace_back(Arg->getValue(), UseRegex); for (auto Arg : InputArgs.filtered(OBJCOPY_strip_symbols)) if (Error E = addSymbolsFromFile(Config.SymbolsToRemove, DC.Alloc, Arg->getValue(), UseRegex)) return std::move(E); for (auto Arg : InputArgs.filtered(OBJCOPY_strip_unneeded_symbol)) Config.UnneededSymbolsToRemove.emplace_back(Arg->getValue(), UseRegex); for (auto Arg : InputArgs.filtered(OBJCOPY_strip_unneeded_symbols)) if (Error E = addSymbolsFromFile(Config.UnneededSymbolsToRemove, DC.Alloc, Arg->getValue(), UseRegex)) return std::move(E); for (auto Arg : InputArgs.filtered(OBJCOPY_keep_symbol)) Config.SymbolsToKeep.emplace_back(Arg->getValue(), UseRegex); for (auto Arg : InputArgs.filtered(OBJCOPY_add_symbol)) { Expected NSI = parseNewSymbolInfo(Arg->getValue()); if (!NSI) return NSI.takeError(); Config.SymbolsToAdd.push_back(*NSI); } Config.DeterministicArchives = InputArgs.hasFlag( OBJCOPY_enable_deterministic_archives, OBJCOPY_disable_deterministic_archives, /*default=*/true); Config.PreserveDates = InputArgs.hasArg(OBJCOPY_preserve_dates); 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()); Config.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 = Config.EntryExpr ? std::move(Config.EntryExpr) : [](uint64_t A) { return A; }; Config.EntryExpr = [Expr, EIncr](uint64_t EAddr) { return Expr(EAddr) + *EIncr; }; } if (Config.DecompressDebugSections && Config.CompressionType != DebugCompressionType::None) { return createStringError( errc::invalid_argument, "Cannot specify --compress-debug-sections at the same time as " "--decompress-debug-sections at the same time"); } if (Config.DecompressDebugSections && !zlib::isAvailable()) return createStringError( errc::invalid_argument, "LLVM was not compiled with LLVM_ENABLE_ZLIB: cannot decompress"); DC.CopyConfigs.push_back(std::move(Config)); 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 parseStripOptions(ArrayRef ArgsArr) { StripOptTable T; unsigned MissingArgumentIndex, MissingArgumentCount; llvm::opt::InputArgList InputArgs = T.ParseArgs(ArgsArr, MissingArgumentIndex, MissingArgumentCount); if (InputArgs.size() == 0) { T.PrintHelp(errs(), "llvm-strip [options] file...", "strip tool"); exit(1); } if (InputArgs.hasArg(STRIP_help)) { T.PrintHelp(outs(), "llvm-strip [options] file...", "strip tool"); 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()); 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"); CopyConfig Config; bool UseRegexp = InputArgs.hasArg(STRIP_regex); Config.StripDebug = InputArgs.hasArg(STRIP_strip_debug); 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); Config.OnlyKeepDebug = InputArgs.hasArg(STRIP_only_keep_debug); Config.KeepFileSymbols = InputArgs.hasArg(STRIP_keep_file_symbols); for (auto Arg : InputArgs.filtered(STRIP_keep_section)) Config.KeepSection.emplace_back(Arg->getValue(), UseRegexp); for (auto Arg : InputArgs.filtered(STRIP_remove_section)) Config.ToRemove.emplace_back(Arg->getValue(), UseRegexp); for (auto Arg : InputArgs.filtered(STRIP_strip_symbol)) Config.SymbolsToRemove.emplace_back(Arg->getValue(), UseRegexp); for (auto Arg : InputArgs.filtered(STRIP_keep_symbol)) Config.SymbolsToKeep.emplace_back(Arg->getValue(), UseRegexp); if (!Config.StripDebug && !Config.StripUnneeded && Config.DiscardMode == DiscardType::None && !Config.StripAllGNU && Config.SymbolsToRemove.empty()) Config.StripAll = true; Config.DeterministicArchives = InputArgs.hasFlag(STRIP_enable_deterministic_archives, STRIP_disable_deterministic_archives, /*default=*/true); Config.PreserveDates = InputArgs.hasArg(STRIP_preserve_dates); DriverConfig DC; if (Positional.size() == 1) { Config.InputFilename = Positional[0]; Config.OutputFilename = InputArgs.getLastArgValue(STRIP_output, Positional[0]); DC.CopyConfigs.push_back(std::move(Config)); } else { for (const char *Filename : Positional) { Config.InputFilename = Filename; Config.OutputFilename = Filename; DC.CopyConfigs.push_back(Config); } } return std::move(DC); } } // namespace objcopy } // namespace llvm