blob: 548b5acd13436c759e633be98cb05c5899cef5c2 [file] [log] [blame]
// 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.
import 'dart:typed_data';
import 'reader.dart';
int _readElfBytes(Reader reader, int bytes, int alignment) {
final alignOffset = reader.offset % alignment;
if (alignOffset != 0) {
// Move the reader to the next aligned position.
reader.seek(reader.offset - alignOffset + alignment);
}
return reader.readBytes(bytes);
}
// Reads an Elf{32,64}_Addr.
int _readElfAddress(Reader reader) {
return _readElfBytes(reader, reader.wordSize, reader.wordSize);
}
// Reads an Elf{32,64}_Off.
int _readElfOffset(Reader reader) {
return _readElfBytes(reader, reader.wordSize, reader.wordSize);
}
// Reads an Elf{32,64}_Half.
int _readElfHalf(Reader reader) {
return _readElfBytes(reader, 2, 2);
}
// Reads an Elf{32,64}_Word.
int _readElfWord(Reader reader) {
return _readElfBytes(reader, 4, 4);
}
// Reads an Elf64_Xword.
int _readElfXword(Reader reader) {
switch (reader.wordSize) {
case 4:
throw "Internal reader error: reading Elf64_Xword in 32-bit ELF file";
case 8:
return _readElfBytes(reader, 8, 8);
default:
throw "Unsupported word size ${reader.wordSize}";
}
}
// Reads an Elf{32,64}_Section.
int _readElfSection(Reader reader) {
return _readElfBytes(reader, 2, 2);
}
// Used in cases where the value read for a given field is Elf32_Word on 32-bit
// and Elf64_Xword on 64-bit.
int _readElfNative(Reader reader) {
switch (reader.wordSize) {
case 4:
return _readElfWord(reader);
case 8:
return _readElfXword(reader);
default:
throw "Unsupported word size ${reader.wordSize}";
}
}
/// The header of the ELF file, which includes information necessary to parse
/// the rest of the file.
class ElfHeader {
final int wordSize;
final Endian endian;
final int entry;
final int flags;
final int headerSize;
final int programHeaderOffset;
final int programHeaderCount;
final int programHeaderEntrySize;
final int sectionHeaderOffset;
final int sectionHeaderCount;
final int sectionHeaderEntrySize;
final int sectionHeaderStringsIndex;
ElfHeader._(
this.wordSize,
this.endian,
this.entry,
this.flags,
this.headerSize,
this.programHeaderOffset,
this.sectionHeaderOffset,
this.programHeaderCount,
this.sectionHeaderCount,
this.programHeaderEntrySize,
this.sectionHeaderEntrySize,
this.sectionHeaderStringsIndex);
static ElfHeader? fromReader(Reader reader) {
final fileSize = reader.length;
for (final sigByte in _ELFMAG.codeUnits) {
if (reader.readByte() != sigByte) {
return null;
}
}
int wordSize;
switch (reader.readByte()) {
case _ELFCLASS32:
wordSize = 4;
break;
case _ELFCLASS64:
wordSize = 8;
break;
default:
throw FormatException("Unexpected e_ident[EI_CLASS] value");
}
final calculatedHeaderSize = 0x18 + 3 * wordSize + 0x10;
if (fileSize < calculatedHeaderSize) {
throw FormatException("ELF file too small for header: "
"file size ${fileSize} < "
"calculated header size $calculatedHeaderSize");
}
Endian endian;
switch (reader.readByte()) {
case _ELFDATA2LSB:
endian = Endian.little;
break;
case _ELFDATA2MSB:
endian = Endian.big;
break;
default:
throw FormatException("Unexpected e_indent[EI_DATA] value");
}
if (reader.readByte() != 0x01) {
throw FormatException("Unexpected e_ident[EI_VERSION] value");
}
// After this point, we need the reader to be correctly set up re: word
// size and endianness, since we start reading more than single bytes.
reader.endian = endian;
reader.wordSize = wordSize;
// Skip rest of e_ident/e_type/e_machine, i.e. move to e_version.
reader.seek(0x14, absolute: true);
if (_readElfWord(reader) != 0x01) {
throw FormatException("Unexpected e_version value");
}
final entry = _readElfAddress(reader);
final programHeaderOffset = _readElfOffset(reader);
final sectionHeaderOffset = _readElfOffset(reader);
final flags = _readElfWord(reader);
final headerSize = _readElfHalf(reader);
final programHeaderEntrySize = _readElfHalf(reader);
final programHeaderCount = _readElfHalf(reader);
final programHeaderSize = programHeaderEntrySize * programHeaderCount;
final sectionHeaderEntrySize = _readElfHalf(reader);
final sectionHeaderCount = _readElfHalf(reader);
final sectionHeaderSize = sectionHeaderEntrySize * sectionHeaderCount;
final sectionHeaderStringsIndex = _readElfHalf(reader);
if (reader.offset != headerSize) {
throw FormatException("Only read ${reader.offset} bytes, not the "
"full header size ${headerSize}");
}
if (headerSize != calculatedHeaderSize) {
throw FormatException("Stored ELF header size ${headerSize} != "
"calculated ELF header size $calculatedHeaderSize");
}
if (fileSize < programHeaderOffset) {
throw FormatException("File is truncated before program header");
}
if (fileSize < programHeaderOffset + programHeaderSize) {
throw FormatException("File is truncated within the program header");
}
if (fileSize < sectionHeaderOffset) {
throw FormatException("File is truncated before section header");
}
if (fileSize < sectionHeaderOffset + sectionHeaderSize) {
throw FormatException("File is truncated within the section header");
}
return ElfHeader._(
wordSize,
endian,
entry,
flags,
headerSize,
programHeaderOffset,
sectionHeaderOffset,
programHeaderCount,
sectionHeaderCount,
programHeaderEntrySize,
sectionHeaderEntrySize,
sectionHeaderStringsIndex);
}
int get programHeaderSize => programHeaderCount * programHeaderEntrySize;
int get sectionHeaderSize => sectionHeaderCount * sectionHeaderEntrySize;
// Constants used within the ELF specification.
static const _ELFMAG = "\x7fELF";
static const _ELFCLASS32 = 0x01;
static const _ELFCLASS64 = 0x02;
static const _ELFDATA2LSB = 0x01;
static const _ELFDATA2MSB = 0x02;
void writeToStringBuffer(StringBuffer buffer) {
buffer..write('Format is ')..write(wordSize * 8)..write(' bits');
switch (endian) {
case Endian.little:
buffer..writeln(' and little-endian');
break;
case Endian.big:
buffer..writeln(' and big-endian');
break;
}
buffer
..write('Entry point: 0x')
..writeln(paddedHex(entry, wordSize))
..write('Flags: 0x')
..writeln(paddedHex(flags, 4))
..write('Program header offset: 0x')
..writeln(paddedHex(programHeaderOffset, wordSize))
..write('Program header entry size: ')
..writeln(programHeaderEntrySize)
..write('Program header entry count: ')
..writeln(programHeaderCount)
..write('Section header offset: 0x')
..writeln(paddedHex(sectionHeaderOffset, wordSize))
..write('Section header entry size: ')
..writeln(sectionHeaderEntrySize)
..write('Section header entry count: ')
..writeln(sectionHeaderCount)
..write('Section header strings index: ')
..write(sectionHeaderStringsIndex);
}
@override
String toString() {
var buffer = StringBuffer();
writeToStringBuffer(buffer);
return buffer.toString();
}
}
/// An entry in the [ProgramHeader] describing a memory segment loaded into
/// memory and used during runtime.
class ProgramHeaderEntry {
final int type;
final int flags;
final int offset;
final int vaddr;
final int paddr;
final int filesz;
final int memsz;
final int align;
final int wordSize;
// p_type constants from ELF specification.
static const _PT_NULL = 0;
static const _PT_LOAD = 1;
static const _PT_DYNAMIC = 2;
static const _PT_PHDR = 6;
ProgramHeaderEntry._(this.type, this.flags, this.offset, this.vaddr,
this.paddr, this.filesz, this.memsz, this.align, this.wordSize);
static ProgramHeaderEntry fromReader(Reader reader) {
int wordSize = reader.wordSize;
assert(wordSize == 4 || wordSize == 8);
final type = _readElfWord(reader);
late int flags;
if (wordSize == 8) {
flags = _readElfWord(reader);
}
final offset = _readElfOffset(reader);
final vaddr = _readElfAddress(reader);
final paddr = _readElfAddress(reader);
final filesz = _readElfNative(reader);
final memsz = _readElfNative(reader);
if (wordSize == 4) {
flags = _readElfWord(reader);
}
final align = _readElfNative(reader);
return ProgramHeaderEntry._(
type, flags, offset, vaddr, paddr, filesz, memsz, align, wordSize);
}
static const _typeStrings = <int, String>{
_PT_NULL: "PT_NULL",
_PT_LOAD: "PT_LOAD",
_PT_DYNAMIC: "PT_DYNAMIC",
_PT_PHDR: "PT_PHDR",
};
static String _typeToString(int type) =>
_typeStrings[type] ?? "unknown (${paddedHex(type, 4)})";
void writeToStringBuffer(StringBuffer buffer) {
buffer
..write('Type: ')
..writeln(_typeToString(type))
..write('Flags: 0x')
..writeln(paddedHex(flags, 4))
..write('Offset: 0x')
..writeln(paddedHex(offset, wordSize))
..write('Virtual address: 0x')
..writeln(paddedHex(vaddr, wordSize))
..write('Physical address: 0x')
..writeln(paddedHex(paddr, wordSize))
..write('Size in file: ')
..writeln(filesz)
..write('Size in memory: ')
..writeln(memsz)
..write('Alignment: 0x')
..write(paddedHex(align, wordSize));
}
String toString() {
final buffer = StringBuffer();
writeToStringBuffer(buffer);
return buffer.toString();
}
}
/// A list of [ProgramHeaderEntry]s describing the memory segments loaded at
/// runtime when this file is used.
class ProgramHeader {
final List<ProgramHeaderEntry> _entries;
ProgramHeader._(this._entries);
int get length => _entries.length;
ProgramHeaderEntry operator [](int index) => _entries[index];
static ProgramHeader fromReader(Reader reader, ElfHeader header) {
final programReader = reader.refocusedCopy(
header.programHeaderOffset, header.programHeaderSize);
final entries =
programReader.readRepeated(ProgramHeaderEntry.fromReader).toList();
return ProgramHeader._(entries);
}
void writeToStringBuffer(StringBuffer buffer) {
for (var i = 0; i < length; i++) {
if (i != 0) buffer..writeln()..writeln();
buffer
..write('Entry ')
..write(i)
..writeln(':');
_entries[i].writeToStringBuffer(buffer);
}
}
String toString() {
final buffer = StringBuffer();
writeToStringBuffer(buffer);
return buffer.toString();
}
}
/// An entry in the [SectionHeader] that describes a single [Section].
class SectionHeaderEntry {
final int nameIndex;
final int type;
final int flags;
final int addr;
final int offset;
final int size;
final int link;
final int info;
final int addrAlign;
final int entrySize;
final int wordSize;
late String name;
SectionHeaderEntry._(
this.nameIndex,
this.type,
this.flags,
this.addr,
this.offset,
this.size,
this.link,
this.info,
this.addrAlign,
this.entrySize,
this.wordSize);
static SectionHeaderEntry fromReader(Reader reader) {
final nameIndex = _readElfWord(reader);
final type = _readElfWord(reader);
final flags = _readElfNative(reader);
final addr = _readElfAddress(reader);
final offset = _readElfOffset(reader);
final size = _readElfNative(reader);
final link = _readElfWord(reader);
final info = _readElfWord(reader);
final addrAlign = _readElfNative(reader);
final entrySize = _readElfNative(reader);
return SectionHeaderEntry._(nameIndex, type, flags, addr, offset, size,
link, info, addrAlign, entrySize, reader.wordSize);
}
// sh_type constants from ELF specification.
static const _SHT_NULL = 0;
static const _SHT_PROGBITS = 1;
static const _SHT_SYMTAB = 2;
static const _SHT_STRTAB = 3;
static const _SHT_HASH = 5;
static const _SHT_DYNAMIC = 6;
static const _SHT_NOTE = 7;
static const _SHT_NOBITS = 8;
static const _SHT_DYNSYM = 11;
void setName(StringTable nameTable) {
name = nameTable[nameIndex]!;
}
static const _typeStrings = <int, String>{
_SHT_NULL: "SHT_NULL",
_SHT_PROGBITS: "SHT_PROGBITS",
_SHT_SYMTAB: "SHT_SYMTAB",
_SHT_STRTAB: "SHT_STRTAB",
_SHT_HASH: "SHT_HASH",
_SHT_DYNAMIC: "SHT_DYNAMIC",
_SHT_NOTE: "SHT_NOTE",
_SHT_NOBITS: "SHT_NOBITS",
_SHT_DYNSYM: "SHT_DYNSYM",
};
static String _typeToString(int type) =>
_typeStrings[type] ?? "unknown (${paddedHex(type, 4)})";
void writeToStringBuffer(StringBuffer buffer) {
buffer.write('Name: ');
buffer
..write('"')
..write(name)
..write('" (@ ')
..write(nameIndex)
..writeln(')');
buffer
..write('Type: ')
..writeln(_typeToString(type))
..write('Flags: 0x')
..writeln(paddedHex(flags, wordSize))
..write('Address: 0x')
..writeln(paddedHex(addr, wordSize))
..write('Offset: 0x')
..writeln(paddedHex(offset, wordSize))
..write('Size: ')
..writeln(size)
..write('Link: ')
..writeln(link)
..write('Info: 0x')
..writeln(paddedHex(info, 4))
..write('Address alignment: 0x')
..writeln(paddedHex(addrAlign, wordSize))
..write('Entry size: ')
..write(entrySize);
}
String toString() {
final buffer = StringBuffer();
writeToStringBuffer(buffer);
return buffer.toString();
}
}
/// A list of [SectionHeaderEntry]s describing the [Section]s in the ELF file.
class SectionHeader {
final List<SectionHeaderEntry> entries;
SectionHeader._(this.entries);
static SectionHeader fromReader(Reader reader, ElfHeader header) {
final headerReader = reader.refocusedCopy(
header.sectionHeaderOffset, header.sectionHeaderSize);
final entries =
headerReader.readRepeated(SectionHeaderEntry.fromReader).toList();
final nameTableEntry = entries[header.sectionHeaderStringsIndex];
assert(nameTableEntry.type == SectionHeaderEntry._SHT_STRTAB);
return SectionHeader._(entries);
}
void writeToStringBuffer(StringBuffer buffer) {
for (var i = 0; i < entries.length; i++) {
if (i != 0) buffer..writeln()..writeln();
buffer
..write('Entry ')
..write(i)
..writeln(':');
entries[i].writeToStringBuffer(buffer);
}
}
@override
String toString() {
final buffer = StringBuffer();
writeToStringBuffer(buffer);
return buffer.toString();
}
}
/// A section in an ELF file.
///
/// Some sections correspond to segments from the [ProgramHeader] and contain
/// the information that will be loaded into memory for that segment, whereas
/// others include information, like debugging sections, that are not loaded
/// at runtime.
///
/// Only some sections are currently parsed by the ELF reader; most are left
/// unparsed as they are not needed for DWARF address translation.
class Section {
final SectionHeaderEntry headerEntry;
Section._(this.headerEntry);
static Section fromReader(Reader reader, SectionHeaderEntry entry) {
switch (entry.type) {
case SectionHeaderEntry._SHT_STRTAB:
return StringTable.fromReader(reader, entry);
case SectionHeaderEntry._SHT_SYMTAB:
return SymbolTable.fromReader(reader, entry);
case SectionHeaderEntry._SHT_DYNSYM:
return SymbolTable.fromReader(reader, entry);
case SectionHeaderEntry._SHT_NOTE:
return Note.fromReader(reader, entry);
default:
return Section._(entry);
}
}
int get offset => headerEntry.offset;
int get virtualAddress => headerEntry.addr;
int get length => headerEntry.size;
// Convenience function for preparing a reader to read a particular section.
Reader refocusedCopy(Reader reader) => reader.refocusedCopy(offset, length);
void writeToStringBuffer(StringBuffer buffer) {
buffer
..write('Section "')
..write(headerEntry.name)
..write('" is unparsed and ')
..write(length)
..writeln(' bytes long.');
}
@override
String toString() {
final buffer = StringBuffer();
writeToStringBuffer(buffer);
return buffer.toString();
}
}
/// A section that contains a single note.
class Note extends Section {
final int type;
final String name;
final Uint8List description;
Note._(entry, this.type, this.name, this.description) : super._(entry);
static Note fromReader(Reader originalReader, SectionHeaderEntry entry) {
final reader = originalReader.refocusedCopy(entry.offset, entry.size);
final nameLength = reader.readBytes(4);
final descriptionLength = reader.readBytes(4);
final type = reader.readBytes(4);
final nameEnd = reader.offset + nameLength;
final name = reader.readNullTerminatedString();
assert(reader.offset == nameEnd);
assert(reader.length - reader.offset == descriptionLength);
final descriptionStart = reader.offset;
final descriptionEnd = descriptionStart + descriptionLength;
final description =
Uint8List.sublistView(reader.bdata, descriptionStart, descriptionEnd);
return Note._(entry, type, name, description);
}
void writeToStringBuffer(StringBuffer buffer) {
buffer
..write('Section "')
..write(headerEntry.name)
..writeln('" is a note:');
buffer
..write(' Type: ')
..writeln(type);
buffer
..write(' Name: "')
..write(name)
..writeln('"');
buffer
..write(' Description: ')
..writeln(description);
}
@override
String toString() {
final buffer = StringBuffer();
writeToStringBuffer(buffer);
return buffer.toString();
}
}
/// A map from table offsets to strings, used to store names of ELF objects.
class StringTable extends Section {
final Map<int, String> _entries;
StringTable._(entry, this._entries) : super._(entry);
static StringTable fromReader(Reader reader, SectionHeaderEntry entry) {
final sectionReader = reader.refocusedCopy(entry.offset, entry.size);
final entries = Map.fromEntries(sectionReader
.readRepeatedWithOffsets((r) => r.readNullTerminatedString()));
return StringTable._(entry, entries);
}
String? operator [](int index) => _entries[index];
bool containsKey(int index) => _entries.containsKey(index);
@override
void writeToStringBuffer(StringBuffer buffer) {
buffer
..write('Section "')
..write(headerEntry.name)
..writeln('" is a string table:');
for (var key in _entries.keys) {
buffer
..write(" ")
..write(key)
..write(" => ")
..writeln(_entries[key]);
}
}
}
enum SymbolBinding {
STB_LOCAL,
STB_GLOBAL,
}
enum SymbolType {
STT_NOTYPE,
STT_OBJECT,
STT_FUNC,
}
enum SymbolVisibility {
STV_DEFAULT,
STV_INTERNAL,
STV_HIDDEN,
STV_PROTECTED,
}
/// A symbol in an ELF file, which names a portion of the virtual address space.
class Symbol {
final int nameIndex;
final int info;
final int other;
final int sectionIndex;
final int value;
final int size;
final int _wordSize;
late String name;
Symbol._(this.nameIndex, this.info, this.other, this.sectionIndex, this.value,
this.size, this._wordSize);
static Symbol fromReader(Reader reader) {
final wordSize = reader.wordSize;
final nameIndex = _readElfWord(reader);
late int info;
late int other;
late int sectionIndex;
if (wordSize == 8) {
info = reader.readByte();
other = reader.readByte();
sectionIndex = _readElfSection(reader);
}
final value = _readElfAddress(reader);
final size = _readElfNative(reader);
if (wordSize == 4) {
info = reader.readByte();
other = reader.readByte();
sectionIndex = _readElfSection(reader);
}
return Symbol._(
nameIndex, info, other, sectionIndex, value, size, wordSize);
}
void _cacheNameFromStringTable(StringTable table) {
final nameFromTable = table[nameIndex];
if (nameFromTable == null) {
throw FormatException("Index $nameIndex not found in string table");
}
name = nameFromTable;
}
SymbolBinding get bind => SymbolBinding.values[info >> 4];
SymbolType get type => SymbolType.values[info & 0x0f];
SymbolVisibility get visibility => SymbolVisibility.values[other & 0x03];
void writeToStringBuffer(StringBuffer buffer) {
buffer..write('"')..write(name)..write('" =>');
switch (bind) {
case SymbolBinding.STB_GLOBAL:
buffer..write(' a global');
break;
case SymbolBinding.STB_LOCAL:
buffer..write(' a local');
break;
}
switch (visibility) {
case SymbolVisibility.STV_DEFAULT:
break;
case SymbolVisibility.STV_HIDDEN:
buffer..write(' hidden');
break;
case SymbolVisibility.STV_INTERNAL:
buffer..write(' internal');
break;
case SymbolVisibility.STV_PROTECTED:
buffer..write(' protected');
break;
}
buffer
..write(" symbol that points to ")
..write(size)
..write(" bytes at location 0x")
..write(paddedHex(value, _wordSize))
..write(" in section ")
..write(sectionIndex);
}
@override
String toString() {
final buffer = StringBuffer();
writeToStringBuffer(buffer);
return buffer.toString();
}
}
/// A table of (static or dynamic) [Symbol]s.
class SymbolTable extends Section {
final List<Symbol> _entries;
final Map<String, Symbol> _nameCache;
SymbolTable._(SectionHeaderEntry entry, this._entries)
: _nameCache = {},
super._(entry);
static SymbolTable fromReader(Reader reader, SectionHeaderEntry entry) {
final sectionReader = reader.refocusedCopy(entry.offset, entry.size);
final entries = sectionReader.readRepeated(Symbol.fromReader).toList();
return SymbolTable._(entry, entries);
}
void _cacheNames(StringTable stringTable) {
_nameCache.clear();
for (final symbol in _entries) {
symbol._cacheNameFromStringTable(stringTable);
_nameCache[symbol.name] = symbol;
}
}
Iterable<String> get keys => _nameCache.keys;
Iterable<Symbol> get values => _entries;
Symbol? operator [](String name) => _nameCache[name];
bool containsKey(String name) => _nameCache.containsKey(name);
@override
void writeToStringBuffer(StringBuffer buffer) {
buffer
..write('Section "')
..write(headerEntry.name)
..writeln('" is a symbol table:');
for (var symbol in _entries) {
buffer.write(" ");
symbol.writeToStringBuffer(buffer);
buffer.writeln();
}
}
}
/// Information parsed from an Executable and Linking Format (ELF) file.
class Elf {
final ElfHeader _header;
final ProgramHeader _programHeader;
final SectionHeader _sectionHeader;
final Map<SectionHeaderEntry, Section> _sections;
final Map<String, Set<Section>> _sectionsByName;
Elf._(this._header, this._programHeader, this._sectionHeader, this._sections,
this._sectionsByName);
/// Creates an [Elf] from [bytes].
///
/// Returns null if the file does not start with the ELF magic number.
static Elf? fromBuffer(Uint8List bytes) =>
Elf.fromReader(Reader.fromTypedData(bytes));
/// Creates an [Elf] from the file at [path].
///
/// Returns null if the file does not start with the ELF magic number.
static Elf? fromFile(String path) => Elf.fromReader(Reader.fromFile(path));
Iterable<Section> namedSections(String name) =>
_sectionsByName[name] ?? <Section>[];
/// Lookup of a dynamic symbol by name.
///
/// Returns -1 if there is no dynamic symbol that matches [name].
Symbol? dynamicSymbolFor(String name) {
for (final section in namedSections(".dynsym")) {
final dynsym = section as SymbolTable;
if (dynsym.containsKey(name)) return dynsym[name];
}
return null;
}
/// Reverse lookup of the static symbol that contains the given virtual
/// address. Returns null if no static symbol matching the address is found.
Symbol? staticSymbolAt(int address) {
for (final section in namedSections('.symtab')) {
final table = section as SymbolTable;
for (final symbol in table.values) {
final start = symbol.value;
final end = start + symbol.size;
if (start <= address && address < end) return symbol;
}
}
return null;
}
/// Creates an [Elf] from the data pointed to by [reader].
///
/// After succesful completion, the [endian] and [wordSize] fields of the
/// reader are set to match the values read from the ELF header. The position
/// of the reader will be unchanged.
///
/// Returns null if the file does not start with the ELF magic number.
static Elf? fromReader(Reader elfReader) {
// ELF files contain absolute offsets from the start of the file, so
// make sure we have a reader that a) makes no assumptions about the
// endianness or word size, since we'll read those in the header and b)
// has an internal offset of 0 so absolute offsets can be used directly.
final reader = Reader.fromTypedData(ByteData.sublistView(
elfReader.bdata, elfReader.bdata.offsetInBytes + elfReader.offset));
final header = ElfHeader.fromReader(reader);
// Only happens if the file didn't start with the expected magic number.
if (header == null) return null;
// At this point, the endianness and wordSize should have been set
// during ElfHeader.fromReader.
final programHeader = ProgramHeader.fromReader(reader, header);
final sectionHeader = SectionHeader.fromReader(reader, header);
final sections = <SectionHeaderEntry, Section>{};
for (var i = 0; i < sectionHeader.entries.length; i++) {
final entry = sectionHeader.entries[i];
sections[entry] = Section.fromReader(reader, entry);
}
// Now set up the by-name section table and cache the names in the section
// header entries.
if (header.sectionHeaderStringsIndex < 0 ||
header.sectionHeaderStringsIndex >= sectionHeader.entries.length) {
throw FormatException("Section header string table index invalid");
}
final sectionHeaderStringTableEntry =
sectionHeader.entries[header.sectionHeaderStringsIndex];
final sectionHeaderStringTable =
sections[sectionHeaderStringTableEntry] as StringTable?;
if (sectionHeaderStringTable == null) {
throw FormatException(
"No section for entry ${sectionHeaderStringTableEntry}");
}
final sectionsByName = <String, Set<Section>>{};
for (final entry in sectionHeader.entries) {
final section = sections[entry];
if (section == null) {
throw FormatException("No section found for entry ${entry}");
}
entry.setName(sectionHeaderStringTable);
sectionsByName.putIfAbsent(entry.name, () => {}).add(section);
}
void _cacheSymbolNames(String stringTableTag, String symbolTableTag) {
final stringTables = sectionsByName[stringTableTag]?.cast<StringTable>();
if (stringTables == null) {
return;
}
final stringTableMap =
Map.fromEntries(stringTables.map((s) => MapEntry(s.headerEntry, s)));
final symbolTables = sectionsByName[symbolTableTag]?.cast<SymbolTable>();
if (symbolTables == null) {
return;
}
for (final symbolTable in symbolTables) {
final link = symbolTable.headerEntry.link;
final entry = sectionHeader.entries[link];
final stringTable = stringTableMap[entry];
if (stringTable == null) {
throw FormatException(
"String table not found at section header entry ${link}");
}
symbolTable._cacheNames(stringTable);
}
}
_cacheSymbolNames('.strtab', '.symtab');
_cacheSymbolNames('.dynstr', '.dynsym');
// Set the wordSize and endian of the original reader before returning.
elfReader.wordSize = reader.wordSize;
elfReader.endian = reader.endian;
return Elf._(
header, programHeader, sectionHeader, sections, sectionsByName);
}
void writeToStringBuffer(StringBuffer buffer) {
buffer
..writeln('-----------------------------------------------------')
..writeln(' ELF header information')
..writeln('-----------------------------------------------------')
..writeln();
_header.writeToStringBuffer(buffer);
buffer
..writeln()
..writeln()
..writeln('-----------------------------------------------------')
..writeln(' Program header information')
..writeln('-----------------------------------------------------')
..writeln();
_programHeader.writeToStringBuffer(buffer);
buffer
..writeln()
..writeln()
..writeln('-----------------------------------------------------')
..writeln(' Section header information')
..writeln('-----------------------------------------------------')
..writeln();
_sectionHeader.writeToStringBuffer(buffer);
buffer
..writeln()
..writeln()
..writeln('-----------------------------------------------------')
..writeln(' Section information')
..writeln('-----------------------------------------------------')
..writeln();
for (final entry in _sectionHeader.entries) {
_sections[entry]!.writeToStringBuffer(buffer);
buffer.writeln();
}
}
@override
String toString() {
StringBuffer buffer = StringBuffer();
writeToStringBuffer(buffer);
return buffer.toString();
}
}