use super::load_command::SymtabCommand;
use crate::errors::FileParseError;
use crate::field::{ByteOrder, Field};
pub const N_STAB: u8 = 0xe0;
pub const N_TYPE: u8 = 0x0e;
pub const N_EXT: u8 = 0x01;
pub const N_UNDF: u8 = 0x0;
pub const N_SECT: u8 = 0xe;
#[derive(Debug)]
pub struct Nlist {
pub n_strx: Field<u32>,
pub n_type: Field<u8>,
pub n_sect: Field<u8>,
pub n_desc: Field<u16>,
pub n_value: Field<u64>,
pub name: String,
}
impl Nlist {
pub fn is_stab(&self) -> bool {
self.n_type.value & N_STAB != 0
}
pub fn is_external(&self) -> bool {
self.n_type.value & N_EXT != 0
}
pub fn is_undefined(&self) -> bool {
!self.is_stab() && (self.n_type.value & N_TYPE) == N_UNDF
}
}
#[derive(Debug)]
pub struct SymbolTable {
pub offset: usize,
pub string_table_offset: usize,
pub symbols: Vec<Nlist>,
}
impl SymbolTable {
pub fn parse(
buffer: &[u8],
symtab: &SymtabCommand,
is_64: bool,
order: ByteOrder,
) -> Result<Self, FileParseError> {
let symoff = symtab.symoff.value as usize;
let nsyms = symtab.nsyms.value as usize;
let stroff = symtab.stroff.value as usize;
let strsize = symtab.strsize.value as usize;
let entry_size = if is_64 { 16 } else { 12 };
let str_end = stroff
.checked_add(strsize)
.ok_or(FileParseError::BufferOverflow)?;
if buffer.len() < str_end {
return Err(FileParseError::BufferOverflow);
}
let mut symbols = Vec::with_capacity(nsyms);
for i in 0..nsyms {
let off = symoff
.checked_add(i * entry_size)
.ok_or(FileParseError::BufferOverflow)?;
if buffer.len() < off + entry_size {
return Err(FileParseError::BufferOverflow);
}
let n_strx = order.read_u32(buffer, off)?;
let n_type = buffer[off + 4];
let n_sect = buffer[off + 5];
let n_desc = order.read_u16(buffer, off + 6)?;
let n_value = if is_64 {
order.read_u64(buffer, off + 8)?
} else {
order.read_u32(buffer, off + 8)? as u64
};
let value_size = if is_64 { 8 } else { 4 };
let name = resolve_name(buffer, stroff, strsize, n_strx as usize)?;
symbols.push(Nlist {
n_strx: Field::new(n_strx, off, 4),
n_type: Field::new(n_type, off + 4, 1),
n_sect: Field::new(n_sect, off + 5, 1),
n_desc: Field::new(n_desc, off + 6, 2),
n_value: Field::new(n_value, off + 8, value_size),
name,
});
}
Ok(SymbolTable {
offset: symoff,
string_table_offset: stroff,
symbols,
})
}
}
fn resolve_name(
buffer: &[u8],
stroff: usize,
strsize: usize,
n_strx: usize,
) -> Result<String, FileParseError> {
if n_strx == 0 || n_strx >= strsize {
return Ok(String::new());
}
let start = stroff + n_strx;
let end = stroff + strsize;
let slice = buffer
.get(start..end)
.ok_or(FileParseError::BufferOverflow)?;
let stop = slice.iter().position(|&b| b == 0).unwrap_or(slice.len());
Ok(String::from_utf8_lossy(&slice[..stop]).into_owned())
}