Skip to content

Commit 243e301

Browse files
authored
Update to the latest Rust nightly. (#41)
* Update to the latest Rust nightly. * Change the relocation code to avoid making any calls. As of rust-lang/rust#115267, the call from `rustix::param::init` to `backend::param::auxv::init` goes through the GOT, which is not yet relocated when the `relocate` code runs. Change the `relocate` code to read the AUX values itself instead of using rustix, and make it avoid using any other calls, and move the `relocate` call before the `rustix::param::init` call.
1 parent cda3fa6 commit 243e301

File tree

2 files changed

+89
-36
lines changed

2 files changed

+89
-36
lines changed

rust-toolchain.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
[toolchain]
2-
channel = "nightly-2023-08-25"
2+
channel = "nightly-2023-09-09"
33
components = ["rustc", "cargo", "rust-std", "rust-src", "rustfmt"]

src/program.rs

+88-35
Original file line numberDiff line numberDiff line change
@@ -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)]
100100
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+
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

Comments
 (0)