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