use std::ops::ControlFlow;
use crate::{
core::RegisterRole,
debug::{
determine_cfa, get_object_reference, get_unwind_info, stack_frame::StackFrameInfo,
unwind_register, DebugError, DebugInfo, DebugRegisters, StackFrame,
},
Error, MemoryInterface, RegisterValue,
};
use bitfield::bitfield;
use probe_rs_target::InstructionSet;
use super::{ExceptionInfo, ExceptionInterface};
pub(crate) static EXCEPTION_STACK_REGISTERS: &[RegisterRole] = &[
RegisterRole::Core("R0"),
RegisterRole::Core("R1"),
RegisterRole::Core("R2"),
RegisterRole::Core("R3"),
RegisterRole::Core("R12"),
RegisterRole::ReturnAddress,
RegisterRole::ProgramCounter,
RegisterRole::ProcessorStatus,
];
bitfield! {
pub struct ExcReturn(u32);
impl Debug;
pub is_exception_flag, _: 31, 28;
pub use_standard_stackframe, _: 4;
pub return_to_thread, _: 3;
pub use_process_stack, _: 2;
pub always_0b01, _: 1,0;
}
bitfield! {
#[derive(Copy, Clone)]
pub struct Xpsr(u32);
impl Debug;
pub apsr_n_bit, _: 31;
pub apsr_z_bit, _: 30;
pub apsr_c_bit, _: 29;
pub apsr_v_bit, _: 28;
pub stack_was_realigned, _:9;
pub exception_number, _: 8,0;
}
pub(crate) fn exception_details(
exception_interface: &impl ExceptionInterface,
memory_interface: &mut dyn MemoryInterface,
stackframe_registers: &DebugRegisters,
debug_info: &DebugInfo,
) -> Result<Option<ExceptionInfo>, DebugError> {
let frame_return_address = get_stack_frame_return_address(stackframe_registers)?;
if ExcReturn(frame_return_address).is_exception_flag() != 0xF {
return Ok(None);
}
let raw_exception = exception_interface.raw_exception(stackframe_registers)?;
let registers = exception_interface.calling_frame_registers(
memory_interface,
stackframe_registers,
raw_exception,
)?;
let description = exception_interface.exception_description(raw_exception, memory_interface)?;
let exception_frame_pc = registers.get_register_value_by_role(&RegisterRole::ProgramCounter)?;
let mut handler_frame = StackFrame {
id: get_object_reference(),
function_name: description.clone(),
source_location: None,
registers,
pc: RegisterValue::U32(exception_frame_pc as u32),
frame_base: None,
is_inlined: false,
local_variables: None,
canonical_frame_address: None,
};
if raw_exception == 3 {
let mut unwind_context = Box::new(gimli::UnwindContext::new());
let unwind_info = get_unwind_info(
&mut unwind_context,
&debug_info.frame_section,
exception_frame_pc,
)?;
handler_frame.canonical_frame_address =
determine_cfa(&handler_frame.registers, unwind_info)?;
let Ok((_, functions)) = debug_info.get_function_dies(exception_frame_pc) else {
handler_frame.function_name = format!("{} : ERROR: While resolving function information for the program counter ({exception_frame_pc:#010x}) that caused the exception.", handler_frame.function_name);
return Ok(Some(ExceptionInfo {
raw_exception,
description,
handler_frame,
}));
};
if functions.is_empty() {
handler_frame.function_name = format!("{} : ERROR: No function information for the program counter ({exception_frame_pc:#010x}) that caused the exception.", handler_frame.function_name);
return Ok(Some(ExceptionInfo {
raw_exception,
description,
handler_frame,
}));
}
handler_frame.frame_base = functions[0].frame_base(
debug_info,
memory_interface,
StackFrameInfo {
registers: &handler_frame.registers,
frame_base: None,
canonical_frame_address: handler_frame.canonical_frame_address,
},
)?;
let callee_frame_registers = handler_frame.registers.clone();
if let ControlFlow::Break(error) = unwind_register(
handler_frame
.registers
.get_register_mut_by_role(&RegisterRole::FramePointer)?,
&callee_frame_registers,
Some(unwind_info),
handler_frame.canonical_frame_address,
&mut None,
memory_interface,
Some(InstructionSet::Thumb2),
) {
tracing::error!("{:?}", &error);
handler_frame.function_name =
format!("{} : ERROR: {error}", handler_frame.function_name);
};
let frame_size = if ExcReturn(frame_return_address).use_standard_stackframe() {
0x20usize
} else {
0x68
};
let sp = handler_frame
.registers
.get_register_mut_by_role(&RegisterRole::StackPointer)?;
if let Some(sp_value) = sp.value.as_mut() {
sp_value.increment_address(frame_size)?;
}
}
Ok(Some(ExceptionInfo {
raw_exception,
description,
handler_frame,
}))
}
fn get_stack_frame_return_address(stackframe_registers: &DebugRegisters) -> Result<u32, Error> {
let frame_return_address: u32 = stackframe_registers
.get_return_address()
.ok_or_else(|| {
Error::Register("No Return Address register. Please report this as a bug.".to_string())
})?
.value
.ok_or_else(|| {
Error::Register(
"No value for Return Address register. Please report this as a bug.".to_string(),
)
})?
.try_into()?;
Ok(frame_return_address)
}
pub(crate) fn raw_exception(
stackframe_registers: &crate::debug::DebugRegisters,
) -> Result<u32, Error> {
let mut exception_number = Xpsr(
stackframe_registers.get_register_value_by_role(&RegisterRole::ProcessorStatus)? as u32,
)
.exception_number();
if exception_number == 0
&& stackframe_registers.get_register_value_by_role(&RegisterRole::ReturnAddress)?
== 0xFFFF_FFFF
{
exception_number = 1;
}
Ok(exception_number)
}
pub(crate) fn calling_frame_registers(
memory: &mut dyn MemoryInterface,
stackframe_registers: &crate::debug::DebugRegisters,
_raw_exception: u32,
) -> Result<crate::debug::DebugRegisters, crate::Error> {
let exception_context_address: u32 =
stackframe_registers.get_register_value_by_role(&RegisterRole::StackPointer)? as u32;
let mut calling_stack_registers = vec![0u32; EXCEPTION_STACK_REGISTERS.len()];
memory.read_32(
(exception_context_address).into(),
&mut calling_stack_registers,
)?;
let mut calling_frame_registers = stackframe_registers.clone();
for (i, register_role) in EXCEPTION_STACK_REGISTERS.iter().enumerate() {
calling_frame_registers
.get_register_mut_by_role(register_role)?
.value = Some(RegisterValue::U32(calling_stack_registers[i]));
}
Ok(calling_frame_registers)
}