diff --git a/Cargo.lock b/Cargo.lock index 9b194f5..198a02c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,24 +2,24 @@ # It is not intended for manual editing. [[package]] name = "adler" -version = "0.2.3" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "aho-corasick" -version = "0.7.13" +version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "043164d8ba5c4c3035fec9bbee8647c0261d788f3474306f93bb65901cae0e86" +checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5" dependencies = [ "memchr", ] [[package]] name = "anyhow" -version = "1.0.32" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b602bfe940d21c130f3895acd65221e8a61270debe89d628b9cb4e3ccb8569b" +checksum = "28b2cd92db5cbd74e8e5028f7e27dd7aa3090e89e4f2a197cc7c8dfb69c7063b" [[package]] name = "atty" @@ -32,26 +32,38 @@ dependencies = [ "winapi", ] +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + [[package]] name = "cfg-if" -version = "0.1.10" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "crc32fast" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" +checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" dependencies = [ "cfg-if", ] [[package]] name = "env_logger" -version = "0.7.1" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" +checksum = "17392a012ea30ef05a610aa97dfb49496e71c9f676b27879922ea5bdf60d9d3f" dependencies = [ "atty", "humantime", @@ -62,9 +74,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.17" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "766d0e77a2c1502169d4a93ff3b8c15a71fd946cd0126309752104e5f3c46d94" +checksum = "cd3aec53de10fe96d7d8c565eb17f2c687bb5518a2ec453b5b1252964526abe0" dependencies = [ "cfg-if", "crc32fast", @@ -85,9 +97,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.1.14" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" +checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8" dependencies = [ "cfg-if", "libc", @@ -96,63 +108,55 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.1.15" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3deed196b6e7f9e44a2ae8d94225d80302d81208b1bb673fd21fe634645c85a9" +checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" dependencies = [ "libc", ] [[package]] name = "humantime" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" -dependencies = [ - "quick-error", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "libc" -version = "0.2.76" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "755456fae044e6fa1ebbbd1b3e902ae19e73097ed4ed87bb79934a867c007bc3" +checksum = "8916b1f6ca17130ec6568feccee27c156ad12037880833a3b842a823236502e7" [[package]] name = "log" -version = "0.4.11" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" dependencies = [ "cfg-if", ] [[package]] name = "memchr" -version = "2.3.3" +version = "2.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" +checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" [[package]] name = "miniz_oxide" -version = "0.4.1" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d7559a8a40d0f97e1edea3220f698f78b1c5ab67532e49f68fde3910323b722" +checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" dependencies = [ "adler", + "autocfg", ] [[package]] name = "object" -version = "0.21.1" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37fd5004feb2ce328a52b0b3d01dbf4ffff72583493900ed15f22d4111c51693" +checksum = "a9a7ab5d64814df0fe4a4b5ead45ed6c5f181ee3ff04ba344313a6c80446c5d4" dependencies = [ "flate2", "wasmparser", @@ -160,23 +164,16 @@ dependencies = [ [[package]] name = "ppv-lite86" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c36fa947111f5c62a733b652544dd0016a43ce89619538a8ef92724a6f501a20" - -[[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" +checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" [[package]] name = "rand" -version = "0.7.3" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e" dependencies = [ - "getrandom", "libc", "rand_chacha", "rand_core", @@ -185,9 +182,9 @@ dependencies = [ [[package]] name = "rand_chacha" -version = "0.2.2" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" dependencies = [ "ppv-lite86", "rand_core", @@ -195,45 +192,47 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.5.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7" dependencies = [ "getrandom", ] [[package]] name = "rand_hc" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" dependencies = [ "rand_core", ] [[package]] name = "redox_syscall" -version = "0.1.57" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" +checksum = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9" +dependencies = [ + "bitflags", +] [[package]] name = "regex" -version = "1.3.9" +version = "1.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c3780fcf44b193bc4d09f36d2a3c87b251da4a046c87795a0d35f4f927ad8e6" +checksum = "957056ecddbeba1b26965114e191d2e8589ce74db242b6ea25fc4062427a5c19" dependencies = [ "aho-corasick", "memchr", "regex-syntax", - "thread_local", ] [[package]] name = "regex-syntax" -version = "0.6.18" +version = "0.6.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8" +checksum = "24d5f089152e60f62d28b835fbff2cd2e8dc0baf1ac13343bef92ab7eed84548" [[package]] name = "remove_dir_all" @@ -246,9 +245,9 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.1.0" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" +checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" dependencies = [ "cfg-if", "libc", @@ -260,27 +259,18 @@ dependencies = [ [[package]] name = "termcolor" -version = "1.1.0" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f" +checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" dependencies = [ "winapi-util", ] -[[package]] -name = "thread_local" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" -dependencies = [ - "lazy_static", -] - [[package]] name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" +version = "0.10.2+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" [[package]] name = "wasmparser" diff --git a/Cargo.toml b/Cargo.toml index 8312bb5..bd080bb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ readme = "README.md" [dependencies] anyhow = "1.0.32" -env_logger = "0.7.1" +env_logger = "0.8.3" log = "0.4.11" -object = "0.21.1" +object = "0.23.0" tempfile = "3.1.0" diff --git a/src/main.rs b/src/main.rs index a13665c..f606415 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,25 +3,25 @@ use std::{ env, fs::{self, File}, io::Write, + ops::RangeInclusive, path::{Path, PathBuf}, process::{self, Command}, }; -use anyhow::{anyhow, bail}; +use anyhow::anyhow; use object::{elf, Object as _, ObjectSection, SectionFlags}; +const EXIT_CODE_FAILURE: i32 = 1; // TODO make this configurable (via command-line flag or similar) const LINKER: &str = "rust-lld"; -const EXIT_CODE_FAILURE: i32 = 1; +/// Stack Pointer alignment required by the ARM architecture +const SP_ALIGN: u64 = 8; -fn main() -> Result<(), anyhow::Error> { +fn main() -> anyhow::Result<()> { notmain().map(|code| process::exit(code)) } -// Stack Pointer alignment required by the ARM architecture -const SP_ALIGN: u64 = 8; - -fn notmain() -> Result { +fn notmain() -> anyhow::Result { env_logger::init(); // NOTE `skip` the name/path of the binary (first argument) @@ -54,12 +54,8 @@ fn notmain() -> Result { break; } } - - let (ram_linker_script, ram_entry) = if let Some((path, entry)) = ram_path_entry { - (path, entry) - } else { - bail!("MEMORY.RAM not found after scanning linker scripts"); - }; + let (ram_linker_script, ram_entry) = ram_path_entry + .ok_or_else(|| anyhow!("MEMORY.RAM not found after scanning linker scripts"))?; let elf = fs::read(output_path)?; let object = object::File::parse(&elf)?; @@ -69,60 +65,16 @@ fn notmain() -> Result { // error in that case (e.g. the stack may have been placed in CCRAM) // compute the span of RAM sections - let mut used_ram_start = u64::MAX; - let mut used_ram_end = 0; - let mut used_ram_align = 0; - let ram_region_span = ram_entry.origin as u64..=ram_entry.end() as u64; - let mut found_a_section = false; - for section in object.sections() { - if let SectionFlags::Elf { sh_flags } = section.flags() { - if sh_flags & elf::SHF_ALLOC as u64 != 0 { - let start = section.address(); - let size = section.size(); - let end = start + size; - - if ram_region_span.contains(&start) && ram_region_span.contains(&end) { - found_a_section = true; - log::debug!( - "{} resides in RAM", - section.name().unwrap_or("nameless section") - ); - used_ram_align = used_ram_align.max(section.align()); - - if used_ram_start > start { - used_ram_start = start; - } - - if used_ram_end < end { - used_ram_end = end; - } - } - } - } - } - - let used_ram_length = if !found_a_section { - used_ram_start = ram_entry.origin as u64; - 0 - } else { - used_ram_end - used_ram_start - }; - - log::info!( - "used RAM spans: origin={:#x}, length={}, align={}", - used_ram_start, - used_ram_length, - used_ram_align - ); + let (used_ram_length, used_ram_align) = compute_span_of_ram_sections(ram_entry, object); // the idea is to push `used_ram` all the way to the end of the RAM region // to do this we'll use a fake ORIGIN and LENGTH for the RAM region // this fake RAM region will be at the end of real RAM region let new_origin = round_down_to_nearest_multiple( - ram_entry.end() as u64 - used_ram_length, + ram_entry.end() - used_ram_length, used_ram_align.max(SP_ALIGN), ); - let new_length = ram_entry.end() as u64 - new_origin; + let new_length = ram_entry.end() - new_origin; log::info!( "new RAM region: ORIGIN={:#x}, LENGTH={}", @@ -154,15 +106,15 @@ fn notmain() -> Result { let mut c2 = Command::new(LINKER); // add the current dir to the linker search path to include all unmodified scripts there // HACK `-L` needs to go after `-flavor gnu`; position is currently hardcoded - c2.args(&args[..2]); - c2.arg("-L".to_string()); - c2.arg(current_dir); - c2.args(&args[2..]); - // we need to override `_stack_start` to make the stack start below fake RAM - c2.arg(format!("--defsym=_stack_start={}", new_origin)); - // set working directory to temporary directory containing our new linker script - // this makes sure that it takes precedence over the original one - c2.current_dir(tempdir.path()); + c2.args(&args[..2]) + .arg("-L".to_string()) + .arg(current_dir) + .args(&args[2..]) + // we need to override `_stack_start` to make the stack start below fake RAM + .arg(format!("--defsym=_stack_start={}", new_origin)) + // set working directory to temporary directory containing our new linker script + // this makes sure that it takes precedence over the original one + .current_dir(tempdir.path()); log::trace!("{:?}", c2); let status = c2.status()?; if !status.success() { @@ -172,6 +124,57 @@ fn notmain() -> Result { Ok(0) } +/// Returns `(used_ram_length, used_ram_align)` +fn compute_span_of_ram_sections(ram_entry: MemoryEntry, object: object::File) -> (u64, u64) { + let mut used_ram_start = u64::MAX; + let mut used_ram_end = 0; + let mut used_ram_align = 0; + let ram_region_span = ram_entry.span(); + let mut found_a_section = false; + for section in object.sections() { + if let SectionFlags::Elf { sh_flags } = section.flags() { + if sh_flags & elf::SHF_ALLOC as u64 != 0 { + let start = section.address(); + let size = section.size(); + let end = start + size; + + if ram_region_span.contains(&start) && ram_region_span.contains(&end) { + found_a_section = true; + log::debug!( + "{} resides in RAM", + section.name().unwrap_or("nameless section") + ); + used_ram_align = used_ram_align.max(section.align()); + + if used_ram_start > start { + used_ram_start = start; + } + + if used_ram_end < end { + used_ram_end = end; + } + } + } + } + } + + let used_ram_length = if !found_a_section { + used_ram_start = ram_entry.origin; + 0 + } else { + used_ram_end - used_ram_start + }; + + log::info!( + "used RAM spans: origin={:#x}, length={}, align={}", + used_ram_start, + used_ram_length, + used_ram_align + ); + + (used_ram_length, used_ram_align) +} + fn round_down_to_nearest_multiple(x: u64, multiple: u64) -> u64 { x - (x % multiple) } @@ -191,38 +194,35 @@ impl LinkerScript { } } -fn get_linker_scripts( - linker_args: &[String], - current_dir: &Path, -) -> Result, anyhow::Error> { - const FLAG: &str = "-L"; - - let mut search_paths = vec![]; - let mut next_is_search_path = false; - for arg in linker_args { - if arg == FLAG { - next_is_search_path = true; - } else if next_is_search_path { - next_is_search_path = false; - log::trace!("new search path: {}", arg); - search_paths.push(Path::new(arg)); - } - } - +fn get_linker_scripts(args: &[String], current_dir: &Path) -> anyhow::Result> { + // search paths are the current dir and args passed by `-L` + let mut search_paths = args + .windows(2) + .filter_map(|x| { + if x[0] == "-L" { + log::trace!("new search path: {}", x[1]); + return Some(Path::new(&x[1])); + } + None + }) + .collect::>(); search_paths.push(current_dir); - let mut search_list = vec![]; - for arg in linker_args.iter() { - // FIXME this doesn't handle "-T memory.x" (as two separate CLI arguments) - const FLAG: &str = "-T"; - - if arg.starts_with(FLAG) { - let filename = &arg[FLAG.len()..]; - - search_list.push(Cow::Borrowed(filename)); - } - } + // get names of linker scripts, passed via `-T` + // FIXME this doesn't handle "-T memory.x" (as two separate CLI arguments) + let mut search_list = args + .iter() + .filter_map(|arg| { + const FLAG: &str = "-T"; + if arg.starts_with(FLAG) { + let filename = &arg[FLAG.len()..]; + return Some(Cow::Borrowed(filename)); + } + None + }) + .collect::>(); + // try to find all linker scripts from `search_list` in the `search_paths` let mut linker_scripts = vec![]; while let Some(filename) = search_list.pop() { for dir in &search_paths { @@ -231,6 +231,8 @@ fn get_linker_scripts( if full_path.exists() { log::trace!("found {} in {}", filename, dir.display()); let contents = fs::read_to_string(&full_path)?; + + // also load linker scripts `INCLUDE`d by other scripts for include in get_includes_from_linker_script(&contents) { log::trace!("{} INCLUDEs {}", filename, include); search_list.push(Cow::Owned(include.to_string())); @@ -261,20 +263,25 @@ fn get_output_path(args: &[String]) -> Option<&str> { None } -// Entry under the `MEMORY` section in a linker script +/// Entry under the `MEMORY` section in a linker script #[derive(Clone, Copy, Debug, PartialEq)] struct MemoryEntry { line: usize, - origin: u32, - length: u32, + origin: u64, + length: u64, } impl MemoryEntry { - fn end(&self) -> u32 { + fn end(&self) -> u64 { self.origin + self.length } + + fn span(&self) -> RangeInclusive { + self.origin..=self.end() + } } +/// Rm `token` from beginning of `line`, else `continue` loop iteration macro_rules! eat { ($line:expr, $token:expr) => { if $line.starts_with($token) { @@ -296,7 +303,7 @@ fn get_includes_from_linker_script(linker_script: &str) -> Vec<&str> { includes } -// looks for "RAM : ORIGIN = $origin, LENGTH = $length" +/// Looks for "RAM : ORIGIN = $origin, LENGTH = $length" // FIXME this is a dumb line-by-line parser fn find_ram_in_linker_script(linker_script: &str) -> Option { macro_rules! tryc { @@ -325,7 +332,7 @@ fn find_ram_in_linker_script(linker_script: &str) -> Option { let boundary_pos = tryc!(line.find(|c| c == ',' || c == ' ')); const HEX: &str = "0x"; let origin = if line.starts_with(HEX) { - tryc!(u32::from_str_radix(&line[HEX.len()..boundary_pos], 16).ok()) + tryc!(u64::from_str_radix(&line[HEX.len()..boundary_pos], 16).ok()) } else { tryc!(line[..boundary_pos].parse().ok()) }; @@ -341,7 +348,7 @@ fn find_ram_in_linker_script(linker_script: &str) -> Option { let boundary_pos = segment .find(|c| c == 'K' || c == 'M') .unwrap_or(segment.len()); - let length: u32 = tryc!(segment[..boundary_pos].parse().ok()); + let length: u64 = tryc!(segment[..boundary_pos].parse().ok()); let raw = &segment[boundary_pos..]; let mut chars = raw.chars(); let unit = chars.next();