nixpkgs mirror (for testing) github.com/NixOS/nixpkgs
nix
at fix-function-merge 188 lines 6.3 kB view raw
1diff --git a/src/dist/component/package.rs b/src/dist/component/package.rs 2index dfccc661..85233f3b 100644 3--- a/src/dist/component/package.rs 4+++ b/src/dist/component/package.rs 5@@ -113,6 +113,7 @@ impl Package for DirectoryPackage { 6 } else { 7 builder.move_file(path.clone(), &src_path)? 8 } 9+ nix_patchelf_if_needed(&target.prefix().path().join(path.clone())) 10 } 11 "dir" => { 12 if self.copy { 13@@ -135,6 +136,175 @@ impl Package for DirectoryPackage { 14 } 15 } 16 17+fn nix_wrap_lld(dest_lld_path: &Path) -> Result<()> { 18+ use std::fs; 19+ use std::io::Write; 20+ use std::os::unix::fs::PermissionsExt; 21+ 22+ let path = dest_lld_path.parent().unwrap(); 23+ let mut unwrapped_name = path.file_name().unwrap().to_string_lossy().to_string(); 24+ unwrapped_name.push_str("-unwrapped"); 25+ let unwrapped_dir = path.with_file_name(unwrapped_name); 26+ fs::create_dir(&unwrapped_dir).context("failed to create unwrapped directory")?; 27+ let mut unwrapped_lld = unwrapped_dir; 28+ unwrapped_lld.push(dest_lld_path.file_name().unwrap()); 29+ fs::rename(dest_lld_path, &unwrapped_lld).context("failed to move file")?; 30+ let mut ld_wrapper_path = std::env::current_exe()? 31+ .parent() 32+ .ok_or(anyhow!("failed to get parent directory"))? 33+ .with_file_name("nix-support"); 34+ let mut file = std::fs::File::create(dest_lld_path)?; 35+ ld_wrapper_path.push("ld-wrapper.sh"); 36+ 37+ let wrapped_script = format!( 38+ "#!/usr/bin/env bash 39+set -eu -o pipefail +o posix 40+shopt -s nullglob 41+export PROG=\"{}\" 42+\"{}\" $@", 43+ unwrapped_lld.to_string_lossy().to_string(), 44+ ld_wrapper_path.to_string_lossy().to_string(), 45+ ); 46+ file.write_all(wrapped_script.as_bytes())?; 47+ let mut permissions = file.metadata()?.permissions(); 48+ permissions.set_mode(0o755); 49+ file.set_permissions(permissions)?; 50+ Ok(()) 51+} 52+ 53+fn nix_patchelf_if_needed(dest_path: &Path) { 54+ use std::fs::File; 55+ use std::os::unix::fs::FileExt; 56+ 57+ struct ELFReader<'a> { 58+ file: &'a mut File, 59+ is_32bit: bool, 60+ is_little_end: bool, 61+ } 62+ 63+ impl<'a> ELFReader<'a> { 64+ const MAGIC_NUMBER: &'static [u8] = &[0x7F, 0x45, 0x4c, 0x46]; 65+ const ET_EXEC: u16 = 0x2; 66+ const ET_DYN: u16 = 0x3; 67+ const PT_INTERP: u32 = 0x3; 68+ 69+ fn new(file: &'a mut File) -> Option<Self> { 70+ let mut magic_number = [0; 4]; 71+ file.read_exact(&mut magic_number).ok()?; 72+ if Self::MAGIC_NUMBER != magic_number { 73+ return None; 74+ } 75+ let mut ei_class = [0; 1]; 76+ file.read_exact_at(&mut ei_class, 0x4).ok()?; 77+ let is_32bit = ei_class[0] == 1; 78+ let mut ei_data = [0; 1]; 79+ file.read_exact_at(&mut ei_data, 0x5).ok()?; 80+ let is_little_end = ei_data[0] == 1; 81+ Some(Self { 82+ file, 83+ is_32bit, 84+ is_little_end, 85+ }) 86+ } 87+ 88+ fn is_exec_or_dyn(&self) -> bool { 89+ let e_type = self.read_u16_at(0x10); 90+ e_type == Self::ET_EXEC || e_type == Self::ET_DYN 91+ } 92+ 93+ fn e_phoff(&self) -> u64 { 94+ if self.is_32bit { 95+ self.read_u32_at(0x1C) as u64 96+ } else { 97+ self.read_u64_at(0x20) 98+ } 99+ } 100+ 101+ fn e_phentsize(&self) -> u64 { 102+ let offset = if self.is_32bit { 0x2A } else { 0x36 }; 103+ self.read_u16_at(offset) as u64 104+ } 105+ 106+ fn e_phnum(&self) -> u64 { 107+ let offset = if self.is_32bit { 0x2C } else { 0x38 }; 108+ self.read_u16_at(offset) as u64 109+ } 110+ 111+ fn has_interp(&self) -> bool { 112+ let e_phoff = self.e_phoff(); 113+ let e_phentsize = self.e_phentsize(); 114+ let e_phnum = self.e_phnum(); 115+ for i in 0..e_phnum { 116+ let p_type = self.read_u32_at(e_phoff + i * e_phentsize); 117+ if p_type == Self::PT_INTERP { 118+ return true; 119+ } 120+ } 121+ false 122+ } 123+ 124+ fn read_u16_at(&self, offset: u64) -> u16 { 125+ let mut data = [0; 2]; 126+ self.file.read_exact_at(&mut data, offset).unwrap(); 127+ if self.is_little_end { 128+ u16::from_le_bytes(data) 129+ } else { 130+ u16::from_be_bytes(data) 131+ } 132+ } 133+ 134+ fn read_u32_at(&self, offset: u64) -> u32 { 135+ let mut data = [0; 4]; 136+ self.file.read_exact_at(&mut data, offset).unwrap(); 137+ if self.is_little_end { 138+ u32::from_le_bytes(data) 139+ } else { 140+ u32::from_be_bytes(data) 141+ } 142+ } 143+ 144+ fn read_u64_at(&self, offset: u64) -> u64 { 145+ let mut data = [0; 8]; 146+ self.file.read_exact_at(&mut data, offset).unwrap(); 147+ if self.is_little_end { 148+ u64::from_le_bytes(data) 149+ } else { 150+ u64::from_be_bytes(data) 151+ } 152+ } 153+ } 154+ 155+ let Some(mut dest_file) = File::open(dest_path).ok() else { 156+ return; 157+ }; 158+ let Some(elf) = ELFReader::new(&mut dest_file) else { 159+ return; 160+ }; 161+ if !elf.is_exec_or_dyn() { 162+ return; 163+ } 164+ let mut patch_command = std::process::Command::new("@patchelf@/bin/patchelf"); 165+ if elf.has_interp() { 166+ patch_command 167+ .arg("--set-interpreter") 168+ .arg("@dynamicLinker@"); 169+ } 170+ if Some(std::ffi::OsStr::new("rust-lld")) == dest_path.file_name() || !elf.has_interp() { 171+ patch_command.arg("--add-rpath").arg("@libPath@"); 172+ } 173+ 174+ debug!("patching {dest_path:?} using patchelf"); 175+ if let Err(err) = patch_command.arg(dest_path).output() { 176+ warn!("failed to execute patchelf: {err:?}"); 177+ } 178+ 179+ if Some(std::ffi::OsStr::new("ld.lld")) == dest_path.file_name() { 180+ if let Err(err) = nix_wrap_lld(dest_path) { 181+ warn!("failed to wrap `ld.lld`: {err:?}"); 182+ } 183+ } 184+} 185+ 186 #[derive(Debug)] 187 pub(crate) struct TarPackage<'a>(DirectoryPackage, temp::Dir<'a>); 188