use crate::error::{FasError, Result};
use crate::fas::bytes;
use crate::fas::header::Header;
pub const SYMBOL_SIZE: usize = 32;
pub const SYM_DEFINED: u16 = 1;
pub const SYM_VARIABLE: u16 = 2;
pub const SYM_MARKER: u16 = 0x400;
pub const NAME_IN_STRINGS: u32 = 0x8000_0000;
pub const RELOC_EXTERNAL: u32 = 0x8000_0000;
#[derive(Debug, Clone)]
pub struct Symbol {
pub id: usize,
pub value: u64,
pub flags: u16,
pub size: u8,
pub val_type: u8,
pub extended_sib: u32,
pub defined_pass: u16,
pub used_pass: u16,
pub reloc: u32,
pub name_field: u32,
pub def_line_off: u32,
}
impl Symbol {
pub fn parse(rec: &[u8]) -> Result<Self> {
Self::parse_with_id(0, rec)
}
pub fn parse_with_id(id: usize, rec: &[u8]) -> Result<Self> {
if rec.len() < SYMBOL_SIZE {
return Err(FasError::Truncated("symbol"));
}
Ok(Self {
id,
value: bytes::u64_at(rec, 0)?,
flags: bytes::u16_at(rec, 8)?,
size: bytes::u8_at(rec, 10)?,
val_type: bytes::u8_at(rec, 11)?,
extended_sib: bytes::u32_at(rec, 12)?,
defined_pass: bytes::u16_at(rec, 16)?,
used_pass: bytes::u16_at(rec, 18)?,
reloc: bytes::u32_at(rec, 20)?,
name_field: bytes::u32_at(rec, 24)?,
def_line_off: bytes::u32_at(rec, 28)?,
})
}
pub fn is_defined(&self) -> bool {
self.flags & SYM_DEFINED != 0
}
pub fn is_variable(&self) -> bool {
self.flags & SYM_VARIABLE != 0
}
pub fn is_marker(&self) -> bool {
self.flags & SYM_MARKER != 0
}
pub fn is_external(&self) -> bool {
self.reloc & RELOC_EXTERNAL != 0
}
pub fn section_index(&self) -> Option<u32> {
(self.val_type != 0 && !self.is_external()).then_some(self.reloc & !RELOC_EXTERNAL)
}
pub fn external_name_offset(&self) -> Option<u32> {
self.is_external().then_some(self.reloc & !RELOC_EXTERNAL)
}
pub fn external_name<'a>(&self, header: &Header, data: &'a [u8]) -> Result<Option<&'a str>> {
self.external_name_offset()
.map(|offset| header.string_at(data, offset))
.transpose()
}
pub fn is_anonymous(&self) -> bool {
self.name_field == 0
}
pub fn name<'a>(&self, header: &Header, data: &'a [u8]) -> Result<Option<&'a str>> {
if self.is_anonymous() {
return Ok(None);
}
if self.name_field & NAME_IN_STRINGS != 0 {
let off = self.name_field & !NAME_IN_STRINGS;
return header.string_at(data, off).map(Some);
}
let prep = header.preprocessed(data)?;
let off = (self.name_field & !NAME_IN_STRINGS) as usize;
bytes::pascal_at(prep, off).map(Some)
}
}
pub fn parse_symbols(header: &Header, data: &[u8]) -> Result<Vec<Symbol>> {
let raw = header.symbols(data)?;
if raw.len() % SYMBOL_SIZE != 0 {
return Err(FasError::Geometry("symbols not a multiple of 32"));
}
raw.chunks_exact(SYMBOL_SIZE)
.enumerate()
.map(|(id, record)| Symbol::parse_with_id(id, record))
.collect()
}