From 8c0d62ecc9f5457cc1cab976b11154fb0e776163 Mon Sep 17 00:00:00 2001 From: Alex Malyshev Date: Tue, 16 Apr 2024 09:54:56 -0700 Subject: [PATCH] Write ELF with .dynsym and .dynstr, in a new readonly segment Summary: Making it closer to what a proper ELF file would look like. Properly aligns the segments as well. Differential Revision: D56079908 fbshipit-source-id: 4eb2d3bd4a47fb57bf8b0587112f709b5c8dd367 --- cinderx/Jit/elf.cpp | 146 +++++++++++++++++++++++++++++++------------- cinderx/Jit/elf.h | 10 +-- 2 files changed, 110 insertions(+), 46 deletions(-) diff --git a/cinderx/Jit/elf.cpp b/cinderx/Jit/elf.cpp index 73835cb4a6d..4116e4e70c5 100644 --- a/cinderx/Jit/elf.cpp +++ b/cinderx/Jit/elf.cpp @@ -16,8 +16,25 @@ static_assert(sizeof(SectionHeader) == 64); static_assert(sizeof(SegmentHeader) == 56); static_assert(sizeof(FileHeader) == FileHeader{}.header_size); -// TODO(T176630720): This should not be a hardcoded value. -constexpr uint64_t kTextStartAddress = 0x1000000; +constexpr uint64_t kPageSize = 0x1000; + +constexpr uint64_t kTextStartAddress = kPageSize; + +constexpr uint64_t alignUp(uint64_t n) { + uint64_t mask = kPageSize - 1; + return (n + mask) & ~mask; +} + +constexpr bool isAligned(uint64_t n) { + return n == alignUp(n); +} + +uint64_t alignOffset(Object& elf) { + uint64_t new_offset = alignUp(elf.section_offset); + uint64_t delta = new_offset - elf.section_offset; + elf.section_offset = new_offset; + return delta; +} void initFileHeader(Object& elf) { FileHeader& header = elf.file_header; @@ -28,47 +45,41 @@ void initFileHeader(Object& elf) { header.section_name_index = raw(SectionIdx::kShstrtab); } -void initTextSection(Object& elf, const std::vector& entries) { - uint64_t text_end_address = kTextStartAddress; - - for (const CodeEntry& entry : entries) { - Symbol sym; - sym.name_offset = elf.strtab.insert(entry.func_name); - sym.info = kGlobal | kFunc; - sym.section_index = raw(SectionIdx::kText); - sym.address = text_end_address; - sym.size = entry.code.size(); - elf.symtab.insert(std::move(sym)); - - // TODO(T176630885): Not writing the filename or lineno yet. - - text_end_address += entry.code.size(); - } +void initTextSection(Object& elf, uint64_t text_size) { + // Program bits. Occupies memory and is executable. Text follows the section + // header table after some padding. - size_t text_size = text_end_address - kTextStartAddress; + JIT_CHECK( + isAligned(elf.section_offset), + "Text section starts at unaligned address {:#x}", + elf.section_offset); - // Program bits. Occupies memory and is executable. Text immediately follows - // the section header table. SectionHeader& header = elf.getSectionHeader(SectionIdx::kText); header.name_offset = elf.shstrtab.insert(".text"); header.type = kProgram; header.flags = kSectionAlloc | kSectionExecutable; - header.address = kTextStartAddress; + header.address = elf.section_offset; header.offset = elf.section_offset; header.size = text_size; - header.align = 0x1000; + header.align = 0x10; elf.section_offset += header.size; } -void initSymtabSection(Object& elf) { - SectionHeader& header = elf.getSectionHeader(SectionIdx::kSymtab); - header.name_offset = elf.shstrtab.insert(".symtab"); +void initDynsymSection(Object& elf) { + JIT_CHECK( + isAligned(elf.section_offset), + "Dynsym section starts at unaligned address {:#x}", + elf.section_offset); + + SectionHeader& header = elf.getSectionHeader(SectionIdx::kDynsym); + header.name_offset = elf.shstrtab.insert(".dynsym"); header.type = kSymbolTable; - header.flags = kSectionInfoLink; + header.flags = kSectionAlloc | kSectionInfoLink; + header.address = elf.section_offset; header.offset = elf.section_offset; - header.size = elf.symtab.bytes().size(); - header.link = raw(SectionIdx::kStrtab); + header.size = elf.dynsym.bytes().size(); + header.link = raw(SectionIdx::kDynstr); // This is the index of the first global symbol, i.e. the first symbol after // the null symbol. header.info = 1; @@ -77,13 +88,14 @@ void initSymtabSection(Object& elf) { elf.section_offset += header.size; } -void initStrtabSection(Object& elf) { - SectionHeader& header = elf.getSectionHeader(SectionIdx::kStrtab); - header.name_offset = elf.shstrtab.insert(".strtab"); +void initDynstrSection(Object& elf) { + SectionHeader& header = elf.getSectionHeader(SectionIdx::kDynstr); + header.name_offset = elf.shstrtab.insert(".dynstr"); header.type = kStringTable; - header.flags = kSectionStrings; + header.flags = kSectionAlloc; + header.address = elf.section_offset; header.offset = elf.section_offset; - header.size = elf.strtab.bytes().size(); + header.size = elf.dynstr.bytes().size(); elf.section_offset += header.size; } @@ -92,7 +104,6 @@ void initShstrtabSection(Object& elf) { SectionHeader& header = elf.getSectionHeader(SectionIdx::kShstrtab); header.name_offset = elf.shstrtab.insert(".shstrtab"); header.type = kStringTable; - header.flags = kSectionStrings; header.offset = elf.section_offset; header.size = elf.shstrtab.bytes().size(); @@ -113,6 +124,23 @@ void initTextSegment(Object& elf) { header.align = 0x1000; } +void initReadonlySegment(Object& elf) { + SectionHeader& dynsym = elf.getSectionHeader(SectionIdx::kDynsym); + SectionHeader& dynstr = elf.getSectionHeader(SectionIdx::kDynstr); + JIT_CHECK( + dynsym.address < dynstr.address, + "Expecting sections to be in a specific order"); + + SegmentHeader& header = elf.getSegmentHeader(SegmentIdx::kReadonly); + header.type = kLoadableSegment; + header.flags = kSegmentReadable; + header.offset = dynsym.offset; + header.address = dynsym.address; + header.file_size = dynsym.size + dynstr.size; + header.mem_size = header.file_size; + header.align = 0x1000; +} + template void write(std::ostream& os, T* data, size_t size) { os.write(reinterpret_cast(data), size); @@ -123,34 +151,70 @@ void write(std::ostream& os, std::span bytes) { write(os, bytes.data(), bytes.size()); } +void pad(std::ostream& os, size_t size) { + for (size_t i = 0; i < size; ++i) { + os.put(0); + } +} + } // namespace void writeEntries(std::ostream& os, const std::vector& entries) { Object elf; initFileHeader(elf); - // Sections begin after all the headers are written out. + // Initialize symbols before any of the sections. + uint64_t text_end_address = kTextStartAddress; + for (const CodeEntry& entry : entries) { + Symbol sym; + sym.name_offset = elf.dynstr.insert(entry.func_name); + sym.info = kGlobal | kFunc; + sym.section_index = raw(SectionIdx::kText); + sym.address = text_end_address; + sym.size = entry.code.size(); + elf.dynsym.insert(std::move(sym)); + + // TODO(T176630885): Not writing the filename or lineno yet. + + text_end_address += entry.code.size(); + } + uint64_t text_size = text_end_address - kTextStartAddress; + + // The headers are all limited to the zeroth page, sections begin on the next + // page. elf.section_offset = offsetof(Object, header_stop); + uint64_t header_padding = alignOffset(elf); + JIT_CHECK( + elf.section_offset == kTextStartAddress, + "ELF headers were too big and went past the zeroth page: {:#x}", + elf.section_offset); // Null section needs no extra initialization. - initTextSection(elf, entries); - initSymtabSection(elf); - initStrtabSection(elf); + + initTextSection(elf, text_size); + uint64_t text_padding = alignOffset(elf); + + initDynsymSection(elf); + initDynstrSection(elf); initShstrtabSection(elf); initTextSegment(elf); + initReadonlySegment(elf); // Write out all the headers. write(os, &elf.file_header, sizeof(elf.file_header)); write(os, &elf.section_headers, sizeof(elf.section_headers)); write(os, &elf.segment_headers, sizeof(elf.segment_headers)); + pad(os, header_padding); // Write out the actual sections themselves. for (const CodeEntry& entry : entries) { write(os, entry.code.data(), entry.code.size()); } - write(os, elf.symtab.bytes()); - write(os, elf.strtab.bytes()); + pad(os, text_padding); + + write(os, elf.dynsym.bytes()); + write(os, elf.dynstr.bytes()); write(os, elf.shstrtab.bytes()); } diff --git a/cinderx/Jit/elf.h b/cinderx/Jit/elf.h index b68b4cb2bd4..cd110edc3ff 100644 --- a/cinderx/Jit/elf.h +++ b/cinderx/Jit/elf.h @@ -25,7 +25,6 @@ constexpr uint32_t kStringTable = 0x03; // Section header flags. constexpr uint64_t kSectionAlloc = 0x02; constexpr uint64_t kSectionExecutable = 0x04; -constexpr uint64_t kSectionStrings = 0x20; constexpr uint64_t kSectionInfoLink = 0x40; // Segment header types. @@ -44,8 +43,8 @@ enum class SectionIdx : uint32_t { // Null section is index 0. kText = 1, - kSymtab, - kStrtab, + kDynsym, + kDynstr, kShstrtab, kTotal, }; @@ -53,6 +52,7 @@ enum class SectionIdx : uint32_t { // Segment header indices / ordering. enum class SegmentIdx : uint32_t { kText, + kReadonly, kTotal, }; @@ -261,8 +261,8 @@ struct Object { // headers stop. char header_stop[0]; - SymbolTable symtab; - StringTable strtab; + SymbolTable dynsym; + StringTable dynstr; StringTable shstrtab; uint32_t section_offset{0};