use crate::expr::Expr;
use crate::parse::{parse_err, parse_uleb128_at, ParseAt, ParseErrorContext as _, ParseResult};
use crate::xo65::Xo65Bytes;
#[derive(Debug)]
pub struct DebugSymbolTable<'data> {
bytes: Xo65Bytes<'data>,
syms_asm: Box<[DebugSymbolAsm]>,
syms_hll: Box<[DebugSymbolHll]>,
}
impl<'data> DebugSymbolTable<'data> {
pub fn as_bytes(&self) -> &'data [u8] {
self.bytes.as_inner()
}
pub fn count_asm(&self) -> usize {
self.syms_asm.len()
}
pub fn get_asm(&self, idx: usize) -> Option<&DebugSymbolAsm> {
self.syms_asm.get(idx)
}
pub fn iter_asm(
&self,
) -> impl DoubleEndedIterator<Item = &DebugSymbolAsm>
+ ExactSizeIterator
+ std::iter::FusedIterator
+ Clone {
self.syms_asm.iter()
}
pub fn count_hll(&self) -> usize {
self.syms_hll.len()
}
pub fn get_hll(&self, idx: usize) -> Option<&DebugSymbolHll> {
self.syms_hll.get(idx)
}
pub fn iter_hll(
&self,
) -> impl DoubleEndedIterator<Item = &DebugSymbolHll>
+ ExactSizeIterator
+ std::iter::FusedIterator
+ Clone {
self.syms_hll.iter()
}
pub(crate) fn parse(bytes: Xo65Bytes<'data>) -> ParseResult<Self> {
let mut off = 0;
let syms_asm = Self::parse_syms_asm(&bytes, &mut off)?;
let syms_hll = Self::parse_syms_hll(&bytes, &mut off)?;
Ok(Self {
bytes,
syms_asm,
syms_hll,
})
}
fn parse_syms_asm(
bytes: &Xo65Bytes<'data>,
off: &mut usize,
) -> ParseResult<Box<[DebugSymbolAsm]>> {
let count = parse_uleb128_at(bytes, off)? as usize;
let mut syms_asm = Vec::<DebugSymbolAsm>::with_capacity(count);
for i in 0..count {
let sym_asm = DebugSymbolAsm::parse_at(bytes, off)
.with_context(|| format!("asm debug symbol {i} parse error"))?;
syms_asm.push(sym_asm);
}
Ok(syms_asm.into())
}
fn parse_syms_hll(
bytes: &Xo65Bytes<'data>,
off: &mut usize,
) -> ParseResult<Box<[DebugSymbolHll]>> {
let count = parse_uleb128_at(bytes, off)? as usize;
let mut syms_hll = Vec::<DebugSymbolHll>::with_capacity(count);
for i in 0..count {
let sym_hll = DebugSymbolHll::parse_at(bytes, off)
.with_context(|| format!("hll debug symbol {i} parse error"))?;
syms_hll.push(sym_hll);
}
Ok(syms_hll.into())
}
}
#[derive(Debug)]
pub struct DebugSymbolAsm {
owner: u32,
name: u32,
info: DebugSymbolAsmInfo,
addr_size: u8,
size: Option<u32>,
expr: Expr,
import: Option<u32>,
export: Option<u32>,
def_lines: Box<[u32]>,
ref_lines: Box<[u32]>,
}
impl DebugSymbolAsm {
pub fn owner(&self) -> u32 {
self.owner
}
pub fn name(&self) -> u32 {
self.name
}
pub fn info(&self) -> DebugSymbolAsmInfo {
self.info
}
pub fn addr_size(&self) -> u8 {
self.addr_size
}
pub fn size(&self) -> Option<u32> {
self.size
}
pub fn expr(&self) -> &Expr {
&self.expr
}
pub fn import(&self) -> Option<u32> {
self.import
}
pub fn export(&self) -> Option<u32> {
self.export
}
pub fn def_lines(&self) -> &[u32] {
&self.def_lines
}
pub fn ref_lines(&self) -> &[u32] {
&self.ref_lines
}
}
impl ParseAt<'_> for DebugSymbolAsm {
fn parse_at(bytes: &Xo65Bytes<'_>, off: &mut usize) -> ParseResult<Self> {
let info = parse_uleb128_at(bytes, off).map(DebugSymbolAsmInfo)?;
let addr_size = u8::parse_at(bytes, off)?;
let owner = parse_uleb128_at(bytes, off)?;
let name = parse_uleb128_at(bytes, off)?;
let expr = if info.is_const() {
let value = i32::parse_at(bytes, off)?;
Expr::literal(i64::from(value))
} else {
Expr::parse_at(bytes, off)?
};
let size = info
.has_size()
.then(|| parse_uleb128_at(bytes, off))
.transpose()?;
let import = info
.is_import()
.then(|| parse_uleb128_at(bytes, off))
.transpose()?;
let export = info
.is_export()
.then(|| parse_uleb128_at(bytes, off))
.transpose()?;
let def_lines = parse_lines_at(bytes, off)?;
let ref_lines = parse_lines_at(bytes, off)?;
Ok(Self {
owner,
name,
info,
addr_size,
size,
expr,
import,
export,
def_lines,
ref_lines,
})
}
}
fn parse_lines_at(bytes: &Xo65Bytes<'_>, off: &mut usize) -> ParseResult<Box<[u32]>> {
let count = parse_uleb128_at(bytes, off)? as usize;
let mut lines = Vec::<u32>::with_capacity(count);
for _ in 0..count {
let line = parse_uleb128_at(bytes, off)?;
lines.push(line);
}
Ok(lines.into())
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct DebugSymbolAsmInfo(u32);
impl DebugSymbolAsmInfo {
pub fn get(self) -> u32 {
self.0
}
pub fn has_size(self) -> bool {
(self.0 & (1 << 3)) != 0
}
pub fn is_const(self) -> bool {
(self.0 & (1 << 4)) == 0
}
pub fn is_expr(self) -> bool {
!self.is_const()
}
pub fn is_label(self) -> bool {
(self.0 & (1 << 5)) != 0
}
pub fn is_cheap_local(self) -> bool {
(self.0 & (1 << 6)) != 0
}
pub fn is_export(self) -> bool {
(self.0 & (1 << 7)) != 0
}
pub fn is_import(self) -> bool {
(self.0 & (1 << 8)) != 0
}
}
#[derive(Debug)]
pub struct DebugSymbolHll {
scope: u32,
name: u32,
kind: DebugSymbolHllKind,
storage: DebugSymbolHllStorage,
ty: u32,
off: Option<u32>,
sym_asm: Option<u32>,
}
impl DebugSymbolHll {
pub fn scope(&self) -> u32 {
self.scope
}
pub fn name(&self) -> u32 {
self.name
}
pub fn kind(&self) -> DebugSymbolHllKind {
self.kind
}
pub fn storage(&self) -> DebugSymbolHllStorage {
self.storage
}
pub fn ty(&self) -> u32 {
self.ty
}
pub fn offset(&self) -> Option<u32> {
self.off
}
pub fn symbol_asm(&self) -> Option<u32> {
self.sym_asm
}
}
impl ParseAt<'_> for DebugSymbolHll {
fn parse_at(bytes: &Xo65Bytes<'_>, off: &mut usize) -> ParseResult<Self> {
let (kind, storage, has_sym_asm) = {
let value = parse_uleb128_at(bytes, off)?;
let kind = (value & 0x7) as u8;
let kind = DebugSymbolHllKind::new(kind)
.ok_or_else(|| parse_err!("unknown hll debug symbol kind: {kind:#X}"))?;
let storage = ((value >> 3) & 0xF) as u8;
let storage = DebugSymbolHllStorage::new(storage).ok_or_else(|| {
parse_err!("unknown hll debug symbol storage class: {storage:#X}")
})?;
let has_sym_asm = (value & (1 << 7)) != 0;
(kind, storage, has_sym_asm)
};
let name = parse_uleb128_at(bytes, off)?;
let sym_asm = has_sym_asm
.then(|| parse_uleb128_at(bytes, off))
.transpose()?;
let has_off = matches!(
storage,
DebugSymbolHllStorage::Auto | DebugSymbolHllStorage::Register
);
let sym_hll_off = has_off.then(|| parse_uleb128_at(bytes, off)).transpose()?;
let ty = parse_uleb128_at(bytes, off)?;
let scope = parse_uleb128_at(bytes, off)?;
Ok(Self {
scope,
name,
kind,
storage,
ty,
off: sym_hll_off,
sym_asm,
})
}
}
#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum DebugSymbolHllKind {
Function = 0,
Symbol = 1,
}
impl DebugSymbolHllKind {
fn new(inner: u8) -> Option<Self> {
match inner {
0 => Some(Self::Function),
1 => Some(Self::Symbol),
_ => None,
}
}
}
#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum DebugSymbolHllStorage {
Auto = 0,
Register = 1,
Static = 2,
Extern = 3,
}
impl DebugSymbolHllStorage {
fn new(inner: u8) -> Option<Self> {
match inner {
0 => Some(Self::Auto),
1 => Some(Self::Register),
2 => Some(Self::Static),
3 => Some(Self::Extern),
_ => None,
}
}
}