diff --git a/llvm/tools/llvm-objcopy/CommonConfig.h b/llvm/tools/llvm-objcopy/CommonConfig.h --- a/llvm/tools/llvm-objcopy/CommonConfig.h +++ b/llvm/tools/llvm-objcopy/CommonConfig.h @@ -141,6 +141,20 @@ bool empty() const { return PosMatchers.empty() && NegMatchers.empty(); } }; +// Symbol info specified by --add-symbol option. Flags is a vector of strings. +// Possible values for the strings are: "global", "local", "weak", "default", +// "hidden", "protected", "file", "section", "object", "function", +// "indirect-function", "debug", "constructor", "warning", "indirect", +// "synthetic", "unique-object". Values not supported on a concrete platform +// should be ignored. +struct NewSymbolInfo { + StringRef SymbolName; + StringRef SectionName; + uint64_t Value = 0; + std::vector Flags; + std::vector BeforeSyms; +}; + // Configuration for copying/stripping a single file. struct CommonConfig { // Main input/output options @@ -200,6 +214,9 @@ // --change-start is used. std::function EntryExpr; + // Symbol info specified by --add-symbol option. + std::vector SymbolsToAdd; + // Boolean options bool AllowBrokenLinks = false; bool DeterministicArchives = true; diff --git a/llvm/tools/llvm-objcopy/ConfigManager.h b/llvm/tools/llvm-objcopy/ConfigManager.h --- a/llvm/tools/llvm-objcopy/ConfigManager.h +++ b/llvm/tools/llvm-objcopy/ConfigManager.h @@ -32,13 +32,9 @@ Expected getMachOConfig() const override; Expected getWasmConfig() const override; - // String representation for lazy ELF options. - std::vector SymbolsToAdd; - Optional NewSymbolVisibility; - // All configs. CommonConfig Common; - mutable Optional ELF; + ELFConfig ELF; COFFConfig COFF; MachOConfig MachO; WasmConfig Wasm; diff --git a/llvm/tools/llvm-objcopy/ConfigManager.cpp b/llvm/tools/llvm-objcopy/ConfigManager.cpp --- a/llvm/tools/llvm-objcopy/ConfigManager.cpp +++ b/llvm/tools/llvm-objcopy/ConfigManager.cpp @@ -466,8 +466,7 @@ OS << "\nPass @FILE as argument to read options from FILE.\n"; } -static Expected parseNewSymbolInfo(StringRef FlagValue, - uint8_t DefaultVisibility) { +static Expected parseNewSymbolInfo(StringRef FlagValue) { // Parse value given with --add-symbol option and create the // new symbol if possible. The value format for --add-symbol is: // @@ -478,17 +477,7 @@ //
- 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=. + // - optional flags affecting symbol type, binding or visibility. NewSymbolInfo SI; StringRef Value; std::tie(SI.SymbolName, Value) = FlagValue.split('='); @@ -512,86 +501,69 @@ return createStringError(errc::invalid_argument, "bad symbol value: '%s'", Flags[0].str().c_str()); - SI.Visibility = DefaultVisibility; - using Functor = std::function; + using Functor = std::function; + + Functor StoreSupportedFlag = [&](StringRef Flag) { + SI.Flags.push_back(Flag); + }; + + Functor StoreBeforeSymNameFlag = [&](StringRef Flag) { + StringRef BeforePart; + StringRef SymNamePart; + std::tie(BeforePart, SymNamePart) = Flag.split('='); + + if (!SymNamePart.empty()) + SI.BeforeSyms.push_back(SymNamePart); + }; + SmallVector UnsupportedFlags; + Functor StoreUnsupportedFlag = [&](StringRef Flag) { + UnsupportedFlags.push_back(Flag); + }; + 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]); }))(); + static_cast(StringSwitch(Flags[I]) + .CaseLower("global", StoreSupportedFlag) + .CaseLower("local", StoreSupportedFlag) + .CaseLower("weak", StoreSupportedFlag) + .CaseLower("default", StoreSupportedFlag) + .CaseLower("hidden", StoreSupportedFlag) + .CaseLower("protected", StoreSupportedFlag) + .CaseLower("file", StoreSupportedFlag) + .CaseLower("section", StoreSupportedFlag) + .CaseLower("object", StoreSupportedFlag) + .CaseLower("function", StoreSupportedFlag) + .CaseLower("indirect-function", StoreSupportedFlag) + .CaseLower("debug", StoreSupportedFlag) + .CaseLower("constructor", StoreSupportedFlag) + .CaseLower("warning", StoreSupportedFlag) + .CaseLower("indirect", StoreSupportedFlag) + .CaseLower("synthetic", StoreSupportedFlag) + .CaseLower("unique-object", StoreSupportedFlag) + .StartsWithLower("before=", StoreBeforeSymNameFlag) + .Default(StoreUnsupportedFlag))(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 ConfigManager::getELFConfig() const { - if (!ELF) { - if (Common.StripSwiftSymbols || Common.KeepUndefined) - return createStringError(llvm::errc::invalid_argument, - "option not supported by llvm-objcopy for ELF"); - - // Parse lazy options. - ELFConfig ResConfig; - - if (NewSymbolVisibility) { - const uint8_t Invalid = 0xff; - ResConfig.NewSymbolVisibility = - StringSwitch(*NewSymbolVisibility) - .Case("default", ELF::STV_DEFAULT) - .Case("hidden", ELF::STV_HIDDEN) - .Case("internal", ELF::STV_INTERNAL) - .Case("protected", ELF::STV_PROTECTED) - .Default(Invalid); - - if (ResConfig.NewSymbolVisibility == Invalid) - return createStringError(errc::invalid_argument, - "'%s' is not a valid symbol visibility", - NewSymbolVisibility->str().c_str()); - } - - for (StringRef Arg : SymbolsToAdd) { - Expected NSI = parseNewSymbolInfo( - Arg, - ResConfig.NewSymbolVisibility.getValueOr((uint8_t)ELF::STV_DEFAULT)); - if (!NSI) - return NSI.takeError(); - ResConfig.SymbolsToAdd.push_back(*NSI); - } - - ELF = std::move(ResConfig); - } + if (Common.StripSwiftSymbols || Common.KeepUndefined) + return createStringError(llvm::errc::invalid_argument, + "option not supported by llvm-objcopy for ELF"); - return *ELF; + return ELF; } Expected ConfigManager::getCOFFConfig() const { if (Common.AllowBrokenLinks || !Common.SplitDWO.empty() || !Common.SymbolsPrefix.empty() || !Common.AllocSectionsPrefix.empty() || !Common.DumpSection.empty() || !Common.KeepSection.empty() || - NewSymbolVisibility || !Common.SymbolsToGlobalize.empty() || + ELF.NewSymbolVisibility || !Common.SymbolsToGlobalize.empty() || !Common.SymbolsToKeep.empty() || !Common.SymbolsToLocalize.empty() || !Common.SymbolsToWeaken.empty() || !Common.SymbolsToKeepGlobal.empty() || !Common.SectionsToRename.empty() || !Common.SetSectionAlignment.empty() || @@ -599,8 +571,8 @@ Common.StripDWO || Common.StripNonAlloc || Common.StripSections || Common.StripSwiftSymbols || Common.KeepUndefined || Common.Weaken || Common.DecompressDebugSections || - Common.DiscardMode == DiscardType::Locals || !SymbolsToAdd.empty() || - Common.EntryExpr) { + Common.DiscardMode == DiscardType::Locals || + !Common.SymbolsToAdd.empty() || Common.EntryExpr) { return createStringError(llvm::errc::invalid_argument, "option not supported by llvm-objcopy for COFF"); } @@ -611,7 +583,7 @@ Expected ConfigManager::getMachOConfig() const { if (Common.AllowBrokenLinks || !Common.SplitDWO.empty() || !Common.SymbolsPrefix.empty() || !Common.AllocSectionsPrefix.empty() || - !Common.KeepSection.empty() || NewSymbolVisibility || + !Common.KeepSection.empty() || ELF.NewSymbolVisibility || !Common.SymbolsToGlobalize.empty() || !Common.SymbolsToKeep.empty() || !Common.SymbolsToLocalize.empty() || !Common.SymbolsToWeaken.empty() || !Common.SymbolsToKeepGlobal.empty() || !Common.SectionsToRename.empty() || @@ -621,7 +593,7 @@ Common.StripAllGNU || Common.StripDWO || Common.StripNonAlloc || Common.StripSections || Common.Weaken || Common.DecompressDebugSections || Common.StripUnneeded || Common.DiscardMode == DiscardType::Locals || - !SymbolsToAdd.empty() || Common.EntryExpr) { + !Common.SymbolsToAdd.empty() || Common.EntryExpr) { return createStringError(llvm::errc::invalid_argument, "option not supported by llvm-objcopy for MachO"); } @@ -633,8 +605,8 @@ if (!Common.AddGnuDebugLink.empty() || Common.ExtractPartition || !Common.SplitDWO.empty() || !Common.SymbolsPrefix.empty() || !Common.AllocSectionsPrefix.empty() || - Common.DiscardMode != DiscardType::None || NewSymbolVisibility || - !SymbolsToAdd.empty() || !Common.RPathToAdd.empty() || + Common.DiscardMode != DiscardType::None || ELF.NewSymbolVisibility || + !Common.SymbolsToAdd.empty() || !Common.RPathToAdd.empty() || !Common.OnlySection.empty() || !Common.SymbolsToGlobalize.empty() || !Common.SymbolsToKeep.empty() || !Common.SymbolsToLocalize.empty() || !Common.SymbolsToRemove.empty() || @@ -705,6 +677,7 @@ ConfigManager ConfigMgr; CommonConfig &Config = ConfigMgr.Common; + ELFConfig &ELFConfig = ConfigMgr.ELF; Config.InputFilename = Positional[0]; Config.OutputFilename = Positional[Positional.size() == 1 ? 0 : 1]; if (InputArgs.hasArg(OBJCOPY_target) && @@ -743,10 +716,24 @@ .Case("ihex", FileFormat::IHex) .Default(FileFormat::Unspecified); - if (InputArgs.hasArg(OBJCOPY_new_symbol_visibility)) - ConfigMgr.NewSymbolVisibility = + if (InputArgs.hasArg(OBJCOPY_new_symbol_visibility)) { + const uint8_t Invalid = 0xff; + StringRef VisibilityStr = InputArgs.getLastArgValue(OBJCOPY_new_symbol_visibility); + ELFConfig.NewSymbolVisibility = StringSwitch(VisibilityStr) + .Case("default", ELF::STV_DEFAULT) + .Case("hidden", ELF::STV_HIDDEN) + .Case("internal", ELF::STV_INTERNAL) + .Case("protected", ELF::STV_PROTECTED) + .Default(Invalid); + + if (ELFConfig.NewSymbolVisibility == Invalid) + return createStringError(errc::invalid_argument, + "'%s' is not a valid symbol visibility", + VisibilityStr.str().c_str()); + } + Config.OutputFormat = StringSwitch(OutputFormat) .Case("binary", FileFormat::Binary) .Case("ihex", FileFormat::IHex) @@ -992,8 +979,13 @@ addSymbolsFromFile(Config.SymbolsToKeep, DC.Alloc, Arg->getValue(), SymbolMatchStyle, ErrorCallback)) return std::move(E); - for (auto Arg : InputArgs.filtered(OBJCOPY_add_symbol)) - ConfigMgr.SymbolsToAdd.push_back(Arg->getValue()); + for (auto *Arg : InputArgs.filtered(OBJCOPY_add_symbol)) { + Expected SymInfo = parseNewSymbolInfo(Arg->getValue()); + if (!SymInfo) + return SymInfo.takeError(); + + Config.SymbolsToAdd.push_back(*SymInfo); + } Config.AllowBrokenLinks = InputArgs.hasArg(OBJCOPY_allow_broken_links); diff --git a/llvm/tools/llvm-objcopy/ELF/ELFConfig.h b/llvm/tools/llvm-objcopy/ELF/ELFConfig.h --- a/llvm/tools/llvm-objcopy/ELF/ELFConfig.h +++ b/llvm/tools/llvm-objcopy/ELF/ELFConfig.h @@ -17,19 +17,9 @@ namespace llvm { namespace objcopy { -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; -}; - // ELF specific configuration for copying/stripping a single file. struct ELFConfig { - Optional NewSymbolVisibility; - std::vector SymbolsToAdd; + uint8_t NewSymbolVisibility = (uint8_t)ELF::STV_DEFAULT; }; } // namespace objcopy diff --git a/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp b/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp --- a/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp +++ b/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp @@ -500,6 +500,39 @@ return Obj.removeSections(Config.AllowBrokenLinks, RemovePred); } +// Add symbol to the Object symbol table according to NewSymbolInfo SymInfo +// descriptor. +static void addSymbol(Object &Obj, const NewSymbolInfo &SymInfo, + uint8_t DefaultVisibility) { + SectionBase *Sec = Obj.findSection(SymInfo.SectionName); + uint64_t Value = Sec ? Sec->Addr + SymInfo.Value : SymInfo.Value; + + uint8_t Bind = ELF::STB_GLOBAL; + uint8_t Type = ELF::STT_NOTYPE; + uint8_t Visibility = DefaultVisibility; + using Functor = std::function; + + for (StringRef FlagValue : SymInfo.Flags) + static_cast( + StringSwitch(FlagValue) + .CaseLower("global", [&] { Bind = ELF::STB_GLOBAL; }) + .CaseLower("local", [&] { Bind = ELF::STB_LOCAL; }) + .CaseLower("weak", [&] { Bind = ELF::STB_WEAK; }) + .CaseLower("default", [&] { Visibility = ELF::STV_DEFAULT; }) + .CaseLower("hidden", [&] { Visibility = ELF::STV_HIDDEN; }) + .CaseLower("protected", [&] { Visibility = ELF::STV_PROTECTED; }) + .CaseLower("file", [&] { Type = ELF::STT_FILE; }) + .CaseLower("section", [&] { Type = ELF::STT_SECTION; }) + .CaseLower("object", [&] { Type = ELF::STT_OBJECT; }) + .CaseLower("function", [&] { Type = ELF::STT_FUNC; }) + .CaseLower("indirect-function", [&] { Type = ELF::STT_GNU_IFUNC; }) + .Default([&] { /* Other flag values are ignored for ELF. */ }))(); + + Obj.SymbolTable->addSymbol( + SymInfo.SymbolName, Bind, Type, Sec, Value, Visibility, + Sec ? (uint16_t)SYMBOL_SIMPLE_INDEX : (uint16_t)SHN_ABS, 0); +} + // This function handles the high level operations of GNU objcopy including // handling command line options. It's important to outline certain properties // we expect to hold of the command line operations. Any operation that "keeps" @@ -633,17 +666,12 @@ // If the symbol table was previously removed, we need to create a new one // before adding new symbols. - if (!Obj.SymbolTable && !ELFConfig.SymbolsToAdd.empty()) + if (!Obj.SymbolTable && !Config.SymbolsToAdd.empty()) if (Error E = Obj.addNewSymbolTable()) return E; - for (const NewSymbolInfo &SI : ELFConfig.SymbolsToAdd) { - SectionBase *Sec = Obj.findSection(SI.SectionName); - uint64_t Value = Sec ? Sec->Addr + SI.Value : SI.Value; - Obj.SymbolTable->addSymbol( - SI.SymbolName, SI.Bind, SI.Type, Sec, Value, SI.Visibility, - Sec ? (uint16_t)SYMBOL_SIMPLE_INDEX : (uint16_t)SHN_ABS, 0); - } + for (const NewSymbolInfo &SI : Config.SymbolsToAdd) + addSymbol(Obj, SI, ELFConfig.NewSymbolVisibility); // --set-section-flags works with sections added by --add-section. if (!Config.SetSectionFlags.empty()) { @@ -688,9 +716,7 @@ Error executeObjcopyOnRawBinary(const CommonConfig &Config, const ELFConfig &ELFConfig, MemoryBuffer &In, raw_ostream &Out) { - uint8_t NewSymbolVisibility = - ELFConfig.NewSymbolVisibility.getValueOr((uint8_t)ELF::STV_DEFAULT); - BinaryReader Reader(&In, NewSymbolVisibility); + BinaryReader Reader(&In, ELFConfig.NewSymbolVisibility); Expected> Obj = Reader.create(true); if (!Obj) return Obj.takeError(); @@ -709,7 +735,7 @@ object::ELFObjectFileBase &In, raw_ostream &Out) { ELFReader Reader(&In, Config.ExtractPartition); Expected> Obj = - Reader.create(!ELFConfig.SymbolsToAdd.empty()); + Reader.create(!Config.SymbolsToAdd.empty()); if (!Obj) return Obj.takeError(); // Prefer OutputArch (-O) if set, otherwise infer it from the input.