Index: test/tools/llvm-objcopy/ELF/add-symbol.test =================================================================== --- test/tools/llvm-objcopy/ELF/add-symbol.test +++ test/tools/llvm-objcopy/ELF/add-symbol.test @@ -0,0 +1,73 @@ +# 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 + +!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 Index: tools/llvm-objcopy/COFF/COFFObjcopy.cpp =================================================================== --- tools/llvm-objcopy/COFF/COFFObjcopy.cpp +++ 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: tools/llvm-objcopy/CopyConfig.h =================================================================== --- tools/llvm-objcopy/CopyConfig.h +++ 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/Regex.h" // Necessary for llvm::DebugCompressionType::None @@ -60,6 +61,15 @@ bool operator!=(StringRef S) const { return !operator==(S); } }; +struct NewSymbolInfo { + StringRef SymbolName; + StringRef SectionName; + unsigned long long 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 @@ -85,6 +95,7 @@ // Repeated options std::vector AddSection; std::vector DumpSection; + std::vector SymbolsToAdd; std::vector KeepSection; std::vector OnlySection; std::vector SymbolsToGlobalize; Index: tools/llvm-objcopy/CopyConfig.cpp =================================================================== --- tools/llvm-objcopy/CopyConfig.cpp +++ 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" @@ -192,6 +191,80 @@ return SFU; } +static bool ignoredAddSymbolFlag(StringRef Flag) { + return Flag.equals_lower("debug") || Flag.equals_lower("constructor") || + Flag.equals_lower("warning") || Flag.equals_lower("indirect") || + Flag.equals_lower("synthetic") || Flag.equals_lower("unique-object") || + Flag.split('=').first.equals_lower("before"); +} + +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 silently 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, ','); + getAsUnsignedInteger(Flags[0], 0, SI.Value); + + size_t NumFlags = Flags.size(); + for (size_t I = 1; I < NumFlags; ++I) + if (Flags[I].equals_lower("global")) + SI.Bind = ELF::STB_GLOBAL; + else if (Flags[I].equals_lower("local")) + SI.Bind = ELF::STB_LOCAL; + else if (Flags[I].equals_lower("weak")) + SI.Bind = ELF::STB_WEAK; + else if (Flags[I].equals_lower("default")) + SI.Visibility = ELF::STV_DEFAULT; + else if (Flags[I].equals_lower("hidden")) + SI.Visibility = ELF::STV_HIDDEN; + else if (Flags[I].equals_lower("file")) + SI.Type = ELF::STT_FILE; + else if (Flags[I].equals_lower("section")) + SI.Type = ELF::STT_SECTION; + else if (Flags[I].equals_lower("object")) + SI.Type = ELF::STT_OBJECT; + else if (Flags[I].equals_lower("function")) + SI.Type = ELF::STT_FUNC; + else if (Flags[I].equals_lower("indirect-function")) + SI.Type = ELF::STT_GNU_IFUNC; + else if (!ignoredAddSymbolFlag(Flags[I])) + error("unsupported flag '" + Flags[I] + "' for --add-symbol"); + + return SI; +} + static const StringMap ArchMap{ // Name, {EMachine, 64bit, LittleEndian} {"aarch64", {ELF::EM_AARCH64, true, true}}, @@ -475,6 +548,8 @@ Arg->getValue(), UseRegex); 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: tools/llvm-objcopy/ELF/ELFObjcopy.cpp =================================================================== --- tools/llvm-objcopy/ELF/ELFObjcopy.cpp +++ tools/llvm-objcopy/ELF/ELFObjcopy.cpp @@ -566,6 +566,13 @@ 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, SHN_ABS, 0); + } + return Error::success(); } Index: tools/llvm-objcopy/ELF/Object.h =================================================================== --- tools/llvm-objcopy/ELF/Object.h +++ tools/llvm-objcopy/ELF/Object.h @@ -800,6 +800,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: tools/llvm-objcopy/ObjcopyOpts.td =================================================================== --- tools/llvm-objcopy/ObjcopyOpts.td +++ tools/llvm-objcopy/ObjcopyOpts.td @@ -237,3 +237,7 @@ def regex : Flag<["-", "--"], "regex">, HelpText<"Permit regular expressions in name comparison">; + +defm add_symbol + : Eq<"add-symbol", "Add new symbol to .symtab.">, + MetaVarName<"name=[section:]value[,flags]">;