Merge branch 'ld_preload_program' into 'master'

feat(ld.so): position independent

See merge request redox-os/relibc!1007
This commit is contained in:
Jeremy Soller
2026-02-16 07:00:08 -07:00
9 changed files with 326 additions and 132 deletions
+2
View File
@@ -7,3 +7,5 @@ sysroot/
*.swp
*.swo
/.vim
.vscode/
-4
View File
@@ -1,4 +0,0 @@
{
"cmake.ignoreCMakeListsMissing": true,
"editor.formatOnSave": true
}
+5 -7
View File
@@ -133,6 +133,10 @@ $(BUILD)/$(PROFILE)/libc.so: $(BUILD)/$(PROFILE)/librelibc.a $(BUILD)/openlibm/l
$(LINKFLAGS) \
-o $@
$(BUILD)/$(PROFILE)/ld_so: $(BUILD)/$(PROFILE)/ld_so.o $(BUILD)/$(PROFILE)/crti.o $(BUILD)/$(PROFILE)/libc.a $(BUILD)/$(PROFILE)/crtn.o
# TODO: merge ld.so with libc.so: --dynamic-list=dynamic-list-file
$(LD) --shared -Bsymbolic --no-relax -T ld_so/ld_script/$(TARGET).ld --allow-multiple-definition --gc-sections $^ -o $@
# Debug targets
$(BUILD)/debug/libc.a: $(BUILD)/debug/librelibc.a $(BUILD)/openlibm/libopenlibm.a
@@ -166,9 +170,6 @@ $(BUILD)/debug/ld_so.o: $(SRC)
$(CARGO) rustc --manifest-path ld_so/Cargo.toml $(CARGOFLAGS) -- --emit obj=$@ -C panic=abort -g -C debug-assertions=no $(RUSTCFLAGS)
touch $@
$(BUILD)/debug/ld_so: $(BUILD)/debug/ld_so.o $(BUILD)/debug/crti.o $(BUILD)/debug/libc.a $(BUILD)/debug/crtn.o
$(LD) --no-relax -T ld_so/ld_script/$(TARGET).ld --allow-multiple-definition --gc-sections $^ -o $@
# Release targets
$(BUILD)/release/libc.a: $(BUILD)/release/librelibc.a $(BUILD)/openlibm/libopenlibm.a
@@ -204,9 +205,6 @@ $(BUILD)/release/ld_so.o: $(SRC)
$(CARGO) rustc --release --manifest-path ld_so/Cargo.toml $(CARGOFLAGS) -- --emit obj=$@ -C panic=abort $(RUSTCFLAGS)
touch $@
$(BUILD)/release/ld_so: $(BUILD)/release/ld_so.o $(BUILD)/release/crti.o $(BUILD)/release/libc.a $(BUILD)/release/crtn.o
$(LD) --no-relax -T ld_so/ld_script/$(TARGET).ld --allow-multiple-definition --gc-sections $^ -o $@
# Other targets
$(BUILD)/openlibm: openlibm
@@ -216,6 +214,6 @@ $(BUILD)/openlibm: openlibm
mv $@.partial $@
touch $@
$(BUILD)/openlibm/libopenlibm.a: $(BUILD)/openlibm $(BUILD)/release/librelibc.a
$(BUILD)/openlibm/libopenlibm.a: $(BUILD)/openlibm $(BUILD)/$(PROFILE)/librelibc.a
$(MAKE) -s AR=$(AR) CC="$(CC_WRAPPER) $(CC)" LD=$(LD) CPPFLAGS="$(CPPFLAGS) -fno-stack-protector -I$(shell pwd)/include -I$(TARGET_HEADERS)" -C $< libopenlibm.a
./renamesyms.sh "$@" "$(BUILD)/release/deps/"
+11 -2
View File
@@ -7,6 +7,9 @@ use core::arch::global_asm;
#[cfg(target_arch = "aarch64")]
global_asm!(
"
.weak _DYNAMIC
.hidden _DYNAMIC
.global _start
_start:
mov x28, sp
@@ -14,7 +17,9 @@ _start:
and sp, x28, #0xfffffffffffffff0
adr x1, _start
mov x0, x28
// ld_so_start(stack=x0, ld_entry=x1)
adrp x2, _DYNAMIC
add x2, x2, #:lo12:_DYNAMIC
// ld_so_start(stack=x0, ld_entry=x1, dynamic=x2)
bl relibc_ld_so_start
// restore original stack, clear registers, and jump to the new start function
mov sp, x28
@@ -69,6 +74,9 @@ _start:
#[cfg(target_arch = "x86_64")]
global_asm!(
"
.weak _DYNAMIC
.hidden _DYNAMIC
.globl _start
_start:
lea rsi, [rip + _start]
@@ -77,8 +85,9 @@ _start:
mov rbp, rsp
and rsp, 0xfffffffffffffff0
# Call ld_so_start(stack=rdi, ld_entry=rsi)
# Call ld_so_start(stack=rdi, ld_entry=rsi, dynamic=rdx)
mov rdi, rbp
lea rdx, [rip + _DYNAMIC]
call relibc_ld_so_start
# Restore original stack, clear registers, and jump to new start function
+83 -52
View File
@@ -1,4 +1,4 @@
use core::{cell::SyncUnsafeCell, cmp, fmt::Debug};
use core::{cell::SyncUnsafeCell, cmp, fmt::Debug, ops::Range};
use crate::{
DYNAMIC_PROC_INFO, RtTcb, StaticProcInfo,
@@ -11,6 +11,7 @@ use crate::{
use alloc::{boxed::Box, vec};
use goblin::elf::header::ET_DYN;
//TODO: allow use of either 32-bit or 64-bit programs
#[cfg(target_pointer_width = "32")]
use goblin::elf32::{
@@ -37,11 +38,13 @@ pub enum FexecResult {
},
}
pub struct InterpOverride {
phs: Box<[u8]>,
phdrs_vaddr: usize,
at_entry: usize,
at_phnum: usize,
at_phent: usize,
name: Box<[u8]>,
min_mmap_addr: usize,
grants_fd: usize,
}
pub struct ExtraInfo<'a> {
@@ -78,7 +81,9 @@ pub fn fexec_impl(
pread_all(&image_file, 0, &mut header_bytes)?;
let header = Header::from_bytes(&header_bytes);
let grants_fd = {
let grants_fd = if let Some(interp) = interp_override.as_ref() {
FdGuard::new(interp.grants_fd).to_upper()?
} else {
let current_addrspace_fd = thread_fd.dup(b"addrspace")?;
current_addrspace_fd.dup(b"empty")?.to_upper()?
};
@@ -99,13 +104,50 @@ pub fn fexec_impl(
let phs = &mut phs_raw[size_of::<Header>()..];
// TODO: Remove clone, but this would require more as_refs and as_muts
let mut min_mmap_addr = PAGE_SIZE;
let mut min_mmap_addr = interp_override
.as_ref()
.map(|interp| interp.min_mmap_addr)
.unwrap_or(PAGE_SIZE);
let mut update_min_mmap_addr = |addr: usize, size: usize| {
min_mmap_addr = cmp::max(min_mmap_addr, (addr + size).next_multiple_of(PAGE_SIZE));
};
pread_all(&image_file, u64::from(header.e_phoff), phs).map_err(|_| Error::new(EIO))?;
let mut span: Option<Range<usize>> = None;
for ph_idx in 0..phnum {
let ph_bytes = &phs[ph_idx * phentsize..(ph_idx + 1) * phentsize];
let segment: &ProgramHeader =
plain::from_bytes(ph_bytes).map_err(|_| Error::new(EINVAL))?;
if segment.p_type != PT_LOAD {
continue;
}
let voff = segment.p_vaddr as usize % PAGE_SIZE;
let vaddr = segment.p_vaddr as usize - voff;
let vsize = (segment.p_memsz as usize + voff).next_multiple_of(segment.p_align as usize);
let b = vaddr..vaddr + vsize;
span = Some(if let Some(a) = span {
a.start.min(b.start)..a.end.max(b.end)
} else {
b
});
}
let span = span.expect("ELF executables must contain at least one `PT_LOAD` segment");
let base_addr = if header.e_type == ET_DYN {
// PIE
let span_size = (span.end - span.start).next_multiple_of(PAGE_SIZE);
let addr = mmap_anon_remote(&grants_fd, 0, 0, span_size, MapFlags::PROT_NONE)?;
update_min_mmap_addr(addr, span_size);
addr
} else {
0
};
let mut phdrs_vaddr = 0;
let mut interpreter = None;
for ph_idx in 0..phnum {
let ph_bytes = &phs[ph_idx * phentsize..(ph_idx + 1) * phentsize];
let segment: &ProgramHeader =
@@ -129,16 +171,7 @@ pub fn fexec_impl(
let mut interp = vec![0_u8; segment.p_filesz as usize];
pread_all(&image_file, u64::from(segment.p_offset), &mut interp)?;
return Ok(FexecResult::Interp {
path: interp.into_boxed_slice(),
interp_override: InterpOverride {
at_entry: header.e_entry as usize,
at_phnum: phnum,
at_phent: phentsize,
phs: phs_raw.into_boxed_slice(),
name: path.into(),
},
});
interpreter = Some(interp.into_boxed_slice());
}
PT_LOAD => {
let voff = segment.p_vaddr as usize % PAGE_SIZE;
@@ -157,18 +190,25 @@ pub fn fexec_impl(
mmap_anon_remote(
&grants_fd,
0,
vaddr,
base_addr + vaddr,
total_page_count * PAGE_SIZE,
flags | MapFlags::MAP_FIXED_NOREPLACE,
flags | MapFlags::MAP_FIXED,
)?;
if segment.p_offset <= header.e_phoff
&& header.e_phoff < segment.p_offset + segment.p_filesz
{
phdrs_vaddr =
(header.e_phoff - segment.p_offset + segment.p_vaddr) as usize + base_addr;
}
// TODO: Attempt to mmap with MAP_PRIVATE directly from the image file instead.
if filesz > 0 {
let (_guard, dst_memory) = unsafe {
MmapGuard::map_mut_anywhere(
&grants_fd,
vaddr, // offset
base_addr + vaddr, // offset
(voff + filesz).next_multiple_of(PAGE_SIZE), // size
)?
};
@@ -178,13 +218,26 @@ pub fn fexec_impl(
&mut dst_memory[voff..voff + filesz],
)?;
}
update_min_mmap_addr(vaddr, total_page_count * PAGE_SIZE);
}
_ => continue,
}
}
if let Some(interpreter_path) = interpreter {
return Ok(FexecResult::Interp {
path: interpreter_path,
interp_override: InterpOverride {
at_entry: header.e_entry as usize,
at_phnum: phnum,
at_phent: phentsize,
phdrs_vaddr,
name: path.into(),
min_mmap_addr,
grants_fd: grants_fd.take(),
},
});
}
mmap_anon_remote(
&grants_fd,
0,
@@ -234,43 +287,21 @@ pub fn fexec_impl(
Ok(())
};
let pheaders_to_convey = if let Some(ref r#override) = interp_override {
&*r#override.phs
} else {
&*phs_raw
};
let pheaders_size_aligned = pheaders_to_convey.len().next_multiple_of(PAGE_SIZE);
let pheaders = mmap_anon_remote(
&grants_fd,
0,
0,
pheaders_size_aligned,
MapFlags::PROT_READ | MapFlags::PROT_WRITE,
)?;
update_min_mmap_addr(pheaders, pheaders_size_aligned);
unsafe {
let (_guard, memory) =
MmapGuard::map_mut_anywhere(&grants_fd, pheaders, pheaders_size_aligned)?;
memory[..pheaders_to_convey.len()].copy_from_slice(pheaders_to_convey);
}
mprotect_remote(
&grants_fd,
pheaders,
pheaders_size_aligned,
MapFlags::PROT_READ,
)?;
push(0)?;
push(AT_NULL)?;
push(header.e_entry as usize)?;
if let Some(ref r#override) = interp_override {
push(AT_BASE)?;
push(r#override.at_entry)?;
push(AT_ENTRY)?;
push(base_addr)?;
push(AT_BASE)?;
push(r#override.phdrs_vaddr)?;
push(AT_PHDR)?;
} else {
push(header.e_entry as usize)?;
push(AT_ENTRY)?;
push(phdrs_vaddr)?;
push(AT_PHDR)?;
}
push(AT_ENTRY)?;
push(pheaders + size_of::<Header>())?;
push(AT_PHDR)?;
push(
interp_override
.as_ref()
@@ -413,7 +444,7 @@ pub fn fexec_impl(
let _ = addrspace_selection_fd.write(&create_set_addr_space_buf(
grants_fd.as_raw_fd(),
header.e_entry as usize,
base_addr + header.e_entry as usize,
sp,
));
+46 -39
View File
@@ -64,6 +64,11 @@ mod shim {
pub use shim::*;
// TODO: missing from the `object` crate
pub const DT_RELRSZ: u32 = 35;
pub const DT_RELR: u32 = 36;
pub const DT_RELRENT: u32 = 37;
/// Undefined Symbol Index
pub const STN_UNDEF: SymbolIndex = SymbolIndex(0);
@@ -170,11 +175,11 @@ unsafe impl Send for Dynamic<'_> {}
unsafe impl Sync for Dynamic<'_> {}
#[derive(Debug)]
struct Relocation {
offset: usize,
addend: Option<usize>,
sym: SymbolIndex,
kind: RelocationKind,
pub(super) struct Relocation {
pub(super) offset: usize,
pub(super) addend: Option<usize>,
pub(super) sym: SymbolIndex,
pub(super) kind: RelocationKind,
}
#[cfg(target_pointer_width = "32")]
@@ -522,8 +527,8 @@ impl DSO {
};
_r_debug
.lock()
.insert_first(addr, path, addr + l_ld as usize);
slice::from_raw_parts_mut(addr as *mut u8, size)
.insert_first(addr + bounds.0, path, addr + l_ld as usize);
slice::from_raw_parts_mut((addr + bounds.0) as *mut u8, size)
} else {
let (start, end) = bounds;
let size = end - start;
@@ -662,10 +667,6 @@ impl DSO {
is_pie: bool,
(_, entries): (&ProgramHeader, &[Dyn]),
) -> object::Result<(Dynamic<'a>, Option<usize>)> {
const DT_RELRSZ: u32 = 35;
const DT_RELR: u32 = 36;
const DT_RELRENT: u32 = 37;
let mut runpath = None;
let mut got = None;
let mut needed = vec![];
@@ -1078,34 +1079,8 @@ impl DSO {
let global_scope = GLOBAL_SCOPE.read();
let base = self.mmap.as_ptr();
// Apply DT_RELR relative relocations.
let mut addr = ptr::null_mut();
for &entry in self.dynamic.relr {
if entry & 1 == 0 {
// An even entry sets up `addr` for subsequent odd entries.
unsafe {
addr = base.add(entry) as *mut usize;
*addr += base as usize;
addr = addr.add(1);
}
} else {
// An odd entry indicates a bitmap describing at maximum 63
// (for 64-bit) or 31 (for 32-bit) locations following `addr`.
// Odd entries can be chained.
let mut entry = entry >> 1;
let mut i = 0;
while entry != 0 {
if entry & 1 != 0 {
unsafe {
*addr.add(i) += base as usize;
}
}
entry >>= 1;
i += 1;
}
addr = unsafe { addr.add(CHAR_BITS * size_of::<Relr>() - 1) };
}
unsafe {
apply_relr(base, self.dynamic.relr);
}
self.dynamic
@@ -1295,3 +1270,35 @@ __tlsdesc_dynamic:
unimp
"
);
/// Applies [`DT_RELR`] relative relocations.
pub unsafe fn apply_relr(base: *const u8, relr: &[Relr]) {
let mut addr = ptr::null_mut();
for &entry in relr {
if entry & 1 == 0 {
// An even entry sets up `addr` for subsequent odd entries.
unsafe {
addr = base.add(entry) as *mut usize;
*addr += base as usize;
addr = addr.add(1);
}
} else {
// An odd entry indicates a bitmap describing at maximum 63
// (for 64-bit) or 31 (for 32-bit) locations following `addr`.
// Odd entries can be chained.
let mut entry = entry >> 1;
let mut i = 0;
while entry != 0 {
if entry & 1 != 0 {
unsafe {
*addr.add(i) += base as usize;
}
}
entry >>= 1;
i += 1;
}
addr = unsafe { addr.add(CHAR_BITS * size_of::<Relr>() - 1) };
}
}
}
+1 -1
View File
@@ -348,7 +348,7 @@ bitflags::bitflags! {
#[derive(Default)]
pub struct Config {
debug_flags: DebugFlags,
pub debug_flags: DebugFlags,
library_path: Option<String>,
/// Resolve symbols at program startup.
bind_now: bool,
+174 -23
View File
@@ -1,5 +1,7 @@
// Start code adapted from https://gitlab.redox-os.org/redox-os/relibc/blob/master/src/start.rs
use core::slice;
use alloc::{
borrow::ToOwned,
boxed::Box,
@@ -7,14 +9,26 @@ use alloc::{
string::{String, ToString},
vec::Vec,
};
use object::{
NativeEndian,
elf::{self, PT_DYNAMIC, PT_PHDR},
read::elf::{Dyn as _, ProgramHeader as _},
};
use crate::{
c_str::CStr,
header::{
sys_auxv::{AT_ENTRY, AT_PHDR},
elf::{AT_BASE, AT_ENTRY, AT_PHDR, AT_PHENT, AT_PHNUM},
unistd,
},
platform::{get_auxv, get_auxvs, types::c_char},
ld_so::{
dso::{
DT_RELR, DT_RELRENT, DT_RELRSZ, Dyn, ProgramHeader, Rel, Rela, Relocation,
RelocationKind, Relr, apply_relr,
},
linker::DebugFlags,
},
platform::{auxv_iter, get_auxvs, types::c_char},
start::Stack,
sync::mutex::Mutex,
};
@@ -149,7 +163,155 @@ fn resolve_path_name(
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn relibc_ld_so_start(sp: &'static mut Stack, ld_entry: usize) -> usize {
pub unsafe extern "C" fn relibc_ld_so_start(
sp: &'static mut Stack,
ld_entry: usize,
dynamic: *const Dyn,
) -> usize {
// Relocate ourselves.
//
// This function is very delicate as it must **not** contain relocations itself. References to
// external symbols **cannot** be made until `stage2()` so, this function might not be very
// elegant.
//
// At this stage the TCB is not setup either so `expect_notls` must be used instead of `expect`
// and `unwrap`.
let mut at_phdr = None;
let mut at_phnum = None;
let mut at_phent = None;
let mut at_base = None;
let mut at_entry = None;
for [kind, value] in unsafe { auxv_iter(sp.auxv().cast::<usize>()) } {
match kind {
AT_PHDR => at_phdr = Some(value as *const ProgramHeader),
AT_PHNUM => at_phnum = Some(value),
AT_PHENT => at_phent = Some(value),
AT_BASE => at_base = Some(value),
AT_ENTRY => at_entry = Some(value),
_ => {}
}
}
let at_phdr = at_phdr.expect_notls("`AT_PHDR` must be present");
let at_phnum = at_phnum.expect_notls("`AT_PHNUM` must be present if `AT_PHDR` is");
let at_phent = at_phent.expect_notls("`AT_PHENT` must be present if `AT_PHDR` is");
assert!(!at_phdr.is_null() && at_phnum != 0 && at_phent == size_of::<ProgramHeader>());
let phdrs = unsafe { slice::from_raw_parts(at_phdr, at_phnum) };
let at_entry = at_entry.expect_notls("`AT_ENTRY` must be present");
let at_base = at_base.unwrap_or_default();
let self_base = if at_base != 0 {
at_base
} else {
let ph = phdrs
.iter()
.find(|ph| ph.p_type(NativeEndian) == PT_DYNAMIC as u32)
.unwrap();
unsafe { dynamic.byte_sub(ph.p_vaddr(NativeEndian) as usize) as usize }
};
let is_manual = at_entry == ld_entry; // Whether the dynamic linker was invoked as a command.
let mut i = dynamic;
let mut rela_ptr = None;
let mut rela_len = None;
let mut relr_ptr = None;
let mut relr_len = None;
let mut rel_ptr = None;
let mut rel_len = None;
loop {
let entry = unsafe { &*i };
let val = entry.d_val(NativeEndian);
let ptr = val as *const u8;
match entry.d_tag(NativeEndian) as u32 {
elf::DT_NULL => break,
elf::DT_RELA => rela_ptr = Some(ptr.cast::<Rela>()),
elf::DT_RELASZ => rela_len = Some(val as usize / size_of::<Rela>()),
elf::DT_RELAENT => {
assert_eq!(val as usize, size_of::<Rela>(),);
}
elf::DT_REL => rel_ptr = Some(ptr.cast::<Rel>()),
elf::DT_RELSZ => rel_len = Some(val as usize / size_of::<Rel>()),
elf::DT_RELENT => {
assert_eq!(val as usize, size_of::<Rel>());
}
DT_RELR => relr_ptr = Some(ptr.cast::<Relr>()),
DT_RELRSZ => relr_len = Some(val as usize / size_of::<Relr>()),
DT_RELRENT => {
assert_eq!(val as usize, size_of::<Relr>());
}
_ => {}
}
i = unsafe { i.add(1) };
}
unsafe fn get_array<'a, T>(
ptr: Option<*const T>,
len: Option<usize>,
base_addr: usize,
) -> &'a [T] {
if let Some(ptr) = ptr {
let len = len.expect_notls("dynamic entry was present without it's corresponding size");
unsafe { core::slice::from_raw_parts(ptr.byte_add(base_addr), len) }
} else {
&[]
}
}
fn do_relocs<'a, T>(relocs: &'a [T], self_base: usize)
where
Relocation: From<&'a T>,
{
for reloc in relocs {
let reloc: Relocation = reloc.into();
let ptr = (reloc.offset + self_base) as *mut usize;
match reloc.kind {
RelocationKind::RELATIVE => {
unsafe { *ptr = self_base + reloc.addend.unwrap_or_default() };
}
_ => {}
}
}
}
let rela = unsafe { get_array::<Rela>(rela_ptr, rela_len, self_base) };
let rel = unsafe { get_array::<Rel>(rel_ptr, rel_len, self_base) };
do_relocs(rela, self_base);
do_relocs(rel, self_base);
unsafe {
let relr = get_array(relr_ptr, relr_len, self_base);
apply_relr(self_base as *const u8, relr);
}
let mut base_addr = None;
if !is_manual {
// if we are not running in manual mode, then the main
// program is already loaded by the kernel and we want
// to use it. on redox, we treat it the same.
for ph in phdrs.iter() {
if ph.p_type(NativeEndian) == PT_PHDR {
assert!(base_addr.is_none(), "`PT_PHDR` cannot occur more than once");
base_addr = Some(unsafe {
phdrs
.as_ptr()
.cast::<u8>()
.sub(ph.p_vaddr(NativeEndian) as usize)
} as usize);
}
}
}
stage2(sp, self_base, is_manual, base_addr)
}
fn stage2(
sp: &'static mut Stack,
self_base: usize,
is_manual: bool,
base_addr: Option<usize>,
) -> usize {
// Setup TCB for ourselves.
unsafe {
#[cfg(target_os = "redox")]
@@ -225,14 +387,8 @@ pub unsafe extern "C" fn relibc_ld_so_start(sp: &'static mut Stack, ld_entry: us
crate::platform::environ = crate::platform::OUR_ENVIRON.unsafe_mut().as_mut_ptr();
}
let is_manual = if let Some(img_entry) = get_auxv(&auxv, AT_ENTRY) {
img_entry == ld_entry
} else {
true
};
// we might need global lock for this kind of stuff
_r_debug.lock().r_ldbase = ld_entry;
_r_debug.lock().r_ldbase = self_base;
// TODO: Fix memory leak, although minimal.
#[cfg(target_os = "redox")]
@@ -264,20 +420,15 @@ pub unsafe extern "C" fn relibc_ld_so_start(sp: &'static mut Stack, ld_entry: us
}
};
// if we are not running in manual mode, then the main
// program is already loaded by the kernel and we want
// to use it. on redox, we treat it the same.
let base_addr = {
let mut base = None;
if !is_manual && cfg!(not(target_os = "redox")) {
let phdr = get_auxv(&auxv, AT_PHDR).unwrap();
if phdr != 0 {
base = Some(phdr - SIZEOF_EHDR);
}
let config = Config::from_env(&envs);
if config.debug_flags.contains(DebugFlags::LOAD) {
println!("[ld.so]: relocated self at {self_base:#x}!");
if let Some(base_addr) = base_addr {
println!("[ld.so]: executable has been already loaded at {base_addr:#x?}");
}
base
};
let mut linker = Linker::new(Config::from_env(&envs));
}
let mut linker = Linker::new(config);
let entry = match linker.load_program(&path, base_addr) {
Ok(entry) => entry,
Err(err) => {
+4 -4
View File
@@ -280,18 +280,18 @@ impl<T: Write> Write for CountingWriter<T> {
// get_auxv.
#[cold]
unsafe fn auxv_iter<'a>(ptr: *const usize) -> impl Iterator<Item = [usize; 2]> + 'a {
pub unsafe fn auxv_iter<'a>(ptr: *const usize) -> impl Iterator<Item = [usize; 2]> + 'a {
struct St(*const usize);
impl Iterator for St {
type Item = [usize; 2];
fn next(&mut self) -> Option<Self::Item> {
unsafe {
if self.0.read() == self::auxv_defs::AT_NULL {
if *self.0 == self::auxv_defs::AT_NULL {
return None;
}
let kind = self.0.read();
let value = self.0.add(1).read();
let kind = *self.0;
let value = *self.0.add(1);
self.0 = self.0.add(2);
Some([kind, value])