Skip to content

Commit

Permalink
[skip ci] use hand-rolled install-name-tool replacement in rust
Browse files Browse the repository at this point in the history
  • Loading branch information
wolfv committed Dec 21, 2023
1 parent d392345 commit f497c58
Showing 1 changed file with 138 additions and 35 deletions.
173 changes: 138 additions & 35 deletions src/macos/link.rs
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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) {
Expand All @@ -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)?;
}

Expand Down Expand Up @@ -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();
}
}

0 comments on commit f497c58

Please sign in to comment.