Files
sdk/runtime/vm/elf.cc
T
Teagan Strickland 26c88bbb74 [vm/compiler] Clean up ELF creation code.
Instead of making the ELF object handle stripping and keep track of two
possible output stream, just revert ELF objects to only output to a
single stream. Now, layers that use ELF, like the ImageWriters, handle
the stripping by deciding whether or not to add certain sections or
static symbols to the generated ELF, and we create separate DWARF
objects for each unstripped ELF object.

We also avoid duplication of writing segment and section table entries
by just creating special Sections that correspond to reserved entries,
the program table header entries for itself, etc, and then move the
section and segment entry writing into Section itself. We also lift
a lot of the same calculations used in subclass construction to the
Section level, and replace the old fields with four different kind
of object members:

* Fields that are known at construction time and thus now const
  (e.g., section_type)

* Fields that are not known at construction time that have reasonable
  defaults for most instances and so need not be changed from their
  default value (e.g., section_link)

* Fields that are not known at construction time which must be set once
  (and only once) before use. These are now accessed via getters/setters
  that check whether or not the field has been set (e.g., section_name)

* Fields that are calculated from the contents of the particular
  subclass, which are now just methods (e.g., FileSize())

We also change the snapshot profile writer test to test the size of
internally stripped ELF output on all platforms without running it
through an external stripping utility (previously untested), and test
assembled or externally stripped output on more platforms where we
support those. Instead of depending on pkg/vm/tool/precompiler2 for
assembling (which always uses gcc), I've added assembleSnapshot and
stripSnapshot to use_flag_test_helper.dart, which mimics the way the
test_runner determines the executable and flags to use.

Change-Id: I4ade45c5caffb443abb3f5c567343504c8085f89
Cq-Include-Trybots: luci.dart.try:vm-kernel-precomp-linux-release-x64-try,vm-kernel-precomp-linux-release-simarm-try,vm-kernel-precomp-linux-release-simarm64-try,vm-kernel-precomp-linux-release-simarm_x64-try,vm-kernel-precomp-android-release-arm64-try,vm-kernel-precomp-mac-release-simarm64-try,vm-kernel-precomp-win-release-x64-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/129083
Commit-Queue: Teagan Strickland <sstrickl@google.com>
Reviewed-by: Ryan Macnak <rmacnak@google.com>
2020-01-10 11:51:57 +00:00

904 lines
29 KiB
C++

// 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/elf.h"
#include "platform/text_buffer.h"
#include "vm/cpu.h"
#include "vm/hash_map.h"
#include "vm/thread.h"
namespace dart {
#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
#define DEFINE_LINEAR_FIELD_METHODS(name, type, init) \
type name() const { \
ASSERT(name##_ != init); \
return name##_; \
} \
void set_##name(type value) { \
ASSERT(name##_ == init); \
name##_ = value; \
}
#define DEFINE_LINEAR_FIELD(name, type, init) type name##_ = init;
class Section : public ZoneAllocated {
public:
Section(intptr_t type,
intptr_t segment_type,
bool allocate,
bool executable,
bool writable,
intptr_t alignment = 1)
: section_type(type),
section_flags(EncodeSectionFlags(allocate, executable, writable)),
alignment(allocate ? SegmentAlignment(alignment) : alignment),
segment_type(segment_type),
segment_flags(EncodeSegmentFlags(allocate, executable, writable)),
// Non-segments will never have a memory offset, here represented by 0.
memory_offset_(allocate ? -1 : 0) {
// Only the reserved section (type 0) should have an alignment of 0.
ASSERT(type == 0 || alignment > 0);
}
// The constructor that most subclasses will use.
Section(intptr_t type,
bool allocate,
bool executable,
bool writable,
intptr_t alignment = 1)
: Section(type,
/*segment_type=*/allocate ? elf::PT_LOAD : 0,
allocate,
executable,
writable,
alignment) {}
virtual ~Section() {}
// Linker view.
const intptr_t section_type;
const intptr_t section_flags;
const intptr_t alignment;
// These are fields that only are not set for most kinds of sections and so we
// set them to a reasonable default.
intptr_t section_link = elf::SHN_UNDEF;
intptr_t section_info = 0;
intptr_t section_entry_size = 0;
#define FOR_EACH_SECTION_LINEAR_FIELD(M) \
M(section_name, intptr_t, -1) \
M(section_index, intptr_t, -1) \
M(file_offset, intptr_t, -1)
FOR_EACH_SECTION_LINEAR_FIELD(DEFINE_LINEAR_FIELD_METHODS);
virtual intptr_t FileSize() = 0;
// Loader view.
const intptr_t segment_type;
const intptr_t segment_flags;
#define FOR_EACH_SEGMENT_LINEAR_FIELD(M) M(memory_offset, intptr_t, -1)
FOR_EACH_SEGMENT_LINEAR_FIELD(DEFINE_LINEAR_FIELD_METHODS);
virtual intptr_t MemorySize() = 0;
// Other methods.
virtual void Write(Elf* stream) = 0;
void WriteSegmentEntry(Elf* stream, bool dynamic = false) {
// This should never be used on either the reserved 0-filled section or
// on sections without a segment.
ASSERT(MemorySize() > 0);
// dynamic should only be true if this section is the dynamic table.
ASSERT(!dynamic || section_type == elf::SHT_DYNAMIC);
#if defined(TARGET_ARCH_IS_32_BIT)
stream->WriteWord(dynamic ? elf::PT_DYNAMIC : segment_type);
stream->WriteOff(file_offset());
stream->WriteAddr(memory_offset()); // Virtual address.
stream->WriteAddr(memory_offset()); // Physical address, not used.
stream->WriteWord(FileSize());
stream->WriteWord(MemorySize());
stream->WriteWord(segment_flags);
stream->WriteWord(alignment);
#else
stream->WriteWord(dynamic ? elf::PT_DYNAMIC : segment_type);
stream->WriteWord(segment_flags);
stream->WriteOff(file_offset());
stream->WriteAddr(memory_offset()); // Virtual address.
stream->WriteAddr(memory_offset()); // Physical address, not used.
stream->WriteXWord(FileSize());
stream->WriteXWord(MemorySize());
stream->WriteXWord(alignment);
#endif
}
void WriteSectionEntry(Elf* stream) {
#if defined(TARGET_ARCH_IS_32_BIT)
stream->WriteWord(section_name());
stream->WriteWord(section_type);
stream->WriteWord(section_flags);
stream->WriteAddr(memory_offset());
stream->WriteOff(file_offset());
stream->WriteWord(FileSize()); // Has different meaning for BSS.
stream->WriteWord(section_link);
stream->WriteWord(section_info);
stream->WriteWord(alignment);
stream->WriteWord(section_entry_size);
#else
stream->WriteWord(section_name());
stream->WriteWord(section_type);
stream->WriteXWord(section_flags);
stream->WriteAddr(memory_offset());
stream->WriteOff(file_offset());
stream->WriteXWord(FileSize()); // Has different meaning for BSS.
stream->WriteWord(section_link);
stream->WriteWord(section_info);
stream->WriteXWord(alignment);
stream->WriteXWord(section_entry_size);
#endif
}
private:
static intptr_t EncodeSectionFlags(bool allocate,
bool executable,
bool writable) {
if (!allocate) return 0;
intptr_t flags = elf::SHF_ALLOC;
if (executable) flags |= elf::SHF_EXECINSTR;
if (writable) flags |= elf::SHF_WRITE;
return flags;
}
static intptr_t EncodeSegmentFlags(bool allocate,
bool executable,
bool writable) {
if (!allocate) return 0;
intptr_t flags = elf::PF_R;
if (executable) flags |= elf::PF_X;
if (writable) flags |= elf::PF_W;
return flags;
}
static intptr_t SegmentAlignment(intptr_t alignment) {
return alignment < Elf::kPageSize ? Elf::kPageSize : alignment;
}
FOR_EACH_SECTION_LINEAR_FIELD(DEFINE_LINEAR_FIELD);
FOR_EACH_SEGMENT_LINEAR_FIELD(DEFINE_LINEAR_FIELD);
#undef FOR_EACH_SECTION_LINEAR_FIELD
#undef FOR_EACH_SEGMENT_LINEAR_FIELD
};
#undef DEFINE_LINEAR_FIELD
#undef DEFINE_LINEAR_FIELD_METHODS
// Represents the first entry in the section table, which should only contain
// zero values. Only used for WriteSectionEntry and should never actually appear
// in sections_.
class ReservedSection : public Section {
public:
ReservedSection()
: Section(/*type=*/0,
/*allocate=*/false,
/*executable=*/false,
/*writable=*/false,
/*alignment=*/0) {
set_section_name(0);
set_section_index(0);
set_file_offset(0);
}
intptr_t FileSize() { return 0; }
intptr_t MemorySize() { return 0; }
void Write(Elf* stream) { UNREACHABLE(); }
};
class BlobSection : public Section {
public:
BlobSection(intptr_t type,
intptr_t segment_type,
bool allocate,
bool executable,
bool writable,
intptr_t filesz,
intptr_t memsz,
int alignment = 1)
: Section(type, segment_type, allocate, executable, writable, alignment),
file_size_(filesz),
memory_size_(allocate ? memsz : 0) {}
BlobSection(intptr_t type,
bool allocate,
bool executable,
bool writable,
intptr_t filesz,
intptr_t memsz,
int alignment = 1)
: BlobSection(type,
/*segment_type=*/allocate ? elf::PT_LOAD : 0,
allocate,
executable,
writable,
filesz,
memsz,
alignment) {}
intptr_t FileSize() { return file_size_; }
intptr_t MemorySize() { return memory_size_; }
virtual void Write(Elf* stream) = 0;
private:
const intptr_t file_size_;
const intptr_t memory_size_;
};
// A section for representing the program header segment in the program header
// table. Only used for WriteSegmentEntry.
class ProgramTable : public BlobSection {
public:
ProgramTable(intptr_t offset, intptr_t size)
: BlobSection(/*type=*/0,
/*segment_type=*/elf::PT_PHDR,
/*allocate=*/true,
/*executable=*/false,
/*writable=*/false,
/*filesz=*/size,
/*memsz=*/size) {
set_file_offset(offset);
set_memory_offset(offset);
}
// This should never actually be added to sections_ or segments_.
void Write(Elf* stream) { UNREACHABLE(); }
};
// A section for representing the program header table load segment in the
// program header table. Only used for WriteSegmentEntry.
class ProgramTableLoad : public BlobSection {
public:
// The Android dynamic linker in Jelly Bean incorrectly assumes that all
// non-writable segments are continguous. Since the BSS segment comes directly
// after the program header segment, we must make this segment writable so
// later non-writable segments does not cause the BSS to be also marked as
// read-only.
//
// The bug is here:
// https://github.com/aosp-mirror/platform_bionic/blob/94963af28e445384e19775a838a29e6a71708179/linker/linker.c#L1991-L2001
explicit ProgramTableLoad(intptr_t size)
: BlobSection(/*type=*/0,
/*allocate=*/true,
/*executable=*/false,
/*writable=*/true,
/*filesz=*/size,
/*memsz=*/size) {
set_file_offset(0);
set_memory_offset(0);
}
// This should never actually be added to sections_ or segments_.
void Write(Elf* stream) { UNREACHABLE(); }
};
class ProgramBits : public BlobSection {
public:
ProgramBits(bool allocate,
bool executable,
bool writable,
const uint8_t* bytes,
intptr_t filesz,
intptr_t memsz = -1)
: BlobSection(elf::SHT_PROGBITS,
allocate,
executable,
writable,
filesz,
memsz != -1 ? memsz : filesz),
bytes_(ASSERT_NOTNULL(bytes)) {}
void Write(Elf* stream) { stream->WriteBytes(bytes_, FileSize()); }
const uint8_t* bytes_;
};
class NoBits : public BlobSection {
public:
NoBits(bool allocate, bool executable, bool writable, intptr_t memsz)
: BlobSection(elf::SHT_NOBITS,
allocate,
executable,
writable,
/*filesz=*/0,
memsz) {}
void Write(Elf* stream) {}
};
class StringTable : public Section {
public:
explicit StringTable(bool allocate)
: Section(elf::SHT_STRTAB,
allocate,
/*executable=*/false,
/*writable=*/false),
dynamic_(allocate),
text_(128),
text_indices_() {
text_.AddChar('\0');
text_indices_.Insert({"", 1});
}
intptr_t FileSize() { return text_.length(); }
intptr_t MemorySize() { return dynamic_ ? FileSize() : 0; }
void Write(Elf* stream) {
stream->WriteBytes(reinterpret_cast<const uint8_t*>(text_.buf()),
text_.length());
}
intptr_t AddString(const char* str) {
if (auto const kv = text_indices_.Lookup(str)) return kv->value - 1;
intptr_t offset = text_.length();
text_.AddString(str);
text_.AddChar('\0');
text_indices_.Insert({str, offset + 1});
return offset;
}
const bool dynamic_;
TextBuffer text_;
// To avoid kNoValue for intptr_t (0), we store an index n as n + 1.
CStringMap<intptr_t> text_indices_;
};
class Symbol : public ZoneAllocated {
public:
Symbol(const char* cstr,
intptr_t name,
intptr_t info,
intptr_t section,
intptr_t offset,
intptr_t size)
: cstr_(cstr),
name_index_(name),
info_(info),
section_index_(section),
offset_(offset),
size_(size) {}
void Write(Elf* stream) const {
stream->WriteWord(name_index_);
#if defined(TARGET_ARCH_IS_32_BIT)
stream->WriteAddr(offset_);
stream->WriteWord(size_);
stream->WriteByte(info_);
stream->WriteByte(0);
stream->WriteHalf(section_index_);
#else
stream->WriteByte(info_);
stream->WriteByte(0);
stream->WriteHalf(section_index_);
stream->WriteAddr(offset_);
stream->WriteXWord(size_);
#endif
}
private:
friend class SymbolHashTable; // For cstr_ access.
const char* cstr_;
intptr_t name_index_;
intptr_t info_;
intptr_t section_index_;
intptr_t offset_;
intptr_t size_;
};
class SymbolTable : public Section {
public:
explicit SymbolTable(bool dynamic)
: Section(dynamic ? elf::SHT_DYNSYM : elf::SHT_SYMTAB,
dynamic,
/*executable=*/false,
/*writable=*/false,
compiler::target::kWordSize),
dynamic_(dynamic),
reserved_("", 0, 0, 0, 0, 0) {
section_entry_size = kElfSymbolTableEntrySize;
// The first symbol table entry is reserved and must be all zeros.
symbols_.Add(&reserved_);
section_info = 1; // One "local" symbol, the reserved first entry.
}
intptr_t FileSize() { return Length() * kElfSymbolTableEntrySize; }
intptr_t MemorySize() { return dynamic_ ? FileSize() : 0; }
void Write(Elf* stream) {
for (intptr_t i = 0; i < Length(); i++) {
auto const symbol = At(i);
const intptr_t start = stream->position();
symbol->Write(stream);
const intptr_t end = stream->position();
ASSERT((end - start) == kElfSymbolTableEntrySize);
}
}
void AddSymbol(const Symbol* symbol) { symbols_.Add(symbol); }
intptr_t Length() const { return symbols_.length(); }
const Symbol* At(intptr_t i) const { return symbols_[i]; }
private:
const bool dynamic_;
const Symbol reserved_;
GrowableArray<const Symbol*> symbols_;
};
static uint32_t ElfHash(const unsigned char* name) {
uint32_t h = 0;
while (*name != '\0') {
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(elf::SHT_HASH,
/*allocate=*/true,
/*executable=*/false,
/*writable=*/false,
compiler::target::kWordSize) {
section_link = symtab->section_index();
section_entry_size = kElfSymbolHashTableEntrySize;
nchain_ = symtab->Length();
nbucket_ = symtab->Length();
auto zone = Thread::Current()->zone();
bucket_ = zone->Alloc<int32_t>(nbucket_);
for (intptr_t i = 0; i < nbucket_; i++) {
bucket_[i] = elf::STN_UNDEF;
}
chain_ = zone->Alloc<int32_t>(nchain_);
for (intptr_t i = 0; i < nchain_; i++) {
chain_[i] = elf::STN_UNDEF;
}
for (intptr_t i = 1; i < symtab->Length(); i++) {
auto const 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
}
}
intptr_t FileSize() { return 4 * (nbucket_ + nchain_ + 2); }
intptr_t MemorySize() { return FileSize(); }
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(elf::SHT_DYNAMIC,
/*allocate=*/true,
/*executable=*/false,
/*writable=*/true,
compiler::target::kWordSize) {
section_link = strtab->section_index();
section_entry_size = kElfDynamicTableEntrySize;
AddEntry(elf::DT_HASH, hash->memory_offset());
AddEntry(elf::DT_STRTAB, strtab->memory_offset());
AddEntry(elf::DT_STRSZ, strtab->MemorySize());
AddEntry(elf::DT_SYMTAB, symtab->memory_offset());
AddEntry(elf::DT_SYMENT, kElfSymbolTableEntrySize);
AddEntry(elf::DT_NULL, 0);
}
intptr_t FileSize() { return entries_.length() * kElfDynamicTableEntrySize; }
intptr_t MemorySize() { return FileSize(); }
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);
}
private:
GrowableArray<Entry*> 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 = Elf::kPageSize;
Elf::Elf(Zone* zone, StreamingWriteStream* stream)
: zone_(zone),
stream_(stream),
shstrtab_(new (zone) StringTable(/*allocate=*/false)),
dynstrtab_(new (zone) StringTable(/*allocate=*/true)),
dynsym_(new (zone) SymbolTable(/*dynamic=*/true)),
memory_offset_(kProgramTableSegmentSize) {
// Assumed by various offset logic in this file.
ASSERT(stream_->position() == 0);
}
void Elf::AddSection(Section* section, const char* name) {
ASSERT(shstrtab_ != nullptr);
section->set_section_name(shstrtab_->AddString(name));
section->set_section_index(sections_.length() + kNumInvalidSections);
sections_.Add(section);
if (section->MemorySize() > 0) {
memory_offset_ = Utils::RoundUp(memory_offset_, section->alignment);
section->set_memory_offset(memory_offset_);
segments_.Add(section);
memory_offset_ += section->MemorySize();
memory_offset_ = Utils::RoundUp(memory_offset_, kPageSize);
}
}
intptr_t Elf::NextMemoryOffset() const {
return memory_offset_;
}
intptr_t Elf::NextSectionIndex() const {
return sections_.length() + kNumInvalidSections;
}
intptr_t Elf::AddSectionSymbol(const Section* section,
const char* name,
intptr_t size) {
auto const name_index = dynstrtab_->AddString(name);
auto const info = (elf::STB_GLOBAL << 4) | elf::STT_FUNC;
auto const section_index = section->section_index();
// For shared libraries, this is the offset from the DSO base. For static
// libraries, this is section relative.
auto const memory_offset = section->memory_offset();
auto const symbol = new (zone_)
Symbol(name, name_index, info, section_index, memory_offset, size);
dynsym_->AddSymbol(symbol);
return memory_offset;
}
intptr_t Elf::AddText(const char* name, const uint8_t* bytes, intptr_t size) {
Section* image = nullptr;
if (bytes != nullptr) {
image = new (zone_) ProgramBits(true, true, false, bytes, size);
} else {
image = new (zone_) NoBits(true, true, false, size);
}
AddSection(image, ".text");
return AddSectionSymbol(image, name, size);
}
void Elf::AddStaticSymbol(intptr_t section,
const char* name,
size_t memory_offset) {
// Lazily allocate the static string and symbol tables, as we only add static
// symbols in unstripped ELF files.
if (strtab_ == nullptr) {
ASSERT(symtab_ == nullptr);
strtab_ = new (zone_) StringTable(/* allocate= */ false);
symtab_ = new (zone_) SymbolTable(/*dynamic=*/false);
}
auto const name_index = strtab_->AddString(name);
auto const info = (elf::STB_GLOBAL << 4) | elf::STT_FUNC;
Symbol* symbol = new (zone_)
Symbol(name, name_index, info, section, memory_offset, /*size=*/0);
symtab_->AddSymbol(symbol);
}
intptr_t Elf::AddBSSData(const char* name, intptr_t size) {
// Ideally the BSS segment would take no space in the object, but Android's
// "strip" utility truncates the memory-size of our segments to their
// file-size.
//
// Therefore we must insert zero-filled pages for the BSS.
uint8_t* const bytes = zone_->Alloc<uint8_t>(size);
memset(bytes, 0, size);
ProgramBits* const image =
new (zone_) ProgramBits(true, false, true, bytes, size);
AddSection(image, ".bss");
return AddSectionSymbol(image, name, size);
}
intptr_t Elf::AddROData(const char* name, const uint8_t* bytes, intptr_t size) {
ASSERT(bytes != nullptr);
ProgramBits* image = new (zone_) ProgramBits(true, false, false, bytes, size);
AddSection(image, ".rodata");
return AddSectionSymbol(image, name, size);
}
void Elf::AddDebug(const char* name, const uint8_t* bytes, intptr_t size) {
ASSERT(bytes != nullptr);
ProgramBits* image =
new (zone_) ProgramBits(false, false, false, bytes, size);
AddSection(image, name);
}
void Elf::Finalize() {
AddSection(dynstrtab_, ".dynstr");
AddSection(dynsym_, ".dynsym");
dynsym_->section_link = dynstrtab_->section_index();
auto const hash = new (zone_) SymbolHashTable(dynstrtab_, dynsym_);
AddSection(hash, ".hash");
if (symtab_ != nullptr) {
ASSERT(strtab_ != nullptr);
AddSection(strtab_, ".strtab");
AddSection(symtab_, ".symtab");
symtab_->section_link = strtab_->section_index();
}
dynamic_ = new (zone_) DynamicTable(dynstrtab_, dynsym_, hash);
AddSection(dynamic_, ".dynamic");
AddSection(shstrtab_, ".shstrtab");
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->set_file_offset(file_offset);
file_offset += section->FileSize();
}
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 = elf::ELFCLASS32;
#else
uint8_t size = elf::ELFCLASS64;
#endif
uint8_t e_ident[16] = {0x7f,
'E',
'L',
'F',
size,
elf::ELFDATA2LSB,
elf::EV_CURRENT,
elf::ELFOSABI_SYSV,
0,
0,
0,
0,
0,
0,
0,
0};
stream_->WriteBytes(e_ident, 16);
WriteHalf(elf::ET_DYN); // Shared library.
#if defined(TARGET_ARCH_IA32)
WriteHalf(elf::EM_386);
#elif defined(TARGET_ARCH_X64)
WriteHalf(elf::EM_X86_64);
#elif defined(TARGET_ARCH_ARM)
WriteHalf(elf::EM_ARM);
#elif defined(TARGET_ARCH_ARM64)
WriteHalf(elf::EM_AARCH64);
#else
FATAL("Unknown ELF architecture");
#endif
WriteWord(elf::EV_CURRENT); // Version
WriteAddr(0); // "Entry point"
WriteOff(program_table_file_offset_);
WriteOff(section_table_file_offset_);
#if defined(TARGET_ARCH_ARM)
uword flags = elf::EF_ARM_ABI | (TargetCPUFeatures::hardfp_supported()
? elf::EF_ARM_ABI_FLOAT_HARD
: elf::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.
{
ProgramTable program_table(program_table_file_offset_,
program_table_file_size_);
ASSERT(kNumImplicitSegments == 3);
const intptr_t start = stream_->position();
program_table.WriteSegmentEntry(this);
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.
auto const program_table_segment_size =
program_table_file_offset_ + program_table_file_size_;
RELEASE_ASSERT(program_table_segment_size < kProgramTableSegmentSize);
// We create a section that, when printed as a segment, contains the
// appropriate info for the program table.
ProgramTableLoad program_table_load(program_table_segment_size);
ASSERT(kNumImplicitSegments == 3);
const intptr_t start = stream_->position();
program_table_load.WriteSegmentEntry(this);
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();
section->WriteSegmentEntry(this);
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();
dynamic_->WriteSegmentEntry(this, /*dynamic=*/true);
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();
ReservedSection reserved;
reserved.WriteSectionEntry(this);
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();
section->WriteSectionEntry(this);
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->FileSize());
}
}
} // namespace dart