@@ -98,17 +98,16 @@ unsafe fn compute_args(mem: *mut usize) -> (i32, *mut *mut u8, *mut *mut u8) {
9898#[ cfg( any( feature = "origin-start" , feature = "external-start" ) ) ]
9999#[ allow( unused_variables) ]
100100unsafe 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+
101106 // Explicitly initialize `rustix` so that we can control the initialization
102107 // order.
103108 #[ cfg( feature = "param" ) ]
104109 rustix:: param:: init ( envp) ;
105110
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-
112111 // Initialize the main thread.
113112 #[ cfg( feature = "origin-thread" ) ]
114113 initialize_main_thread ( mem. cast ( ) ) ;
@@ -156,7 +155,7 @@ unsafe fn call_ctors(argc: c_int, argv: *mut *mut u8, envp: *mut *mut u8) {
156155 let init_end = & __init_array_end as * const _ as * const InitFn ;
157156 // Prevent the optimizer from optimizing the `!=` comparison to true;
158157 // `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 ) ) ;
160159
161160 while init != init_end {
162161 #[ cfg( feature = "log" ) ]
@@ -262,7 +261,7 @@ pub fn exit(status: c_int) -> ! {
262261 let fini_start: * const InitFn = & __fini_array_start as * const _ as * const InitFn ;
263262 // Prevent the optimizer from optimizing the `!=` comparison to true;
264263 // `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 ) ) ;
266265
267266 while fini != fini_start {
268267 fini = fini. sub ( 1 ) ;
@@ -313,9 +312,15 @@ pub fn exit_immediately(status: c_int) -> ! {
313312/// code is running but before relocations have been performed. There are no
314313/// guarantees that this code won't segfault at any moment.
315314///
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.
319324///
320325/// And if this code panics, the panic code will probably segfault, because
321326/// `core::fmt` is known to use an address that needs relocation.
@@ -324,78 +329,104 @@ pub fn exit_immediately(status: c_int) -> ! {
324329#[ cfg( all( feature = "experimental-relocate" , feature = "origin-start" ) ) ]
325330#[ cfg( relocation_model = "pic" ) ]
326331#[ cold]
327- unsafe fn relocate ( ) {
332+ unsafe fn relocate ( envp : * mut * mut u8 ) {
328333 use core:: arch:: asm;
329334 use core:: ffi:: c_void;
330335 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 } ;
335338 use rustix:: mm:: { mprotect, MprotectFlags } ;
336- use rustix:: param:: page_size;
337339
338340 // Please do not take any of the following code as an example for how to
339341 // write Rust code in general.
340342
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+
341372 // Compute the static address of `_start`. This relies on the sneaky fact
342373 // that we initialize a static variable with the address of `_start`, and
343374 // if we haven't performed relocations yet, we'll be able to see the static
344375 // address. Also, the program *just* started so there are no other threads
345376 // 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.
348379 struct StaticStart ( * const u8 ) ;
349380 unsafe impl Sync for StaticStart { }
350381 static STATIC_START : StaticStart = StaticStart ( crate :: _start as * const u8 ) ;
351382 let mut static_start_addr: * const * const u8 = & STATIC_START . 0 ;
352383 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 ( ) ;
354385 asm ! ( "# {}" , inout( reg) static_start) ;
355386
356387 // Obtain the dynamic address of `_start` from the AUX records.
357- let dynamic_start = rustix :: runtime :: entry ( ) ;
388+ let dynamic_start = auxv_entry ;
358389
359390 // Our offset is the difference between these two.
360391 let offset = dynamic_start. wrapping_sub ( static_start) ;
361392
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-
366393 // If we're loaded at our static address, then there's nothing to do.
367394 if offset == 0 {
368395 return ;
369396 }
370397
371398 // Now, obtain the static Phdrs, which have been mapped into the address
372399 // 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 > ( ) ;
375401
376402 // Next, look through the Phdrs to find the Dynamic section and the relro
377403 // description if present. In the `Dynamic` section, find the relocations
378404 // and perform them.
379405 let mut relro = 0 ;
380406 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 ( ) ;
382411 while current_phdr != phdrs_end {
383412 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 ( ) ;
385414
386415 match phdr. p_type {
387416 PT_DYNAMIC => {
388417 // 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) ) ;
390419 let num_dyn = phdr. p_memsz / size_of :: < Elf_Dyn > ( ) ;
391- let dyns: & [ Elf_Dyn ] = from_raw_parts ( dynamic, num_dyn) ;
392420
393421 // Look through the `Elf_Dyn` entries to find the location of
394422 // 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 ( ) ;
396424 let mut num_rela = 0 ;
397425 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;
399430 match dyn_. d_tag as u32 {
400431 DT_RELA => {
401432 rela_ptr = from_exposed_addr ( dyn_. d_un . d_ptr . wrapping_add ( offset) )
@@ -404,6 +435,7 @@ unsafe fn relocate() {
404435 DT_RELAENT => rela_ent_size = dyn_. d_un . d_val as usize ,
405436 _ => ( ) ,
406437 }
438+ current_dyn = current_dyn. add ( 1 ) ;
407439 }
408440
409441 // Now, perform the relocations. As above, the optimizer won't
@@ -421,7 +453,7 @@ unsafe fn relocate() {
421453 let mut reloc_addr = addr. cast ( ) ;
422454 let mut reloc_value = rela. r_addend . wrapping_add ( offset) ;
423455 asm ! ( "# {} {}" , inout( reg) reloc_addr, inout( reg) reloc_value) ;
424- write_volatile ( reloc_addr, reloc_value) ;
456+ * reloc_addr = reloc_value;
425457 asm ! ( "" ) ;
426458 }
427459 _ => unimplemented ! ( ) ,
@@ -438,9 +470,15 @@ unsafe fn relocate() {
438470 }
439471 }
440472
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+
441477 // Check that relocation did its job.
442478 #[ cfg( debug_assertions) ]
443479 {
480+ use core:: ptr:: read_volatile;
481+
444482 let mut static_start_addr: * const * const u8 = & STATIC_START . 0 ;
445483 asm ! ( "# {}" , inout( reg) static_start_addr) ;
446484 let mut static_start = read_volatile ( static_start_addr) . addr ( ) ;
@@ -451,7 +489,7 @@ unsafe fn relocate() {
451489 // If we saw a relro description, mark the memory readonly.
452490 if relro_len != 0 {
453491 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 ( ) ) ;
455493 mprotect ( mprotect_addr, relro_len, MprotectFlags :: READ ) . unwrap ( ) ;
456494 }
457495
@@ -485,27 +523,31 @@ unsafe fn relocate() {
485523
486524 #[ cfg( target_pointer_width = "32" ) ]
487525 #[ repr( C ) ]
526+ #[ derive( Copy , Clone ) ]
488527 struct Elf_Dyn {
489528 d_tag : i32 ,
490529 d_un : Elf_Dyn_Union ,
491530 }
492531
493532 #[ cfg( target_pointer_width = "32" ) ]
494533 #[ repr( C ) ]
534+ #[ derive( Copy , Clone ) ]
495535 union Elf_Dyn_Union {
496536 d_val : u32 ,
497537 d_ptr : usize ,
498538 }
499539
500540 #[ cfg( target_pointer_width = "64" ) ]
501541 #[ repr( C ) ]
542+ #[ derive( Copy , Clone ) ]
502543 struct Elf_Dyn {
503544 d_tag : i64 ,
504545 d_un : Elf_Dyn_Union ,
505546 }
506547
507548 #[ cfg( target_pointer_width = "64" ) ]
508549 #[ repr( C ) ]
550+ #[ derive( Copy , Clone ) ]
509551 union Elf_Dyn_Union {
510552 d_val : u64 ,
511553 d_ptr : usize ,
@@ -556,4 +598,15 @@ unsafe fn relocate() {
556598 const R_RELATIVE : u32 = 3 ; // `R_RISCV_RELATIVE`
557599 #[ cfg( target_arch = "arm" ) ]
558600 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+ }
559612}
0 commit comments