use std::{ env, fs::{self, File}, io::Write, path::PathBuf, }; fn main() { // if feature `firmware` is not enabled if let Err(env::VarError::NotPresent) = env::var("CARGO_FEATURE_FIRMWARE") { let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); // TODO: Parse `common.ld` let script_contents = fs::read_to_string("./common.ld").unwrap(); let firmware = get_memory_entry(script_contents.as_str(), "FIRMWARE").unwrap(); let fwupdate = get_memory_entry(script_contents.as_str(), "FWUPDATE").unwrap(); let fwswap = get_memory_entry(script_contents.as_str(), "FWSWAP").unwrap(); File::create(out.join("memory_map.rs")) .unwrap() .write_all( format!( " #[allow(non_upper_case_globals)] const __header_start__: u32 = {:#x?}; #[allow(non_upper_case_globals)] const __header_end__: u32 = {:#x?}; #[allow(non_upper_case_globals)] const __firmware_start__: u32 = {:#x?}; #[allow(non_upper_case_globals)] const __firmware_end__: u32 = {:#x?}; #[allow(non_upper_case_globals)] const __fwupdate_start__: u32 = {:#x?}; #[allow(non_upper_case_globals)] const __fwupdate_end__: u32 = {:#x?}; #[allow(non_upper_case_globals)] const __fwswap_start__: u32 = {:#x?}; #[allow(non_upper_case_globals)] const __fwswap_end__: u32 = {:#x?};", firmware.origin, firmware.origin + 0x400, firmware.origin, firmware.end(), fwupdate.origin, fwupdate.end(), fwswap.origin, fwswap.end(), ) .as_bytes(), ) .unwrap(); } } /// Entry under the `MEMORY` section in a linker script #[derive(Clone, Copy, Debug, PartialEq)] struct MemoryEntry { line: usize, origin: u64, length: u64, } impl MemoryEntry { fn end(&self) -> u64 { self.origin + self.length } } /// Rm `token` from beginning of `line`, else `continue` loop iteration macro_rules! eat { ($line:expr, $token:expr) => { if let Some(a) = $line.strip_prefix($token) { a.trim() } else { continue; } }; ($line:expr, $token:expr, $alt_token:expr) => { if let Some(a) = $line.strip_prefix($token) { a.trim() } else if let Some(a) = $line.strip_prefix($alt_token) { a.trim() } else { continue; } }; } fn get_memory_entry(linker_script: &str, name: &str) -> Option { macro_rules! tryc { ($expr:expr) => { if let Ok(x) = $expr { x } else { continue; } }; } for (index, mut line) in linker_script.lines().enumerate() { line = line.trim(); line = eat!(line, name); println!("{:?}", line); // jump over attributes like (xrw) see parse_attributes() if let Some(i) = line.find(':') { line = line[i..].trim(); } line = eat!(line, ":"); line = eat!(line, "ORIGIN", "org"); line = eat!(line, "="); let boundary_pos = tryc!(line.find(|c| c == ',' || c == ' ').ok_or(())); const HEX: &str = "0x"; let origin = if line.starts_with(HEX) { tryc!(u64::from_str_radix(&line[HEX.len()..boundary_pos], 16)) } else { tryc!(line[..boundary_pos].parse()) }; line = &line[boundary_pos..].trim(); line = eat!(line, ","); line = eat!(line, "LENGTH", "len"); line = eat!(line, "="); let segments: Vec<&str> = line.split('+').map(|s| s.trim().trim_end()).collect(); let mut total_length = 0; for segment in segments { let boundary_pos = segment .find(|c| c == 'K' || c == 'M' || c == 'k' || c == 'm') .unwrap_or_else(|| segment.len()); let length: u64 = tryc!(segment[..boundary_pos].parse()); let raw = &segment[boundary_pos..]; let mut chars = raw.chars(); let unit = chars.next(); if unit == Some('K') || unit == Some('k') { total_length += length * 1024; } else if unit == Some('M') || unit == Some('m') { total_length += length * 1024 * 1024; } else if unit == None { total_length += length; } } return Some(MemoryEntry { line: index, origin, length: total_length, }); } None }