// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. #include "vm/elf.h" #include "platform/text_buffer.h" #include "vm/cpu.h" #include "vm/thread.h" namespace dart { #define ELFCLASS32 1 #define ELFCLASS64 2 static const intptr_t ELFDATA2LSB = 1; static const intptr_t ELFOSABI_SYSV = 0; #define EF_ARM_ABI_FLOAT_HARD 0x00000400 #define EF_ARM_ABI_FLOAT_SOFT 0x00000200 #define EF_ARM_ABI 0x05000000 static const intptr_t ET_DYN = 3; #define EM_386 3 #define EM_ARM 40 #define EM_X86_64 62 #define EM_AARCH64 183 static const intptr_t EV_CURRENT = 1; static const intptr_t SHT_PROGBITS = 1; static const intptr_t SHT_STRTAB = 3; static const intptr_t SHT_HASH = 5; static const intptr_t SHT_DYNSYM = 11; static const intptr_t SHT_DYNAMIC = 6; static const intptr_t SHF_WRITE = 0x1; static const intptr_t SHF_ALLOC = 0x2; static const intptr_t SHF_EXECINSTR = 0x4; static const intptr_t SHN_UNDEF = 0; static const intptr_t STN_UNDEF = 0; static const intptr_t PT_LOAD = 1; static const intptr_t PT_DYNAMIC = 2; static const intptr_t PT_PHDR = 6; static const intptr_t PF_X = 1; static const intptr_t PF_W = 2; static const intptr_t PF_R = 4; static const intptr_t STB_GLOBAL = 1; static const intptr_t STT_OBJECT = 1; // I.e., data. static const intptr_t STT_FUNC = 2; static const intptr_t DT_NULL = 0; static const intptr_t DT_HASH = 4; static const intptr_t DT_STRTAB = 5; static const intptr_t DT_SYMTAB = 6; static const intptr_t DT_STRSZ = 10; static const intptr_t DT_SYMENT = 11; #if defined(TARGET_ARCH_IS_32_BIT) static const intptr_t kElfHeaderSize = 52; static const intptr_t kElfSectionTableAlignment = 4; static const intptr_t kElfSectionTableEntrySize = 40; static const intptr_t kElfProgramTableEntrySize = 32; static const intptr_t kElfSymbolTableEntrySize = 16; static const intptr_t kElfDynamicTableEntrySize = 8; static const intptr_t kElfSymbolHashTableEntrySize = 4; #else static const intptr_t kElfHeaderSize = 64; static const intptr_t kElfSectionTableAlignment = 8; static const intptr_t kElfSectionTableEntrySize = 64; static const intptr_t kElfProgramTableEntrySize = 56; static const intptr_t kElfSymbolTableEntrySize = 24; static const intptr_t kElfDynamicTableEntrySize = 16; static const intptr_t kElfSymbolHashTableEntrySize = 4; #endif static const intptr_t kPageSize = 4096; class Section : public ZoneAllocated { public: Section() {} virtual ~Section() {} virtual void Write(Elf* stream) = 0; // Linker view. intptr_t section_name = 0; // Index into string table. intptr_t section_type = 0; intptr_t section_flags = 0; intptr_t section_index = -1; intptr_t section_link = SHN_UNDEF; intptr_t section_info = 0; intptr_t section_entry_size = 0; intptr_t file_size = 0; intptr_t file_offset = -1; intptr_t alignment = 1; // Loader view. intptr_t segment_type = -1; intptr_t segment_flags = 0; intptr_t memory_size = 0; intptr_t memory_offset = -1; }; class ProgramBits : public Section { public: ProgramBits(bool allocate, bool executable, const uint8_t* bytes, intptr_t size) { section_type = SHT_PROGBITS; if (allocate) { section_flags = SHF_ALLOC; if (executable) section_flags |= SHF_EXECINSTR; segment_type = PT_LOAD; segment_flags = PF_R; if (executable) segment_flags |= PF_X; } bytes_ = bytes; file_size = memory_size = size; } void Write(Elf* stream) { stream->WriteBytes(bytes_, memory_size); } const uint8_t* bytes_; }; class StringTable : public Section { public: explicit StringTable(bool allocate) : text_(128) { section_type = SHT_STRTAB; section_flags = allocate ? SHF_ALLOC : 0; segment_type = PT_LOAD; segment_flags = PF_R; text_.AddChar('\0'); memory_size = file_size = text_.length(); } intptr_t AddString(const char* str) { intptr_t offset = text_.length(); text_.AddString(str); text_.AddChar('\0'); memory_size = file_size = text_.length(); return offset; } void Write(Elf* stream) { stream->WriteBytes(reinterpret_cast(text_.buf()), text_.length()); } TextBuffer text_; }; class Symbol : public ZoneAllocated { public: const char* cstr; intptr_t name; intptr_t info; intptr_t section; intptr_t offset; intptr_t size; }; class SymbolTable : public Section { public: SymbolTable() { section_type = SHT_DYNSYM; section_flags = SHF_ALLOC; segment_type = PT_LOAD; segment_flags = PF_R; section_entry_size = kElfSymbolTableEntrySize; AddSymbol(NULL); section_info = 1; // One "local" symbol, the reserved first entry. } void AddSymbol(Symbol* symbol) { symbols_.Add(symbol); memory_size += kElfSymbolTableEntrySize; file_size += kElfSymbolTableEntrySize; } void Write(Elf* stream) { // The first symbol table entry is reserved and must be all zeros. { const intptr_t start = stream->position(); #if defined(TARGET_ARCH_IS_32_BIT) stream->WriteWord(0); stream->WriteAddr(0); stream->WriteWord(0); stream->WriteByte(0); stream->WriteByte(0); stream->WriteHalf(0); #else stream->WriteWord(0); stream->WriteByte(0); stream->WriteByte(0); stream->WriteHalf(0); stream->WriteAddr(0); stream->WriteXWord(0); #endif const intptr_t end = stream->position(); ASSERT((end - start) == kElfSymbolTableEntrySize); } for (intptr_t i = 1; i < symbols_.length(); i++) { Symbol* symbol = symbols_[i]; const intptr_t start = stream->position(); #if defined(TARGET_ARCH_IS_32_BIT) stream->WriteWord(symbol->name); stream->WriteAddr(symbol->offset); stream->WriteWord(symbol->size); stream->WriteByte(symbol->info); stream->WriteByte(0); stream->WriteHalf(symbol->section); #else stream->WriteWord(symbol->name); stream->WriteByte(symbol->info); stream->WriteByte(0); stream->WriteHalf(symbol->section); stream->WriteAddr(symbol->offset); stream->WriteXWord(symbol->size); #endif const intptr_t end = stream->position(); ASSERT((end - start) == kElfSymbolTableEntrySize); } } intptr_t length() const { return symbols_.length(); } Symbol* at(intptr_t i) const { return symbols_[i]; } GrowableArray symbols_; }; static uint32_t ElfHash(const unsigned char* name) { uint32_t h = 0; while (*name) { h = (h << 4) + *name++; uint32_t g = h & 0xf0000000; h ^= g; h ^= g >> 24; } return h; } class SymbolHashTable : public Section { public: SymbolHashTable(StringTable* strtab, SymbolTable* symtab) { section_type = SHT_HASH; section_flags = SHF_ALLOC; section_link = symtab->section_index; section_entry_size = kElfSymbolHashTableEntrySize; segment_type = PT_LOAD; segment_flags = PF_R; nchain_ = symtab->length(); nbucket_ = symtab->length(); bucket_ = Thread::Current()->zone()->Alloc(nbucket_); for (intptr_t i = 0; i < nbucket_; i++) { bucket_[i] = STN_UNDEF; } chain_ = Thread::Current()->zone()->Alloc(nchain_); for (intptr_t i = 0; i < nchain_; i++) { chain_[i] = STN_UNDEF; } for (intptr_t i = 1; i < symtab->length(); i++) { Symbol* symbol = symtab->at(i); uint32_t hash = ElfHash((const unsigned char*)symbol->cstr); uint32_t probe = hash % nbucket_; chain_[i] = bucket_[probe]; // next = head bucket_[probe] = i; // head = symbol } memory_size = file_size = 4 * (nbucket_ + nchain_ + 2); } void Write(Elf* stream) { stream->WriteWord(nbucket_); stream->WriteWord(nchain_); for (intptr_t i = 0; i < nbucket_; i++) { stream->WriteWord(bucket_[i]); } for (intptr_t i = 0; i < nchain_; i++) { stream->WriteWord(chain_[i]); } } private: int32_t nbucket_; int32_t nchain_; int32_t* bucket_; // "Head" int32_t* chain_; // "Next" }; class DynamicTable : public Section { public: DynamicTable(StringTable* strtab, SymbolTable* symtab, SymbolHashTable* hash) { section_type = SHT_DYNAMIC; section_link = strtab->section_index; section_flags = SHF_ALLOC | SHF_WRITE; section_entry_size = kElfDynamicTableEntrySize; segment_type = PT_LOAD; segment_flags = PF_R | PF_W; AddEntry(DT_HASH, hash->memory_offset); AddEntry(DT_STRTAB, strtab->memory_offset); AddEntry(DT_STRSZ, strtab->memory_size); AddEntry(DT_SYMTAB, symtab->memory_offset); AddEntry(DT_SYMENT, kElfSymbolTableEntrySize); AddEntry(DT_NULL, 0); } void Write(Elf* stream) { for (intptr_t i = 0; i < entries_.length(); i++) { const intptr_t start = stream->position(); #if defined(TARGET_ARCH_IS_32_BIT) stream->WriteWord(entries_[i]->tag); stream->WriteAddr(entries_[i]->value); #else stream->WriteXWord(entries_[i]->tag); stream->WriteAddr(entries_[i]->value); #endif const intptr_t end = stream->position(); ASSERT((end - start) == kElfDynamicTableEntrySize); } } class Entry { public: intptr_t tag; intptr_t value; }; void AddEntry(intptr_t tag, intptr_t value) { Entry* entry = new Entry(); entry->tag = tag; entry->value = value; entries_.Add(entry); memory_size += kElfDynamicTableEntrySize; file_size += kElfDynamicTableEntrySize; } private: GrowableArray entries_; }; // The first section must be written out and contains only zeros. static const intptr_t kNumInvalidSections = 1; // Extra segments put in the program table that aren't reified in // Elf::segments_. static const intptr_t kNumImplicitSegments = 3; static const intptr_t kProgramTableSegmentSize = kPageSize; Elf::Elf(Zone* zone, StreamingWriteStream* stream) : zone_(zone), stream_(stream), memory_offset_(0) { // Assumed by various offset logic in this file. ASSERT(stream_->position() == 0); // All our strings would fit in a single page. However, we use separate // .shstrtab and .dynstr to work around a bug in Android's strip utility. shstrtab_ = new (zone_) StringTable(/* allocate= */ false); shstrtab_->section_name = shstrtab_->AddString(".shstrtab"); symstrtab_ = new (zone_) StringTable(/* allocate= */ true); symstrtab_->section_name = shstrtab_->AddString(".dynstr"); symtab_ = new (zone_) SymbolTable(); symtab_->section_name = shstrtab_->AddString(".dynsym"); // Allocate regular segments after the program table. memory_offset_ = kProgramTableSegmentSize; } void Elf::AddSection(Section* section) { section->section_index = sections_.length() + kNumInvalidSections; sections_.Add(section); } void Elf::AddSegment(Section* section) { if (section->alignment < kPageSize) { section->alignment = kPageSize; } memory_offset_ = Utils::RoundUp(memory_offset_, section->alignment); section->memory_offset = memory_offset_; memory_offset_ += section->memory_size; segments_.Add(section); memory_offset_ = Utils::RoundUp(memory_offset_, kPageSize); } intptr_t Elf::NextMemoryOffset() { return memory_offset_; } intptr_t Elf::AddText(const char* name, const uint8_t* bytes, intptr_t size) { ProgramBits* image = new (zone_) ProgramBits(true, true, bytes, size); image->section_name = shstrtab_->AddString(".text"); AddSection(image); AddSegment(image); Symbol* symbol = new (zone_) Symbol(); symbol->cstr = name; symbol->name = symstrtab_->AddString(name); symbol->info = (STB_GLOBAL << 4) | STT_FUNC; symbol->section = image->section_index; // For shared libraries, this is the offset from the DSO base. For static // libraries, this is section relative. symbol->offset = image->memory_offset; symbol->size = size; symtab_->AddSymbol(symbol); return symbol->offset; } intptr_t Elf::AddROData(const char* name, const uint8_t* bytes, intptr_t size) { ProgramBits* image = new (zone_) ProgramBits(true, false, bytes, size); image->section_name = shstrtab_->AddString(".rodata"); AddSection(image); AddSegment(image); Symbol* symbol = new (zone_) Symbol(); symbol->cstr = name; symbol->name = symstrtab_->AddString(name); symbol->info = (STB_GLOBAL << 4) | STT_OBJECT; symbol->section = image->section_index; // For shared libraries, this is the offset from the DSO base. For static // libraries, this is section relative. symbol->offset = image->memory_offset; symbol->size = size; symtab_->AddSymbol(symbol); return symbol->offset; } void Elf::AddDebug(const char* name, const uint8_t* bytes, intptr_t size) { ProgramBits* image = new (zone_) ProgramBits(false, false, bytes, size); image->section_name = shstrtab_->AddString(name); AddSection(image); } void Elf::Finalize() { SymbolHashTable* hash = new (zone_) SymbolHashTable(symstrtab_, symtab_); hash->section_name = shstrtab_->AddString(".hash"); AddSection(hash); AddSection(symtab_); AddSection(symstrtab_); symtab_->section_link = symstrtab_->section_index; hash->section_link = symtab_->section_index; // Before finalizing the string table's memory size: intptr_t name_dynamic = shstrtab_->AddString(".dynamic"); // Finalizes memory size of string and symbol tables. AddSegment(hash); AddSegment(symtab_); AddSegment(symstrtab_); dynamic_ = new (zone_) DynamicTable(symstrtab_, symtab_, hash); dynamic_->section_name = name_dynamic; AddSection(dynamic_); AddSegment(dynamic_); AddSection(shstrtab_); shstrtab_->memory_offset = 0; // No segment. ComputeFileOffsets(); WriteHeader(); WriteProgramTable(); WriteSections(); WriteSectionTable(); } void Elf::ComputeFileOffsets() { intptr_t file_offset = kElfHeaderSize; program_table_file_offset_ = file_offset; program_table_file_size_ = (segments_.length() + kNumImplicitSegments) * kElfProgramTableEntrySize; file_offset += program_table_file_size_; for (intptr_t i = 0; i < sections_.length(); i++) { Section* section = sections_[i]; file_offset = Utils::RoundUp(file_offset, section->alignment); section->file_offset = file_offset; file_offset += section->file_size; } file_offset = Utils::RoundUp(file_offset, kElfSectionTableAlignment); section_table_file_offset_ = file_offset; section_table_file_size_ = (sections_.length() + kNumInvalidSections) * kElfSectionTableEntrySize; file_offset += section_table_file_size_; } void Elf::WriteHeader() { #if defined(TARGET_ARCH_IS_32_BIT) uint8_t size = ELFCLASS32; #else uint8_t size = ELFCLASS64; #endif uint8_t e_ident[16] = { 0x7f, 'E', 'L', 'F', size, ELFDATA2LSB, EV_CURRENT, ELFOSABI_SYSV, 0, 0, 0, 0, 0, 0, 0, 0}; stream_->WriteBytes(e_ident, 16); WriteHalf(ET_DYN); // Shared library. #if defined(TARGET_ARCH_IA32) WriteHalf(EM_386); #elif defined(TARGET_ARCH_X64) WriteHalf(EM_X86_64); #elif defined(TARGET_ARCH_ARM) WriteHalf(EM_ARM); #elif defined(TARGET_ARCH_ARM64) WriteHalf(EM_AARCH64); #else // E.g., DBC. FATAL("Unknown ELF architecture"); #endif WriteWord(EV_CURRENT); // Version WriteAddr(0); // "Entry point" WriteOff(program_table_file_offset_); WriteOff(section_table_file_offset_); #if defined(TARGET_ARCH_ARM) uword flags = EF_ARM_ABI | (TargetCPUFeatures::hardfp_supported() ? EF_ARM_ABI_FLOAT_HARD : EF_ARM_ABI_FLOAT_SOFT); #else uword flags = 0; #endif WriteWord(flags); WriteHalf(kElfHeaderSize); WriteHalf(kElfProgramTableEntrySize); WriteHalf(segments_.length() + kNumImplicitSegments); WriteHalf(kElfSectionTableEntrySize); WriteHalf(sections_.length() + kNumInvalidSections); WriteHalf(shstrtab_->section_index); ASSERT(stream_->position() == kElfHeaderSize); } void Elf::WriteProgramTable() { ASSERT(stream_->position() == program_table_file_offset_); // Self-reference to program header table. Required by Android but not by // Linux. Must appear before any PT_LOAD entries. { ASSERT(kNumImplicitSegments == 3); const intptr_t start = stream_->position(); #if defined(TARGET_ARCH_IS_32_BIT) WriteWord(PT_PHDR); WriteOff(program_table_file_offset_); // File offset. WriteAddr(program_table_file_offset_); // Virtual address. WriteAddr(program_table_file_offset_); // Physical address, not used. WriteWord(program_table_file_size_); WriteWord(program_table_file_size_); WriteWord(PF_R); WriteWord(kPageSize); #else WriteWord(PT_PHDR); WriteWord(PF_R); WriteOff(program_table_file_offset_); // File offset. WriteAddr(program_table_file_offset_); // Virtual address. WriteAddr(program_table_file_offset_); // Physical address, not used. WriteXWord(program_table_file_size_); WriteXWord(program_table_file_size_); WriteXWord(kPageSize); #endif const intptr_t end = stream_->position(); ASSERT((end - start) == kElfProgramTableEntrySize); } // Load for self-reference to program header table. Required by Android but // not by Linux. { // We pre-allocated the virtual memory space for the program table itself. // Check that we didn't generate too many segments. Currently we generate a // fixed num of segments based on the four pieces of a snapshot, but if we // use more in the future we'll likely need to do something more compilated // to generate DWARF without knowing a piece's virtual address in advance. RELEASE_ASSERT((program_table_file_offset_ + program_table_file_size_) < kProgramTableSegmentSize); ASSERT(kNumImplicitSegments == 3); const intptr_t start = stream_->position(); #if defined(TARGET_ARCH_IS_32_BIT) WriteWord(PT_LOAD); WriteOff(0); // File offset. WriteAddr(0); // Virtual address. WriteAddr(0); // Physical address, not used. WriteWord(program_table_file_offset_ + program_table_file_size_); WriteWord(program_table_file_offset_ + program_table_file_size_); WriteWord(PF_R); WriteWord(kPageSize); #else WriteWord(PT_LOAD); WriteWord(PF_R); WriteOff(0); // File offset. WriteAddr(0); // Virtual address. WriteAddr(0); // Physical address, not used. WriteXWord(program_table_file_offset_ + program_table_file_size_); WriteXWord(program_table_file_offset_ + program_table_file_size_); WriteXWord(kPageSize); #endif const intptr_t end = stream_->position(); ASSERT((end - start) == kElfProgramTableEntrySize); } for (intptr_t i = 0; i < segments_.length(); i++) { Section* section = segments_[i]; const intptr_t start = stream_->position(); #if defined(TARGET_ARCH_IS_32_BIT) WriteWord(section->segment_type); WriteOff(section->file_offset); WriteAddr(section->memory_offset); // Virtual address. WriteAddr(section->memory_offset); // Physical address, not used. WriteWord(section->file_size); WriteWord(section->memory_size); WriteWord(section->segment_flags); WriteWord(section->alignment); #else WriteWord(section->segment_type); WriteWord(section->segment_flags); WriteOff(section->file_offset); WriteAddr(section->memory_offset); // Virtual address. WriteAddr(section->memory_offset); // Physical address, not used. WriteXWord(section->file_size); WriteXWord(section->memory_size); WriteXWord(section->alignment); #endif const intptr_t end = stream_->position(); ASSERT((end - start) == kElfProgramTableEntrySize); } // Special case: the dynamic section requires both LOAD and DYNAMIC program // header table entries. { ASSERT(kNumImplicitSegments == 3); const intptr_t start = stream_->position(); #if defined(TARGET_ARCH_IS_32_BIT) WriteWord(PT_DYNAMIC); WriteOff(dynamic_->file_offset); WriteAddr(dynamic_->memory_offset); // Virtual address. WriteAddr(dynamic_->memory_offset); // Physical address, not used. WriteWord(dynamic_->file_size); WriteWord(dynamic_->memory_size); WriteWord(dynamic_->segment_flags); WriteWord(dynamic_->alignment); #else WriteWord(PT_DYNAMIC); WriteWord(dynamic_->segment_flags); WriteOff(dynamic_->file_offset); WriteAddr(dynamic_->memory_offset); // Virtual address. WriteAddr(dynamic_->memory_offset); // Physical address, not used. WriteXWord(dynamic_->file_size); WriteXWord(dynamic_->memory_size); WriteXWord(dynamic_->alignment); #endif const intptr_t end = stream_->position(); ASSERT((end - start) == kElfProgramTableEntrySize); } } void Elf::WriteSectionTable() { stream_->Align(kElfSectionTableAlignment); ASSERT(stream_->position() == section_table_file_offset_); { // The first entry in the section table is reserved and must be all zeros. ASSERT(kNumInvalidSections == 1); const intptr_t start = stream_->position(); #if defined(TARGET_ARCH_IS_32_BIT) WriteWord(0); WriteWord(0); WriteWord(0); WriteAddr(0); WriteOff(0); WriteWord(0); WriteWord(0); WriteWord(0); WriteWord(0); WriteWord(0); #else WriteWord(0); WriteWord(0); WriteXWord(0); WriteAddr(0); WriteOff(0); WriteXWord(0); WriteWord(0); WriteWord(0); WriteXWord(0); WriteXWord(0); #endif const intptr_t end = stream_->position(); ASSERT((end - start) == kElfSectionTableEntrySize); } for (intptr_t i = 0; i < sections_.length(); i++) { Section* section = sections_[i]; const intptr_t start = stream_->position(); #if defined(TARGET_ARCH_IS_32_BIT) WriteWord(section->section_name); WriteWord(section->section_type); WriteWord(section->section_flags); WriteAddr(section->memory_offset); WriteOff(section->file_offset); WriteWord(section->file_size); // Has different meaning for BSS. WriteWord(section->section_link); WriteWord(section->section_info); WriteWord(section->alignment); WriteWord(section->section_entry_size); #else WriteWord(section->section_name); WriteWord(section->section_type); WriteXWord(section->section_flags); WriteAddr(section->memory_offset); WriteOff(section->file_offset); WriteXWord(section->file_size); // Has different meaning for BSS. WriteWord(section->section_link); WriteWord(section->section_info); WriteXWord(section->alignment); WriteXWord(section->section_entry_size); #endif const intptr_t end = stream_->position(); ASSERT((end - start) == kElfSectionTableEntrySize); } } void Elf::WriteSections() { for (intptr_t i = 0; i < sections_.length(); i++) { Section* section = sections_[i]; stream_->Align(section->alignment); ASSERT(stream_->position() == section->file_offset); section->Write(this); ASSERT(stream_->position() == section->file_offset + section->file_size); } } } // namespace dart