@@ -113,6 +113,7 @@ impl Package for DirectoryPackage {
113
113
} else {
114
114
builder. move_file ( path. clone ( ) , & src_path) ?
115
115
}
116
+ nix_patchelf_if_needed ( & target. prefix ( ) . path ( ) . join ( path. clone ( ) ) )
116
117
}
117
118
"dir" => {
118
119
if self . copy {
@@ -135,6 +136,175 @@ impl Package for DirectoryPackage {
135
136
}
136
137
}
137
138
139
+ fn nix_wrap_lld ( dest_lld_path : & Path ) -> Result < ( ) > {
140
+ use std:: fs;
141
+ use std:: io:: Write ;
142
+ use std:: os:: unix:: fs:: PermissionsExt ;
143
+
144
+ let path = dest_lld_path. parent ( ) . unwrap ( ) ;
145
+ let mut unwrapped_name = path. file_name ( ) . unwrap ( ) . to_string_lossy ( ) . to_string ( ) ;
146
+ unwrapped_name. push_str ( "-unwrapped" ) ;
147
+ let unwrapped_dir = path. with_file_name ( unwrapped_name) ;
148
+ fs:: create_dir ( & unwrapped_dir) . context ( "failed to create unwrapped directory" ) ?;
149
+ let mut unwrapped_lld = unwrapped_dir;
150
+ unwrapped_lld. push ( dest_lld_path. file_name ( ) . unwrap ( ) ) ;
151
+ fs:: rename ( dest_lld_path, & unwrapped_lld) . context ( "failed to move file" ) ?;
152
+ let mut ld_wrapper_path = std:: env:: current_exe ( ) ?
153
+ . parent ( )
154
+ . ok_or ( anyhow ! ( "failed to get parent directory" ) ) ?
155
+ . with_file_name ( "nix-support" ) ;
156
+ let mut file = std:: fs:: File :: create ( dest_lld_path) ?;
157
+ ld_wrapper_path. push ( "ld-wrapper.sh" ) ;
158
+
159
+ let wrapped_script = format ! (
160
+ "#!/usr/bin/env bash
161
+ set -eu -o pipefail +o posix
162
+ shopt -s nullglob
163
+ export PROG=\" {}\"
164
+ \" {}\" $@",
165
+ unwrapped_lld. to_string_lossy( ) . to_string( ) ,
166
+ ld_wrapper_path. to_string_lossy( ) . to_string( ) ,
167
+ ) ;
168
+ file. write_all ( wrapped_script. as_bytes ( ) ) ?;
169
+ let mut permissions = file. metadata ( ) ?. permissions ( ) ;
170
+ permissions. set_mode ( 0o755 ) ;
171
+ file. set_permissions ( permissions) ?;
172
+ Ok ( ( ) )
173
+ }
174
+
175
+ fn nix_patchelf_if_needed ( dest_path : & Path ) {
176
+ use std:: fs:: File ;
177
+ use std:: os:: unix:: fs:: FileExt ;
178
+
179
+ struct ELFReader < ' a > {
180
+ file : & ' a mut File ,
181
+ is_32bit : bool ,
182
+ is_little_end : bool ,
183
+ }
184
+
185
+ impl < ' a > ELFReader < ' a > {
186
+ const MAGIC_NUMBER : & ' static [ u8 ] = & [ 0x7F , 0x45 , 0x4c , 0x46 ] ;
187
+ const ET_EXEC : u16 = 0x2 ;
188
+ const ET_DYN : u16 = 0x3 ;
189
+ const PT_INTERP : u32 = 0x3 ;
190
+
191
+ fn new ( file : & ' a mut File ) -> Option < Self > {
192
+ let mut magic_number = [ 0 ; 4 ] ;
193
+ file. read_exact ( & mut magic_number) . ok ( ) ?;
194
+ if Self :: MAGIC_NUMBER != magic_number {
195
+ return None ;
196
+ }
197
+ let mut ei_class = [ 0 ; 1 ] ;
198
+ file. read_exact_at ( & mut ei_class, 0x4 ) . ok ( ) ?;
199
+ let is_32bit = ei_class[ 0 ] == 1 ;
200
+ let mut ei_data = [ 0 ; 1 ] ;
201
+ file. read_exact_at ( & mut ei_data, 0x5 ) . ok ( ) ?;
202
+ let is_little_end = ei_data[ 0 ] == 1 ;
203
+ Some ( Self {
204
+ file,
205
+ is_32bit,
206
+ is_little_end,
207
+ } )
208
+ }
209
+
210
+ fn is_exec_or_dyn ( & self ) -> bool {
211
+ let e_type = self . read_u16_at ( 0x10 ) ;
212
+ e_type == Self :: ET_EXEC || e_type == Self :: ET_DYN
213
+ }
214
+
215
+ fn e_phoff ( & self ) -> u64 {
216
+ if self . is_32bit {
217
+ self . read_u32_at ( 0x1C ) as u64
218
+ } else {
219
+ self . read_u64_at ( 0x20 )
220
+ }
221
+ }
222
+
223
+ fn e_phentsize ( & self ) -> u64 {
224
+ let offset = if self . is_32bit { 0x2A } else { 0x36 } ;
225
+ self . read_u16_at ( offset) as u64
226
+ }
227
+
228
+ fn e_phnum ( & self ) -> u64 {
229
+ let offset = if self . is_32bit { 0x2C } else { 0x38 } ;
230
+ self . read_u16_at ( offset) as u64
231
+ }
232
+
233
+ fn has_interp ( & self ) -> bool {
234
+ let e_phoff = self . e_phoff ( ) ;
235
+ let e_phentsize = self . e_phentsize ( ) ;
236
+ let e_phnum = self . e_phnum ( ) ;
237
+ for i in 0 ..e_phnum {
238
+ let p_type = self . read_u32_at ( e_phoff + i * e_phentsize) ;
239
+ if p_type == Self :: PT_INTERP {
240
+ return true ;
241
+ }
242
+ }
243
+ false
244
+ }
245
+
246
+ fn read_u16_at ( & self , offset : u64 ) -> u16 {
247
+ let mut data = [ 0 ; 2 ] ;
248
+ self . file . read_exact_at ( & mut data, offset) . unwrap ( ) ;
249
+ if self . is_little_end {
250
+ u16:: from_le_bytes ( data)
251
+ } else {
252
+ u16:: from_be_bytes ( data)
253
+ }
254
+ }
255
+
256
+ fn read_u32_at ( & self , offset : u64 ) -> u32 {
257
+ let mut data = [ 0 ; 4 ] ;
258
+ self . file . read_exact_at ( & mut data, offset) . unwrap ( ) ;
259
+ if self . is_little_end {
260
+ u32:: from_le_bytes ( data)
261
+ } else {
262
+ u32:: from_be_bytes ( data)
263
+ }
264
+ }
265
+
266
+ fn read_u64_at ( & self , offset : u64 ) -> u64 {
267
+ let mut data = [ 0 ; 8 ] ;
268
+ self . file . read_exact_at ( & mut data, offset) . unwrap ( ) ;
269
+ if self . is_little_end {
270
+ u64:: from_le_bytes ( data)
271
+ } else {
272
+ u64:: from_be_bytes ( data)
273
+ }
274
+ }
275
+ }
276
+
277
+ let Some ( mut dest_file) = File :: open ( dest_path) . ok ( ) else {
278
+ return ;
279
+ } ;
280
+ let Some ( elf) = ELFReader :: new ( & mut dest_file) else {
281
+ return ;
282
+ } ;
283
+ if !elf. is_exec_or_dyn ( ) {
284
+ return ;
285
+ }
286
+ let mut patch_command = std:: process:: Command :: new ( "@patchelf@/bin/patchelf" ) ;
287
+ if elf. has_interp ( ) {
288
+ patch_command
289
+ . arg ( "--set-interpreter" )
290
+ . arg ( "@dynamicLinker@" ) ;
291
+ }
292
+ if Some ( std:: ffi:: OsStr :: new ( "rust-lld" ) ) == dest_path. file_name ( ) || !elf. has_interp ( ) {
293
+ patch_command. arg ( "--add-rpath" ) . arg ( "@libPath@" ) ;
294
+ }
295
+
296
+ debug ! ( "patching {dest_path:?} using patchelf" ) ;
297
+ if let Err ( err) = patch_command. arg ( dest_path) . output ( ) {
298
+ warn ! ( "failed to execute patchelf: {err:?}" ) ;
299
+ }
300
+
301
+ if Some ( std:: ffi:: OsStr :: new ( "ld.lld" ) ) == dest_path. file_name ( ) {
302
+ if let Err ( err) = nix_wrap_lld ( dest_path) {
303
+ warn ! ( "failed to wrap `ld.lld`: {err:?}" ) ;
304
+ }
305
+ }
306
+ }
307
+
138
308
#[ derive( Debug ) ]
139
309
pub ( crate ) struct TarPackage < ' a > ( DirectoryPackage , temp:: Dir < ' a > ) ;
140
310
0 commit comments