Index: llvm/trunk/test/tools/llvm-objcopy/ELF/add-symbol.test =================================================================== --- llvm/trunk/test/tools/llvm-objcopy/ELF/add-symbol.test +++ llvm/trunk/test/tools/llvm-objcopy/ELF/add-symbol.test @@ -0,0 +1,76 @@ +# RUN: yaml2obj %s -o %t +# RUN: llvm-objcopy --add-symbol='abs1=1' \ +# RUN: --add-symbol='abs2=2,LoCaL,HiDdEn,FuNcTiOn' \ +# RUN: --add-symbol='abs3=3,global,default,object' \ +# RUN: --add-symbol='foo.cpp=0,file' \ +# RUN: --add-symbol='=.text:0,section' \ +# RUN: --add-symbol='data=.data:0x100,weak' \ +# RUN: --add-symbol='ifunc=.text:0,indirect-function' \ +# RUN: %t %t2 +# RUN: llvm-readelf -s %t2 | FileCheck %s + +# Checked ignored options +# RUN: llvm-objcopy \ +# RUN: --add-symbol='dummy1=0,indirect,constructor,debug,synthetic' \ +# RUN: --add-symbol='dummy2=0,before=foo,unique-object,warning' %t %t3 +# RUN: llvm-readelf -s %t3 | FileCheck %s --check-prefix=IGNORED + +# Check errors +# RUN: not llvm-objcopy --add-symbol='test' %t %t4 2>&1 | FileCheck %s --check-prefix=ERR1 +# RUN: not llvm-objcopy --add-symbol='test=:0' %t %t5 2>&1 | FileCheck %s --check-prefix=ERR2 +# RUN: not llvm-objcopy --add-symbol='test=foo:' %t %t6 2>&1 | FileCheck %s --check-prefix=ERR2 +# RUN: not llvm-objcopy --add-symbol='test=0,cool' %t %t7 2>&1 | FileCheck %s --check-prefix=ERR3 +# RUN: not llvm-objcopy --add-symbol='test=xyz' %t %t8 2>&1 | FileCheck %s --check-prefix=ERR4 + +!ELF +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_ARM +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x1000 + AddressAlign: 0x0000000000001000 + Size: 64 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + Address: 0x2000 + AddressAlign: 0x0000000000001000 + Size: 64 +ProgramHeaders: + - Type: PT_LOAD + Flags: [ PF_X, PF_R ] + VAddr: 0x1000 + PAddr: 0x1000 + Align: 0x1000 + Sections: + - Section: .text + - Type: PT_LOAD + Flags: [ PF_R, PF_W ] + VAddr: 0x2000 + PAddr: 0x2000 + Align: 0x1000 + Sections: + - Section: .data + +# CHECK: 0: 00000000 0 NOTYPE LOCAL DEFAULT UND +# CHECK-NEXT: 1: 00000001 0 NOTYPE GLOBAL DEFAULT ABS abs1 +# CHECK-NEXT: 2: 00000002 0 FUNC LOCAL HIDDEN ABS abs2 +# CHECK-NEXT: 3: 00000003 0 OBJECT GLOBAL DEFAULT ABS abs3 +# CHECK-NEXT: 4: 00000000 0 FILE GLOBAL DEFAULT ABS foo.cpp +# CHECK-NEXT: 5: 00001000 0 SECTION GLOBAL DEFAULT 1 +# CHECK-NEXT: 6: 00002100 0 NOTYPE WEAK DEFAULT 2 data +# CHECK-NEXT: 7: 00001000 0 IFUNC GLOBAL DEFAULT 1 ifunc + +# IGNORED: 1: 00000000 0 NOTYPE GLOBAL DEFAULT ABS dummy1 +# IGNORED-NEXT: 2: 00000000 0 NOTYPE GLOBAL DEFAULT ABS dummy2 + +# ERR1: error: bad format for --add-symbol, missing '=' after 'test' +# ERR2: error: bad format for --add-symbol, missing section name or symbol value +# ERR3: error: unsupported flag 'cool' for --add-symbol +# ERR4: error: bad symbol value: 'xyz' + Index: llvm/trunk/tools/llvm-objcopy/COFF/COFFObjcopy.cpp =================================================================== --- llvm/trunk/tools/llvm-objcopy/COFF/COFFObjcopy.cpp +++ llvm/trunk/tools/llvm-objcopy/COFF/COFFObjcopy.cpp @@ -185,7 +185,8 @@ Config.ExtractDWO || Config.KeepFileSymbols || Config.LocalizeHidden || Config.PreserveDates || Config.StripDWO || Config.StripNonAlloc || Config.StripSections || Config.Weaken || Config.DecompressDebugSections || - Config.DiscardMode == DiscardType::Locals) { + Config.DiscardMode == DiscardType::Locals || + !Config.SymbolsToAdd.empty()) { return createStringError(llvm::errc::invalid_argument, "Option not supported by llvm-objcopy for COFF"); } Index: llvm/trunk/tools/llvm-objcopy/CopyConfig.h =================================================================== --- llvm/trunk/tools/llvm-objcopy/CopyConfig.h +++ llvm/trunk/tools/llvm-objcopy/CopyConfig.h @@ -14,6 +14,7 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Object/ELFTypes.h" #include "llvm/Support/Allocator.h" #include "llvm/Support/Error.h" #include "llvm/Support/Regex.h" @@ -61,6 +62,15 @@ bool operator!=(StringRef S) const { return !operator==(S); } }; +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 { // Main input/output options @@ -86,6 +96,7 @@ // Repeated options std::vector AddSection; std::vector DumpSection; + std::vector SymbolsToAdd; std::vector KeepSection; std::vector OnlySection; std::vector SymbolsToGlobalize; Index: llvm/trunk/tools/llvm-objcopy/CopyConfig.cpp =================================================================== --- llvm/trunk/tools/llvm-objcopy/CopyConfig.cpp +++ llvm/trunk/tools/llvm-objcopy/CopyConfig.cpp @@ -13,7 +13,6 @@ #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" -#include "llvm/Object/ELFTypes.h" #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" #include "llvm/Support/CommandLine.h" @@ -206,6 +205,76 @@ return SFU; } +static 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: + // + // =[
:][,] + // + // 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()) + error("bad format for --add-symbol, missing '=' after '" + SI.SymbolName + + "'"); + + if (Value.contains(':')) { + std::tie(SI.SectionName, Value) = Value.split(':'); + if (SI.SectionName.empty() || Value.empty()) + error( + "bad format for --add-symbol, missing section name or symbol value"); + } + + SmallVector Flags; + Value.split(Flags, ','); + if (Flags[0].getAsInteger(0, SI.Value)) + error("bad symbol value: '" + Flags[0] + "'"); + + typedef std::function Functor; + size_t NumFlags = Flags.size(); + for (size_t I = 1; I < NumFlags; ++I) + static_cast( + StringSwitch(Flags[I]) + .CaseLower("global", [&SI] { SI.Bind = ELF::STB_GLOBAL; }) + .CaseLower("local", [&SI] { SI.Bind = ELF::STB_LOCAL; }) + .CaseLower("weak", [&SI] { SI.Bind = ELF::STB_WEAK; }) + .CaseLower("default", [&SI] { SI.Visibility = ELF::STV_DEFAULT; }) + .CaseLower("hidden", [&SI] { SI.Visibility = ELF::STV_HIDDEN; }) + .CaseLower("file", [&SI] { SI.Type = ELF::STT_FILE; }) + .CaseLower("section", [&SI] { SI.Type = ELF::STT_SECTION; }) + .CaseLower("object", [&SI] { SI.Type = ELF::STT_OBJECT; }) + .CaseLower("function", [&SI] { SI.Type = ELF::STT_FUNC; }) + .CaseLower("indirect-function", + [&SI] { SI.Type = ELF::STT_GNU_IFUNC; }) + .CaseLower("debug", [] {}) + .CaseLower("constructor", [] {}) + .CaseLower("warning", [] {}) + .CaseLower("indirect", [] {}) + .CaseLower("synthetic", [] {}) + .CaseLower("unique-object", [] {}) + .StartsWithLower("before", [] {}) + .Default([&] { + error("unsupported flag '" + Flags[I] + "' for --add-symbol"); + }))(); + return SI; +} + static const StringMap ArchMap{ // Name, {EMachine, 64bit, LittleEndian} {"aarch64", {ELF::EM_AARCH64, true, true}}, @@ -539,6 +608,8 @@ return std::move(E); for (auto Arg : InputArgs.filtered(OBJCOPY_keep_symbol)) Config.SymbolsToKeep.emplace_back(Arg->getValue(), UseRegex); + for (auto Arg : InputArgs.filtered(OBJCOPY_add_symbol)) + Config.SymbolsToAdd.push_back(parseNewSymbolInfo(Arg->getValue())); Config.DeterministicArchives = InputArgs.hasFlag( OBJCOPY_enable_deterministic_archives, Index: llvm/trunk/tools/llvm-objcopy/ELF/ELFObjcopy.cpp =================================================================== --- llvm/trunk/tools/llvm-objcopy/ELF/ELFObjcopy.cpp +++ llvm/trunk/tools/llvm-objcopy/ELF/ELFObjcopy.cpp @@ -566,6 +566,14 @@ if (!Config.AddGnuDebugLink.empty()) Obj.addSection(Config.AddGnuDebugLink); + for (const NewSymbolInfo &SI : Config.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 ? SYMBOL_SIMPLE_INDEX : SHN_ABS, 0); + } + return Error::success(); } Index: llvm/trunk/tools/llvm-objcopy/ELF/Object.h =================================================================== --- llvm/trunk/tools/llvm-objcopy/ELF/Object.h +++ llvm/trunk/tools/llvm-objcopy/ELF/Object.h @@ -805,6 +805,11 @@ ConstRange sections() const { return make_pointee_range(Sections); } + SectionBase *findSection(StringRef Name) { + auto SecIt = + find_if(Sections, [&](const SecPtr &Sec) { return Sec->Name == Name; }); + return SecIt == Sections.end() ? nullptr : SecIt->get(); + } Range segments() { return make_pointee_range(Segments); } ConstRange segments() const { return make_pointee_range(Segments); } Index: llvm/trunk/tools/llvm-objcopy/ObjcopyOpts.td =================================================================== --- llvm/trunk/tools/llvm-objcopy/ObjcopyOpts.td +++ llvm/trunk/tools/llvm-objcopy/ObjcopyOpts.td @@ -237,3 +237,11 @@ def regex : Flag<["-", "--"], "regex">, HelpText<"Permit regular expressions in name comparison">; + +defm add_symbol + : Eq<"add-symbol", "Add new symbol to .symtab. Accepted flags: " + "global, local, weak, default, hidden, file, section, object, " + "function, indirect-function. Accepted but ignored for " + "compatibility: debug, constructor, warning, indirect, synthetic, " + "unique-object, before.">, + MetaVarName<"name=[section:]value[,flags]">;