target_gen/
parser.rs

1use crate::flash_device::FlashDevice;
2use anyhow::{Context, Result, anyhow};
3use probe_rs_target::{FlashProperties, MemoryRange, RawFlashAlgorithm, SectorDescription};
4
5/// Extract a chunk of data from an ELF binary.
6///
7/// This does only return the data chunk if it is fully contained in one section.
8/// If it is across two sections, no chunk will be returned.
9pub(crate) fn read_elf_bin_data<'a>(
10    elf: &'a goblin::elf::Elf<'_>,
11    buffer: &'a [u8],
12    address: u32,
13    size: u32,
14) -> Option<&'a [u8]> {
15    log::debug!("Trying to read {size} bytes from {address:#010x}.");
16
17    let start = address as u64;
18    let end = (address + size) as u64;
19    let range_to_read = start..end;
20
21    // Iterate all segments.
22    for ph in &elf.program_headers {
23        let segment_address = ph.p_paddr;
24        let segment_size = ph.p_memsz.min(ph.p_filesz);
25
26        log::debug!("Segment address: {segment_address:#010x}");
27        log::debug!("Segment size:    {segment_size} bytes");
28
29        let segment = segment_address..segment_address + segment_size;
30        // If the requested data is not fully inside of the current segment, skip the segment.
31        if !segment.contains_range(&range_to_read) {
32            log::debug!("Skipping segment.");
33            continue;
34        }
35
36        let start = ph.p_offset as u32 + address - segment_address as u32;
37        return Some(&buffer[start as usize..][..size as usize]);
38    }
39
40    None
41}
42
43fn extract_flash_device(elf: &goblin::elf::Elf, buffer: &[u8]) -> Result<FlashDevice> {
44    // Extract the flash device info.
45    for sym in elf.syms.iter() {
46        let name = &elf.strtab[sym.st_name];
47
48        if name == "FlashDevice" {
49            // This struct contains information about the FLM file structure.
50            let address = sym.st_value as u32;
51            return FlashDevice::new(elf, buffer, address);
52        }
53    }
54
55    // Failed to find flash device
56    Err(anyhow!("Failed to find 'FlashDevice' symbol in ELF file."))
57}
58
59/// Extracts a position & memory independent flash algorithm blob from the provided ELF file.
60pub fn extract_flash_algo(
61    existing_algo: Option<RawFlashAlgorithm>,
62    buffer: &[u8],
63    file_name: &std::path::Path,
64    default: bool,
65    fixed_load_address: bool,
66) -> Result<RawFlashAlgorithm> {
67    let mut algo = existing_algo.unwrap_or_default();
68
69    let elf = goblin::elf::Elf::parse(buffer)?;
70
71    let flash_device = extract_flash_device(&elf, buffer).context(format!(
72        "Failed to extract flash information from ELF file '{}'.",
73        file_name.display()
74    ))?;
75
76    // Extract binary blob.
77    let algorithm_binary = crate::algorithm_binary::AlgorithmBinary::new(&elf, buffer)?;
78    algo.instructions = algorithm_binary.blob();
79
80    let code_section_offset = algorithm_binary.code_section.start;
81
82    // Extract the function pointers,
83    // and check if a RTT symbol is present.
84    for sym in elf.syms.iter() {
85        let name = &elf.strtab[sym.st_name];
86
87        match name {
88            "Init" => algo.pc_init = Some(sym.st_value - code_section_offset as u64),
89            "UnInit" => algo.pc_uninit = Some(sym.st_value - code_section_offset as u64),
90            "EraseChip" => algo.pc_erase_all = Some(sym.st_value - code_section_offset as u64),
91            "EraseSector" => algo.pc_erase_sector = sym.st_value - code_section_offset as u64,
92            "ProgramPage" => algo.pc_program_page = sym.st_value - code_section_offset as u64,
93            "Verify" => algo.pc_verify = Some(sym.st_value - code_section_offset as u64),
94            "BlankCheck" => algo.pc_blank_check = Some(sym.st_value - code_section_offset as u64),
95            // probe-rs additions
96            "ReadFlash" => algo.pc_read = Some(sym.st_value - code_section_offset as u64),
97            "FlashSize" => algo.pc_flash_size = Some(sym.st_value - code_section_offset as u64),
98            "_SEGGER_RTT" => {
99                algo.rtt_location = Some(sym.st_value);
100                log::debug!("Found RTT control block at address {:#010x}", sym.st_value);
101            }
102            "PAGE_BUFFER" => {
103                algo.data_load_address = Some(sym.st_value);
104                log::debug!("Found PAGE_BUFFER at address {:#010x}", sym.st_value);
105            }
106
107            _ => {}
108        }
109    }
110
111    if fixed_load_address {
112        log::debug!(
113            "Flash algorithm will be loaded at fixed address {:#010x}",
114            algorithm_binary.code_section.load_address
115        );
116
117        anyhow::ensure!(
118            algorithm_binary.is_continuous_in_ram(),
119            "If the flash algorithm is not position independent, all sections have to follow each other in RAM. \
120            Please check your linkerscript."
121        );
122
123        algo.load_address = Some(algorithm_binary.code_section.load_address as u64);
124        algo.data_section_offset = (algorithm_binary.data_section.start
125            - algorithm_binary.code_section.load_address) as u64;
126    } else {
127        algo.data_section_offset = algorithm_binary.data_section.start as u64;
128    }
129
130    algo.description.clone_from(&flash_device.name);
131    algo.name = file_name
132        .file_stem()
133        .and_then(|f| f.to_str())
134        .unwrap()
135        .to_lowercase();
136    algo.default = default;
137    algo.flash_properties = FlashProperties::from(flash_device);
138    algo.big_endian = !elf.little_endian;
139
140    Ok(algo)
141}
142
143impl From<FlashDevice> for FlashProperties {
144    fn from(device: FlashDevice) -> Self {
145        let sectors = device
146            .sectors
147            .iter()
148            .map(|si| SectorDescription {
149                address: si.address.into(),
150                size: si.size.into(),
151            })
152            .collect();
153
154        FlashProperties {
155            address_range: device.start_address as u64
156                ..(device.start_address as u64 + device.device_size as u64),
157
158            page_size: device.page_size,
159            erased_byte_value: device.erased_default_value,
160
161            program_page_timeout: device.program_page_timeout,
162            erase_sector_timeout: device.erase_sector_timeout,
163
164            sectors,
165        }
166    }
167}