diff options
Diffstat (limited to 'contrib/llvm-project/llvm/tools/llvm-objcopy')
8 files changed, 2149 insertions, 0 deletions
diff --git a/contrib/llvm-project/llvm/tools/llvm-objcopy/BitcodeStripOpts.td b/contrib/llvm-project/llvm/tools/llvm-objcopy/BitcodeStripOpts.td new file mode 100644 index 000000000000..21db854b1e6f --- /dev/null +++ b/contrib/llvm-project/llvm/tools/llvm-objcopy/BitcodeStripOpts.td @@ -0,0 +1,30 @@ +//===-- BitcodeStripOpts.td - llvm-bitcode-strip options ---------------*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This file describes the command line options of llvm-bitcode-strip. +// +//===----------------------------------------------------------------------===// + +include "llvm/Option/OptParser.td" + +def help : Flag<["--"], "help">; + +def h : Flag<["-"], "h">, Alias<help>; + +def version : Flag<["--"], "version">, + HelpText<"Print the version and exit">; + +def V : Flag<["-"], "V">, + Alias<version>, + HelpText<"Alias for --version">; + +def remove : Flag<["-"], "r">, + HelpText<"Remove the __LLVM bitcode segment entirely">; + +def output : JoinedOrSeparate<["-"], "o">, HelpText<"Write output to <file>">, + MetaVarName<"<file>">; diff --git a/contrib/llvm-project/llvm/tools/llvm-objcopy/CommonOpts.td b/contrib/llvm-project/llvm/tools/llvm-objcopy/CommonOpts.td new file mode 100644 index 000000000000..4222532a1a38 --- /dev/null +++ b/contrib/llvm-project/llvm/tools/llvm-objcopy/CommonOpts.td @@ -0,0 +1,129 @@ +include "llvm/Option/OptParser.td" + +multiclass Eq<string name, string help> { + def NAME : Separate<["--"], name>; + def NAME #_eq : Joined<["--"], name #"=">, + Alias<!cast<Separate>(NAME)>, + HelpText<help>; +} + +def help : Flag<["--"], "help">; +def h : Flag<["-"], "h">, Alias<help>; + +def allow_broken_links + : Flag<["--"], "allow-broken-links">, + HelpText<"Allow the tool to remove sections even if it would leave " + "invalid section references. The appropriate sh_link fields " + "will be set to zero.">; + +def enable_deterministic_archives + : Flag<["--"], "enable-deterministic-archives">, + HelpText<"Enable deterministic mode when operating on archives (use " + "zero for UIDs, GIDs, and timestamps).">; +def D : Flag<["-"], "D">, + Alias<enable_deterministic_archives>, + HelpText<"Alias for --enable-deterministic-archives">; + +def disable_deterministic_archives + : Flag<["--"], "disable-deterministic-archives">, + HelpText<"Disable deterministic mode when operating on archives (use " + "real values for UIDs, GIDs, and timestamps).">; +def U : Flag<["-"], "U">, + Alias<disable_deterministic_archives>, + HelpText<"Alias for --disable-deterministic-archives">; + +def preserve_dates : Flag<["--"], "preserve-dates">, + HelpText<"Preserve access and modification timestamps">; +def p : Flag<["-"], "p">, + Alias<preserve_dates>, + HelpText<"Alias for --preserve-dates">; + +def strip_all : Flag<["--"], "strip-all">, + HelpText<"Remove non-allocated sections outside segments. " + ".gnu.warning* and .ARM.attribute sections are not " + "removed">; + +def strip_all_gnu + : Flag<["--"], "strip-all-gnu">, + HelpText<"Compatible with GNU's --strip-all">; + +def strip_debug : Flag<["--"], "strip-debug">, + HelpText<"Remove all debug sections">; +def g : Flag<["-"], "g">, + Alias<strip_debug>, + HelpText<"Alias for --strip-debug">; + +def strip_unneeded : Flag<["--"], "strip-unneeded">, + HelpText<"Remove all symbols not needed by relocations">; + +defm remove_section : Eq<"remove-section", "Remove <section>">, + MetaVarName<"section">; +def R : JoinedOrSeparate<["-"], "R">, + Alias<remove_section>, + HelpText<"Alias for --remove-section">; + +def strip_sections + : Flag<["--"], "strip-sections">, + HelpText<"Remove all section headers and all sections not in segments">; + +defm strip_symbol : Eq<"strip-symbol", "Strip <symbol>">, + MetaVarName<"symbol">; +def N : JoinedOrSeparate<["-"], "N">, + Alias<strip_symbol>, + HelpText<"Alias for --strip-symbol">; + +defm keep_section : Eq<"keep-section", "Keep <section>">, + MetaVarName<"section">; + +defm keep_symbol : Eq<"keep-symbol", "Do not remove symbol <symbol>">, + MetaVarName<"symbol">; +def K : JoinedOrSeparate<["-"], "K">, + Alias<keep_symbol>, + HelpText<"Alias for --keep-symbol">; + +def keep_file_symbols : Flag<["--"], "keep-file-symbols">, + HelpText<"Do not remove file symbols">; + +def keep_undefined : Flag<["--"], "keep-undefined">, + HelpText<"Do not remove undefined symbols">; + +def only_keep_debug + : Flag<["--"], "only-keep-debug">, + HelpText< + "Produce a debug file as the output that only preserves contents of " + "sections useful for debugging purposes">; + +def discard_locals : Flag<["--"], "discard-locals">, + HelpText<"Remove compiler-generated local symbols, (e.g. " + "symbols starting with .L)">; +def X : Flag<["-"], "X">, + Alias<discard_locals>, + HelpText<"Alias for --discard-locals">; + +def discard_all + : Flag<["--"], "discard-all">, + HelpText<"Remove all local symbols except file and section symbols. Also " + "remove all debug sections">; +def x : Flag<["-"], "x">, + Alias<discard_all>, + HelpText<"Alias for --discard-all">; + +def regex + : Flag<["--"], "regex">, + HelpText<"Permit regular expressions in name comparison">; + +def version : Flag<["--"], "version">, + HelpText<"Print the version and exit.">; +def V : Flag<["-"], "V">, + Alias<version>, + HelpText<"Alias for --version">; + +def wildcard + : Flag<["--"], "wildcard">, + HelpText<"Allow wildcard syntax for symbol-related flags. Incompatible " + "with --regex. Allows using '*' to match any number of " + "characters, '?' to match any single character, '\' to escape " + "special characters, and '[]' to define character classes. " + "Wildcards beginning with '!' will prevent a match, for example " + "\"-N '*' -N '!x'\" will strip all symbols except for \"x\".">; +def w : Flag<["-"], "w">, Alias<wildcard>, HelpText<"Alias for --wildcard">; diff --git a/contrib/llvm-project/llvm/tools/llvm-objcopy/InstallNameToolOpts.td b/contrib/llvm-project/llvm/tools/llvm-objcopy/InstallNameToolOpts.td new file mode 100644 index 000000000000..88dea84400fb --- /dev/null +++ b/contrib/llvm-project/llvm/tools/llvm-objcopy/InstallNameToolOpts.td @@ -0,0 +1,44 @@ +//===-- InstallNameToolOpts.td - llvm-install-name-tool options --------*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This file describes the command line options of llvm-install-name. +// +//===----------------------------------------------------------------------===// + +include "llvm/Option/OptParser.td" + +def help : Flag<["--"], "help">; +def h : Flag<["-"], "h">, Alias<help>; + +def add_rpath : Option<["-", "--"], "add_rpath", KIND_SEPARATE>, + HelpText<"Add new rpath">; + +def prepend_rpath : Option<["-", "--"], "prepend_rpath", KIND_SEPARATE>, + HelpText<"Add new rpath before other rpaths">; + +def delete_rpath: Option<["-", "--"], "delete_rpath", KIND_SEPARATE>, + HelpText<"Delete specified rpath">; + +def delete_all_rpaths: Flag<["-", "--"], "delete_all_rpaths">, + HelpText<"Delete all rpath directives">; + +def rpath: MultiArg<["-", "--"], "rpath", 2>, + HelpText<"Change rpath path name">; + +def id : Option<["-","--"], "id", KIND_SEPARATE>, + HelpText<"Change dynamic shared library id">; + +def change: MultiArg<["-", "--"], "change", 2>, + HelpText<"Change dependent shared library install name">; + +def version : Flag<["--"], "version">, + HelpText<"Print the version and exit.">; + +def V : Flag<["-"], "V">, + Alias<version>, + HelpText<"Alias for --version">; diff --git a/contrib/llvm-project/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp b/contrib/llvm-project/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp new file mode 100644 index 000000000000..577b837320ae --- /dev/null +++ b/contrib/llvm-project/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp @@ -0,0 +1,1377 @@ +//===- 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<StringLiteral> 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<StringLiteral> 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<StringLiteral> 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<StringLiteral> 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<SectionFlag>(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<SectionFlag> +parseSectionFlagSet(ArrayRef<StringRef> 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<SectionRename> 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<StringRef, 6> NameAndFlags; + Old2New.second.split(NameAndFlags, ','); + SR.NewName = NameAndFlags[0]; + + if (NameAndFlags.size() > 1) { + Expected<SectionFlag> ParsedFlagSet = + parseSectionFlagSet(ArrayRef(NameAndFlags).drop_front()); + if (!ParsedFlagSet) + return ParsedFlagSet.takeError(); + SR.NewFlags = *ParsedFlagSet; + } + + return SR; +} + +static Expected<std::pair<StringRef, uint64_t>> +parseSetSectionAttribute(StringRef Option, StringRef FlagValue) { + if (!FlagValue.contains('=')) + return make_error<StringError>("bad format for " + Option + ": missing '='", + errc::invalid_argument); + auto Split = StringRef(FlagValue).split('='); + if (Split.first.empty()) + return make_error<StringError>("bad format for " + Option + + ": missing section name", + errc::invalid_argument); + uint64_t Value; + if (Split.second.getAsInteger(0, Value)) + return make_error<StringError>("invalid value for " + Option + ": '" + + Split.second + "'", + errc::invalid_argument); + return std::make_pair(Split.first, Value); +} + +static Expected<SectionFlagsUpdate> +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<StringRef, 6> SectionFlags; + Section2Flags.second.split(SectionFlags, ','); + Expected<SectionFlag> 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<MachineInfo> 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<TargetInfo> +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<Error(Error)> ErrorCallback) { + StringSaver Saver(Alloc); + SmallVector<StringRef, 16> 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<StringRef> &SymbolsToRename, + BumpPtrAllocator &Alloc, + StringRef Filename) { + StringSaver Saver(Alloc); + SmallVector<StringRef, 16> 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<StringRef, StringRef> 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 <class T> static ErrorOr<T> 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<NewSymbolInfo> parseNewSymbolInfo(StringRef FlagValue) { + // Parse value given with --add-symbol option and create the + // new symbol if possible. The value format for --add-symbol is: + // + // <name>=[<section>:]<value>[,<flags>] + // + // where: + // <name> - symbol name, can be empty string + // <section> - optional section name. If not given ABS symbol is created + // <value> - symbol value, can be decimal or hexadecimal number prefixed + // with 0x. + // <flags> - 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<StringRef, 6> 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<void()>; + SmallVector<StringRef, 6> UnsupportedFlags; + for (size_t I = 1, NumFlags = Flags.size(); I < NumFlags; ++I) + static_cast<Functor>( + StringSwitch<Functor>(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<NewSectionInfo> &NewSections) { + if (!ArgValue.contains('=')) + return createStringError(errc::invalid_argument, + "bad format for " + OptionName + ": missing '='"); + + std::pair<StringRef, StringRef> SecPair = ArgValue.split("="); + if (SecPair.second.empty()) + return createStringError(errc::invalid_argument, "bad format for " + + OptionName + + ": missing file name"); + + ErrorOr<std::unique_ptr<MemoryBuffer>> 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<DriverConfig> +objcopy::parseObjcopyOptions(ArrayRef<const char *> RawArgsArr, + function_ref<Error(Error)> ErrorCallback) { + DriverConfig DC; + ObjcopyOptTable T; + + const char *const *DashDash = + llvm::find_if(RawArgsArr, [](StringRef Str) { return Str == "--"; }); + ArrayRef<const char *> 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<const char *, 2> 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<FileFormat>(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<uint8_t>(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<unsigned>(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<FileFormat>(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<TargetInfo> 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<DebugCompressionType>(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<SectionRename> 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<std::pair<StringRef, uint64_t>> 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<SectionFlagsUpdate> 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<std::pair<StringRef, uint64_t>> 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<NewSymbolInfo> 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<uint64_t>(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<int64_t>(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<DriverConfig> +objcopy::parseInstallNameToolOptions(ArrayRef<const char *> 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<StringRef, StringRef>::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<StringRef, 2> 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<DriverConfig> +objcopy::parseBitcodeStripOptions(ArrayRef<const char *> ArgsArr, + function_ref<Error(Error)> 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<StringRef, 2> 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<DriverConfig> +objcopy::parseStripOptions(ArrayRef<const char *> RawArgsArr, + function_ref<Error(Error)> ErrorCallback) { + const char *const *DashDash = + llvm::find_if(RawArgsArr, [](StringRef Str) { return Str == "--"; }); + ArrayRef<const char *> 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<StringRef, 2> 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<unsigned> 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); +} diff --git a/contrib/llvm-project/llvm/tools/llvm-objcopy/ObjcopyOptions.h b/contrib/llvm-project/llvm/tools/llvm-objcopy/ObjcopyOptions.h new file mode 100644 index 000000000000..f7fa2af304d7 --- /dev/null +++ b/contrib/llvm-project/llvm/tools/llvm-objcopy/ObjcopyOptions.h @@ -0,0 +1,58 @@ +//===- ObjcopyOptions.h ---------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_OBJCOPY_OBJCOPYOPTIONS_H +#define LLVM_TOOLS_LLVM_OBJCOPY_OBJCOPYOPTIONS_H + +#include "llvm/ObjCopy/ConfigManager.h" +#include "llvm/Support/Allocator.h" +#include <vector> + +namespace llvm { +namespace objcopy { + +// Configuration for the overall invocation of this tool. When invoked as +// objcopy, will always contain exactly one CopyConfig. When invoked as strip, +// will contain one or more CopyConfigs. +struct DriverConfig { + SmallVector<ConfigManager, 1> CopyConfigs; + BumpPtrAllocator Alloc; +}; + +// ParseObjcopyOptions returns the config and sets the input arguments. If a +// help flag is set then ParseObjcopyOptions will print the help messege and +// exit. ErrorCallback is used to handle recoverable errors. An Error returned +// by the callback aborts the parsing and is then returned by this function. +Expected<DriverConfig> +parseObjcopyOptions(ArrayRef<const char *> ArgsArr, + llvm::function_ref<Error(Error)> ErrorCallback); + +// 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<DriverConfig> +parseInstallNameToolOptions(ArrayRef<const char *> ArgsArr); + +// ParseBitcodeStripOptions returns the config and sets the input arguments. +// If a help flag is set then ParseBitcodeStripOptions will print the help +// messege and exit. +Expected<DriverConfig> +parseBitcodeStripOptions(ArrayRef<const char *> ArgsArr, + llvm::function_ref<Error(Error)> ErrorCallback); + +// ParseStripOptions returns the config and sets the input arguments. If a +// help flag is set then ParseStripOptions will print the help messege and +// exit. ErrorCallback is used to handle recoverable errors. An Error returned +// by the callback aborts the parsing and is then returned by this function. +Expected<DriverConfig> +parseStripOptions(ArrayRef<const char *> ArgsArr, + llvm::function_ref<Error(Error)> ErrorCallback); +} // namespace objcopy +} // namespace llvm + +#endif // LLVM_TOOLS_LLVM_OBJCOPY_OBJCOPYOPTIONS_H diff --git a/contrib/llvm-project/llvm/tools/llvm-objcopy/ObjcopyOpts.td b/contrib/llvm-project/llvm/tools/llvm-objcopy/ObjcopyOpts.td new file mode 100644 index 000000000000..0fddd443a4cc --- /dev/null +++ b/contrib/llvm-project/llvm/tools/llvm-objcopy/ObjcopyOpts.td @@ -0,0 +1,232 @@ +include "CommonOpts.td" + +defm binary_architecture + : Eq<"binary-architecture", "Ignored for compatibility">; +def B : JoinedOrSeparate<["-"], "B">, + Alias<binary_architecture>, + HelpText<"Alias for --binary-architecture">; + +defm target : Eq<"target", "Format of the input and output file">, + Values<"binary">; +def F : JoinedOrSeparate<["-"], "F">, + Alias<target>, + HelpText<"Alias for --target">; + +defm input_target : Eq<"input-target", "Format of the input file">, + Values<"binary">; +def I : JoinedOrSeparate<["-"], "I">, + Alias<input_target>, + HelpText<"Alias for --input-target">; + +defm output_target : Eq<"output-target", "Format of the output file">, + Values<"binary">; +def O : JoinedOrSeparate<["-"], "O">, + Alias<output_target>, + HelpText<"Alias for --output-target">; + +defm new_symbol_visibility : Eq<"new-symbol-visibility", "Visibility of " + "symbols generated for binary input or added" + " with --add-symbol unless otherwise" + " specified. The default value is 'default'.">; + +def compress_debug_sections + : Joined<["--"], "compress-debug-sections=">, + MetaVarName<"format">, + HelpText<"Compress DWARF debug sections using specified format. Supported " + "formats: zlib, zstd. Select zlib if <format> is omitted">; +def : Flag<["--"], "compress-debug-sections">, Alias<compress_debug_sections>, + AliasArgs<["zlib"]>; +def decompress_debug_sections : Flag<["--"], "decompress-debug-sections">, + HelpText<"Decompress DWARF debug sections.">; +defm split_dwo + : Eq<"split-dwo", "Equivalent to extract-dwo on the input file to " + "<dwo-file>, then strip-dwo on the input file">, + MetaVarName<"dwo-file">; + +defm add_gnu_debuglink + : Eq<"add-gnu-debuglink", "Add a .gnu_debuglink for <debug-file>">, + MetaVarName<"debug-file">; + +defm rename_section + : Eq<"rename-section", + "Renames a section from old to new, optionally with specified flags. " + "Flags supported for GNU compatibility: alloc, load, noload, " + "readonly, exclude, debug, code, data, rom, share, contents, merge, " + "strings.">, + MetaVarName<"old=new[,flag1,...]">; +defm redefine_symbol + : Eq<"redefine-sym", "Change the name of a symbol old to new">, + MetaVarName<"old=new">; +defm redefine_symbols + : Eq<"redefine-syms", + "Reads a list of symbol pairs from <filename> and runs as if " + "--redefine-sym=<old>=<new> is set for each one. <filename> " + "contains two symbols per line separated with whitespace and may " + "contain comments beginning with '#'. Leading and trailing " + "whitespace is stripped from each line. May be repeated to read " + "symbols from many files.">, + MetaVarName<"filename">; + +defm only_section : Eq<"only-section", "Remove all but <section>">, + MetaVarName<"section">; +def j : JoinedOrSeparate<["-"], "j">, + Alias<only_section>, + HelpText<"Alias for --only-section">; +defm add_section + : Eq<"add-section", + "Make a section named <section> with the contents of <file>.">, + MetaVarName<"section=file">; + +defm set_section_alignment + : Eq<"set-section-alignment", "Set alignment for a given section.">, + MetaVarName<"section=align">; + +defm set_section_flags + : Eq<"set-section-flags", + "Set section flags for a given section. Flags supported for GNU " + "compatibility: alloc, load, noload, readonly, exclude, debug, code, " + "data, rom, share, contents, merge, strings.">, + MetaVarName<"section=flag1[,flag2,...]">; + +defm set_section_type + : Eq<"set-section-type", + "Set the type of section <section> to the integer <type>">, + MetaVarName<"section=type">; + +def S : Flag<["-"], "S">, + Alias<strip_all>, + HelpText<"Alias for --strip-all">; +def strip_dwo : Flag<["--"], "strip-dwo">, + HelpText<"Remove all DWARF .dwo sections from file">; +def strip_non_alloc + : Flag<["--"], "strip-non-alloc">, + HelpText<"Remove all non-allocated sections outside segments">; +defm strip_unneeded_symbol + : Eq<"strip-unneeded-symbol", + "Remove symbol <symbol> if it is not needed by relocations">, + MetaVarName<"symbol">; +defm strip_unneeded_symbols + : Eq<"strip-unneeded-symbols", + "Reads a list of symbols from <filename> and removes them " + "if they are not needed by relocations">, + MetaVarName<"filename">; + +defm subsystem + : Eq<"subsystem", + "Set PE subsystem and version">, + MetaVarName<"name[:version]">; + +def extract_dwo + : Flag<["--"], "extract-dwo">, + HelpText< + "Remove all sections that are not DWARF .dwo sections from file">; + +defm extract_partition + : Eq<"extract-partition", "Extract named partition from input file">, + MetaVarName<"name">; +def extract_main_partition + : Flag<["--"], "extract-main-partition">, + HelpText<"Extract main partition from the input file">; + +def localize_hidden + : Flag<["--"], "localize-hidden">, + HelpText< + "Mark all symbols that have hidden or internal visibility as local">; +defm localize_symbol : Eq<"localize-symbol", "Mark <symbol> as local">, + MetaVarName<"symbol">; +defm localize_symbols + : Eq<"localize-symbols", + "Reads a list of symbols from <filename> and marks them local.">, + MetaVarName<"filename">; + +def L : JoinedOrSeparate<["-"], "L">, + Alias<localize_symbol>, + HelpText<"Alias for --localize-symbol">; + +defm globalize_symbol : Eq<"globalize-symbol", "Mark <symbol> as global">, + MetaVarName<"symbol">; + +defm globalize_symbols + : Eq<"globalize-symbols", + "Reads a list of symbols from <filename> and marks them global.">, + MetaVarName<"filename">; + +defm keep_global_symbol + : Eq<"keep-global-symbol", + "Convert all symbols except <symbol> to local. May be repeated to " + "convert all except a set of symbols to local.">, + MetaVarName<"symbol">; +def G : JoinedOrSeparate<["-"], "G">, + Alias<keep_global_symbol>, + HelpText<"Alias for --keep-global-symbol">; + +defm keep_global_symbols + : Eq<"keep-global-symbols", + "Reads a list of symbols from <filename> and runs as if " + "--keep-global-symbol=<symbol> is set for each one. <filename> " + "contains one symbol per line and may contain comments beginning with " + "'#'. Leading and trailing whitespace is stripped from each line. May " + "be repeated to read symbols from many files.">, + MetaVarName<"filename">; + +defm weaken_symbol : Eq<"weaken-symbol", "Mark <symbol> as weak">, + MetaVarName<"symbol">; +defm weaken_symbols + : Eq<"weaken-symbols", + "Reads a list of symbols from <filename> and marks them weak.">, + MetaVarName<"filename">; + +def W : JoinedOrSeparate<["-"], "W">, + Alias<weaken_symbol>, + HelpText<"Alias for --weaken-symbol">; +def weaken : Flag<["--"], "weaken">, + HelpText<"Mark all global symbols as weak">; + +defm strip_symbols + : Eq<"strip-symbols", + "Reads a list of symbols from <filename> and removes them.">, + MetaVarName<"filename">; + +defm keep_symbols + : Eq<"keep-symbols", + "Reads a list of symbols from <filename> and runs as if " + "--keep-symbol=<symbol> is set for each one. <filename> " + "contains one symbol per line and may contain comments beginning with " + "'#'. Leading and trailing whitespace is stripped from each line. May " + "be repeated to read symbols from many files.">, + MetaVarName<"filename">; + +defm dump_section + : Eq<"dump-section", + "Dump contents of section named <section> into file <file>">, + MetaVarName<"section=file">; +defm prefix_symbols + : Eq<"prefix-symbols", "Add <prefix> to the start of every symbol name">, + MetaVarName<"prefix">; + +defm prefix_alloc_sections + : Eq<"prefix-alloc-sections", "Add <prefix> to the start of every allocated section name">, + MetaVarName<"prefix">; + +defm set_start : Eq<"set-start", "Set the start address to <addr>. Overrides " + "any previous --change-start or --adjust-start values.">, + MetaVarName<"addr">; +defm change_start : Eq<"change-start", "Add <incr> to the start address. Can be " + "specified multiple times, all values will be applied " + "cumulatively.">, + MetaVarName<"incr">; +def adjust_start : JoinedOrSeparate<["--"], "adjust-start">, + Alias<change_start>, + HelpText<"Alias for --change-start">; + +defm add_symbol + : Eq<"add-symbol", "Add new symbol <name> to .symtab. Accepted flags: " + "global, local, weak, default, hidden, protected, file, section, object, " + "function, indirect-function. Accepted but ignored for " + "compatibility: debug, constructor, warning, indirect, synthetic, " + "unique-object, before.">, + MetaVarName<"name=[section:]value[,flags]">; + +defm update_section + : Eq<"update-section", "Replace the contents of section <name> with contents from a file <file>.">, + MetaVarName<"name=file">; diff --git a/contrib/llvm-project/llvm/tools/llvm-objcopy/StripOpts.td b/contrib/llvm-project/llvm/tools/llvm-objcopy/StripOpts.td new file mode 100644 index 000000000000..001da23528d7 --- /dev/null +++ b/contrib/llvm-project/llvm/tools/llvm-objcopy/StripOpts.td @@ -0,0 +1,20 @@ +include "CommonOpts.td" + +def output : JoinedOrSeparate<["-"], "o">, HelpText<"Write output to <file>">, + MetaVarName<"<file>">; + +def s : Flag<["-"], "s">, + Alias<strip_all>, + HelpText<"Alias for --strip-all">; +def no_strip_all : Flag<["--"], "no-strip-all">, + HelpText<"Disable --strip-all">; + +def d : Flag<["-"], "d">, + Alias<strip_debug>, + HelpText<"Alias for --strip-debug">; +def S : Flag<["-"], "S">, + Alias<strip_debug>, + HelpText<"Alias for --strip-debug">; + +def strip_swift_symbols : Flag<["-"], "T">, + HelpText<"Remove Swift symbols">; diff --git a/contrib/llvm-project/llvm/tools/llvm-objcopy/llvm-objcopy.cpp b/contrib/llvm-project/llvm/tools/llvm-objcopy/llvm-objcopy.cpp new file mode 100644 index 000000000000..a24cd889b83f --- /dev/null +++ b/contrib/llvm-project/llvm/tools/llvm-objcopy/llvm-objcopy.cpp @@ -0,0 +1,259 @@ +//===- llvm-objcopy.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/STLExtras.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Twine.h" +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/ObjCopy/COFF/COFFConfig.h" +#include "llvm/ObjCopy/COFF/COFFObjcopy.h" +#include "llvm/ObjCopy/CommonConfig.h" +#include "llvm/ObjCopy/ELF/ELFConfig.h" +#include "llvm/ObjCopy/ELF/ELFObjcopy.h" +#include "llvm/ObjCopy/MachO/MachOConfig.h" +#include "llvm/ObjCopy/MachO/MachOObjcopy.h" +#include "llvm/ObjCopy/ObjCopy.h" +#include "llvm/ObjCopy/wasm/WasmConfig.h" +#include "llvm/ObjCopy/wasm/WasmObjcopy.h" +#include "llvm/Object/Archive.h" +#include "llvm/Object/ArchiveWriter.h" +#include "llvm/Object/Binary.h" +#include "llvm/Object/COFF.h" +#include "llvm/Object/ELFObjectFile.h" +#include "llvm/Object/ELFTypes.h" +#include "llvm/Object/Error.h" +#include "llvm/Object/MachO.h" +#include "llvm/Object/MachOUniversal.h" +#include "llvm/Object/Wasm.h" +#include "llvm/Option/Arg.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Option/Option.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/ErrorOr.h" +#include "llvm/Support/FileUtilities.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/InitLLVM.h" +#include "llvm/Support/Memory.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Process.h" +#include "llvm/Support/SmallVectorMemoryBuffer.h" +#include "llvm/Support/StringSaver.h" +#include "llvm/Support/WithColor.h" +#include "llvm/Support/raw_ostream.h" +#include <algorithm> +#include <cassert> +#include <cstdlib> +#include <memory> +#include <string> +#include <system_error> +#include <utility> + +using namespace llvm; +using namespace llvm::objcopy; +using namespace llvm::object; + +// The name this program was invoked as. +static StringRef ToolName; + +static ErrorSuccess reportWarning(Error E) { + assert(E); + WithColor::warning(errs(), ToolName) << toString(std::move(E)) << '\n'; + return Error::success(); +} + +static Expected<DriverConfig> getDriverConfig(ArrayRef<const char *> Args) { + StringRef Stem = sys::path::stem(ToolName); + auto Is = [=](StringRef Tool) { + // We need to recognize the following filenames: + // + // llvm-objcopy -> objcopy + // strip-10.exe -> strip + // powerpc64-unknown-freebsd13-objcopy -> objcopy + // llvm-install-name-tool -> install-name-tool + auto I = Stem.rfind_insensitive(Tool); + return I != StringRef::npos && + (I + Tool.size() == Stem.size() || !isAlnum(Stem[I + Tool.size()])); + }; + + if (Is("bitcode-strip") || Is("bitcode_strip")) + return parseBitcodeStripOptions(Args, reportWarning); + else if (Is("strip")) + return parseStripOptions(Args, reportWarning); + else if (Is("install-name-tool") || Is("install_name_tool")) + return parseInstallNameToolOptions(Args); + else + return parseObjcopyOptions(Args, reportWarning); +} + +/// The function executeObjcopyOnIHex does the dispatch based on the format +/// of the output specified by the command line options. +static Error executeObjcopyOnIHex(ConfigManager &ConfigMgr, MemoryBuffer &In, + raw_ostream &Out) { + // TODO: support output formats other than ELF. + Expected<const ELFConfig &> ELFConfig = ConfigMgr.getELFConfig(); + if (!ELFConfig) + return ELFConfig.takeError(); + + return elf::executeObjcopyOnIHex(ConfigMgr.getCommonConfig(), *ELFConfig, In, + Out); +} + +/// The function executeObjcopyOnRawBinary does the dispatch based on the format +/// of the output specified by the command line options. +static Error executeObjcopyOnRawBinary(ConfigManager &ConfigMgr, + MemoryBuffer &In, raw_ostream &Out) { + const CommonConfig &Config = ConfigMgr.getCommonConfig(); + switch (Config.OutputFormat) { + case FileFormat::ELF: + // FIXME: Currently, we call elf::executeObjcopyOnRawBinary even if the + // output format is binary/ihex or it's not given. This behavior differs from + // GNU objcopy. See https://bugs.llvm.org/show_bug.cgi?id=42171 for details. + case FileFormat::Binary: + case FileFormat::IHex: + case FileFormat::Unspecified: + Expected<const ELFConfig &> ELFConfig = ConfigMgr.getELFConfig(); + if (!ELFConfig) + return ELFConfig.takeError(); + + return elf::executeObjcopyOnRawBinary(Config, *ELFConfig, In, Out); + } + + llvm_unreachable("unsupported output format"); +} + +/// The function executeObjcopy does the higher level dispatch based on the type +/// of input (raw binary, archive or single object file) and takes care of the +/// format-agnostic modifications, i.e. preserving dates. +static Error executeObjcopy(ConfigManager &ConfigMgr) { + CommonConfig &Config = ConfigMgr.Common; + + Expected<FilePermissionsApplier> PermsApplierOrErr = + FilePermissionsApplier::create(Config.InputFilename); + if (!PermsApplierOrErr) + return PermsApplierOrErr.takeError(); + + std::function<Error(raw_ostream & OutFile)> ObjcopyFunc; + + OwningBinary<llvm::object::Binary> BinaryHolder; + std::unique_ptr<MemoryBuffer> MemoryBufferHolder; + + if (Config.InputFormat == FileFormat::Binary || + Config.InputFormat == FileFormat::IHex) { + ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr = + MemoryBuffer::getFileOrSTDIN(Config.InputFilename); + if (!BufOrErr) + return createFileError(Config.InputFilename, BufOrErr.getError()); + MemoryBufferHolder = std::move(*BufOrErr); + + if (Config.InputFormat == FileFormat::Binary) + ObjcopyFunc = [&](raw_ostream &OutFile) -> Error { + // Handle FileFormat::Binary. + return executeObjcopyOnRawBinary(ConfigMgr, *MemoryBufferHolder, + OutFile); + }; + else + ObjcopyFunc = [&](raw_ostream &OutFile) -> Error { + // Handle FileFormat::IHex. + return executeObjcopyOnIHex(ConfigMgr, *MemoryBufferHolder, OutFile); + }; + } else { + Expected<OwningBinary<llvm::object::Binary>> BinaryOrErr = + createBinary(Config.InputFilename); + if (!BinaryOrErr) + return createFileError(Config.InputFilename, BinaryOrErr.takeError()); + BinaryHolder = std::move(*BinaryOrErr); + + if (Archive *Ar = dyn_cast<Archive>(BinaryHolder.getBinary())) { + // Handle Archive. + if (Error E = executeObjcopyOnArchive(ConfigMgr, *Ar)) + return E; + } else { + // Handle llvm::object::Binary. + ObjcopyFunc = [&](raw_ostream &OutFile) -> Error { + return executeObjcopyOnBinary(ConfigMgr, *BinaryHolder.getBinary(), + OutFile); + }; + } + } + + if (ObjcopyFunc) { + if (Config.SplitDWO.empty()) { + // Apply transformations described by Config and store result into + // Config.OutputFilename using specified ObjcopyFunc function. + if (Error E = writeToOutput(Config.OutputFilename, ObjcopyFunc)) + return E; + } else { + Config.ExtractDWO = true; + Config.StripDWO = false; + // Copy .dwo tables from the Config.InputFilename into Config.SplitDWO + // file using specified ObjcopyFunc function. + if (Error E = writeToOutput(Config.SplitDWO, ObjcopyFunc)) + return E; + Config.ExtractDWO = false; + Config.StripDWO = true; + // Apply transformations described by Config, remove .dwo tables and + // store result into Config.OutputFilename using specified ObjcopyFunc + // function. + if (Error E = writeToOutput(Config.OutputFilename, ObjcopyFunc)) + return E; + } + } + + if (Error E = + PermsApplierOrErr->apply(Config.OutputFilename, Config.PreserveDates)) + return E; + + if (!Config.SplitDWO.empty()) + if (Error E = + PermsApplierOrErr->apply(Config.SplitDWO, Config.PreserveDates, + static_cast<sys::fs::perms>(0666))) + return E; + + return Error::success(); +} + +int llvm_objcopy_main(int argc, char **argv) { + InitLLVM X(argc, argv); + ToolName = argv[0]; + + // Expand response files. + // TODO: Move these lines, which are copied from lib/Support/CommandLine.cpp, + // into a separate function in the CommandLine library and call that function + // here. This is duplicated code. + SmallVector<const char *, 20> NewArgv(argv, argv + argc); + BumpPtrAllocator A; + StringSaver Saver(A); + cl::ExpandResponseFiles(Saver, + Triple(sys::getProcessTriple()).isOSWindows() + ? cl::TokenizeWindowsCommandLine + : cl::TokenizeGNUCommandLine, + NewArgv); + + auto Args = ArrayRef(NewArgv).drop_front(); + Expected<DriverConfig> DriverConfig = getDriverConfig(Args); + + if (!DriverConfig) { + logAllUnhandledErrors(DriverConfig.takeError(), + WithColor::error(errs(), ToolName)); + return 1; + } + for (ConfigManager &ConfigMgr : DriverConfig->CopyConfigs) { + if (Error E = executeObjcopy(ConfigMgr)) { + logAllUnhandledErrors(std::move(E), WithColor::error(errs(), ToolName)); + return 1; + } + } + + return 0; +} |
