diff --git a/src/macos/link.rs b/src/macos/link.rs index 77cb51320..7b226e529 100644 --- a/src/macos/link.rs +++ b/src/macos/link.rs @@ -1,11 +1,11 @@ //! Relink a dylib to use relative paths for rpaths use goblin::mach::Mach; +use scroll::Pread; use std::collections::HashSet; use std::fs::{self, File}; use std::io::Read; use std::path::{Path, PathBuf}; use std::process::Command; - /// A macOS dylib (Mach-O) pub struct Dylib { /// Path to the dylib @@ -107,29 +107,6 @@ impl Dylib { pub fn relink(&self, prefix: &Path, encoded_prefix: &Path) -> Result<(), RelinkError> { let mut changes = DylibChanges::default(); let mut modified = false; - for rpath in &self.rpaths { - if rpath.is_absolute() { - let orig_path = encoded_prefix.join( - self.path - .strip_prefix(prefix)? - .parent() - .expect("Could not get parent"), - ); - - let relpath = - pathdiff::diff_paths(rpath, &orig_path).ok_or(RelinkError::PathDiffError { - from: orig_path.clone(), - to: rpath.clone(), - })?; - - let new_rpath = - PathBuf::from(format!("@loader_path/{}", relpath.to_string_lossy())); - - changes.add_rpath.insert(new_rpath); - changes.delete_rpath.insert(rpath.clone()); - modified = true; - } - } let exchange_dylib = |path: &Path| { if let Ok(relpath) = path.strip_prefix(prefix) { @@ -140,22 +117,126 @@ impl Dylib { } }; - if let Some(id) = &self.id { - if let Some(new_dylib) = exchange_dylib(id) { - changes.change_id = Some(new_dylib); - modified = true; - } - } + let data = fs::read(&self.path)?; - for lib in &self.libraries { - if let Some(new_dylib) = exchange_dylib(lib) { - changes.change_dylib.push((lib.clone(), new_dylib)); - modified = true; + let object = match goblin::mach::Mach::parse(&data)? { + Mach::Binary(mach) => mach, + _ => { + tracing::error!("Not a valid Mach-O binary."); + return Err(RelinkError::FileTypeNotHandled); + } + }; + let mut new_data = data.clone(); + + for cmd in object.load_commands.iter() { + match cmd.command { + goblin::mach::load_command::CommandVariant::Rpath(ref rpath) => { + let cmdsize = rpath.cmdsize as usize; + let offset = cmd.offset + rpath.path as usize; + let path_data = data.pread::<&str>(offset).unwrap().to_string(); + println!("path_data: {:?}", path_data); + let path = PathBuf::from(&path_data); + if path.is_absolute() { + let orig_path = encoded_prefix.join( + self.path + .strip_prefix(prefix)? + .parent() + .expect("Could not get parent"), + ); + + let relpath = pathdiff::diff_paths(&path, &orig_path).ok_or( + RelinkError::PathDiffError { + from: orig_path.clone(), + to: path.clone(), + }, + )?; + + let new_rpath = + PathBuf::from(format!("@loader_path/{}", relpath.to_string_lossy())); + println!("Exchange rpath {:?} -> {:?}", path, new_rpath); + let new_rpath_string = new_rpath.to_string_lossy(); + let mut new_rpath_bytes = new_rpath_string.as_bytes().to_vec(); + // extend with null bytes + let string_len = path_data.len(); + new_rpath_bytes.resize(string_len, 0); + + new_data.splice(offset..offset + string_len, new_rpath_bytes); + + modified = true; + } + } + // check dylib id + goblin::mach::load_command::CommandVariant::IdDylib(ref id) + | goblin::mach::load_command::CommandVariant::LoadWeakDylib(ref id) + | goblin::mach::load_command::CommandVariant::LoadUpwardDylib(ref id) + | goblin::mach::load_command::CommandVariant::ReexportDylib(ref id) + | goblin::mach::load_command::CommandVariant::LazyLoadDylib(ref id) + | goblin::mach::load_command::CommandVariant::LoadDylib(ref id) => { + let offset = cmd.offset + id.dylib.name as usize; + let path_data = data.pread::<&str>(offset).unwrap().to_string(); + println!("ID path_data: {:?}", path_data); + + let path = PathBuf::from(&path_data); + + if let Some(new_path) = exchange_dylib(&path) { + let new_rpath_string = new_path.to_string_lossy(); + println!("Exchange dylib {:?} -> {:?}", path, new_rpath_string); + let mut new_rpath_bytes = new_rpath_string.as_bytes().to_vec(); + // extend with null bytes + let string_len = path_data.len() + 1; + new_rpath_bytes.resize(string_len, 0); + + new_data.splice(offset..offset + string_len, new_rpath_bytes); + + modified = true; + } + } + _ => {} } } + // for rpath in &self.rpaths { + // if rpath.is_absolute() { + // let orig_path = encoded_prefix.join( + // self.path + // .strip_prefix(prefix)? + // .parent() + // .expect("Could not get parent"), + // ); + + // let relpath = + // pathdiff::diff_paths(rpath, &orig_path).ok_or(RelinkError::PathDiffError { + // from: orig_path.clone(), + // to: rpath.clone(), + // })?; + + // let new_rpath = + // PathBuf::from(format!("@loader_path/{}", relpath.to_string_lossy())); + + // changes.add_rpath.insert(new_rpath); + // changes.delete_rpath.insert(rpath.clone()); + // modified = true; + // } + // } + + // if let Some(id) = &self.id { + // if let Some(new_dylib) = exchange_dylib(id) { + // changes.change_id = Some(new_dylib); + // modified = true; + // } + // } + + // for lib in &self.libraries { + // if let Some(new_dylib) = exchange_dylib(lib) { + // changes.change_dylib.push((lib.clone(), new_dylib)); + // modified = true; + // } + // } + if modified { - install_name_tool(&self.path, &changes)?; + // install_name_tool(&self.path, &changes)?; + // overwrite the file + fs::write(&self.path, new_data)?; codesign(&self.path)?; } @@ -228,3 +309,25 @@ fn install_name_tool(dylib_path: &Path, changes: &DylibChanges) -> Result<(), Re Ok(()) } + +#[cfg(test)] +mod test { + use super::*; + use std::path::PathBuf; + + #[test] + fn test_relink() { + let binary_orig = + PathBuf::from("/Users/wolfv/Programs/rattler-build/libcurl.4.dylib.start"); + + let binary = PathBuf::from("/Users/wolfv/Programs/rattler-build/libcurl.4.dylib"); + fs::copy(&binary_orig, &binary).unwrap(); + + // let prefix = PathBuf::from("/Users/runner/work/_temp/_runner_file_commands"); + let encoded_prefix = PathBuf::from("/Users/wolfv/Programs/rattler-build/output/bld/rattler-build_curl_1703190008/host_env_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold"); + let dylib = Dylib::new(&binary).unwrap(); + dylib + .relink(&binary.parent().unwrap(), &encoded_prefix) + .unwrap(); + } +}