use super::header::ElfClass;
use crate::errors::FileParseError;
use crate::field::{ByteOrder, Field, FieldMut, NumericFieldMut};
pub const STB_LOCAL: u8 = 0;
pub const STB_GLOBAL: u8 = 1;
pub const STB_WEAK: u8 = 2;
pub const STT_NOTYPE: u8 = 0;
pub const STT_OBJECT: u8 = 1;
pub const STT_FUNC: u8 = 2;
pub const STT_SECTION: u8 = 3;
pub const STT_FILE: u8 = 4;
#[derive(Debug)]
pub struct Sym32Fields {
pub st_name: Field<u32>,
pub st_value: Field<u32>,
pub st_size: Field<u32>,
pub st_info: Field<u8>,
pub st_other: Field<u8>,
pub st_shndx: Field<u16>,
}
#[derive(Debug)]
pub struct Sym64Fields {
pub st_name: Field<u32>,
pub st_info: Field<u8>,
pub st_other: Field<u8>,
pub st_shndx: Field<u16>,
pub st_value: Field<u64>,
pub st_size: Field<u64>,
}
#[derive(Debug)]
pub enum SymbolEntry {
Sym32(Sym32Fields),
Sym64(Sym64Fields),
}
impl SymbolEntry {
pub fn size_of(class: ElfClass) -> usize {
match class {
ElfClass::Elf32 => 16,
ElfClass::Elf64 => 24,
}
}
pub fn st_name(&self) -> u32 {
match self {
SymbolEntry::Sym32(f) => f.st_name.value,
SymbolEntry::Sym64(f) => f.st_name.value,
}
}
pub fn st_name_mut(&mut self) -> FieldMut<'_, u32> {
match self {
SymbolEntry::Sym32(f) => FieldMut::<u32>::new(&mut f.st_name),
SymbolEntry::Sym64(f) => FieldMut::<u32>::new(&mut f.st_name),
}
}
pub fn st_value(&self) -> u64 {
match self {
SymbolEntry::Sym32(f) => f.st_value.value as u64,
SymbolEntry::Sym64(f) => f.st_value.value,
}
}
pub fn st_value_mut(&mut self) -> NumericFieldMut<'_> {
match self {
SymbolEntry::Sym32(f) => NumericFieldMut::U32(&mut f.st_value),
SymbolEntry::Sym64(f) => NumericFieldMut::U64(&mut f.st_value),
}
}
pub fn st_size(&self) -> u64 {
match self {
SymbolEntry::Sym32(f) => f.st_size.value as u64,
SymbolEntry::Sym64(f) => f.st_size.value,
}
}
pub fn st_size_mut(&mut self) -> NumericFieldMut<'_> {
match self {
SymbolEntry::Sym32(f) => NumericFieldMut::U32(&mut f.st_size),
SymbolEntry::Sym64(f) => NumericFieldMut::U64(&mut f.st_size),
}
}
pub fn st_info(&self) -> u8 {
match self {
SymbolEntry::Sym32(f) => f.st_info.value,
SymbolEntry::Sym64(f) => f.st_info.value,
}
}
pub fn st_other(&self) -> u8 {
match self {
SymbolEntry::Sym32(f) => f.st_other.value,
SymbolEntry::Sym64(f) => f.st_other.value,
}
}
pub fn st_shndx(&self) -> u16 {
match self {
SymbolEntry::Sym32(f) => f.st_shndx.value,
SymbolEntry::Sym64(f) => f.st_shndx.value,
}
}
pub fn binding(&self) -> u8 {
self.st_info() >> 4
}
pub fn symbol_type(&self) -> u8 {
self.st_info() & 0xf
}
pub(crate) fn parse_table(
buffer: &[u8],
offset: usize,
count: usize,
class: ElfClass,
order: ByteOrder,
) -> Result<Vec<SymbolEntry>, FileParseError> {
let ent_size = Self::size_of(class);
let mut symbols = Vec::with_capacity(count);
for i in 0..count {
let base = offset + i * ent_size;
if buffer.len() < base + ent_size {
return Err(FileParseError::BufferOverflow);
}
let entry = match class {
ElfClass::Elf32 => SymbolEntry::Sym32(Sym32Fields {
st_name: Field::new(order.read_u32(buffer, base)?, base, 4),
st_value: Field::new(order.read_u32(buffer, base + 4)?, base + 4, 4),
st_size: Field::new(order.read_u32(buffer, base + 8)?, base + 8, 4),
st_info: Field::new(buffer[base + 12], base + 12, 1),
st_other: Field::new(buffer[base + 13], base + 13, 1),
st_shndx: Field::new(order.read_u16(buffer, base + 14)?, base + 14, 2),
}),
ElfClass::Elf64 => SymbolEntry::Sym64(Sym64Fields {
st_name: Field::new(order.read_u32(buffer, base)?, base, 4),
st_info: Field::new(buffer[base + 4], base + 4, 1),
st_other: Field::new(buffer[base + 5], base + 5, 1),
st_shndx: Field::new(order.read_u16(buffer, base + 6)?, base + 6, 2),
st_value: Field::new(order.read_u64(buffer, base + 8)?, base + 8, 8),
st_size: Field::new(order.read_u64(buffer, base + 16)?, base + 16, 8),
}),
};
symbols.push(entry);
}
Ok(symbols)
}
}
pub struct SymbolTable {
pub offset: usize,
pub strtab_offset: usize,
pub symbols: Vec<SymbolEntry>,
}
impl SymbolTable {
pub fn name(&self, buffer: &[u8], symbol: &SymbolEntry) -> Result<String, FileParseError> {
super::read_str_at(buffer, self.strtab_offset + symbol.st_name() as usize)
}
}