///|
/// Lazy table of ELF symbol entries.
///
/// Symbol tables are usually paired with a linked `StringTable` that resolves
/// each symbol's `st_name` offset into a human-readable name.
pub struct SymbolTable {
table : ParsingTable[Symbol]
}
///|
fn SymbolTable::SymbolTable(
endian : Endian,
class : Class,
data : BytesView,
) -> SymbolTable {
{
table: ParsingTable(
endian,
class,
data,
Symbol::size_for(class),
Symbol::parse_at,
),
}
}
///|
/// Number of symbols in the table.
pub fn SymbolTable::len(self : SymbolTable) -> Int {
self.table.len()
}
///|
/// Returns `true` when the symbol table has no entries.
pub fn SymbolTable::is_empty(self : SymbolTable) -> Bool {
self.table.is_empty()
}
///|
/// Parse the symbol at `index`.
pub fn SymbolTable::get(
self : SymbolTable,
index : Int,
) -> Symbol raise ParseError {
self.table.get(index)
}
///|
/// Iterate over symbols, parsing each entry lazily.
pub fn SymbolTable::iter(self : SymbolTable) -> Iter[Symbol] {
self.table.iter()
}
///|
/// ELF symbol table entry normalized across ELF32 and ELF64 layouts.
///
/// The `st_name` field is an offset into the symbol table's linked string
/// table. `st_info` packs binding and type, and `st_other` packs visibility.
pub(all) struct Symbol {
/// Offset of the symbol name in the linked string table.
st_name : UInt
/// Section index that defines this symbol, or a special `SHN_*` value.
st_shndx : UInt16
/// Packed symbol binding and symbol type.
st_info : Byte
/// Packed symbol visibility and reserved bits.
st_other : Byte
/// Symbol value: section offset, virtual address, absolute value, or alignment.
st_value : UInt64
/// Symbol size in bytes, or zero when unknown/not applicable.
st_size : UInt64
} derive(Debug, Eq)
///|
/// Returns `true` when the symbol is undefined in this object.
///
/// Undefined symbols are expected to be resolved from another object during
/// static or dynamic linking.
pub fn Symbol::is_undefined(self : Symbol) -> Bool {
self.st_shndx == SHN_UNDEF
}
///|
/// Extract the low-nibble symbol type from `st_info`.
pub fn Symbol::st_symtype(self : Symbol) -> Byte {
self.st_info & 0x0f
}
///|
/// Extract the high-nibble symbol binding from `st_info`.
pub fn Symbol::st_bind(self : Symbol) -> Byte {
self.st_info >> 4
}
///|
/// Extract the two-bit visibility from `st_other`.
pub fn Symbol::st_vis(self : Symbol) -> Byte {
self.st_other & 0x03
}
///|
impl ParseAt for Symbol with parse_at(endian, class, offset, data) {
let end = checked_add(offset, Symbol::size_for(class))
let record = slice_checked(data, offset, end)
let symbol = match (class, endian) {
(ELF32, Little) =>
match record {
[
u32le(st_name),
u32le(st_value32),
u32le(st_size32),
st_info,
st_other,
u16le(st_shndx),
] =>
{
st_name,
st_shndx: st_shndx.to_uint16(),
st_info,
st_other,
st_value: st_value32.to_uint64(),
st_size: st_size32.to_uint64(),
}
_ => raise SliceReadError(offset, end)
}
(ELF32, Big) =>
match record {
[
u32be(st_name),
u32be(st_value32),
u32be(st_size32),
st_info,
st_other,
u16be(st_shndx),
] =>
{
st_name,
st_shndx: st_shndx.to_uint16(),
st_info,
st_other,
st_value: st_value32.to_uint64(),
st_size: st_size32.to_uint64(),
}
_ => raise SliceReadError(offset, end)
}
(ELF64, Little) =>
match record {
[
u32le(st_name),
st_info,
st_other,
u16le(st_shndx),
u64le(st_value),
u64le(st_size),
] =>
{
st_name,
st_shndx: st_shndx.to_uint16(),
st_info,
st_other,
st_value,
st_size,
}
_ => raise SliceReadError(offset, end)
}
(ELF64, Big) =>
match record {
[
u32be(st_name),
st_info,
st_other,
u16be(st_shndx),
u64be(st_value),
u64be(st_size),
] =>
{
st_name,
st_shndx: st_shndx.to_uint16(),
st_info,
st_other,
st_value,
st_size,
}
_ => raise SliceReadError(offset, end)
}
}
(symbol, end)
}
///|
/// Size in bytes of one symbol entry for the given ELF class.
pub fn Symbol::size_for(class : Class) -> Int {
match class {
ELF32 => 16
ELF64 => 24
}
}