From 8dec28cb5d156d3f10c9112b2aaae668aee74499 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 --- Cargo.lock | 7 ++++ libwild/Cargo.toml | 1 + libwild/src/args.rs | 28 ++++++++++++--- libwild/src/input_data.rs | 7 ++-- libwild/src/linker_script.rs | 68 ++++++++++++++++++++++++++++++++++++ 5 files changed, 104 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dede62a7..996363cf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -652,6 +652,7 @@ dependencies = [ "linker-utils", "memchr", "memmap2", + "normalize-path", "object", "rayon", "sharded-offset-map", @@ -794,6 +795,12 @@ dependencies = [ "bitflags", ] +[[package]] +name = "normalize-path" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5438dd2b2ff4c6df6e1ce22d825ed2fa93ee2922235cc45186991717f0a892d" + [[package]] name = "nu-ansi-term" version = "0.46.0" diff --git a/libwild/Cargo.toml b/libwild/Cargo.toml index 516ac538..68bdc271 100644 --- a/libwild/Cargo.toml +++ b/libwild/Cargo.toml @@ -45,6 +45,7 @@ blake3 = { version = "1.5.5", features = ["rayon"] } uuid = { version = "1.13.1", features = ["v4"] } hex = "0.4.3" atomic-take = "1.1.0" +normalize-path = "0.2.1" [dev-dependencies] ar = "0.9.0" 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..d49ef08e 100644 --- a/libwild/src/linker_script.rs +++ b/libwild/src/linker_script.rs @@ -14,6 +14,7 @@ use crate::symbol::SymbolName; use anyhow::anyhow; use anyhow::bail; use anyhow::Context; +use normalize_path::NormalizePath; use std::collections::HashSet; use std::path::Path; @@ -23,6 +24,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 +35,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 linker_script_path.normalize().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 +488,45 @@ mod tests { ); assert!(version.locals.matches_all); } + + #[test] + fn test_sysroot_application() { + let sysroot = Path::new("/usr/aarch64-linux-gnu"); + // Linker script is located in the sysroot + assert_equal( + maybe_apply_sysroot( + &sysroot.join("lib/libc.so"), + Path::new("/lib/libc.so.6"), + sysroot, + ), + Some(Box::from(sysroot.join("lib/libc.so.6"))), + ); + // Linker script is not located in the sysroot + assert_equal( + maybe_apply_sysroot( + Path::new("/lib/libc.so"), + Path::new("/lib/libc.so.6"), + sysroot, + ), + None, + ); + // Sysroot enforced by `=` + assert_equal( + maybe_apply_sysroot( + Path::new("/lib/libc.so"), + Path::new("=/lib/libc.so.6"), + sysroot, + ), + Some(Box::from(sysroot.join("lib/libc.so.6"))), + ); + // Sysroot enforced by `$SYSROOT` + assert_equal( + maybe_apply_sysroot( + Path::new("/lib/libc.so"), + Path::new("$SYSROOT/lib/libc.so.6"), + sysroot, + ), + Some(Box::from(sysroot.join("lib/libc.so.6"))), + ); + } }