1use crate::architecture::arm::core::registers::aarch32::{
2 AARCH32_CORE_REGISTERS, AARCH32_WITH_FP_16_CORE_REGISTERS, AARCH32_WITH_FP_32_CORE_REGISTERS,
3};
4use crate::architecture::arm::core::registers::aarch64::AARCH64_CORE_REGISTERS;
5use crate::architecture::arm::core::registers::cortex_m::{
6 CORTEX_M_CORE_REGISTERS, CORTEX_M_WITH_FP_CORE_REGISTERS,
7};
8use crate::architecture::riscv::registers::{RISCV_CORE_REGISTERS, RISCV_WITH_FP_CORE_REGISTERS};
9use crate::architecture::xtensa::arch::{Register as XtensaRegister, SpecialRegister};
10use crate::architecture::xtensa::registers::XTENSA_CORE_REGISTERS;
11use crate::{Core, CoreRegisters, CoreType, Error, InstructionSet, MemoryInterface};
12use crate::{RegisterId, RegisterValue};
13use object::elf::PT_NOTE;
14use object::read::elf::ProgramHeader;
15use object::{Object, ObjectSegment};
16use probe_rs_target::MemoryRange;
17use scroll::Cread;
18use serde::{Deserialize, Serialize};
19use std::array;
20use std::sync::LazyLock;
21use std::{
22 collections::HashMap,
23 fs::OpenOptions,
24 ops::Range,
25 path::{Path, PathBuf},
26};
27
28trait Processor {
29 fn instruction_set(&self) -> InstructionSet;
31
32 fn core_type(&self) -> CoreType;
34
35 fn supports_native_64bit_access(&self) -> bool {
37 false
38 }
39
40 fn register_data_len(&self) -> usize;
42
43 fn register_map(&self) -> &[(usize, RegisterId)];
47
48 fn read_registers(
50 &self,
51 note_data: &[u8],
52 registers: &mut HashMap<RegisterId, RegisterValue>,
53 ) -> Result<(), CoreDumpError> {
54 for (offset, reg_id) in self.register_map().iter().copied() {
55 let value = self.read_register(note_data, offset)?;
56 registers.insert(reg_id, value);
57 }
58
59 Ok(())
60 }
61
62 fn read_register(&self, note_data: &[u8], idx: usize) -> Result<RegisterValue, CoreDumpError> {
65 let value = u32::from_le_bytes(note_data[idx * 4..][..4].try_into().unwrap());
66 Ok(RegisterValue::U32(value))
67 }
68}
69
70struct XtensaProcessor;
71impl Processor for XtensaProcessor {
72 fn instruction_set(&self) -> InstructionSet {
73 InstructionSet::Xtensa
74 }
75 fn core_type(&self) -> CoreType {
76 CoreType::Xtensa
77 }
78 fn register_data_len(&self) -> usize {
79 128 * 4
80 }
81 fn register_map(&self) -> &[(usize, RegisterId)] {
82 static REGS: LazyLock<[(usize, RegisterId); 24]> = LazyLock::new(|| {
83 let core_regs = &XTENSA_CORE_REGISTERS;
84
85 array::from_fn(|idx| {
86 match idx {
87 0 => (idx, RegisterId::from(XtensaRegister::CurrentPc)),
89 1 => (idx, RegisterId::from(SpecialRegister::Ps)),
90 2 => (idx, RegisterId::from(SpecialRegister::Lbeg)),
91 3 => (idx, RegisterId::from(SpecialRegister::Lend)),
92 4 => (idx, RegisterId::from(SpecialRegister::Lcount)),
93 5 => (idx, RegisterId::from(SpecialRegister::Sar)),
94 6 => (idx, RegisterId::from(SpecialRegister::Windowstart)),
95 7 => (idx, RegisterId::from(SpecialRegister::Windowbase)),
96 8..24 => {
98 let ar_idx = idx - 8;
101 (ar_idx + 64, core_regs.core_register(ar_idx).id())
102 }
103 _ => unreachable!(),
104 }
105 })
106 });
107
108 &*REGS
109 }
110}
111
112struct RiscvProcessor;
113impl Processor for RiscvProcessor {
114 fn instruction_set(&self) -> InstructionSet {
115 InstructionSet::RV32
116 }
117 fn core_type(&self) -> CoreType {
118 CoreType::Riscv
119 }
120 fn register_data_len(&self) -> usize {
121 32 * 4
122 }
123 fn register_map(&self) -> &[(usize, RegisterId)] {
124 static REGS: LazyLock<[(usize, RegisterId); 32]> = LazyLock::new(|| {
125 let core_regs = &RISCV_CORE_REGISTERS;
126
127 array::from_fn(|idx| {
128 let regid = if idx == 0 {
130 core_regs.pc().unwrap().id()
131 } else {
132 core_regs.core_register(idx).id()
133 };
134 (idx, regid)
135 })
136 });
137 &*REGS
138 }
139}
140
141#[derive(Debug, Clone, Serialize, Deserialize)]
143pub struct CoreDump {
144 pub registers: HashMap<RegisterId, RegisterValue>,
146 pub data: Vec<(Range<u64>, Vec<u8>)>,
148 pub instruction_set: InstructionSet,
150 pub supports_native_64bit_access: bool,
152 pub core_type: CoreType,
154 pub fpu_support: bool,
156 pub floating_point_register_count: Option<usize>,
158}
159
160impl CoreDump {
161 pub fn dump_core(core: &mut Core<'_>, ranges: Vec<Range<u64>>) -> Result<Self, Error> {
167 core.spill_registers()?;
168
169 let mut registers = HashMap::new();
170 for register in core.registers().all_registers() {
171 let value = core.read_core_reg(register.id())?;
172 registers.insert(register.id(), value);
173 }
174
175 let mut data = Vec::new();
176 for range in ranges {
177 let mut values = vec![0; (range.end - range.start) as usize];
178 core.read(range.start, &mut values)?;
179 data.push((range, values));
180 }
181
182 Ok(CoreDump {
183 registers,
184 data,
185 instruction_set: core.instruction_set()?,
186 supports_native_64bit_access: core.supports_native_64bit_access(),
187 core_type: core.core_type(),
188 fpu_support: core.fpu_support()?,
189 floating_point_register_count: Some(core.floating_point_register_count()?),
190 })
191 }
192
193 pub fn store(&self, path: &Path) -> Result<(), CoreDumpError> {
195 let mut file = OpenOptions::new()
196 .create(true)
197 .write(true)
198 .truncate(true)
199 .open(path)
200 .map_err(|e| {
201 CoreDumpError::CoreDumpFileWrite(e, dunce::canonicalize(path).unwrap_or_default())
202 })?;
203 rmp_serde::encode::write_named(&mut file, self).map_err(CoreDumpError::EncodingCoreDump)?;
204 Ok(())
205 }
206
207 pub fn load(path: &Path) -> Result<Self, CoreDumpError> {
209 let file_contents = std::fs::read(path).map_err(|e| {
210 CoreDumpError::CoreDumpFileRead(e, dunce::canonicalize(path).unwrap_or_default())
211 })?;
212 Self::load_raw(&file_contents)
213 }
214
215 pub fn load_raw(data: &[u8]) -> Result<Self, CoreDumpError> {
217 if let Ok(elf) = object::read::elf::ElfFile32::parse(data) {
218 Self::load_elf(elf)
219 } else if let Ok(elf) = object::read::elf::ElfFile64::parse(data) {
220 Self::load_elf(elf)
221 } else {
222 rmp_serde::from_slice(data).map_err(CoreDumpError::DecodingCoreDump)
223 }
224 }
225
226 fn load_elf<Elf: object::read::elf::FileHeader<Endian = object::Endianness>>(
227 elf: object::read::elf::ElfFile<'_, Elf>,
228 ) -> Result<Self, CoreDumpError> {
229 let endianness = elf.endianness();
230 let elf_data = elf.data();
231
232 let processor: Box<dyn Processor> = match elf.architecture() {
233 object::Architecture::Riscv32 => Box::new(RiscvProcessor),
234 object::Architecture::Xtensa => Box::new(XtensaProcessor),
235 other => {
236 return Err(CoreDumpError::DecodingElfCoreDump(format!(
237 "Unsupported architecture: {other:?}",
238 )));
239 }
240 };
241
242 let mut data = Vec::new();
244 for segment in elf.segments() {
246 let address: u64 = segment.elf_program_header().p_vaddr(endianness).into();
247 let size: u64 = segment.elf_program_header().p_memsz(endianness).into();
248 let memory = segment.data()?;
249 tracing::debug!(
250 "Adding memory segment: {:#x} - {:#x}",
251 address,
252 address + size
253 );
254 data.push((address..address + size, memory.to_vec()));
255 }
256
257 let Some(register_note) = elf
259 .elf_program_headers()
260 .iter()
261 .find(|s| s.p_type(endianness) == PT_NOTE)
262 else {
263 return Err(CoreDumpError::DecodingElfCoreDump(
264 "No note segment found".to_string(),
265 ));
266 };
267
268 let mut registers = HashMap::new();
269 for note in register_note
270 .notes(endianness, elf_data)?
271 .expect("Failed to read notes from a PT_NOTE segment. This is a bug, please report it.")
272 {
273 let note = note?;
274 if note.name() != b"CORE" {
275 continue;
276 }
277
278 const CORE_NOTE_HEADER_SIZE: usize = 72;
283 let note_length = processor.register_data_len();
284
285 if note.desc().len() < CORE_NOTE_HEADER_SIZE + note_length {
286 return Err(CoreDumpError::DecodingElfCoreDump(format!(
287 "Note segment is too small: {} bytes instead of at least {}",
288 note.desc().len(),
289 CORE_NOTE_HEADER_SIZE + note_length
290 )));
291 }
292
293 let note_data = ¬e.desc()[CORE_NOTE_HEADER_SIZE..][..note_length];
294 processor.read_registers(note_data, &mut registers)?;
295 }
296
297 Ok(Self {
298 registers,
299 data,
300 instruction_set: processor.instruction_set(),
301 supports_native_64bit_access: processor.supports_native_64bit_access(),
302 core_type: processor.core_type(),
303 fpu_support: false,
304 floating_point_register_count: None,
305 })
306 }
307
308 pub fn core_type(&self) -> CoreType {
310 self.core_type
311 }
312
313 pub fn instruction_set(&self) -> InstructionSet {
315 self.instruction_set
316 }
317
318 fn get_memory_from_coredump(
320 &self,
321 address: u64,
322 size_in_bytes: u64,
323 ) -> Result<&[u8], crate::Error> {
324 for (range, memory) in &self.data {
325 if range.contains_range(&(address..(address + size_in_bytes))) {
326 let offset = (address - range.start) as usize;
327
328 return Ok(&memory[offset..][..size_in_bytes as usize]);
329 }
330 }
331 Err(crate::Error::Other(format!(
333 "The coredump does not include the memory for address {address:#x} of size {size_in_bytes:#x}"
334 )))
335 }
336
337 fn read_memory_range<T>(&self, address: u64, data: &mut [T]) -> Result<(), crate::Error>
340 where
341 T: scroll::ctx::FromCtx<scroll::Endian>,
342 {
343 let memory =
344 self.get_memory_from_coredump(address, (std::mem::size_of_val(data)) as u64)?;
345
346 let value_size = std::mem::size_of::<T>();
347
348 for (n, data) in data.iter_mut().enumerate() {
349 *data = memory.cread_with::<T>(n * value_size, scroll::LE);
350 }
351 Ok(())
352 }
353
354 pub fn registers(&self) -> &'static CoreRegisters {
356 match self.core_type {
357 CoreType::Armv6m => &CORTEX_M_CORE_REGISTERS,
358 CoreType::Armv7a => match self.floating_point_register_count {
359 Some(16) => &AARCH32_WITH_FP_16_CORE_REGISTERS,
360 Some(32) => &AARCH32_WITH_FP_32_CORE_REGISTERS,
361 _ => &AARCH32_CORE_REGISTERS,
362 },
363 CoreType::Armv7m => {
364 if self.fpu_support {
365 &CORTEX_M_WITH_FP_CORE_REGISTERS
366 } else {
367 &CORTEX_M_CORE_REGISTERS
368 }
369 }
370 CoreType::Armv7em => {
371 if self.fpu_support {
372 &CORTEX_M_WITH_FP_CORE_REGISTERS
373 } else {
374 &CORTEX_M_CORE_REGISTERS
375 }
376 }
377 CoreType::Armv8a => &AARCH64_CORE_REGISTERS,
380 CoreType::Armv8m => {
381 if self.fpu_support {
382 &CORTEX_M_WITH_FP_CORE_REGISTERS
383 } else {
384 &CORTEX_M_CORE_REGISTERS
385 }
386 }
387 CoreType::Riscv => {
388 if self.fpu_support {
389 &RISCV_WITH_FP_CORE_REGISTERS
390 } else {
391 &RISCV_CORE_REGISTERS
392 }
393 }
394 CoreType::Xtensa => &XTENSA_CORE_REGISTERS,
395 }
396 }
397}
398
399impl MemoryInterface for CoreDump {
400 fn supports_native_64bit_access(&mut self) -> bool {
401 self.supports_native_64bit_access
402 }
403
404 fn read_word_64(&mut self, address: u64) -> Result<u64, crate::Error> {
405 let mut data = [0u64; 1];
406 self.read_memory_range(address, &mut data)?;
407 Ok(data[0])
408 }
409
410 fn read_word_32(&mut self, address: u64) -> Result<u32, crate::Error> {
411 let mut data = [0u32; 1];
412 self.read_memory_range(address, &mut data)?;
413 Ok(data[0])
414 }
415
416 fn read_word_16(&mut self, address: u64) -> Result<u16, crate::Error> {
417 let mut data = [0u16; 1];
418 self.read_memory_range(address, &mut data)?;
419 Ok(data[0])
420 }
421
422 fn read_word_8(&mut self, address: u64) -> Result<u8, crate::Error> {
423 let mut data = [0u8; 1];
424 self.read_memory_range(address, &mut data)?;
425 Ok(data[0])
426 }
427
428 fn read_64(&mut self, address: u64, data: &mut [u64]) -> Result<(), crate::Error> {
429 self.read_memory_range(address, data)?;
430 Ok(())
431 }
432
433 fn read_32(&mut self, address: u64, data: &mut [u32]) -> Result<(), crate::Error> {
434 self.read_memory_range(address, data)?;
435 Ok(())
436 }
437
438 fn read_16(&mut self, address: u64, data: &mut [u16]) -> Result<(), crate::Error> {
439 self.read_memory_range(address, data)?;
440 Ok(())
441 }
442
443 fn read_8(&mut self, address: u64, data: &mut [u8]) -> Result<(), crate::Error> {
444 self.read_memory_range(address, data)?;
445 Ok(())
446 }
447
448 fn write_word_64(&mut self, _address: u64, _data: u64) -> Result<(), crate::Error> {
449 todo!()
450 }
451
452 fn write_word_32(&mut self, _address: u64, _data: u32) -> Result<(), crate::Error> {
453 todo!()
454 }
455
456 fn write_word_16(&mut self, _address: u64, _data: u16) -> Result<(), crate::Error> {
457 todo!()
458 }
459
460 fn write_word_8(&mut self, _address: u64, _data: u8) -> Result<(), crate::Error> {
461 todo!()
462 }
463
464 fn write_64(&mut self, _address: u64, _data: &[u64]) -> Result<(), crate::Error> {
465 todo!()
466 }
467
468 fn write_32(&mut self, _address: u64, _data: &[u32]) -> Result<(), crate::Error> {
469 todo!()
470 }
471
472 fn write_16(&mut self, _address: u64, _data: &[u16]) -> Result<(), crate::Error> {
473 todo!()
474 }
475
476 fn write_8(&mut self, _address: u64, _data: &[u8]) -> Result<(), crate::Error> {
477 todo!()
478 }
479
480 fn supports_8bit_transfers(&self) -> Result<bool, crate::Error> {
481 todo!()
482 }
483
484 fn flush(&mut self) -> Result<(), crate::Error> {
485 todo!()
486 }
487}
488
489#[derive(thiserror::Error, Debug)]
491pub enum CoreDumpError {
492 #[error("Opening {1} for writing the core dump failed.")]
494 CoreDumpFileWrite(std::io::Error, PathBuf),
495 #[error("Opening {1} for reading the core dump failed.")]
497 CoreDumpFileRead(std::io::Error, PathBuf),
498 #[error("Encoding the coredump MessagePack failed.")]
500 EncodingCoreDump(rmp_serde::encode::Error),
501 #[error("Decoding the coredump MessagePack failed.")]
503 DecodingCoreDump(rmp_serde::decode::Error),
504 #[error("Decoding the coredump .elf failed.")]
506 DecodingElfCoreDump(String),
507 #[error("Invalid ELF file.")]
509 ElfCoreDumpFormat(#[from] object::read::Error),
510}