use std::ops::ControlFlow;
use probe_rs_target::CoreType;
use crate::unwind_pc_without_debuginfo;
use probe_rs::{
CoreRegister, InstructionSet, MemoryInterface, RegisterDataType, RegisterRole, RegisterValue,
UnwindRule,
};
use super::{DebugError, DebugInfo, DebugRegisters, StackFrame};
pub(crate) mod armv6m;
pub(crate) mod armv6m_armv7m_shared;
pub(crate) mod armv7m;
pub(crate) mod armv8m;
pub(crate) mod riscv;
pub(crate) mod xtensa;
pub fn exception_handler_for_core(core_type: CoreType) -> Box<dyn ExceptionInterface> {
use self::{armv6m, armv7m, armv8m};
match core_type {
CoreType::Armv6m => Box::new(armv6m::ArmV6MExceptionHandler),
CoreType::Armv7m | CoreType::Armv7em => Box::new(armv7m::ArmV7MExceptionHandler),
CoreType::Armv8m => Box::new(armv8m::ArmV8MExceptionHandler),
CoreType::Xtensa => Box::<xtensa::XtensaExceptionHandler>::default(),
CoreType::Riscv | CoreType::Riscv64 => Box::new(riscv::RiscvExceptionHandler),
CoreType::Armv7a | CoreType::Armv7r | CoreType::Armv8a => {
Box::new(UnimplementedExceptionHandler)
}
}
}
pub struct UnimplementedExceptionHandler;
impl ExceptionInterface for UnimplementedExceptionHandler {}
#[derive(PartialEq)]
pub struct ExceptionInfo {
pub raw_exception: u32,
pub description: String,
pub handler_frame: StackFrame,
}
pub trait ExceptionInterface {
fn exception_details(
&self,
_memory: &mut dyn MemoryInterface,
_stackframe_registers: &DebugRegisters,
_debug_info: &DebugInfo,
) -> Result<Option<ExceptionInfo>, DebugError> {
Ok(None)
}
fn calling_frame_registers(
&self,
_memory: &mut dyn MemoryInterface,
_stackframe_registers: &crate::DebugRegisters,
_raw_exception: u32,
) -> Result<crate::DebugRegisters, DebugError> {
Err(DebugError::NotImplemented("calling frame registers"))
}
fn raw_exception(
&self,
_stackframe_registers: &crate::DebugRegisters,
) -> Result<u32, DebugError> {
Err(DebugError::NotImplemented("raw exception"))
}
fn exception_description(
&self,
_raw_exception: u32,
_memory: &mut dyn MemoryInterface,
) -> Result<String, DebugError> {
Err(DebugError::NotImplemented("exception description"))
}
fn unwind_without_debuginfo(
&self,
unwind_registers: &mut DebugRegisters,
frame_pc: u64,
_stack_frames: &[StackFrame],
instruction_set: Option<InstructionSet>,
_memory: &mut dyn MemoryInterface,
) -> ControlFlow<Option<DebugError>> {
unwind_pc_without_debuginfo(unwind_registers, frame_pc, instruction_set)
}
fn unwind_undefined_register(
&self,
debug_register: &CoreRegister,
callee_frame_registers: &DebugRegisters,
unwind_cfa: Option<u64>,
_memory: &mut dyn MemoryInterface,
register_rule: &mut String,
) -> Result<Option<RegisterValue>, DebugError> {
if debug_register.register_has_role(RegisterRole::FramePointer)
&& debug_register.unwind_rule != UnwindRule::Preserve
{
*register_rule = "FP=CFA (dwarf Undefined)".to_string();
return Ok(cfa_as_register(debug_register, unwind_cfa));
}
if debug_register.register_has_role(RegisterRole::StackPointer) {
*register_rule = "SP=CFA (dwarf Undefined)".to_string();
return Ok(cfa_as_register(debug_register, unwind_cfa));
}
if debug_register.register_has_role(RegisterRole::ReturnAddress) {
let current_pc = callee_frame_registers
.get_register_value_by_role(&RegisterRole::ProgramCounter)
.map_err(|_| {
DebugError::Other(
"UNWIND: Tried to unwind return address value where current program counter is unknown."
.to_string(),
)
})?;
let current_lr = callee_frame_registers
.get_register_by_role(&RegisterRole::ReturnAddress)
.ok()
.and_then(|lr| lr.value)
.ok_or_else(|| {
DebugError::Other(
"UNWIND: Tried to unwind return address value where current return address is unknown."
.to_string(),
)
})?;
let current_lr_value: u64 = current_lr.try_into()?;
return Ok(if current_pc == current_lr_value & !0b1 {
*register_rule = "LR=Undefined (dwarf Undefined)".to_string();
None
} else {
*register_rule = "LR=Current LR (dwarf Undefined)".to_string();
Some(current_lr)
});
}
if debug_register.register_has_role(RegisterRole::ProgramCounter) {
unreachable!("The program counter is handled separately")
}
Ok(match debug_register.unwind_rule {
UnwindRule::Preserve => {
*register_rule = "Preserve".to_string();
callee_frame_registers
.get_register(debug_register.id)
.and_then(|reg| reg.value)
}
UnwindRule::Clear => {
*register_rule = "Clear".to_string();
None
}
UnwindRule::SpecialRule => {
*register_rule = "Clear (no unwind rules specified)".to_string();
None
}
})
}
}
fn cfa_as_register(
debug_register: &CoreRegister,
unwind_cfa: Option<u64>,
) -> Option<RegisterValue> {
unwind_cfa.map(|cfa| {
if debug_register.data_type == RegisterDataType::UnsignedInteger(32) {
RegisterValue::U32(cfa as u32 & !0b11)
} else {
RegisterValue::U64(cfa & !0b11)
}
})
}