From 83d752d434284eb8f1a330bfbf5de7ac2f3cefcb Mon Sep 17 00:00:00 2001 From: Urhengulas Date: Thu, 1 Apr 2021 17:16:59 +0200 Subject: [PATCH 01/21] Dissolve `fn notmain` into `fn main` --- src/main.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/main.rs b/src/main.rs index f606415..c62617a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,10 +18,6 @@ const LINKER: &str = "rust-lld"; const SP_ALIGN: u64 = 8; fn main() -> anyhow::Result<()> { - notmain().map(|code| process::exit(code)) -} - -fn notmain() -> anyhow::Result { env_logger::init(); // NOTE `skip` the name/path of the binary (first argument) @@ -33,7 +29,7 @@ fn notmain() -> anyhow::Result { log::trace!("{:?}", c1); let status = c1.status()?; if !status.success() { - return Ok(status.code().unwrap_or(EXIT_CODE_FAILURE)); + process::exit(status.code().unwrap_or(EXIT_CODE_FAILURE)); } // if linking succeeds then linker scripts are well-formed; we'll rely on that in the parser @@ -118,10 +114,10 @@ fn notmain() -> anyhow::Result { log::trace!("{:?}", c2); let status = c2.status()?; if !status.success() { - return Ok(status.code().unwrap_or(EXIT_CODE_FAILURE)); + process::exit(status.code().unwrap_or(EXIT_CODE_FAILURE)); } - Ok(0) + Ok(()) } /// Returns `(used_ram_length, used_ram_align)` From b21e3ccf748042ac6fc3d2887fe5827e932e3f8f Mon Sep 17 00:00:00 2001 From: Urhengulas Date: Thu, 1 Apr 2021 17:32:58 +0200 Subject: [PATCH 02/21] `fn link_normally(...)` --- src/main.rs | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/main.rs b/src/main.rs index c62617a..d2e8ba5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -23,16 +23,9 @@ fn main() -> anyhow::Result<()> { // NOTE `skip` the name/path of the binary (first argument) let args = env::args().skip(1).collect::>(); - // link normally - let mut c1 = Command::new(LINKER); - c1.args(&args); - log::trace!("{:?}", c1); - let status = c1.status()?; - if !status.success() { - process::exit(status.code().unwrap_or(EXIT_CODE_FAILURE)); - } - // if linking succeeds then linker scripts are well-formed; we'll rely on that in the parser + link_normally(&args).unwrap_or_else(|code| process::exit(code)); + let current_dir = env::current_dir()?; let linker_scripts = get_linker_scripts(&args, ¤t_dir)?; let output_path = @@ -120,6 +113,18 @@ fn main() -> anyhow::Result<()> { Ok(()) } +fn link_normally(args: &[String]) -> Result<(), i32> { + let mut c = Command::new(LINKER); + c.args(args); + log::trace!("{:?}", c); + + let status = c.status().unwrap(); + if !status.success() { + return Err(status.code().unwrap_or(EXIT_CODE_FAILURE)); + } + Ok(()) +} + /// 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; From e13937adbd52ddbcec93171a44161b5490a81ac7 Mon Sep 17 00:00:00 2001 From: Urhengulas Date: Thu, 1 Apr 2021 23:28:28 +0200 Subject: [PATCH 03/21] `fn link_again(...)` --- src/main.rs | 42 +++++++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/src/main.rs b/src/main.rs index d2e8ba5..1f46d06 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,6 +10,7 @@ use std::{ use anyhow::anyhow; use object::{elf, Object as _, ObjectSection, SectionFlags}; +use tempfile::TempDir; const EXIT_CODE_FAILURE: i32 = 1; // TODO make this configurable (via command-line flag or similar) @@ -91,11 +92,34 @@ fn main() -> anyhow::Result<()> { // commit file to disk drop(new_linker_script); - // invoke the linker a second time - let mut c2 = Command::new(LINKER); + link_again(&args, ¤t_dir, new_origin, &tempdir) + .unwrap_or_else(|code| process::exit(code)); + + Ok(()) +} + +fn link_normally(args: &[String]) -> Result<(), i32> { + let mut c = Command::new(LINKER); + c.args(args); + log::trace!("{:?}", c); + + let status = c.status().unwrap(); + if !status.success() { + return Err(status.code().unwrap_or(EXIT_CODE_FAILURE)); + } + Ok(()) +} + +fn link_again( + args: &[String], + current_dir: &Path, + new_origin: u64, + tempdir: &TempDir, +) -> Result<(), i32> { + let mut c = 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]) + c.args(&args[..2]) .arg("-L".to_string()) .arg(current_dir) .args(&args[2..]) @@ -104,18 +128,6 @@ fn main() -> anyhow::Result<()> { // 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() { - process::exit(status.code().unwrap_or(EXIT_CODE_FAILURE)); - } - - Ok(()) -} - -fn link_normally(args: &[String]) -> Result<(), i32> { - let mut c = Command::new(LINKER); - c.args(args); log::trace!("{:?}", c); let status = c.status().unwrap(); From 8e91dbfb6f532f7e2cfbf9cbf05d12e766dbd9e6 Mon Sep 17 00:00:00 2001 From: Urhengulas Date: Thu, 1 Apr 2021 23:32:33 +0200 Subject: [PATCH 04/21] `fn success_or_exitstatus` --- src/main.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main.rs b/src/main.rs index 1f46d06..d994b35 100644 --- a/src/main.rs +++ b/src/main.rs @@ -103,11 +103,7 @@ fn link_normally(args: &[String]) -> Result<(), i32> { c.args(args); log::trace!("{:?}", c); - let status = c.status().unwrap(); - if !status.success() { - return Err(status.code().unwrap_or(EXIT_CODE_FAILURE)); - } - Ok(()) + success_or_exitstatus(c) } fn link_again( @@ -130,6 +126,10 @@ fn link_again( .current_dir(tempdir.path()); log::trace!("{:?}", c); + success_or_exitstatus(c) +} + +fn success_or_exitstatus(mut c: Command) -> Result<(), i32> { let status = c.status().unwrap(); if !status.success() { return Err(status.code().unwrap_or(EXIT_CODE_FAILURE)); From 2c9b82513f1c2db46d23bf443f78ee5a11173d50 Mon Sep 17 00:00:00 2001 From: Urhengulas Date: Thu, 1 Apr 2021 23:36:45 +0200 Subject: [PATCH 05/21] `mod linking` --- src/linking.rs | 46 +++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 51 +++++--------------------------------------------- 2 files changed, 51 insertions(+), 46 deletions(-) create mode 100644 src/linking.rs diff --git a/src/linking.rs b/src/linking.rs new file mode 100644 index 0000000..0ab3578 --- /dev/null +++ b/src/linking.rs @@ -0,0 +1,46 @@ +use std::{path::Path, process::Command}; + +use tempfile::TempDir; + +const EXIT_CODE_FAILURE: i32 = 1; +// TODO make this configurable (via command-line flag or similar) +const LINKER: &str = "rust-lld"; + +pub fn link_normally(args: &[String]) -> Result<(), i32> { + let mut c = Command::new(LINKER); + c.args(args); + log::trace!("{:?}", c); + + success_or_exitstatus(c) +} + +pub fn link_again( + args: &[String], + current_dir: &Path, + new_origin: u64, + tempdir: &TempDir, +) -> Result<(), i32> { + let mut c = 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 + c.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!("{:?}", c); + + success_or_exitstatus(c) +} + +fn success_or_exitstatus(mut c: Command) -> Result<(), i32> { + let status = c.status().unwrap(); + if !status.success() { + return Err(status.code().unwrap_or(EXIT_CODE_FAILURE)); + } + Ok(()) +} diff --git a/src/main.rs b/src/main.rs index d994b35..9ceb434 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,5 @@ +mod linking; + use std::{ borrow::Cow, env, @@ -5,16 +7,12 @@ use std::{ io::Write, ops::RangeInclusive, path::{Path, PathBuf}, - process::{self, Command}, + process, }; use anyhow::anyhow; use object::{elf, Object as _, ObjectSection, SectionFlags}; -use tempfile::TempDir; -const EXIT_CODE_FAILURE: i32 = 1; -// TODO make this configurable (via command-line flag or similar) -const LINKER: &str = "rust-lld"; /// Stack Pointer alignment required by the ARM architecture const SP_ALIGN: u64 = 8; @@ -25,7 +23,7 @@ fn main() -> anyhow::Result<()> { let args = env::args().skip(1).collect::>(); // if linking succeeds then linker scripts are well-formed; we'll rely on that in the parser - link_normally(&args).unwrap_or_else(|code| process::exit(code)); + linking::link_normally(&args).unwrap_or_else(|code| process::exit(code)); let current_dir = env::current_dir()?; let linker_scripts = get_linker_scripts(&args, ¤t_dir)?; @@ -92,51 +90,12 @@ fn main() -> anyhow::Result<()> { // commit file to disk drop(new_linker_script); - link_again(&args, ¤t_dir, new_origin, &tempdir) + linking::link_again(&args, ¤t_dir, new_origin, &tempdir) .unwrap_or_else(|code| process::exit(code)); Ok(()) } -fn link_normally(args: &[String]) -> Result<(), i32> { - let mut c = Command::new(LINKER); - c.args(args); - log::trace!("{:?}", c); - - success_or_exitstatus(c) -} - -fn link_again( - args: &[String], - current_dir: &Path, - new_origin: u64, - tempdir: &TempDir, -) -> Result<(), i32> { - let mut c = 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 - c.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!("{:?}", c); - - success_or_exitstatus(c) -} - -fn success_or_exitstatus(mut c: Command) -> Result<(), i32> { - let status = c.status().unwrap(); - if !status.success() { - return Err(status.code().unwrap_or(EXIT_CODE_FAILURE)); - } - Ok(()) -} - /// 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; From a7f68dd8222ea2cce03177805377e35ae88266ed Mon Sep 17 00:00:00 2001 From: Urhengulas Date: Thu, 1 Apr 2021 23:44:39 +0200 Subject: [PATCH 06/21] `linking`: Rm todo-comment (tracked in #1) --- src/linking.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/linking.rs b/src/linking.rs index 0ab3578..4414605 100644 --- a/src/linking.rs +++ b/src/linking.rs @@ -3,7 +3,6 @@ use std::{path::Path, process::Command}; use tempfile::TempDir; const EXIT_CODE_FAILURE: i32 = 1; -// TODO make this configurable (via command-line flag or similar) const LINKER: &str = "rust-lld"; pub fn link_normally(args: &[String]) -> Result<(), i32> { From d704af088baffb388b8f0840755aea8c67cc1a50 Mon Sep 17 00:00:00 2001 From: Urhengulas Date: Fri, 2 Apr 2021 00:06:46 +0200 Subject: [PATCH 07/21] Mv `fn get_output_path` to `mod argument_parser` --- src/argument_parser.rs | 13 +++++++++++++ src/main.rs | 18 +++--------------- 2 files changed, 16 insertions(+), 15 deletions(-) create mode 100644 src/argument_parser.rs diff --git a/src/argument_parser.rs b/src/argument_parser.rs new file mode 100644 index 0000000..c3ab3e5 --- /dev/null +++ b/src/argument_parser.rs @@ -0,0 +1,13 @@ +/// Get `output_path`, specified by `-o` +pub fn get_output_path(args: &[String]) -> Option<&str> { + let mut next_is_output = false; + for arg in args { + if arg == "-o" { + next_is_output = true; + } else if next_is_output { + return Some(arg); + } + } + + None +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 9ceb434..efed5a7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,4 @@ +mod argument_parser; mod linking; use std::{ @@ -27,8 +28,8 @@ fn main() -> anyhow::Result<()> { let current_dir = env::current_dir()?; let linker_scripts = get_linker_scripts(&args, ¤t_dir)?; - let output_path = - get_output_path(&args).ok_or_else(|| anyhow!("(BUG?) `-o` flag not found"))?; + let output_path = argument_parser::get_output_path(&args) + .ok_or_else(|| anyhow!("(BUG?) `-o` flag not found"))?; // here we assume that we'll end with the same linker script as LLD // I'm unsure about how LLD picks a linker script when there are multiple candidates in the @@ -222,19 +223,6 @@ fn get_linker_scripts(args: &[String], current_dir: &Path) -> anyhow::Result Option<&str> { - let mut next_is_output = false; - for arg in args { - if arg == "-o" { - next_is_output = true; - } else if next_is_output { - return Some(arg); - } - } - - None -} - /// Entry under the `MEMORY` section in a linker script #[derive(Clone, Copy, Debug, PartialEq)] struct MemoryEntry { From 486ed54f00cc6fa338b6e5ae10da319815e9f343 Mon Sep 17 00:00:00 2001 From: Urhengulas Date: Fri, 2 Apr 2021 00:11:12 +0200 Subject: [PATCH 08/21] Outsource `fn get_search_paths` to `mod argument_parser` --- src/argument_parser.rs | 17 ++++++++++++++++- src/main.rs | 14 ++------------ 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/src/argument_parser.rs b/src/argument_parser.rs index c3ab3e5..e08ef4f 100644 --- a/src/argument_parser.rs +++ b/src/argument_parser.rs @@ -1,3 +1,5 @@ +use std::path::PathBuf; + /// Get `output_path`, specified by `-o` pub fn get_output_path(args: &[String]) -> Option<&str> { let mut next_is_output = false; @@ -10,4 +12,17 @@ pub fn get_output_path(args: &[String]) -> Option<&str> { } None -} \ No newline at end of file +} + +/// Get `search_paths`, specified by `-L` +pub fn get_search_paths(args: &[String]) -> Vec { + args.windows(2) + .filter_map(|x| { + if x[0] == "-L" { + log::trace!("new search path: {}", x[1]); + return Some(PathBuf::from(&x[1])); + } + None + }) + .collect::>() +} diff --git a/src/main.rs b/src/main.rs index efed5a7..29016cf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -168,18 +168,8 @@ impl LinkerScript { } 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_paths = argument_parser::get_search_paths(args); + search_paths.push(current_dir.into()); // get names of linker scripts, passed via `-T` // FIXME this doesn't handle "-T memory.x" (as two separate CLI arguments) From 310130d8dd1e2ec551abdf4a9f45da38106a4028 Mon Sep 17 00:00:00 2001 From: Urhengulas Date: Fri, 2 Apr 2021 00:13:16 +0200 Subject: [PATCH 09/21] Outsource `fn get_search_targets` to `mod argument_parser` --- src/argument_parser.rs | 16 +++++++++++++++- src/main.rs | 18 +++--------------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/argument_parser.rs b/src/argument_parser.rs index e08ef4f..7eb60f3 100644 --- a/src/argument_parser.rs +++ b/src/argument_parser.rs @@ -1,4 +1,4 @@ -use std::path::PathBuf; +use std::{borrow::Cow, path::PathBuf}; /// Get `output_path`, specified by `-o` pub fn get_output_path(args: &[String]) -> Option<&str> { @@ -26,3 +26,17 @@ pub fn get_search_paths(args: &[String]) -> Vec { }) .collect::>() } + +/// Get `search_targets`, the names of the linker scripts, specified by `-T` +pub fn get_search_targets(args: &[String]) -> Vec> { + 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::>() +} diff --git a/src/main.rs b/src/main.rs index 29016cf..fa16204 100644 --- a/src/main.rs +++ b/src/main.rs @@ -171,23 +171,11 @@ fn get_linker_scripts(args: &[String], current_dir: &Path) -> anyhow::Result>(); + let mut search_targets = argument_parser::get_search_targets(args); // 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() { + while let Some(filename) = search_targets.pop() { for dir in &search_paths { let full_path = dir.join(&*filename); @@ -198,7 +186,7 @@ fn get_linker_scripts(args: &[String], current_dir: &Path) -> anyhow::Result Date: Fri, 2 Apr 2021 00:22:00 +0200 Subject: [PATCH 10/21] `argument_parser`: Simplfiy `fn get_output_path` --- src/argument_parser.rs | 22 +++++++++++----------- src/main.rs | 3 +-- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/argument_parser.rs b/src/argument_parser.rs index 7eb60f3..e309675 100644 --- a/src/argument_parser.rs +++ b/src/argument_parser.rs @@ -1,17 +1,17 @@ use std::{borrow::Cow, path::PathBuf}; -/// Get `output_path`, specified by `-o` -pub fn get_output_path(args: &[String]) -> Option<&str> { - let mut next_is_output = false; - for arg in args { - if arg == "-o" { - next_is_output = true; - } else if next_is_output { - return Some(arg); - } - } +use anyhow::anyhow; - None +/// Get `output_path`, specified by `-o` +pub fn get_output_path(args: &[String]) -> anyhow::Result<&String> { + args.windows(2) + .find_map(|x| { + if x[0] == "-o" { + return Some(&x[1]); + } + None + }) + .ok_or_else(|| anyhow!("(BUG?) `-o` flag not found")) } /// Get `search_paths`, specified by `-L` diff --git a/src/main.rs b/src/main.rs index fa16204..0c43552 100644 --- a/src/main.rs +++ b/src/main.rs @@ -28,8 +28,7 @@ fn main() -> anyhow::Result<()> { let current_dir = env::current_dir()?; let linker_scripts = get_linker_scripts(&args, ¤t_dir)?; - let output_path = argument_parser::get_output_path(&args) - .ok_or_else(|| anyhow!("(BUG?) `-o` flag not found"))?; + let output_path = argument_parser::get_output_path(&args)?; // here we assume that we'll end with the same linker script as LLD // I'm unsure about how LLD picks a linker script when there are multiple candidates in the From 8ed9b957ca91dd6f76cada605aebed80c766bf81 Mon Sep 17 00:00:00 2001 From: Urhengulas Date: Fri, 2 Apr 2021 00:23:23 +0200 Subject: [PATCH 11/21] Mv obtaining of `output_path` just before usage --- src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 0c43552..f0899e2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -28,7 +28,6 @@ fn main() -> anyhow::Result<()> { let current_dir = env::current_dir()?; let linker_scripts = get_linker_scripts(&args, ¤t_dir)?; - let output_path = argument_parser::get_output_path(&args)?; // here we assume that we'll end with the same linker script as LLD // I'm unsure about how LLD picks a linker script when there are multiple candidates in the @@ -45,6 +44,7 @@ fn main() -> anyhow::Result<()> { let (ram_linker_script, ram_entry) = ram_path_entry .ok_or_else(|| anyhow!("MEMORY.RAM not found after scanning linker scripts"))?; + let output_path = argument_parser::get_output_path(&args)?; let elf = fs::read(output_path)?; let object = object::File::parse(&elf)?; From 72c182b694ac1cb5d50722e3d3d38d1e07842844 Mon Sep 17 00:00:00 2001 From: Urhengulas Date: Fri, 2 Apr 2021 00:04:45 +0200 Subject: [PATCH 12/21] Simplify `struct LinkerScript` --- src/main.rs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/main.rs b/src/main.rs index f0899e2..e2cc625 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,6 +4,7 @@ mod linking; use std::{ borrow::Cow, env, + ffi::OsStr, fs::{self, File}, io::Write, ops::RangeInclusive, @@ -74,7 +75,7 @@ fn main() -> anyhow::Result<()> { let tempdir = tempfile::tempdir()?; let original_linker_script = fs::read_to_string(ram_linker_script.path())?; // XXX in theory could collide with a user-specified linker script - let mut new_linker_script = File::create(tempdir.path().join(ram_linker_script.filename()))?; + let mut new_linker_script = File::create(tempdir.path().join(ram_linker_script.file_name()))?; for (index, line) in original_linker_script.lines().enumerate() { if index == ram_entry.line { @@ -151,18 +152,20 @@ fn round_down_to_nearest_multiple(x: u64, multiple: u64) -> u64 { x - (x % multiple) } -struct LinkerScript { - filename: String, - full_path: PathBuf, -} +struct LinkerScript(PathBuf); impl LinkerScript { - fn filename(&self) -> &str { - &self.filename + fn new(path: PathBuf) -> Self { + assert!(path.is_file()); + Self(path) + } + + fn file_name(&self) -> &OsStr { + self.path().file_name().unwrap() } fn path(&self) -> &Path { - &self.full_path + &self.0 } } @@ -188,10 +191,7 @@ fn get_linker_scripts(args: &[String], current_dir: &Path) -> anyhow::Result Date: Tue, 6 Apr 2021 21:10:05 +0200 Subject: [PATCH 13/21] WIP LinkerScript::file_name --- src/main.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main.rs b/src/main.rs index e2cc625..78025ae 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,7 +4,6 @@ mod linking; use std::{ borrow::Cow, env, - ffi::OsStr, fs::{self, File}, io::Write, ops::RangeInclusive, @@ -160,8 +159,8 @@ impl LinkerScript { Self(path) } - fn file_name(&self) -> &OsStr { - self.path().file_name().unwrap() + fn file_name(&self) -> &str { + self.path().file_name().unwrap().to_str().unwrap() } fn path(&self) -> &Path { From 12b5e6155079c7640be7fb90ad2926fb12fb9ca1 Mon Sep 17 00:00:00 2001 From: Urhengulas Date: Tue, 6 Apr 2021 22:00:24 +0200 Subject: [PATCH 14/21] WIP: clarify `mod linking` --- src/linking.rs | 14 ++++++++++---- src/main.rs | 2 +- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/linking.rs b/src/linking.rs index 4414605..448e31a 100644 --- a/src/linking.rs +++ b/src/linking.rs @@ -5,6 +5,7 @@ use tempfile::TempDir; const EXIT_CODE_FAILURE: i32 = 1; const LINKER: &str = "rust-lld"; +/// Normal linking with just the arguments the user provides pub fn link_normally(args: &[String]) -> Result<(), i32> { let mut c = Command::new(LINKER); c.args(args); @@ -13,18 +14,23 @@ pub fn link_normally(args: &[String]) -> Result<(), i32> { success_or_exitstatus(c) } -pub fn link_again( +/// Re-link with modified arguments, which is the whole point of `flip-link` +/// +/// See inline comments for details of the modifications. +pub fn link_modified( args: &[String], current_dir: &Path, new_origin: u64, tempdir: &TempDir, ) -> Result<(), i32> { let mut c = 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 - c.args(&args[..2]) + c + // HACK `-L` needs to go after `-flavor gnu`; position is currently hardcoded + .args(&args[..2]) + // add the current dir to the linker search path to include all unmodified scripts there .arg("-L".to_string()) .arg(current_dir) + // rest of arguments, except `-flavor gnu` .args(&args[2..]) // we need to override `_stack_start` to make the stack start below fake RAM .arg(format!("--defsym=_stack_start={}", new_origin)) diff --git a/src/main.rs b/src/main.rs index 78025ae..efaf436 100644 --- a/src/main.rs +++ b/src/main.rs @@ -90,7 +90,7 @@ fn main() -> anyhow::Result<()> { // commit file to disk drop(new_linker_script); - linking::link_again(&args, ¤t_dir, new_origin, &tempdir) + linking::link_modified(&args, ¤t_dir, new_origin, &tempdir) .unwrap_or_else(|code| process::exit(code)); Ok(()) From 77ed913d974727adc59322bd56bc522839061bd7 Mon Sep 17 00:00:00 2001 From: Urhengulas Date: Wed, 7 Apr 2021 22:36:47 +0200 Subject: [PATCH 15/21] `linking`: Make linker functions return `io::Result` --- src/linking.rs | 23 +++++++++-------------- src/main.rs | 18 ++++++++++++++---- 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/src/linking.rs b/src/linking.rs index 448e31a..9d1bbed 100644 --- a/src/linking.rs +++ b/src/linking.rs @@ -1,17 +1,20 @@ -use std::{path::Path, process::Command}; +use std::{ + io, + path::Path, + process::{Command, ExitStatus}, +}; use tempfile::TempDir; -const EXIT_CODE_FAILURE: i32 = 1; const LINKER: &str = "rust-lld"; /// Normal linking with just the arguments the user provides -pub fn link_normally(args: &[String]) -> Result<(), i32> { +pub fn link_normally(args: &[String]) -> io::Result { let mut c = Command::new(LINKER); c.args(args); log::trace!("{:?}", c); - success_or_exitstatus(c) + c.status() } /// Re-link with modified arguments, which is the whole point of `flip-link` @@ -22,7 +25,7 @@ pub fn link_modified( current_dir: &Path, new_origin: u64, tempdir: &TempDir, -) -> Result<(), i32> { +) -> io::Result { let mut c = Command::new(LINKER); c // HACK `-L` needs to go after `-flavor gnu`; position is currently hardcoded @@ -39,13 +42,5 @@ pub fn link_modified( .current_dir(tempdir.path()); log::trace!("{:?}", c); - success_or_exitstatus(c) -} - -fn success_or_exitstatus(mut c: Command) -> Result<(), i32> { - let status = c.status().unwrap(); - if !status.success() { - return Err(status.code().unwrap_or(EXIT_CODE_FAILURE)); - } - Ok(()) + c.status() } diff --git a/src/main.rs b/src/main.rs index efaf436..b1f1b8c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,6 +14,7 @@ use std::{ use anyhow::anyhow; use object::{elf, Object as _, ObjectSection, SectionFlags}; +const EXIT_CODE_FAILURE: i32 = 1; /// Stack Pointer alignment required by the ARM architecture const SP_ALIGN: u64 = 8; @@ -23,8 +24,13 @@ fn main() -> anyhow::Result<()> { // NOTE `skip` the name/path of the binary (first argument) let args = env::args().skip(1).collect::>(); - // if linking succeeds then linker scripts are well-formed; we'll rely on that in the parser - linking::link_normally(&args).unwrap_or_else(|code| process::exit(code)); + { + // if linking succeeds then linker scripts are well-formed; we'll rely on that in the parser + let exit_status = linking::link_normally(&args)?; + if !exit_status.success() { + process::exit(exit_status.code().unwrap_or(EXIT_CODE_FAILURE)) + } + } let current_dir = env::current_dir()?; let linker_scripts = get_linker_scripts(&args, ¤t_dir)?; @@ -90,8 +96,12 @@ fn main() -> anyhow::Result<()> { // commit file to disk drop(new_linker_script); - linking::link_modified(&args, ¤t_dir, new_origin, &tempdir) - .unwrap_or_else(|code| process::exit(code)); + { + let exit_status = linking::link_modified(&args, ¤t_dir, new_origin, &tempdir)?; + if !exit_status.success() { + process::exit(exit_status.code().unwrap_or(EXIT_CODE_FAILURE)) + } + } Ok(()) } From bab2cd25a8b5ee37007727e2e635be7fcaaabc82 Mon Sep 17 00:00:00 2001 From: Urhengulas Date: Wed, 7 Apr 2021 22:40:09 +0200 Subject: [PATCH 16/21] Mv comment --- src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index b1f1b8c..f33903f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -25,11 +25,11 @@ fn main() -> anyhow::Result<()> { let args = env::args().skip(1).collect::>(); { - // if linking succeeds then linker scripts are well-formed; we'll rely on that in the parser let exit_status = linking::link_normally(&args)?; if !exit_status.success() { process::exit(exit_status.code().unwrap_or(EXIT_CODE_FAILURE)) } + // if linking succeeds then linker scripts are well-formed; we'll rely on that in the parser } let current_dir = env::current_dir()?; From 5cfca982a9d319637dda97b4e4a8cbe0e67c36c4 Mon Sep 17 00:00:00 2001 From: Urhengulas Date: Wed, 7 Apr 2021 22:56:55 +0200 Subject: [PATCH 17/21] `linking`: Improve doc-comment of `fn link_modfied` --- src/linking.rs | 27 +++++++++++++++++++++------ src/main.rs | 2 +- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/linking.rs b/src/linking.rs index 9d1bbed..f1a0d3d 100644 --- a/src/linking.rs +++ b/src/linking.rs @@ -17,14 +17,29 @@ pub fn link_normally(args: &[String]) -> io::Result { c.status() } -/// Re-link with modified arguments, which is the whole point of `flip-link` +/// Link using a custom linker script and stack starting point. _(This is the whole point of `flip-link`)_ /// -/// See inline comments for details of the modifications. +/// * `args` are arguments passed to the linker invocation +/// * `current_dir` is the directory from which the linker was invoked +/// * `stack_start` is the new, custom starting point from which our stack grows downwards– +/// * this should be right *below* the `.bss+.data` region that we've moved to the top, e.g.: +/// ``` +/// +-------------+ +/// | .bss+.data | +/// +-------------+ <-- `stack_start` +/// | stack | +/// | | | +/// | v | +/// | ~~~~~~~~~~~ | +/// | | +/// +-------------+ +/// ``` +/// * `custom_linker_script_location` is the directory in which the linker script to be used is located pub fn link_modified( args: &[String], current_dir: &Path, - new_origin: u64, - tempdir: &TempDir, + custom_linker_script_location: &TempDir, + stack_start: u64, ) -> io::Result { let mut c = Command::new(LINKER); c @@ -36,10 +51,10 @@ pub fn link_modified( // rest of arguments, except `-flavor gnu` .args(&args[2..]) // we need to override `_stack_start` to make the stack start below fake RAM - .arg(format!("--defsym=_stack_start={}", new_origin)) + .arg(format!("--defsym=_stack_start={}", stack_start)) // 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()); + .current_dir(custom_linker_script_location.path()); log::trace!("{:?}", c); c.status() diff --git a/src/main.rs b/src/main.rs index f33903f..4cc258b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -97,7 +97,7 @@ fn main() -> anyhow::Result<()> { drop(new_linker_script); { - let exit_status = linking::link_modified(&args, ¤t_dir, new_origin, &tempdir)?; + let exit_status = linking::link_modified(&args, ¤t_dir, &tempdir, new_origin)?; if !exit_status.success() { process::exit(exit_status.code().unwrap_or(EXIT_CODE_FAILURE)) } From 5fece345750ee4b2fd37290021b39ce8170aef0b Mon Sep 17 00:00:00 2001 From: Urhengulas Date: Wed, 7 Apr 2021 22:58:00 +0200 Subject: [PATCH 18/21] Commit file, through calling `.flush()`, instead of `drop(...)` --- src/main.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index 4cc258b..bc73b08 100644 --- a/src/main.rs +++ b/src/main.rs @@ -93,8 +93,7 @@ fn main() -> anyhow::Result<()> { writeln!(new_linker_script, "{}", line)?; } } - // commit file to disk - drop(new_linker_script); + new_linker_script.flush()?; { let exit_status = linking::link_modified(&args, ¤t_dir, &tempdir, new_origin)?; From cea2f702eb43e4f1d67996d7ac6ca47f9b646f27 Mon Sep 17 00:00:00 2001 From: Urhengulas Date: Sun, 11 Apr 2021 00:00:23 +0200 Subject: [PATCH 19/21] re: jarpic --- src/linking.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/linking.rs b/src/linking.rs index f1a0d3d..dd70e59 100644 --- a/src/linking.rs +++ b/src/linking.rs @@ -46,7 +46,7 @@ pub fn link_modified( // HACK `-L` needs to go after `-flavor gnu`; position is currently hardcoded .args(&args[..2]) // add the current dir to the linker search path to include all unmodified scripts there - .arg("-L".to_string()) + .arg("-L") .arg(current_dir) // rest of arguments, except `-flavor gnu` .args(&args[2..]) From ee04cf36ec92dcae859025808aa6d7774879efde Mon Sep 17 00:00:00 2001 From: Urhengulas Date: Sun, 11 Apr 2021 00:03:15 +0200 Subject: [PATCH 20/21] Revert "Dissolve `fn notmain` into `fn main`" This reverts commit 83d752d434284eb8f1a330bfbf5de7ac2f3cefcb. --- src/main.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/main.rs b/src/main.rs index bc73b08..a923ad5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,6 +19,10 @@ const EXIT_CODE_FAILURE: i32 = 1; const SP_ALIGN: u64 = 8; fn main() -> anyhow::Result<()> { + notmain().map(|code| process::exit(code)) +} + +fn notmain() -> anyhow::Result { env_logger::init(); // NOTE `skip` the name/path of the binary (first argument) @@ -27,7 +31,7 @@ fn main() -> anyhow::Result<()> { { let exit_status = linking::link_normally(&args)?; if !exit_status.success() { - process::exit(exit_status.code().unwrap_or(EXIT_CODE_FAILURE)) + return Ok(exit_status.code().unwrap_or(EXIT_CODE_FAILURE)); } // if linking succeeds then linker scripts are well-formed; we'll rely on that in the parser } @@ -98,11 +102,11 @@ fn main() -> anyhow::Result<()> { { let exit_status = linking::link_modified(&args, ¤t_dir, &tempdir, new_origin)?; if !exit_status.success() { - process::exit(exit_status.code().unwrap_or(EXIT_CODE_FAILURE)) + return Ok(exit_status.code().unwrap_or(EXIT_CODE_FAILURE)); } } - Ok(()) + Ok(0) } /// Returns `(used_ram_length, used_ram_align)` From c2af667641f1c9b943e839f1701cc2f2e70789d8 Mon Sep 17 00:00:00 2001 From: Urhengulas Date: Sun, 11 Apr 2021 00:27:14 +0200 Subject: [PATCH 21/21] clippy optimizations --- src/argument_parser.rs | 3 +-- src/main.rs | 18 +++++++++--------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/argument_parser.rs b/src/argument_parser.rs index e309675..3e82b89 100644 --- a/src/argument_parser.rs +++ b/src/argument_parser.rs @@ -32,8 +32,7 @@ pub fn get_search_targets(args: &[String]) -> Vec> { args.iter() .filter_map(|arg| { const FLAG: &str = "-T"; - if arg.starts_with(FLAG) { - let filename = &arg[FLAG.len()..]; + if let Some(filename) = arg.strip_prefix(FLAG) { return Some(Cow::Borrowed(filename)); } None diff --git a/src/main.rs b/src/main.rs index a923ad5..46d036c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -233,8 +233,8 @@ impl MemoryEntry { /// Rm `token` from beginning of `line`, else `continue` loop iteration macro_rules! eat { ($line:expr, $token:expr) => { - if $line.starts_with($token) { - $line[$token.len()..].trim() + if let Some(a) = $line.strip_prefix($token) { + a.trim() } else { continue; } @@ -257,7 +257,7 @@ fn get_includes_from_linker_script(linker_script: &str) -> Vec<&str> { fn find_ram_in_linker_script(linker_script: &str) -> Option { macro_rules! tryc { ($expr:expr) => { - if let Some(x) = $expr { + if let Ok(x) = $expr { x } else { continue; @@ -270,7 +270,7 @@ fn find_ram_in_linker_script(linker_script: &str) -> Option { line = eat!(line, "RAM"); // jump over attributes like (xrw) see parse_attributes() - if let Some(i) = line.find(":") { + if let Some(i) = line.find(':') { line = line[i..].trim(); } @@ -278,12 +278,12 @@ fn find_ram_in_linker_script(linker_script: &str) -> Option { line = eat!(line, "ORIGIN"); line = eat!(line, "="); - let boundary_pos = tryc!(line.find(|c| c == ',' || c == ' ')); + 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).ok()) + tryc!(u64::from_str_radix(&line[HEX.len()..boundary_pos], 16)) } else { - tryc!(line[..boundary_pos].parse().ok()) + tryc!(line[..boundary_pos].parse()) }; line = &line[boundary_pos..].trim(); @@ -296,8 +296,8 @@ fn find_ram_in_linker_script(linker_script: &str) -> Option { for segment in segments { let boundary_pos = segment .find(|c| c == 'K' || c == 'M') - .unwrap_or(segment.len()); - let length: u64 = tryc!(segment[..boundary_pos].parse().ok()); + .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();