use std::{
convert::TryInto,
ops::{Range, RangeInclusive},
path::Path,
};
use object::{Object, ObjectSection as _};
use probe_rs::{
config::Core,
config::{MemoryRegion, RamRegion},
CoreType,
};
use crate::elf::Elf;
pub struct TargetInfo {
pub probe_target: probe_rs::Target,
pub active_ram_region: Option<RamRegion>,
pub stack_info: Option<StackInfo>,
}
pub struct StackInfo {
pub range: RangeInclusive<u32>,
pub data_below_stack: bool,
}
impl TargetInfo {
pub fn new(chip: &str, elf: &Elf) -> anyhow::Result<Self> {
let probe_target = probe_rs::config::get_target_by_name(chip)?;
check_processor_target_compatability(&probe_target.cores, elf.elf_path);
let active_ram_region =
extract_active_ram_region(&probe_target, elf.vector_table.initial_stack_pointer);
let stack_info = active_ram_region
.as_ref()
.and_then(|ram_region| extract_stack_info(elf, &ram_region.range));
Ok(Self {
probe_target,
active_ram_region,
stack_info,
})
}
}
fn check_processor_target_compatability(cores: &[Core], elf_path: &Path) {
let target = elf_path.iter().find_map(|a| {
let b = a.to_string_lossy();
match b.starts_with("thumbv") {
true => Some(b),
false => None,
}
});
let target = match target {
Some(target) => target,
None => return, };
let core_type = cores[0].core_type;
let matches = match core_type {
CoreType::Armv6m => target == "thumbv6m-none-eabi",
CoreType::Armv7m => target == "thumbv7m-none-eabi",
CoreType::Armv7em => target == "thumbv7em-none-eabi" || target == "thumbv7em-none-eabihf",
CoreType::Armv8m => {
target == "thumbv8m.base-none-eabi"
|| target == "thumbv8m.main-none-eabi"
|| target == "thumbv8m.main-none-eabihf"
}
CoreType::Armv7a | CoreType::Armv8a => {
log::warn!("Unsupported architecture ({core_type:?}");
return;
}
CoreType::Riscv => return,
};
if matches {
return;
}
let recommendation = match core_type {
CoreType::Armv6m => "must be 'thumbv6m-none-eabi'",
CoreType::Armv7m => "should be 'thumbv7m-none-eabi'",
CoreType::Armv7em => {
"should be 'thumbv7em-none-eabi' (no FPU) or 'thumbv7em-none-eabihf' (with FPU)"
}
CoreType::Armv8m => {
"should be 'thumbv8m.base-none-eabi' (M23), 'thumbv8m.main-none-eabi' (M33 no FPU), or 'thumbv8m.main-none-eabihf' (M33 with FPU)"
}
CoreType::Armv7a | CoreType::Armv8a => unreachable!(),
CoreType::Riscv => unreachable!(),
};
log::warn!("Compilation target ({target}) and core type ({core_type:?}) do not match. Your compilation target {recommendation}.");
}
fn extract_active_ram_region(
target: &probe_rs::Target,
initial_stack_pointer: u32,
) -> Option<RamRegion> {
target
.memory_map
.iter()
.find_map(|region| match region {
MemoryRegion::Ram(ram_region) => {
let inclusive_range = ram_region.range.start..=ram_region.range.end;
if inclusive_range.contains(&initial_stack_pointer.into()) {
log::debug!(
"RAM region: 0x{:08X}-0x{:08X}",
ram_region.range.start,
ram_region.range.end - 1
);
Some(ram_region)
} else {
None
}
}
_ => None,
})
.cloned()
}
fn extract_stack_info(elf: &Elf, ram_range: &Range<u64>) -> Option<StackInfo> {
let initial_stack_pointer = elf.vector_table.initial_stack_pointer;
let mut stack_range =
ram_range.start.try_into().unwrap_or(u32::MAX)..=initial_stack_pointer - 4;
for section in elf.sections() {
let size: u32 = section.size().try_into().expect("expected 32-bit ELF");
if size == 0 {
continue;
}
let lowest_address: u32 = section.address().try_into().expect("expected 32-bit ELF");
let highest_address = lowest_address + size - 1;
let section_range = lowest_address..=highest_address;
let name = section.name().unwrap_or("<unknown>");
if ram_range.contains(&(*section_range.end() as u64)) {
log::debug!("section `{}` is in RAM at {:#010X?}", name, section_range);
if section_range.contains(stack_range.end()) {
log::debug!(
"initial SP is in section `{}`, cannot determine valid stack range",
name
);
return None;
} else if stack_range.contains(section_range.end()) {
stack_range = section_range.end() + 1..=*stack_range.end();
}
}
}
log::debug!("valid SP range: {:#010X?}", stack_range);
Some(StackInfo {
data_below_stack: *stack_range.start() as u64 > ram_range.start,
range: stack_range,
})
}