Skip to content

Commit

Permalink
Write a .dynamic section to generated ELF files
Browse files Browse the repository at this point in the history
Summary:
Doesn't include everything needed to make a valid ELF object, for instance the
.hash section and associated dynamic item is missing.  But it is successfully
loaded with dlopen() now.

Reviewed By: swtaarrs

Differential Revision: D56472682

fbshipit-source-id: ae78c65d993ed873b0d7c9973ce710cc365c5616
  • Loading branch information
Alex Malyshev authored and facebook-github-bot committed Apr 29, 2024
1 parent c26dde2 commit 53c58ac
Show file tree
Hide file tree
Showing 2 changed files with 127 additions and 3 deletions.
77 changes: 75 additions & 2 deletions cinderx/Jit/elf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,25 @@ void initDynstrSection(Object& elf) {
elf.section_offset += header.size;
}

void initDynamicSection(Object& elf) {
JIT_CHECK(
isAligned(elf.section_offset, kPageSize),
"Dynamic section starts at unaligned address {:#x}",
elf.section_offset);

SectionHeader& header = elf.getSectionHeader(SectionIdx::kDynamic);
header.name_offset = elf.shstrtab.insert(".dynamic");
header.type = kDynamic;
header.flags = kSectionAlloc | kSectionWritable;
header.address = elf.section_offset;
header.offset = elf.section_offset;
header.size = elf.dynamic.bytes().size();
header.link = raw(SectionIdx::kDynstr);
header.entry_size = sizeof(Dyn);

elf.section_offset += header.size;
}

void initShstrtabSection(Object& elf) {
SectionHeader& header = elf.getSectionHeader(SectionIdx::kShstrtab);
header.name_offset = elf.shstrtab.insert(".shstrtab");
Expand All @@ -138,7 +157,7 @@ void initTextSegment(Object& elf) {

// The .text section immediately follows all the ELF headers.
SegmentHeader& header = elf.getSegmentHeader(SegmentIdx::kText);
header.type = kLoadableSegment;
header.type = kSegmentLoadable;
header.flags = kSegmentExecutable | kSegmentReadable;
header.offset = section.offset;
header.address = section.address;
Expand All @@ -157,7 +176,7 @@ void initReadonlySegment(Object& elf) {
"Expecting sections to be in a specific order");

SegmentHeader& header = elf.getSegmentHeader(SegmentIdx::kReadonly);
header.type = kLoadableSegment;
header.type = kSegmentLoadable;
header.flags = kSegmentReadable;
header.offset = dynsym.offset;
header.address = dynsym.address;
Expand All @@ -168,6 +187,48 @@ void initReadonlySegment(Object& elf) {
checkAlignedSegment(header);
}

void initReadwriteSegment(Object& elf) {
SectionHeader& dynamic = elf.getSectionHeader(SectionIdx::kDynamic);

SegmentHeader& header = elf.getSegmentHeader(SegmentIdx::kReadwrite);
header.type = kSegmentLoadable;
header.flags = kSegmentReadable | kSegmentWritable;
header.offset = dynamic.offset;
header.address = dynamic.address;
header.file_size = dynamic.size;
header.mem_size = header.file_size;
header.align = 0x1000;

checkAlignedSegment(header);
}

void initDynamicSegment(Object& elf) {
SectionHeader& dynamic = elf.getSectionHeader(SectionIdx::kDynamic);

SegmentHeader& header = elf.getSegmentHeader(SegmentIdx::kDynamic);
header.type = kSegmentDynamic;
header.flags = kSegmentReadable | kSegmentWritable;
header.offset = dynamic.offset;
header.address = dynamic.address;
header.file_size = dynamic.size;
header.mem_size = header.file_size;
header.align = 0x1000;
}

void initDynamics(Object& elf) {
// Has to be run after .dynsym and .dynstr are mapped out.
SectionHeader& dynsym = elf.getSectionHeader(SectionIdx::kDynsym);
SectionHeader& dynstr = elf.getSectionHeader(SectionIdx::kDynstr);

// TODO(T183002717): kNeeded for _cinderx.so and kHash for .hash.
elf.dynamic.insert(DynTag::kNeeded, elf.libpython_name);

elf.dynamic.insert(DynTag::kStrtab, dynstr.address);
elf.dynamic.insert(DynTag::kStrSz, dynstr.size);
elf.dynamic.insert(DynTag::kSymtab, dynsym.address);
elf.dynamic.insert(DynTag::kSymEnt, sizeof(Symbol));
}

template <class T>
void write(std::ostream& os, T* data, size_t size) {
os.write(reinterpret_cast<const char*>(data), size);
Expand Down Expand Up @@ -207,6 +268,8 @@ void writeEntries(std::ostream& os, const std::vector<CodeEntry>& entries) {
}
uint64_t text_size = text_end_address - kTextStartAddress;

elf.libpython_name = elf.dynstr.insert("libpython3.10.so");

// The headers are all limited to the zeroth page, sections begin on the next
// page.
elf.section_offset = offsetof(Object, header_stop);
Expand All @@ -223,10 +286,17 @@ void writeEntries(std::ostream& os, const std::vector<CodeEntry>& entries) {

initDynsymSection(elf);
initDynstrSection(elf);
uint64_t dynsym_padding = alignOffset(elf, kPageSize);

initDynamics(elf);

initDynamicSection(elf);
initShstrtabSection(elf);

initTextSegment(elf);
initReadonlySegment(elf);
initReadwriteSegment(elf);
initDynamicSegment(elf);

// Write out all the headers.
write(os, &elf.file_header, sizeof(elf.file_header));
Expand All @@ -242,6 +312,9 @@ void writeEntries(std::ostream& os, const std::vector<CodeEntry>& entries) {

write(os, elf.dynsym.bytes());
write(os, elf.dynstr.bytes());
pad(os, dynsym_padding);

write(os, elf.dynamic.bytes());
write(os, elf.shstrtab.bytes());
}

Expand Down
53 changes: 52 additions & 1 deletion cinderx/Jit/elf.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,21 @@ namespace jit::elf {
constexpr uint32_t kProgram = 0x01;
constexpr uint32_t kSymbolTable = 0x02;
constexpr uint32_t kStringTable = 0x03;
constexpr uint32_t kDynamic = 0x06;

// Section header flags.
constexpr uint64_t kSectionWritable = 0x01;
constexpr uint64_t kSectionAlloc = 0x02;
constexpr uint64_t kSectionExecutable = 0x04;
constexpr uint64_t kSectionInfoLink = 0x40;

// Segment header types.
constexpr uint32_t kLoadableSegment = 0x1;
constexpr uint32_t kSegmentLoadable = 0x1;
constexpr uint32_t kSegmentDynamic = 0x2;

// Segment header flags.
constexpr uint32_t kSegmentExecutable = 0x1;
constexpr uint32_t kSegmentWritable = 0x2;
constexpr uint32_t kSegmentReadable = 0x4;

// Symbol flags.
Expand All @@ -45,6 +49,7 @@ enum class SectionIdx : uint32_t {
kText = 1,
kDynsym,
kDynstr,
kDynamic,
kShstrtab,
kTotal,
};
Expand All @@ -53,6 +58,8 @@ enum class SectionIdx : uint32_t {
enum class SegmentIdx : uint32_t {
kText,
kReadonly,
kReadwrite,
kDynamic,
kTotal,
};

Expand Down Expand Up @@ -248,6 +255,48 @@ class SymbolTable {
std::vector<Symbol> syms_;
};

enum class DynTag : uint64_t {
kNull = 0,
kNeeded = 1,
kHash = 4,
kStrtab = 5,
kSymtab = 6,
kStrSz = 10,
kSymEnt = 11,
};

struct Dyn {
Dyn() = default;
Dyn(DynTag tag, uint64_t val) : tag{tag}, val{val} {}

DynTag tag{DynTag::kNull};
uint64_t val{0};
};

class DynamicTable {
public:
DynamicTable() {
// Table must always end with a null dynamic item.
dyns_.emplace_back();
}

template <class... Args>
void insert(Args&&... args) {
dyns_.emplace_back(std::forward<Args>(args)...);
// Always swap the null item back to the end.
auto const len = dyns_.size();
JIT_DCHECK(len >= 2, "DynamicTable missing its required null item");
std::swap(dyns_[len - 1], dyns_[len - 2]);
}

std::span<const std::byte> bytes() const {
return std::as_bytes(std::span{dyns_});
}

private:
std::vector<Dyn> dyns_;
};

// Represents an ELF object/file.
//
// The headers are laid out in the exact order that they will appear in the
Expand All @@ -263,9 +312,11 @@ struct Object {

SymbolTable dynsym;
StringTable dynstr;
DynamicTable dynamic;
StringTable shstrtab;

uint32_t section_offset{0};
uint32_t libpython_name{0};

SectionHeader& getSectionHeader(SectionIdx idx) {
return section_headers[raw(idx)];
Expand Down

0 comments on commit 53c58ac

Please sign in to comment.