From 17dcdada9885122258cf2b05f7bd43501bfac863 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Miku=C5=82a?= Date: Sat, 15 Feb 2025 18:57:31 +0100 Subject: [PATCH] Implement sysroot handling --- libwild/src/args.rs | 28 ++++++++++++--- libwild/src/input_data.rs | 7 ++-- libwild/src/linker_script.rs | 70 ++++++++++++++++++++++++++++++++++++ 3 files changed, 98 insertions(+), 7 deletions(-) diff --git a/libwild/src/args.rs b/libwild/src/args.rs index f07eed46..f58a64db 100644 --- a/libwild/src/args.rs +++ b/libwild/src/args.rs @@ -16,6 +16,7 @@ use crate::alignment::Alignment; use crate::arch::Architecture; use crate::error::Result; use crate::input_data::FileId; +use crate::linker_script::maybe_forced_sysroot; use crate::save_dir::SaveDir; use anyhow::bail; use anyhow::ensure; @@ -56,6 +57,7 @@ pub(crate) struct Args { pub(crate) file_write_mode: FileWriteMode, pub(crate) no_undefined: bool, pub(crate) allow_copy_relocations: bool, + pub(crate) sysroot: Option>, /// If set, GC stats will be written to the specified filename. pub(crate) write_gc_stats: Option, @@ -240,6 +242,7 @@ impl Default for Args { files_per_group: None, no_undefined: false, should_print_version: false, + sysroot: None, } } } @@ -302,13 +305,19 @@ pub(crate) fn parse, I: Iterator>(mut input: I) -> Resul }; if let Some(rest) = arg.strip_prefix("-L") { + let handle_sysroot = |path| { + args.sysroot + .as_ref() + .and_then(|sysroot| maybe_forced_sysroot(path, sysroot)) + .unwrap_or_else(|| Box::from(path)) + }; if rest.is_empty() { if let Some(next) = input.next() { args.lib_search_path - .push(Box::from(Path::new(next.as_ref()))); + .push(handle_sysroot(Path::new(next.as_ref()))); } } else { - args.lib_search_path.push(Box::from(Path::new(rest))); + args.lib_search_path.push(handle_sysroot(Path::new(rest))); } } else if let Some(rest) = arg.strip_prefix("-l") { args.inputs.push(Input { @@ -507,8 +516,14 @@ pub(crate) fn parse, I: Iterator>(mut input: I) -> Resul } else if strip_option(arg) .is_some_and(|stripped_arg| SILENTLY_IGNORED_FLAGS.contains(&stripped_arg)) { - } else if long_arg_split_prefix("sysroot=").is_some() { - warn_unsupported("--sysroot")?; + } else if let Some(sysroot) = long_arg_split_prefix("sysroot=") { + let sysroot = Path::new(sysroot); + args.sysroot = Some(Box::from(sysroot)); + for path in &mut args.lib_search_path { + if let Some(new_path) = maybe_forced_sysroot(path, sysroot) { + *path = new_path; + } + } } else if arg.starts_with('-') { unrecognised.push(format!("`{arg}`")); } else { @@ -889,6 +904,7 @@ mod tests { "-X", "-EL", "-v", + "--sysroot=/usr/aarch64-linux-gnu", ]; #[track_caller] @@ -923,6 +939,10 @@ mod tests { assert_eq!(args.soname, Some("bar".to_owned())); assert_eq!(args.num_threads, NonZeroUsize::new(1).unwrap()); assert!(args.should_print_version); + assert_eq!( + args.sysroot, + Some(Box::from(Path::new("/usr/aarch64-linux-gnu"))) + ); } #[test] diff --git a/libwild/src/input_data.rs b/libwild/src/input_data.rs index 66387d16..47a4befb 100644 --- a/libwild/src/input_data.rs +++ b/libwild/src/input_data.rs @@ -99,7 +99,7 @@ impl<'config> InputData<'config> { }; for input in &config.inputs { - input_data.register_input(input)?; + input_data.register_input(input, config.sysroot.as_deref())?; } // Our last "file", similar to the prelude is responsible for internal stuff, but this time @@ -114,7 +114,7 @@ impl<'config> InputData<'config> { Ok(input_data) } - fn register_input(&mut self, input: &Input) -> Result { + fn register_input(&mut self, input: &Input, sysroot: Option<&Path>) -> Result { let paths = input.path(self.config)?; let absolute_path = &paths.absolute; if !self.filenames.insert(absolute_path.clone()) { @@ -154,8 +154,9 @@ impl<'config> InputData<'config> { &bytes, absolute_path, input.modifiers, + sysroot, )? { - self.register_input(&input)?; + self.register_input(&input, sysroot)?; } return Ok(()); } diff --git a/libwild/src/linker_script.rs b/libwild/src/linker_script.rs index 6b80afa0..3ee00efe 100644 --- a/libwild/src/linker_script.rs +++ b/libwild/src/linker_script.rs @@ -23,6 +23,7 @@ pub(crate) fn linker_script_to_inputs( bytes: &[u8], path: &Path, modifiers: Modifiers, + sysroot: Option<&Path>, ) -> Result> { let text = std::str::from_utf8(bytes)?; let directory = path @@ -33,11 +34,36 @@ pub(crate) fn linker_script_to_inputs( .into_iter() .map(|mut input| { input.search_first = Some(directory.to_owned()); + if let (Some(sysroot), InputSpec::File(file)) = (sysroot, &mut input.spec) { + if let Some(new_file) = maybe_apply_sysroot(path, file, sysroot) { + *file = new_file; + } + } + input }) .collect()) } +fn maybe_apply_sysroot( + linker_script_path: &Path, + input_path: &Path, + sysroot: &Path, +) -> Option> { + if matches!(linker_script_path.canonicalize(), Ok(p) if p.starts_with(sysroot)) { + Some(Box::from(sysroot.join(input_path.strip_prefix("/").ok()?))) + } else { + maybe_forced_sysroot(input_path, sysroot) + } +} + +pub(crate) fn maybe_forced_sysroot(path: &Path, sysroot: &Path) -> Option> { + path.strip_prefix("=") + .or_else(|_| path.strip_prefix("$SYSROOT")) + .ok() + .map(|stripped| Box::from(sysroot.join(stripped))) +} + /// A version script. See https://sourceware.org/binutils/docs/ld/VERSION.html #[derive(Default)] pub(crate) struct VersionScript<'data> { @@ -461,4 +487,48 @@ mod tests { ); assert!(version.locals.matches_all); } + + #[test] + fn test_sysroot_application() { + // Use real paths because of canonicalization + let fake_sysroot = Path::new(env!("CARGO_MANIFEST_DIR")); + let fake_linker_script_inside_sysroot = fake_sysroot.join("Cargo.toml"); + let fake_linker_script_outside_sysroot = Path::new("/Cargo.toml"); + // Linker script is located in the sysroot + assert_equal( + maybe_apply_sysroot( + &fake_linker_script_inside_sysroot, + Path::new("/lib/libc.so.6"), + fake_sysroot, + ), + Some(Box::from(fake_sysroot.join("lib/libc.so.6"))), + ); + // Linker script is not located in the sysroot + assert_equal( + maybe_apply_sysroot( + fake_linker_script_outside_sysroot, + Path::new("/lib/libc.so.6"), + fake_sysroot, + ), + None, + ); + // Sysroot enforced by `=` + assert_equal( + maybe_apply_sysroot( + fake_linker_script_outside_sysroot, + Path::new("=/lib/libc.so.6"), + fake_sysroot, + ), + Some(Box::from(fake_sysroot.join("lib/libc.so.6"))), + ); + // Sysroot enforced by `$SYSROOT` + assert_equal( + maybe_apply_sysroot( + fake_linker_script_outside_sysroot, + Path::new("$SYSROOT/lib/libc.so.6"), + fake_sysroot, + ), + Some(Box::from(fake_sysroot.join("lib/libc.so.6"))), + ); + } }