diff --git a/llvm/lib/InterfaceStub/ELFObjHandler.cpp b/llvm/lib/InterfaceStub/ELFObjHandler.cpp --- a/llvm/lib/InterfaceStub/ELFObjHandler.cpp +++ b/llvm/lib/InterfaceStub/ELFObjHandler.cpp @@ -335,6 +335,89 @@ write(Data + shdrOffset(Sec), Sec.Shdr); } }; + +/// This function takes an error, and appends a string of text to the end of +/// that error. Since "appending" to an Error isn't supported behavior of an +/// Error, this function technically creates a new error with the combined +/// message and consumes the old error. +/// +/// @param Err Source error. +/// @param After Text to append at the end of Err's error message. +Error appendToError(Error Err, StringRef After) { + std::string Message; + raw_string_ostream Stream(Message); + Stream << Err; + Stream << " " << After; + consumeError(std::move(Err)); + return createError(Stream.str()); +} + +template class DynSym { + using Elf_Shdr_Range = typename ELFT::ShdrRange; + using Elf_Shdr = typename ELFT::Shdr; + +public: + static Expected create(const ELFFile &ElfFile, + const DynamicEntries &DynEnt) { + Expected Shdrs = ElfFile.sections(); + if (!Shdrs) + return Shdrs.takeError(); + return DynSym(ElfFile, DynEnt, *Shdrs); + } + + Expected getDynSym() { + if (DynSymHdr) + return ElfFile.base() + DynSymHdr->sh_offset; + return getDynamicData(DynEnt.DynSymAddr, "dynamic symbol table"); + } + + Expected getDynStr() { + if (DynSymHdr) + return ElfFile.getStringTableForSymtab(*DynSymHdr, Shdrs); + Expected DataOrErr = getDynamicData( + DynEnt.StrTabAddr, "dynamic string table", DynEnt.StrSize); + if (!DataOrErr) + return DataOrErr.takeError(); + return StringRef(reinterpret_cast(*DataOrErr), + DynEnt.StrSize); + } + +private: + DynSym(const ELFFile &ElfFile, const DynamicEntries &DynEnt, + Elf_Shdr_Range Shdrs) + : ElfFile(ElfFile), DynEnt(DynEnt), Shdrs(Shdrs), + DynSymHdr(findDynSymHdr()) {} + + const Elf_Shdr *findDynSymHdr() { + for (const Elf_Shdr &Sec : Shdrs) + if (Sec.sh_type == SHT_DYNSYM) { + // If multiple .dynsym are present, use the first one. + // This behavior aligns with llvm::object::ELFFile::getDynSymtabSize() + return &Sec; + } + return nullptr; + } + + Expected getDynamicData(uint64_t EntAddr, StringRef Name, + uint64_t Size = 0) { + Expected SecPtr = ElfFile.toMappedAddr(EntAddr); + if (!SecPtr) + return appendToError( + SecPtr.takeError(), + ("when locating " + Name + " section contents").str()); + Expected SecEndPtr = ElfFile.toMappedAddr(EntAddr + Size); + if (!SecEndPtr) + return appendToError( + SecEndPtr.takeError(), + ("when locating " + Name + " section contents").str()); + return *SecPtr; + } + + const ELFFile &ElfFile; + const DynamicEntries &DynEnt; + Elf_Shdr_Range Shdrs; + const Elf_Shdr *DynSymHdr; +}; } // end anonymous namespace /// This function behaves similarly to StringRef::substr(), but attempts to @@ -354,22 +437,6 @@ return Str.substr(Offset, StrLen); } -/// This function takes an error, and appends a string of text to the end of -/// that error. Since "appending" to an Error isn't supported behavior of an -/// Error, this function technically creates a new error with the combined -/// message and consumes the old error. -/// -/// @param Err Source error. -/// @param After Text to append at the end of Err's error message. -Error appendToError(Error Err, StringRef After) { - std::string Message; - raw_string_ostream Stream(Message); - Stream << Err; - Stream << " " << After; - consumeError(std::move(Err)); - return createError(Stream.str()); -} - /// This function populates a DynamicEntries struct using an ELFT::DynRange. /// After populating the struct, the members are validated with /// some basic correctness checks. @@ -508,7 +575,6 @@ static Expected> buildStub(const ELFObjectFile &ElfObj) { using Elf_Dyn_Range = typename ELFT::DynRange; - using Elf_Phdr_Range = typename ELFT::PhdrRange; using Elf_Sym_Range = typename ELFT::SymRange; using Elf_Sym = typename ELFT::Sym; std::unique_ptr DestStub = std::make_unique(); @@ -519,25 +585,19 @@ return DynTable.takeError(); } - // Fetch program headers. - Expected PHdrs = ElfFile.program_headers(); - if (!PHdrs) { - return PHdrs.takeError(); - } - // Collect relevant .dynamic entries. DynamicEntries DynEnt; if (Error Err = populateDynamic(DynEnt, *DynTable)) return std::move(Err); + Expected> EDynSym = DynSym::create(ElfFile, DynEnt); + if (!EDynSym) + return EDynSym.takeError(); - // Get pointer to in-memory location of .dynstr section. - Expected DynStrPtr = ElfFile.toMappedAddr(DynEnt.StrTabAddr); - if (!DynStrPtr) - return appendToError(DynStrPtr.takeError(), - "when locating .dynstr section contents"); + Expected EDynStr = EDynSym->getDynStr(); + if (!EDynStr) + return EDynStr.takeError(); - StringRef DynStr(reinterpret_cast(DynStrPtr.get()), - DynEnt.StrSize); + StringRef DynStr = *EDynStr; // Populate Arch from ELF header. DestStub->Target.Arch = static_cast(ElfFile.getHeader().e_machine); @@ -573,8 +633,7 @@ return SymCount.takeError(); if (*SymCount > 0) { // Get pointer to in-memory location of .dynsym section. - Expected DynSymPtr = - ElfFile.toMappedAddr(DynEnt.DynSymAddr); + Expected DynSymPtr = EDynSym->getDynSym(); if (!DynSymPtr) return appendToError(DynSymPtr.takeError(), "when locating .dynsym section contents"); diff --git a/llvm/test/tools/llvm-ifs/binary-read-bad-vaddr.test b/llvm/test/tools/llvm-ifs/binary-read-bad-vaddr.test --- a/llvm/test/tools/llvm-ifs/binary-read-bad-vaddr.test +++ b/llvm/test/tools/llvm-ifs/binary-read-bad-vaddr.test @@ -44,4 +44,4 @@ FirstSec: .dynamic LastSec: .dynamic -# CHECK: virtual address is not in any segment: 0x260 when locating .dynstr section contents +# CHECK: virtual address is not in any segment: 0x260 when locating dynamic string table section contents diff --git a/llvm/test/tools/llvm-ifs/ifs-elf-conversion.test b/llvm/test/tools/llvm-ifs/ifs-elf-conversion.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-ifs/ifs-elf-conversion.test @@ -0,0 +1,24 @@ +## Test writing stub ELF from IFS and read stub ELF to regenerate IFS. + +# RUN: llvm-ifs --output-elf=%t.elf64l --arch=x86_64 --bitwidth=64 --endianness=little %s +# RUN: llvm-ifs --output-ifs=- --strip-ifs-target %t.elf64l | FileCheck %s + +--- !ifs-v1 +IfsVersion: 3.0 +NeededLibs: + - libc.so.6 +Symbols: + - { Name: bar, Type: Object, Size: 42 } + - { Name: baz, Type: TLS, Size: 3 } + - { Name: plus, Type: Func } +... + +# CHECK: --- !ifs-v1 +# CHECK-NEXT: IfsVersion: 3.0 +# CHECK-NEXT: NeededLibs: +# CHECK-NEXT: - libc.so.6 +# CHECK-NEXT: Symbols: +# CHECK-NEXT: - { Name: bar, Type: Object, Size: 42 } +# CHECK-NEXT: - { Name: baz, Type: TLS, Size: 3 } +# CHECK-NEXT: - { Name: plus, Type: Func } +# CHECK-NEXT: ...