2727#![ allow( clippy:: cmp_null) ]
2828
2929use crate :: arch:: {
30- dynamic_table_addr, relocation_load, relocation_mprotect_readonly, relocation_store,
30+ dynamic_table_addr, ehdr_addr , relocation_load, relocation_mprotect_readonly, relocation_store,
3131} ;
3232use core:: ffi:: c_void;
3333use core:: ptr:: { null, null_mut, with_exposed_provenance} ;
@@ -81,12 +81,12 @@ pub(super) unsafe fn relocate(envp: *mut *mut u8) {
8181
8282 // There are four cases to consider here:
8383 //
84- // 1. Static executable
84+ // 1. Static executable.
8585 // This is the trivial case. No relocations necessary.
86- // 2. Static pie executable
86+ // 2. Static pie executable.
8787 // We need to relocate ourself. `AT_PHDR` points to our own program
8888 // headers and `AT_ENTRY` to our own entry point.
89- // 3. Dynamic pie executable with external dynamic linker
89+ // 3. Dynamic pie executable with external dynamic linker.
9090 // We don't need to relocate ourself as the dynamic linker has already
9191 // done this. `AT_PHDR` points to our own program headers and `AT_ENTRY`
9292 // to our own entry point. `AT_BASE` contains the relocation offset of
@@ -96,19 +96,20 @@ pub(super) unsafe fn relocate(envp: *mut *mut u8) {
9696 // program headers and `AT_ENTRY` doesn't point to our own entry point.
9797 // `AT_BASE` contains our own relocation offset.
9898
99- // Compute the static address of `_start`.
100- let static_start = compute_static_start ( ) ;
101-
102- if static_start == auxv_entry {
103- // This is case 1) or case 3). If AT_BASE doesn't exist, then we are
99+ if load_static_start ( ) == auxv_entry {
100+ // This is case 1) or case 3). If `AT_BASE` doesn't exist, then we are
104101 // already loaded at our static address despite the lack of any dynamic
105- // linker. As such it would be case 1). If AT_BASE exists , we have
102+ // linker. As such it would be case 1). If ` AT_BASE` does exist , we have
106103 // already been relocated by the dynamic linker, which is case 3).
107104 // In either case there is no need to do any relocations.
108105 return ;
109106 }
110107
111108 let offset = if auxv_base == 0 {
109+ // Obtain the static address of `_start`, which is recorded in the
110+ // entry field of the ELF header.
111+ let static_start = ( * ehdr_addr ( ) ) . e_entry ;
112+
112113 // This is case 2) as without dynamic linker `AT_BASE` doesn't exist
113114 // and we have already excluded case 1) above.
114115 auxv_entry. wrapping_sub ( static_start)
@@ -119,7 +120,7 @@ pub(super) unsafe fn relocate(envp: *mut *mut u8) {
119120 auxv_base
120121 } ;
121122
122- // This is case 2) or 4). We need to do all R_RELATIVE relocations.
123+ // This is case 2) or 4). We need to do all ` R_RELATIVE` relocations.
123124 // There should be no other kind of relocation because we are either a
124125 // static PIE binary or a dynamic linker compiled with `-Bsymbolic`.
125126
@@ -228,7 +229,7 @@ pub(super) unsafe fn relocate(envp: *mut *mut u8) {
228229 // entry point when AT_BASE is not zero and thus a dynamic linker is in
229230 // use. In this case the assertion would fail.
230231 if auxv_base == 0 {
231- assert_eq ! ( compute_static_start ( ) , auxv_entry) ;
232+ assert_eq ! ( load_static_start ( ) , auxv_entry) ;
232233 }
233234 }
234235
@@ -288,21 +289,24 @@ unsafe fn compute_auxp(envp: *mut *mut u8) -> *const Elf_auxv_t {
288289 auxp. add ( 1 ) . cast ( )
289290}
290291
291- /// Compute the static address of `_start`.
292+ /// Load the address of `_start` from static memory.
293+ ///
294+ /// This function contains a static variable initialized with the address of
295+ /// the `_start` function. This requires an `R_RELATIVE` relocation, because
296+ /// it requires an absolute address exist in memory, rather than being able
297+ /// to use PC-relative addressing. This allows us to tell whether relocation
298+ /// has already been done or whether we need to do it.
292299///
293300/// This returns a `usize` because we don't dereference the address; we just
294301/// use it for address computation.
295- fn compute_static_start ( ) -> usize {
296- // This relies on the sneaky fact that we initialize a static variable with
297- // the address of `_start`, and if we haven't performed relocations yet,
298- // we'll be able to see the static address. Also, the program *just*
299- // started so there are no other threads yet, so loading from static memory
300- // without synchronization is fine. But we still use `asm` to do everything
301- // we can to protect this code because the optimizer won't have any idea
302- // what we're up to.
302+ fn load_static_start ( ) -> usize {
303+ // Initialize a static variable with the address of `_start`.
303304 struct StaticStart ( * const c_void ) ;
304305 unsafe impl Sync for StaticStart { }
305306 static STATIC_START : StaticStart = StaticStart ( crate :: arch:: _start as * const c_void ) ;
307+
308+ // Use `relocation_load` to do the load because the optimizer won't have
309+ // any idea what we're up to.
306310 let static_start_addr: * const * const c_void = & STATIC_START . 0 ;
307311 unsafe { relocation_load ( static_start_addr. addr ( ) ) }
308312}
0 commit comments