[ifs] Allow llvm-ifs to generate text stub from elf stub

ELF stubs generated from llvm-ifs lacks program headers, which prevents
llvm-ifs from parsing them properly as program headers are required by
llvm's own ELF libraries. This patch adds a few workaround bypass this
limitation.

Differential Revision: https://reviews.llvm.org/D116769
This commit is contained in:
Haowei Wu 2022-01-11 21:16:49 -08:00
parent a59bb21bf8
commit 15dfe03022
3 changed files with 116 additions and 33 deletions

View File

@ -335,6 +335,89 @@ private:
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 ELFT> class DynSym {
using Elf_Shdr_Range = typename ELFT::ShdrRange;
using Elf_Shdr = typename ELFT::Shdr;
public:
static Expected<DynSym> create(const ELFFile<ELFT> &ElfFile,
const DynamicEntries &DynEnt) {
Expected<Elf_Shdr_Range> Shdrs = ElfFile.sections();
if (!Shdrs)
return Shdrs.takeError();
return DynSym(ElfFile, DynEnt, *Shdrs);
}
Expected<const uint8_t *> getDynSym() {
if (DynSymHdr)
return ElfFile.base() + DynSymHdr->sh_offset;
return getDynamicData(DynEnt.DynSymAddr, "dynamic symbol table");
}
Expected<StringRef> getDynStr() {
if (DynSymHdr)
return ElfFile.getStringTableForSymtab(*DynSymHdr, Shdrs);
Expected<const uint8_t *> DataOrErr = getDynamicData(
DynEnt.StrTabAddr, "dynamic string table", DynEnt.StrSize);
if (!DataOrErr)
return DataOrErr.takeError();
return StringRef(reinterpret_cast<const char *>(*DataOrErr),
DynEnt.StrSize);
}
private:
DynSym(const ELFFile<ELFT> &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<const uint8_t *> getDynamicData(uint64_t EntAddr, StringRef Name,
uint64_t Size = 0) {
Expected<const uint8_t *> SecPtr = ElfFile.toMappedAddr(EntAddr);
if (!SecPtr)
return appendToError(
SecPtr.takeError(),
("when locating " + Name + " section contents").str());
Expected<const uint8_t *> SecEndPtr = ElfFile.toMappedAddr(EntAddr + Size);
if (!SecEndPtr)
return appendToError(
SecEndPtr.takeError(),
("when locating " + Name + " section contents").str());
return *SecPtr;
}
const ELFFile<ELFT> &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 @@ static Expected<StringRef> terminatedSubstr(StringRef Str, size_t Offset) {
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 @@ template <class ELFT>
static Expected<std::unique_ptr<IFSStub>>
buildStub(const ELFObjectFile<ELFT> &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<IFSStub> DestStub = std::make_unique<IFSStub>();
@ -519,25 +585,19 @@ buildStub(const ELFObjectFile<ELFT> &ElfObj) {
return DynTable.takeError();
}
// Fetch program headers.
Expected<Elf_Phdr_Range> PHdrs = ElfFile.program_headers();
if (!PHdrs) {
return PHdrs.takeError();
}
// Collect relevant .dynamic entries.
DynamicEntries DynEnt;
if (Error Err = populateDynamic<ELFT>(DynEnt, *DynTable))
return std::move(Err);
Expected<DynSym<ELFT>> EDynSym = DynSym<ELFT>::create(ElfFile, DynEnt);
if (!EDynSym)
return EDynSym.takeError();
// Get pointer to in-memory location of .dynstr section.
Expected<const uint8_t *> DynStrPtr = ElfFile.toMappedAddr(DynEnt.StrTabAddr);
if (!DynStrPtr)
return appendToError(DynStrPtr.takeError(),
"when locating .dynstr section contents");
Expected<StringRef> EDynStr = EDynSym->getDynStr();
if (!EDynStr)
return EDynStr.takeError();
StringRef DynStr(reinterpret_cast<const char *>(DynStrPtr.get()),
DynEnt.StrSize);
StringRef DynStr = *EDynStr;
// Populate Arch from ELF header.
DestStub->Target.Arch = static_cast<IFSArch>(ElfFile.getHeader().e_machine);
@ -573,8 +633,7 @@ buildStub(const ELFObjectFile<ELFT> &ElfObj) {
return SymCount.takeError();
if (*SymCount > 0) {
// Get pointer to in-memory location of .dynsym section.
Expected<const uint8_t *> DynSymPtr =
ElfFile.toMappedAddr(DynEnt.DynSymAddr);
Expected<const uint8_t *> DynSymPtr = EDynSym->getDynSym();
if (!DynSymPtr)
return appendToError(DynSymPtr.takeError(),
"when locating .dynsym section contents");

View File

@ -44,4 +44,4 @@ ProgramHeaders:
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

View File

@ -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: ...