nixpkgs mirror (for testing)
github.com/NixOS/nixpkgs
nix
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