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,53 @@ +# 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: %t %t2 +# RUN: llvm-readelf -s %t2 | FileCheck %s + +!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 + 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 @@ -60,6 +60,15 @@ bool operator!=(StringRef S) const { return !operator==(S); } }; +struct NewSymbolInfo { + StringRef SymbolName; + StringRef SectionName; + unsigned long long Value; + uint8_t Type; + uint8_t Bind; + uint8_t Visibility; +}; + // Configuration for copying/stripping a single file. struct CopyConfig { // Main input/output options @@ -85,6 +94,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 @@ -192,6 +192,51 @@ return SFU; } +static NewSymbolInfo parseNewSymbolInfo(StringRef FlagValue) { + // Format is: =[
:][,] + NewSymbolInfo SI = { + "", "", 0, ELF::STT_NOTYPE, ELF::STB_GLOBAL, ELF::STV_DEFAULT}; + StringRef Value; + std::tie(SI.SymbolName, Value) = FlagValue.split('='); + if (Value.empty()) + error("bad format for --add-symbol"); + + if (Value.contains(':')) { + std::tie(SI.SectionName, Value) = Value.split(':'); + if (SI.SectionName.empty() || Value.empty()) + error("bad format for --add-symbol"); + } + + 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 + error("unsupported flag for --add-symbol"); + + return SI; +} + static const StringMap ArchMap{ // Name, {EMachine, 64bit, LittleEndian} {"aarch64", {ELF::EM_AARCH64, true, true}}, @@ -475,6 +520,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]">;