@@ -98,17 +98,16 @@ unsafe fn compute_args(mem: *mut usize) -> (i32, *mut *mut u8, *mut *mut u8) {
98
98
#[ cfg( any( feature = "origin-start" , feature = "external-start" ) ) ]
99
99
#[ allow( unused_variables) ]
100
100
unsafe fn init_runtime ( mem : * mut usize , envp : * mut * mut u8 ) {
101
+ // Before doing anything else, perform dynamic relocations.
102
+ #[ cfg( all( feature = "experimental-relocate" , feature = "origin-start" ) ) ]
103
+ #[ cfg( relocation_model = "pic" ) ]
104
+ relocate ( envp) ;
105
+
101
106
// Explicitly initialize `rustix` so that we can control the initialization
102
107
// order.
103
108
#[ cfg( feature = "param" ) ]
104
109
rustix:: param:: init ( envp) ;
105
110
106
- // After initializing the AUX data in rustix, but before doing anything
107
- // else, perform dynamic relocations.
108
- #[ cfg( all( feature = "experimental-relocate" , feature = "origin-start" ) ) ]
109
- #[ cfg( relocation_model = "pic" ) ]
110
- relocate ( ) ;
111
-
112
111
// Initialize the main thread.
113
112
#[ cfg( feature = "origin-thread" ) ]
114
113
initialize_main_thread ( mem. cast ( ) ) ;
@@ -156,7 +155,7 @@ unsafe fn call_ctors(argc: c_int, argv: *mut *mut u8, envp: *mut *mut u8) {
156
155
let init_end = & __init_array_end as * const _ as * const InitFn ;
157
156
// Prevent the optimizer from optimizing the `!=` comparison to true;
158
157
// `init` and `init_start` may have the same address.
159
- asm ! ( "# {}" , inout( reg) init) ;
158
+ asm ! ( "# {}" , inout( reg) init, options ( pure , nomem , nostack , preserves_flags ) ) ;
160
159
161
160
while init != init_end {
162
161
#[ cfg( feature = "log" ) ]
@@ -262,7 +261,7 @@ pub fn exit(status: c_int) -> ! {
262
261
let fini_start: * const InitFn = & __fini_array_start as * const _ as * const InitFn ;
263
262
// Prevent the optimizer from optimizing the `!=` comparison to true;
264
263
// `fini` and `fini_start` may have the same address.
265
- asm ! ( "# {}" , inout( reg) fini) ;
264
+ asm ! ( "# {}" , inout( reg) fini, options ( pure , nomem , nostack , preserves_flags ) ) ;
266
265
267
266
while fini != fini_start {
268
267
fini = fini. sub ( 1 ) ;
@@ -313,9 +312,15 @@ pub fn exit_immediately(status: c_int) -> ! {
313
312
/// code is running but before relocations have been performed. There are no
314
313
/// guarantees that this code won't segfault at any moment.
315
314
///
316
- /// We use volatile accesses and `asm` optimization barriers to try to
317
- /// discourage optimizers from thinking they understand what's happening, to
318
- /// increase the probability of this code working.
315
+ /// In practice, things work if we don't make any calls to functions outside
316
+ /// of this crate, not counting functions directly implemented by the compiler.
317
+ /// So we can do eg. `x == null()` but we can't do `x.is_null()`, because
318
+ /// `null` is directly implemented by the compiler, while `is_null` is not.
319
+ ///
320
+ /// We use `asm` optimization barriers to try to discourage optimizers from
321
+ /// thinking they understand what's happening, to increase the probability of
322
+ /// this code working. We'd use volatile accesses too, except those aren't
323
+ /// directly implemented by the compiler.
319
324
///
320
325
/// And if this code panics, the panic code will probably segfault, because
321
326
/// `core::fmt` is known to use an address that needs relocation.
@@ -324,78 +329,104 @@ pub fn exit_immediately(status: c_int) -> ! {
324
329
#[ cfg( all( feature = "experimental-relocate" , feature = "origin-start" ) ) ]
325
330
#[ cfg( relocation_model = "pic" ) ]
326
331
#[ cold]
327
- unsafe fn relocate ( ) {
332
+ unsafe fn relocate ( envp : * mut * mut u8 ) {
328
333
use core:: arch:: asm;
329
334
use core:: ffi:: c_void;
330
335
use core:: mem:: size_of;
331
- use core:: ptr:: {
332
- from_exposed_addr, from_exposed_addr_mut, read_volatile, write_volatile, NonNull ,
333
- } ;
334
- use core:: slice:: from_raw_parts;
336
+ use core:: ptr:: { from_exposed_addr, from_exposed_addr_mut, null, null_mut} ;
337
+ use linux_raw_sys:: general:: { AT_ENTRY , AT_NULL , AT_PAGESZ , AT_PHDR , AT_PHENT , AT_PHNUM } ;
335
338
use rustix:: mm:: { mprotect, MprotectFlags } ;
336
- use rustix:: param:: page_size;
337
339
338
340
// Please do not take any of the following code as an example for how to
339
341
// write Rust code in general.
340
342
343
+ // Locate the AUXV records we need. We don't use rustix to do this because
344
+ // that would involve calling a function in another crate.
345
+ let mut auxp = envp;
346
+ // Don't use `is_null` because that's a call.
347
+ while ( * auxp) != null_mut ( ) {
348
+ auxp = auxp. add ( 1 ) ;
349
+ }
350
+ let mut auxp = auxp. add ( 1 ) . cast :: < Elf_auxv_t > ( ) ;
351
+ let mut auxv_page_size = 0 ;
352
+ let mut auxv_phdr = null ( ) ;
353
+ let mut auxv_phent = 0 ;
354
+ let mut auxv_phnum = 0 ;
355
+ let mut auxv_entry = 0 ;
356
+ loop {
357
+ let Elf_auxv_t { a_type, a_val } = * auxp;
358
+
359
+ match a_type as _ {
360
+ AT_PAGESZ => auxv_page_size = a_val. addr ( ) ,
361
+ AT_PHDR => auxv_phdr = a_val. cast :: < Elf_Phdr > ( ) ,
362
+ AT_PHNUM => auxv_phnum = a_val. addr ( ) ,
363
+ AT_PHENT => auxv_phent = a_val. addr ( ) ,
364
+ AT_ENTRY => auxv_entry = a_val. addr ( ) ,
365
+
366
+ AT_NULL => break ,
367
+ _ => ( ) ,
368
+ }
369
+ auxp = auxp. add ( 1 ) ;
370
+ }
371
+
341
372
// Compute the static address of `_start`. This relies on the sneaky fact
342
373
// that we initialize a static variable with the address of `_start`, and
343
374
// if we haven't performed relocations yet, we'll be able to see the static
344
375
// address. Also, the program *just* started so there are no other threads
345
376
// yet, so loading from static memory without synchronization is fine. But
346
- // we still use volatile and `asm` to do everything we can to protect this
347
- // code because the optimizer won't have any idea what we're up to.
377
+ // we still use `asm` to do everything we can to protect this code because
378
+ // the optimizer won't have any idea what we're up to.
348
379
struct StaticStart ( * const u8 ) ;
349
380
unsafe impl Sync for StaticStart { }
350
381
static STATIC_START : StaticStart = StaticStart ( crate :: _start as * const u8 ) ;
351
382
let mut static_start_addr: * const * const u8 = & STATIC_START . 0 ;
352
383
asm ! ( "# {}" , inout( reg) static_start_addr) ;
353
- let mut static_start = read_volatile ( static_start_addr) . addr ( ) ;
384
+ let mut static_start = ( * static_start_addr) . addr ( ) ;
354
385
asm ! ( "# {}" , inout( reg) static_start) ;
355
386
356
387
// Obtain the dynamic address of `_start` from the AUX records.
357
- let dynamic_start = rustix :: runtime :: entry ( ) ;
388
+ let dynamic_start = auxv_entry ;
358
389
359
390
// Our offset is the difference between these two.
360
391
let offset = dynamic_start. wrapping_sub ( static_start) ;
361
392
362
- // This code doesn't rely on the offset being page aligned, but it is
363
- // useful to check to make sure we computed it correctly.
364
- debug_assert_eq ! ( offset & ( page_size( ) - 1 ) , 0 ) ;
365
-
366
393
// If we're loaded at our static address, then there's nothing to do.
367
394
if offset == 0 {
368
395
return ;
369
396
}
370
397
371
398
// Now, obtain the static Phdrs, which have been mapped into the address
372
399
// space at an address provided to us in the AUX array.
373
- let ( first_phdr, phent, phnum) = rustix:: runtime:: exe_phdrs ( ) ;
374
- let mut current_phdr = first_phdr. cast :: < Elf_Phdr > ( ) ;
400
+ let mut current_phdr = auxv_phdr. cast :: < Elf_Phdr > ( ) ;
375
401
376
402
// Next, look through the Phdrs to find the Dynamic section and the relro
377
403
// description if present. In the `Dynamic` section, find the relocations
378
404
// and perform them.
379
405
let mut relro = 0 ;
380
406
let mut relro_len = 0 ;
381
- let phdrs_end = current_phdr. cast :: < u8 > ( ) . add ( phnum * phent) . cast ( ) ;
407
+ let phdrs_end = current_phdr
408
+ . cast :: < u8 > ( )
409
+ . add ( auxv_phnum * auxv_phent)
410
+ . cast ( ) ;
382
411
while current_phdr != phdrs_end {
383
412
let phdr = & * current_phdr;
384
- current_phdr = current_phdr. cast :: < u8 > ( ) . add ( phent ) . cast ( ) ;
413
+ current_phdr = current_phdr. cast :: < u8 > ( ) . add ( auxv_phent ) . cast ( ) ;
385
414
386
415
match phdr. p_type {
387
416
PT_DYNAMIC => {
388
417
// We found the dynamic section.
389
- let dynamic = from_exposed_addr ( phdr. p_vaddr . wrapping_add ( offset) ) ;
418
+ let dynamic: * const Elf_Dyn = from_exposed_addr ( phdr. p_vaddr . wrapping_add ( offset) ) ;
390
419
let num_dyn = phdr. p_memsz / size_of :: < Elf_Dyn > ( ) ;
391
- let dyns: & [ Elf_Dyn ] = from_raw_parts ( dynamic, num_dyn) ;
392
420
393
421
// Look through the `Elf_Dyn` entries to find the location of
394
422
// the relocation table.
395
- let mut rela_ptr = NonNull :: dangling ( ) . as_ptr ( ) as * const Elf_Rela ;
423
+ let mut rela_ptr: * const Elf_Rela = null ( ) ;
396
424
let mut num_rela = 0 ;
397
425
let mut rela_ent_size = 0 ;
398
- for dyn_ in dyns {
426
+ let mut current_dyn = dynamic;
427
+ let dyns_end = dynamic. add ( num_dyn) ;
428
+ while current_dyn != dyns_end {
429
+ let dyn_ = * current_dyn;
399
430
match dyn_. d_tag as u32 {
400
431
DT_RELA => {
401
432
rela_ptr = from_exposed_addr ( dyn_. d_un . d_ptr . wrapping_add ( offset) )
@@ -404,6 +435,7 @@ unsafe fn relocate() {
404
435
DT_RELAENT => rela_ent_size = dyn_. d_un . d_val as usize ,
405
436
_ => ( ) ,
406
437
}
438
+ current_dyn = current_dyn. add ( 1 ) ;
407
439
}
408
440
409
441
// Now, perform the relocations. As above, the optimizer won't
@@ -421,7 +453,7 @@ unsafe fn relocate() {
421
453
let mut reloc_addr = addr. cast ( ) ;
422
454
let mut reloc_value = rela. r_addend . wrapping_add ( offset) ;
423
455
asm ! ( "# {} {}" , inout( reg) reloc_addr, inout( reg) reloc_value) ;
424
- write_volatile ( reloc_addr, reloc_value) ;
456
+ * reloc_addr = reloc_value;
425
457
asm ! ( "" ) ;
426
458
}
427
459
_ => unimplemented ! ( ) ,
@@ -438,9 +470,15 @@ unsafe fn relocate() {
438
470
}
439
471
}
440
472
473
+ // This code doesn't rely on the offset being page aligned, but it is
474
+ // useful to check to make sure we computed it correctly.
475
+ debug_assert_eq ! ( offset & ( auxv_page_size - 1 ) , 0 ) ;
476
+
441
477
// Check that relocation did its job.
442
478
#[ cfg( debug_assertions) ]
443
479
{
480
+ use core:: ptr:: read_volatile;
481
+
444
482
let mut static_start_addr: * const * const u8 = & STATIC_START . 0 ;
445
483
asm ! ( "# {}" , inout( reg) static_start_addr) ;
446
484
let mut static_start = read_volatile ( static_start_addr) . addr ( ) ;
@@ -451,7 +489,7 @@ unsafe fn relocate() {
451
489
// If we saw a relro description, mark the memory readonly.
452
490
if relro_len != 0 {
453
491
let mprotect_addr =
454
- from_exposed_addr_mut ( relro. wrapping_add ( offset) & page_size ( ) . wrapping_neg ( ) ) ;
492
+ from_exposed_addr_mut ( relro. wrapping_add ( offset) & auxv_page_size . wrapping_neg ( ) ) ;
455
493
mprotect ( mprotect_addr, relro_len, MprotectFlags :: READ ) . unwrap ( ) ;
456
494
}
457
495
@@ -485,27 +523,31 @@ unsafe fn relocate() {
485
523
486
524
#[ cfg( target_pointer_width = "32" ) ]
487
525
#[ repr( C ) ]
526
+ #[ derive( Copy , Clone ) ]
488
527
struct Elf_Dyn {
489
528
d_tag : i32 ,
490
529
d_un : Elf_Dyn_Union ,
491
530
}
492
531
493
532
#[ cfg( target_pointer_width = "32" ) ]
494
533
#[ repr( C ) ]
534
+ #[ derive( Copy , Clone ) ]
495
535
union Elf_Dyn_Union {
496
536
d_val : u32 ,
497
537
d_ptr : usize ,
498
538
}
499
539
500
540
#[ cfg( target_pointer_width = "64" ) ]
501
541
#[ repr( C ) ]
542
+ #[ derive( Copy , Clone ) ]
502
543
struct Elf_Dyn {
503
544
d_tag : i64 ,
504
545
d_un : Elf_Dyn_Union ,
505
546
}
506
547
507
548
#[ cfg( target_pointer_width = "64" ) ]
508
549
#[ repr( C ) ]
550
+ #[ derive( Copy , Clone ) ]
509
551
union Elf_Dyn_Union {
510
552
d_val : u64 ,
511
553
d_ptr : usize ,
@@ -556,4 +598,15 @@ unsafe fn relocate() {
556
598
const R_RELATIVE : u32 = 3 ; // `R_RISCV_RELATIVE`
557
599
#[ cfg( target_arch = "arm" ) ]
558
600
const R_RELATIVE : u32 = 23 ; // `R_ARM_RELATIVE`
601
+
602
+ #[ repr( C ) ]
603
+ #[ derive( Copy , Clone ) ]
604
+ struct Elf_auxv_t {
605
+ a_type : usize ,
606
+
607
+ // Some of the values in the auxv array are pointers, so we make `a_val` a
608
+ // pointer, in order to preserve their provenance. For the values which are
609
+ // integers, we cast this to `usize`.
610
+ a_val : * mut c_void ,
611
+ }
559
612
}
0 commit comments