Index: llvm/tools/llvm-objcopy/CMakeLists.txt =================================================================== --- llvm/tools/llvm-objcopy/CMakeLists.txt +++ llvm/tools/llvm-objcopy/CMakeLists.txt @@ -21,6 +21,7 @@ COFF/Object.cpp COFF/Reader.cpp COFF/Writer.cpp + ELF/ELFConfig.cpp ELF/ELFObjcopy.cpp ELF/Object.cpp MachO/MachOObjcopy.cpp Index: llvm/tools/llvm-objcopy/CopyConfig.h =================================================================== --- llvm/tools/llvm-objcopy/CopyConfig.h +++ llvm/tools/llvm-objcopy/CopyConfig.h @@ -9,6 +9,7 @@ #ifndef LLVM_TOOLS_LLVM_OBJCOPY_COPY_CONFIG_H #define LLVM_TOOLS_LLVM_OBJCOPY_COPY_CONFIG_H +#include "ELF/ELFConfig.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/BitmaskEnum.h" #include "llvm/ADT/Optional.h" @@ -111,17 +112,11 @@ bool empty() const { return Matchers.empty(); } }; -struct NewSymbolInfo { - StringRef SymbolName; - StringRef SectionName; - uint64_t Value = 0; - uint8_t Type = ELF::STT_NOTYPE; - uint8_t Bind = ELF::STB_GLOBAL; - uint8_t Visibility = ELF::STV_DEFAULT; -}; - // Configuration for copying/stripping a single file. struct CopyConfig { + // Format-specific options to be initialized lazily when needed. + Optional ELF; + // Main input/output options StringRef InputFilename; FileFormat InputFormat; @@ -143,12 +138,12 @@ StringRef SymbolsPrefix; StringRef AllocSectionsPrefix; DiscardType DiscardMode = DiscardType::None; - Optional NewSymbolVisibility; + Optional NewSymbolVisibility; // Repeated options std::vector AddSection; std::vector DumpSection; - std::vector SymbolsToAdd; + std::vector SymbolsToAdd; // Section matchers NameMatcher KeepSection; @@ -194,6 +189,18 @@ bool Weaken = false; bool DecompressDebugSections = false; DebugCompressionType CompressionType = DebugCompressionType::None; + + // parseELFConfig performs ELF-specific command-line parsing. Fills `ELF` on + // success or returns an Error otherwise. + Error parseELFConfig() { + if (!ELF) { + Expected ELFConfig = elf::parseConfig(*this); + if (!ELFConfig) + return ELFConfig.takeError(); + ELF = *ELFConfig; + } + return Error::success(); + } }; // Configuration for the overall invocation of this tool. When invoked as Index: llvm/tools/llvm-objcopy/CopyConfig.cpp =================================================================== --- llvm/tools/llvm-objcopy/CopyConfig.cpp +++ llvm/tools/llvm-objcopy/CopyConfig.cpp @@ -177,87 +177,6 @@ return SFU; } -static Expected parseNewSymbolInfo(StringRef FlagValue, - uint8_t DefaultVisibility) { - // 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()); - - SI.Visibility = DefaultVisibility; - - 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("protected", [&SI] { SI.Visibility = ELF::STV_PROTECTED; }) - .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; -} - struct TargetInfo { FileFormat Format; MachineInfo Machine; @@ -467,20 +386,9 @@ .Case("ihex", FileFormat::IHex) .Default(FileFormat::Unspecified); - if (opt::Arg *A = InputArgs.getLastArg(OBJCOPY_new_symbol_visibility)) { - const uint8_t Invalid = 0xff; - Config.NewSymbolVisibility = StringSwitch(A->getValue()) - .Case("default", ELF::STV_DEFAULT) - .Case("hidden", ELF::STV_HIDDEN) - .Case("internal", ELF::STV_INTERNAL) - .Case("protected", ELF::STV_PROTECTED) - .Default(Invalid); - - if (Config.NewSymbolVisibility == Invalid) - return createStringError( - errc::invalid_argument, "'%s' is not a valid symbol visibility", - InputArgs.getLastArgValue(OBJCOPY_new_symbol_visibility).str().c_str()); - } + if (opt::Arg *A = InputArgs.getLastArg(OBJCOPY_new_symbol_visibility)) + Config.NewSymbolVisibility = + InputArgs.getLastArgValue(OBJCOPY_new_symbol_visibility); Config.OutputFormat = StringSwitch(OutputFormat) .Case("binary", FileFormat::Binary) @@ -694,14 +602,8 @@ if (Error E = addSymbolsFromFile(Config.SymbolsToKeep, DC.Alloc, Arg->getValue(), UseRegex)) return std::move(E); - for (auto Arg : InputArgs.filtered(OBJCOPY_add_symbol)) { - Expected NSI = parseNewSymbolInfo( - Arg->getValue(), - Config.NewSymbolVisibility.getValueOr((uint8_t)ELF::STV_DEFAULT)); - if (!NSI) - return NSI.takeError(); - Config.SymbolsToAdd.push_back(*NSI); - } + for (auto Arg : InputArgs.filtered(OBJCOPY_add_symbol)) + Config.SymbolsToAdd.push_back(Arg->getValue()); Config.AllowBrokenLinks = InputArgs.hasArg(OBJCOPY_allow_broken_links); Index: llvm/tools/llvm-objcopy/ELF/ELFConfig.h =================================================================== --- /dev/null +++ llvm/tools/llvm-objcopy/ELF/ELFConfig.h @@ -0,0 +1,44 @@ +//===- ELFConfig.h ----------------------------------------------*- C++ -*-===// +// +// 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_OBJCOPY_ELFCONFIG_H +#define LLVM_TOOLS_OBJCOPY_ELFCONFIG_H + +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Object/ELFTypes.h" +#include "llvm/Support/Error.h" +#include + +namespace llvm { +namespace objcopy { +struct CopyConfig; + +namespace elf { + +struct NewSymbolInfo { + StringRef SymbolName; + StringRef SectionName; + uint64_t Value = 0; + uint8_t Type = ELF::STT_NOTYPE; + uint8_t Bind = ELF::STB_GLOBAL; + uint8_t Visibility = ELF::STV_DEFAULT; +}; + +struct ELFCopyConfig { + Optional NewSymbolVisibility; + std::vector SymbolsToAdd; +}; + +Expected parseConfig(const CopyConfig &Config); + +} // namespace elf +} // namespace objcopy +} // namespace llvm + +#endif Index: llvm/tools/llvm-objcopy/ELF/ELFConfig.cpp =================================================================== --- /dev/null +++ llvm/tools/llvm-objcopy/ELF/ELFConfig.cpp @@ -0,0 +1,133 @@ +//===- ELFConfig.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/Optional.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/Error.h" + +namespace llvm { +namespace objcopy { +namespace elf { + +static Expected parseNewSymbolInfo(StringRef FlagValue, + uint8_t DefaultVisibility) { + // 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()); + + SI.Visibility = DefaultVisibility; + + 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("protected", + [&SI] { SI.Visibility = ELF::STV_PROTECTED; }) + .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; +} + +Expected parseConfig(const CopyConfig &Config) { + ELFCopyConfig ELFConfig; + if (Config.NewSymbolVisibility) { + const uint8_t Invalid = 0xff; + ELFConfig.NewSymbolVisibility = + StringSwitch(*Config.NewSymbolVisibility) + .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", + Config.NewSymbolVisibility->str().c_str()); + } + + for (StringRef Arg : Config.SymbolsToAdd) { + Expected NSI = parseNewSymbolInfo( + Arg, + ELFConfig.NewSymbolVisibility.getValueOr((uint8_t)ELF::STV_DEFAULT)); + if (!NSI) + return NSI.takeError(); + ELFConfig.SymbolsToAdd.push_back(*NSI); + } + + return ELFConfig; +} + +} // end namespace elf +} // end namespace objcopy +} // end namespace llvm Index: llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp =================================================================== --- llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp +++ llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp @@ -710,7 +710,7 @@ Obj.addSection(Config.AddGnuDebugLink, Config.GnuDebugLinkCRC32); - for (const NewSymbolInfo &SI : Config.SymbolsToAdd) { + for (const NewSymbolInfo &SI : Config.ELF->SymbolsToAdd) { SectionBase *Sec = Obj.findSection(SI.SectionName); uint64_t Value = Sec ? Sec->Addr + SI.Value : SI.Value; Obj.SymbolTable->addSymbol( @@ -746,7 +746,7 @@ Error executeObjcopyOnRawBinary(const CopyConfig &Config, MemoryBuffer &In, Buffer &Out) { uint8_t NewSymbolVisibility = - Config.NewSymbolVisibility.getValueOr((uint8_t)ELF::STV_DEFAULT); + Config.ELF->NewSymbolVisibility.getValueOr((uint8_t)ELF::STV_DEFAULT); BinaryReader Reader(&In, NewSymbolVisibility); std::unique_ptr Obj = Reader.create(); Index: llvm/tools/llvm-objcopy/llvm-objcopy.cpp =================================================================== --- llvm/tools/llvm-objcopy/llvm-objcopy.cpp +++ llvm/tools/llvm-objcopy/llvm-objcopy.cpp @@ -132,16 +132,18 @@ /// The function executeObjcopyOnIHex does the dispatch based on the format /// of the output specified by the command line options. -static Error executeObjcopyOnIHex(const CopyConfig &Config, MemoryBuffer &In, +static Error executeObjcopyOnIHex(CopyConfig &Config, MemoryBuffer &In, Buffer &Out) { // TODO: support output formats other than ELF. + if (Error E = Config.parseELFConfig()) + return E; return elf::executeObjcopyOnIHex(Config, In, Out); } /// The function executeObjcopyOnRawBinary does the dispatch based on the format /// of the output specified by the command line options. -static Error executeObjcopyOnRawBinary(const CopyConfig &Config, - MemoryBuffer &In, Buffer &Out) { +static Error executeObjcopyOnRawBinary(CopyConfig &Config, MemoryBuffer &In, + Buffer &Out) { switch (Config.OutputFormat) { case FileFormat::ELF: // FIXME: Currently, we call elf::executeObjcopyOnRawBinary even if the @@ -150,6 +152,8 @@ case FileFormat::Binary: case FileFormat::IHex: case FileFormat::Unspecified: + if (Error E = Config.parseELFConfig()) + return E; return elf::executeObjcopyOnRawBinary(Config, In, Out); } @@ -158,11 +162,13 @@ /// The function executeObjcopyOnBinary does the dispatch based on the format /// of the input binary (ELF, MachO or COFF). -static Error executeObjcopyOnBinary(const CopyConfig &Config, - object::Binary &In, Buffer &Out) { - if (auto *ELFBinary = dyn_cast(&In)) +static Error executeObjcopyOnBinary(CopyConfig &Config, object::Binary &In, + Buffer &Out) { + if (auto *ELFBinary = dyn_cast(&In)) { + if (Error E = Config.parseELFConfig()) + return E; return elf::executeObjcopyOnBinary(Config, *ELFBinary, Out); - else if (auto *COFFBinary = dyn_cast(&In)) + } else if (auto *COFFBinary = dyn_cast(&In)) return coff::executeObjcopyOnBinary(Config, *COFFBinary, Out); else if (auto *MachOBinary = dyn_cast(&In)) return macho::executeObjcopyOnBinary(Config, *MachOBinary, Out); @@ -171,8 +177,7 @@ "unsupported object file format"); } -static Error executeObjcopyOnArchive(const CopyConfig &Config, - const Archive &Ar) { +static Error executeObjcopyOnArchive(CopyConfig &Config, const Archive &Ar) { std::vector NewArchiveMembers; Error Err = Error::success(); for (const Archive::Child &Child : Ar.children(Err)) { @@ -248,7 +253,7 @@ /// 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(const CopyConfig &Config) { +static Error executeObjcopy(CopyConfig &Config) { sys::fs::file_status Stat; if (Config.InputFilename != "-") { if (auto EC = sys::fs::status(Config.InputFilename, Stat)) @@ -257,7 +262,7 @@ Stat.permissions(static_cast(0777)); } - typedef Error (*ProcessRawFn)(const CopyConfig &, MemoryBuffer &, Buffer &); + using ProcessRawFn = Error (*)(CopyConfig &, MemoryBuffer &, Buffer &); ProcessRawFn ProcessRaw; switch (Config.InputFormat) { case FileFormat::Binary: @@ -336,7 +341,7 @@ WithColor::error(errs(), ToolName)); return 1; } - for (const CopyConfig &CopyConfig : DriverConfig->CopyConfigs) { + for (CopyConfig &CopyConfig : DriverConfig->CopyConfigs) { if (Error E = executeObjcopy(CopyConfig)) { logAllUnhandledErrors(std::move(E), WithColor::error(errs(), ToolName)); return 1;