///|
/// ELF word-size class decoded from `e_ident[EI_CLASS]`.
///
/// The class determines the on-disk layout and field widths for headers,
/// symbols, relocations, and compression records.
pub(all) enum Class {
/// 32-bit ELF layout (`ELFCLASS32`).
ELF32
/// 64-bit ELF layout (`ELFCLASS64`).
ELF64
} derive(Debug, Eq)
///|
/// Parsed ELF file header.
///
/// The ELF file header identifies the file, target architecture, byte order,
/// entry point, and the locations of the program and section header tables.
/// Width-dependent fields are widened to `UInt64` where needed so callers can
/// use one type for both `ELF32` and `ELF64` inputs.
pub(all) struct FileHeader {
/// 32-bit or 64-bit object layout.
class : Class
/// Byte order used by multi-byte integer fields.
endianness : Endian
/// ELF object version, normally `EV_CURRENT`.
version : UInt
/// Operating-system ABI identifier from `e_ident[EI_OSABI]`.
osabi : Byte
/// ABI version byte from `e_ident[EI_ABIVERSION]`.
abiversion : Byte
/// Object file type such as `ET_EXEC`, `ET_DYN`, or `ET_REL`.
e_type : UInt16
/// Target instruction-set architecture, such as `EM_X86_64`.
e_machine : UInt16
/// Virtual address where execution starts, or zero if there is no entry point.
e_entry : UInt64
/// File offset of the program header table, or zero if absent.
e_phoff : UInt64
/// File offset of the section header table, or zero if absent.
e_shoff : UInt64
/// Processor-specific file flags.
e_flags : UInt
/// Size of this ELF header in bytes.
e_ehsize : UInt16
/// Size of one program header table entry in bytes.
e_phentsize : UInt16
/// Number of program header entries, with `PN_XNUM` meaning extended count.
e_phnum : UInt16
/// Size of one section header table entry in bytes.
e_shentsize : UInt16
/// Number of section headers, with zero meaning an extended count in section 0.
e_shnum : UInt16
/// Section-header index of the section-name string table.
e_shstrndx : UInt16
} derive(Debug, Eq)
///|
fn verify_ident(buf : BytesView) -> Unit raise ParseError {
// The ELF identity starts with four fixed magic bytes followed by class,
// data encoding, version, OS ABI, and padding bytes.
let magic = slice_checked(buf, 0, EI_CLASS)
if magic != ELFMAGIC[:] {
raise BadMagic(magic.to_owned())
}
let version = buf[EI_VERSION]
if version != EV_CURRENT {
raise UnsupportedVersion(version.to_uint64(), EV_CURRENT.to_uint64())
}
}
///|
fn parse_ident(
data : BytesView,
) -> (Endian, Class, Byte, Byte) raise ParseError {
verify_ident(data)
let class = match data[EI_CLASS] {
ELFCLASS32 => ELF32
ELFCLASS64 => ELF64
other => raise UnsupportedElfClass(other)
}
let endian = Endian::from_ei_data(data[EI_DATA])
(endian, class, data[EI_OSABI], data[EI_ABIVERSION])
}
///|
fn FileHeader::parse_tail(
ident : (Endian, Class, Byte, Byte),
data : BytesView,
) -> FileHeader raise ParseError {
let (endian, class, osabi, abiversion) = ident
let size = match class {
ELF32 => ELF32_EHDR_TAILSIZE
ELF64 => ELF64_EHDR_TAILSIZE
}
let end = checked_add(0, size)
let record = slice_checked(data, 0, end)
let (
e_type,
e_machine,
version,
e_entry,
e_phoff,
e_shoff,
e_flags,
e_ehsize,
e_phentsize,
e_phnum,
e_shentsize,
e_shnum,
e_shstrndx,
) = match (class, endian) {
(ELF32, Little) =>
match record {
[
u16le(e_type),
u16le(e_machine),
u32le(version),
u32le(entry32),
u32le(phoff32),
u32le(shoff32),
u32le(e_flags),
u16le(e_ehsize),
u16le(e_phentsize),
u16le(e_phnum),
u16le(e_shentsize),
u16le(e_shnum),
u16le(e_shstrndx),
] =>
(
e_type.to_uint16(),
e_machine.to_uint16(),
version,
entry32.to_uint64(),
phoff32.to_uint64(),
shoff32.to_uint64(),
e_flags,
e_ehsize.to_uint16(),
e_phentsize.to_uint16(),
e_phnum.to_uint16(),
e_shentsize.to_uint16(),
e_shnum.to_uint16(),
e_shstrndx.to_uint16(),
)
_ => raise SliceReadError(0, end)
}
(ELF32, Big) =>
match record {
[
u16be(e_type),
u16be(e_machine),
u32be(version),
u32be(entry32),
u32be(phoff32),
u32be(shoff32),
u32be(e_flags),
u16be(e_ehsize),
u16be(e_phentsize),
u16be(e_phnum),
u16be(e_shentsize),
u16be(e_shnum),
u16be(e_shstrndx),
] =>
(
e_type.to_uint16(),
e_machine.to_uint16(),
version,
entry32.to_uint64(),
phoff32.to_uint64(),
shoff32.to_uint64(),
e_flags,
e_ehsize.to_uint16(),
e_phentsize.to_uint16(),
e_phnum.to_uint16(),
e_shentsize.to_uint16(),
e_shnum.to_uint16(),
e_shstrndx.to_uint16(),
)
_ => raise SliceReadError(0, end)
}
(ELF64, Little) =>
match record {
[
u16le(e_type),
u16le(e_machine),
u32le(version),
u64le(e_entry),
u64le(e_phoff),
u64le(e_shoff),
u32le(e_flags),
u16le(e_ehsize),
u16le(e_phentsize),
u16le(e_phnum),
u16le(e_shentsize),
u16le(e_shnum),
u16le(e_shstrndx),
] =>
(
e_type.to_uint16(),
e_machine.to_uint16(),
version,
e_entry,
e_phoff,
e_shoff,
e_flags,
e_ehsize.to_uint16(),
e_phentsize.to_uint16(),
e_phnum.to_uint16(),
e_shentsize.to_uint16(),
e_shnum.to_uint16(),
e_shstrndx.to_uint16(),
)
_ => raise SliceReadError(0, end)
}
(ELF64, Big) =>
match record {
[
u16be(e_type),
u16be(e_machine),
u32be(version),
u64be(e_entry),
u64be(e_phoff),
u64be(e_shoff),
u32be(e_flags),
u16be(e_ehsize),
u16be(e_phentsize),
u16be(e_phnum),
u16be(e_shentsize),
u16be(e_shnum),
u16be(e_shstrndx),
] =>
(
e_type.to_uint16(),
e_machine.to_uint16(),
version,
e_entry,
e_phoff,
e_shoff,
e_flags,
e_ehsize.to_uint16(),
e_phentsize.to_uint16(),
e_phnum.to_uint16(),
e_shentsize.to_uint16(),
e_shnum.to_uint16(),
e_shstrndx.to_uint16(),
)
_ => raise SliceReadError(0, end)
}
}
if version != EV_CURRENT.to_uint() {
raise UnsupportedVersion(version.to_uint64(), EV_CURRENT.to_uint64())
}
{
class,
endianness: endian,
version,
osabi,
abiversion,
e_type,
e_machine,
e_entry,
e_phoff,
e_shoff,
e_flags,
e_ehsize,
e_phentsize,
e_phnum,
e_shentsize,
e_shnum,
e_shstrndx,
}
}