xo65 0.1.0

Library for parsing cc65 object files (xo65 format)
Documentation
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
    }
}

/// 高級言語 (High Level Language, 要するに C) 用のデバッグシンボル。
#[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> {
        // bit0-2: kind
        // bit3-6: storage
        // bit7  : has_sym_asm
        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,
        }
    }
}