From b69672cf8062029bf60d4efdbcc4d634755bc453 Mon Sep 17 00:00:00 2001 From: Anhad Singh Date: Fri, 13 Feb 2026 22:00:20 +1100 Subject: [PATCH 1/9] misc(.gitignore): add `.vscode` Signed-off-by: Anhad Singh --- .gitignore | 2 ++ .vscode/settings.json | 4 ---- 2 files changed, 2 insertions(+), 4 deletions(-) delete mode 100644 .vscode/settings.json diff --git a/.gitignore b/.gitignore index 6c439934ff..e7dcd0d68d 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,5 @@ sysroot/ *.swp *.swo /.vim +.vscode/ + diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 731634dd32..0000000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "cmake.ignoreCMakeListsMissing": true, - "editor.formatOnSave": true -} \ No newline at end of file From f9487321c064a00fba24d7c8d9a31c393c755a19 Mon Sep 17 00:00:00 2001 From: Anhad Singh Date: Sun, 15 Feb 2026 15:47:02 +1100 Subject: [PATCH 2/9] fix(ld.so): `base_addr` Not guaranteed that the program headers immediately follow the ELF header. Signed-off-by: Anhad Singh --- src/ld_so/start.rs | 76 ++++++++++++++++++++++++++++++++------------- src/platform/mod.rs | 2 +- 2 files changed, 56 insertions(+), 22 deletions(-) diff --git a/src/ld_so/start.rs b/src/ld_so/start.rs index c52cbc9bd5..4d237ecb92 100644 --- a/src/ld_so/start.rs +++ b/src/ld_so/start.rs @@ -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,16 @@ use alloc::{ string::{String, ToString}, vec::Vec, }; +use object::{NativeEndian, elf::PT_PHDR, read::elf::ProgramHeader as _}; use crate::{ c_str::CStr, header::{ - sys_auxv::{AT_ENTRY, AT_PHDR}, + elf::{AT_ENTRY, AT_PHDR, AT_PHENT, AT_PHNUM}, unistd, }, - platform::{get_auxv, get_auxvs, types::c_char}, + ld_so::dso::ProgramHeader, + platform::{auxv_iter, get_auxvs, types::c_char}, start::Stack, sync::mutex::Mutex, }; @@ -150,6 +154,55 @@ fn resolve_path_name( #[unsafe(no_mangle)] pub unsafe extern "C" fn relibc_ld_so_start(sp: &'static mut Stack, ld_entry: usize) -> usize { + let mut at_phdr = None; + let mut at_phnum = None; + let mut at_phent = None; + let mut at_entry = None; + for [kind, value] in unsafe { auxv_iter(sp.auxv().cast::()) } { + match kind { + AT_PHDR => at_phdr = Some(value as *const ProgramHeader), + AT_PHNUM => at_phnum = Some(value), + AT_PHENT => at_phent = Some(value), + AT_ENTRY => at_entry = Some(value), + _ => {} + } + } + + let at_entry = at_entry.expect("`AT_ENTRY` must be present"); + let (is_manual, base_addr) = if at_entry == ld_entry { + (true, None) + } else { + // 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 at_phdr = at_phdr.unwrap(); + let at_phnum = at_phnum.expect("`AT_PHNUM` must be present if `AT_PHDR` is"); + let at_phent = at_phent.expect("`AT_PHENT` must be present if `AT_PHDR` is"); + assert!(!at_phdr.is_null() && at_phnum != 0 && at_phent == size_of::()); + let phdrs = unsafe { slice::from_raw_parts(at_phdr, at_phnum) }; + + let mut base_addr = None; + 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::() + .sub(ph.p_vaddr(NativeEndian) as usize) + } as usize); + } + } + + ( + false, + #[cfg(target_os = "redox")] + None, + #[cfg(target_os = "linux")] + Some(base_addr.expect("`PT_PHDR` must be present for executables")), + ) + }; + // Setup TCB for ourselves. unsafe { #[cfg(target_os = "redox")] @@ -225,12 +278,6 @@ 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; @@ -264,19 +311,6 @@ 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); - } - } - base - }; let mut linker = Linker::new(Config::from_env(&envs)); let entry = match linker.load_program(&path, base_addr) { Ok(entry) => entry, diff --git a/src/platform/mod.rs b/src/platform/mod.rs index d489485b38..19792ba194 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -280,7 +280,7 @@ impl Write for CountingWriter { // get_auxv. #[cold] -unsafe fn auxv_iter<'a>(ptr: *const usize) -> impl Iterator + 'a { +pub unsafe fn auxv_iter<'a>(ptr: *const usize) -> impl Iterator + 'a { struct St(*const usize); impl Iterator for St { type Item = [usize; 2]; From 8cb41ed4e27cc184ddabaf5433914260f5f8cea6 Mon Sep 17 00:00:00 2001 From: Anhad Singh Date: Sun, 15 Feb 2026 19:14:07 +1100 Subject: [PATCH 3/9] feat(ld.so): shared library Signed-off-by: Anhad Singh --- Makefile | 12 ++--- ld_so/src/lib.rs | 16 ++++-- src/ld_so/dso.rs | 71 ++++++++++++++------------ src/ld_so/start.rs | 124 +++++++++++++++++++++++++++++++++++---------- 4 files changed, 153 insertions(+), 70 deletions(-) diff --git a/Makefile b/Makefile index 3c10c9d893..2bb04c48c1 100644 --- a/Makefile +++ b/Makefile @@ -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 @@ -145,7 +149,7 @@ $(BUILD)/debug/libc.a: $(BUILD)/debug/librelibc.a $(BUILD)/openlibm/libopenlibm. $(AR) -M < "$@.mri" $(BUILD)/debug/librelibc.a: $(SRC) - $(CARGO) rustc $(CARGOFLAGS) -- --emit link=$@ -g -C debug-assertions=no $(RUSTCFLAGS) + $(CARGO) rustc $(CARGOFLAGS) -- --emit link=$@ -g -C debug-assertions=no -C opt-level=2 $(RUSTCFLAGS) ./renamesyms.sh "$@" "$(BUILD)/debug/deps/" ./stripcore.sh "$@" touch $@ @@ -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 diff --git a/ld_so/src/lib.rs b/ld_so/src/lib.rs index 6c7d4736a9..ee7ab3b6cf 100644 --- a/ld_so/src/lib.rs +++ b/ld_so/src/lib.rs @@ -7,14 +7,18 @@ use core::arch::global_asm; #[cfg(target_arch = "aarch64")] global_asm!( " +.weak _DYNAMIC +.hidden _DYNAMIC + .global _start _start: mov x28, sp // align stack to 16 bytes and sp, x28, #0xfffffffffffffff0 - adr x1, _start mov x0, x28 - // ld_so_start(stack=x0, ld_entry=x1) + adrp x1, _DYNAMIC + add x1, x1, #:lo12:_DYNAMIC + // ld_so_start(stack=x0, dynamic=x1) bl relibc_ld_so_start // restore original stack, clear registers, and jump to the new start function mov sp, x28 @@ -69,16 +73,18 @@ _start: #[cfg(target_arch = "x86_64")] global_asm!( " +.weak _DYNAMIC +.hidden _DYNAMIC + .globl _start _start: - lea rsi, [rip + _start] - # Save original stack and align stack to 16 bytes mov rbp, rsp and rsp, 0xfffffffffffffff0 - # Call ld_so_start(stack=rdi, ld_entry=rsi) + # Call ld_so_start(stack=rdi, dynamic=rsi) mov rdi, rbp + lea rsi, [rip + _DYNAMIC] call relibc_ld_so_start # Restore original stack, clear registers, and jump to new start function diff --git a/src/ld_so/dso.rs b/src/ld_so/dso.rs index 5e94d0f636..55c6c559f1 100644 --- a/src/ld_so/dso.rs +++ b/src/ld_so/dso.rs @@ -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); @@ -662,10 +667,6 @@ impl DSO { is_pie: bool, (_, entries): (&ProgramHeader, &[Dyn]), ) -> object::Result<(Dynamic<'a>, Option)> { - 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::() - 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::() - 1) }; + } + } +} diff --git a/src/ld_so/start.rs b/src/ld_so/start.rs index 4d237ecb92..ac37af04ea 100644 --- a/src/ld_so/start.rs +++ b/src/ld_so/start.rs @@ -9,15 +9,19 @@ use alloc::{ string::{String, ToString}, vec::Vec, }; -use object::{NativeEndian, elf::PT_PHDR, read::elf::ProgramHeader as _}; +use object::{ + NativeEndian, + elf::{self, PT_PHDR}, + read::elf::{Dyn as _, ProgramHeader as _, Rela as _}, +}; use crate::{ c_str::CStr, header::{ - elf::{AT_ENTRY, AT_PHDR, AT_PHENT, AT_PHNUM}, + elf::{AT_BASE, AT_PHDR, AT_PHENT, AT_PHNUM, PT_DYNAMIC}, unistd, }, - ld_so::dso::ProgramHeader, + ld_so::dso::{DT_RELR, DT_RELRENT, DT_RELRSZ, Dyn, ProgramHeader, Rela, Relr, apply_relr}, platform::{auxv_iter, get_auxvs, types::c_char}, start::Stack, sync::mutex::Mutex, @@ -153,35 +157,111 @@ 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, dynamic: *const Dyn) -> usize { let mut at_phdr = None; let mut at_phnum = None; let mut at_phent = None; - let mut at_entry = None; + let mut at_base = None; for [kind, value] in unsafe { auxv_iter(sp.auxv().cast::()) } { match kind { AT_PHDR => at_phdr = Some(value as *const ProgramHeader), AT_PHNUM => at_phnum = Some(value), AT_PHENT => at_phent = Some(value), - AT_ENTRY => at_entry = Some(value), + AT_BASE => at_base = Some(value), _ => {} } } - let at_entry = at_entry.expect("`AT_ENTRY` must be present"); - let (is_manual, base_addr) = if at_entry == ld_entry { - (true, None) + let at_phdr = at_phdr.unwrap(); + let at_phnum = at_phnum.expect("`AT_PHNUM` must be present if `AT_PHDR` is"); + let at_phent = at_phent.expect("`AT_PHENT` must be present if `AT_PHDR` is"); + assert!(!at_phdr.is_null() && at_phnum != 0 && at_phent == size_of::()); + let phdrs = unsafe { slice::from_raw_parts(at_phdr, at_phnum) }; + + // [`AT_BASE`] is the base address at which the dynamic linker was loaded in memory. On Linux, + // this entry is always present. If the dynamic linker was not loaded (i.e. run as a command), + // its value is 0. On Redox, it is only present if the dynamic linker is loaded. + let at_base = at_base.unwrap_or_default(); + + let (is_manual, self_base) = if at_base != 0 { + (false, at_base) } else { + // Dynamic linker was run as a command. + let ph = phdrs + .iter() + .find(|ph| ph.p_type(NativeEndian) == PT_DYNAMIC as u32) + .unwrap(); + (true, unsafe { + dynamic.byte_sub(ph.p_vaddr(NativeEndian) as usize) as usize + }) + }; + + let mut i = dynamic; + let mut rela_ptr = None; + let mut rela_len = None; + let mut relr_ptr = None; + let mut relr_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::()), + elf::DT_RELASZ => rela_len = Some(val as usize / size_of::()), + elf::DT_RELAENT => { + assert_eq!(val as usize, size_of::(),); + } + DT_RELR => relr_ptr = Some(ptr.cast::()), + DT_RELRSZ => relr_len = Some(val as usize / size_of::()), + DT_RELRENT => { + assert_eq!(val as usize, size_of::()); + } + _ => {} + } + i = unsafe { i.add(1) }; + } + + unsafe fn get_array<'a, T>( + ptr: Option<*const T>, + len: Option, + base_addr: usize, + ) -> &'a [T] { + if let Some(ptr) = ptr { + let len = len.expect("dynamic entry was present without it's corresponding size"); + unsafe { core::slice::from_raw_parts(ptr.byte_add(base_addr), len) } + } else { + assert!(len.is_none()); + &[] + } + } + + for rela in unsafe { get_array(rela_ptr, rela_len, self_base) } { + let offset = rela.r_offset(NativeEndian); + let addend = rela.r_addend(NativeEndian) as isize; + let reloc_ptr = (offset + self_base as u64) as *mut usize; + match rela.r_type(NativeEndian, false) as u32 { + elf::R_X86_64_RELATIVE => { + unsafe { reloc_ptr.write((self_base as isize + addend) as usize) }; + } + _ => {} + } + } + + unsafe { + let relr = get_array(relr_ptr, relr_len, self_base); + apply_relr(self_base as *const u8, relr); + } + + println!( + "[ld.so]: relocated self at {self_base:#x} (DT_RELASZ={rela_len:?}, DT_RELRSZ={relr_len:?})" + ); + + let mut base_addr = None; + if is_manual && cfg!(target_os = "linux") { // 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 at_phdr = at_phdr.unwrap(); - let at_phnum = at_phnum.expect("`AT_PHNUM` must be present if `AT_PHDR` is"); - let at_phent = at_phent.expect("`AT_PHENT` must be present if `AT_PHDR` is"); - assert!(!at_phdr.is_null() && at_phnum != 0 && at_phent == size_of::()); - let phdrs = unsafe { slice::from_raw_parts(at_phdr, at_phnum) }; - - let mut base_addr = None; for ph in phdrs.iter() { if ph.p_type(NativeEndian) == PT_PHDR { assert!(base_addr.is_none(), "`PT_PHDR` cannot occur more than once"); @@ -193,15 +273,7 @@ pub unsafe extern "C" fn relibc_ld_so_start(sp: &'static mut Stack, ld_entry: us } as usize); } } - - ( - false, - #[cfg(target_os = "redox")] - None, - #[cfg(target_os = "linux")] - Some(base_addr.expect("`PT_PHDR` must be present for executables")), - ) - }; + } // Setup TCB for ourselves. unsafe { @@ -279,7 +351,7 @@ pub unsafe extern "C" fn relibc_ld_so_start(sp: &'static mut Stack, ld_entry: us } // 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")] From 0430081f4a149db6e560e7aadff590f3542da5c9 Mon Sep 17 00:00:00 2001 From: Anhad Singh Date: Sun, 15 Feb 2026 23:13:11 +1100 Subject: [PATCH 4/9] feat(redox-rt): preload the executable Signed-off-by: Anhad Singh --- ld_so/src/lib.rs | 13 +++-- redox-rt/src/proc.rs | 135 ++++++++++++++++++++++++++----------------- src/ld_so/dso.rs | 4 +- src/ld_so/linker.rs | 2 +- src/ld_so/start.rs | 48 +++++++++------ 5 files changed, 123 insertions(+), 79 deletions(-) diff --git a/ld_so/src/lib.rs b/ld_so/src/lib.rs index ee7ab3b6cf..e68f260ef6 100644 --- a/ld_so/src/lib.rs +++ b/ld_so/src/lib.rs @@ -15,10 +15,11 @@ _start: mov x28, sp // align stack to 16 bytes and sp, x28, #0xfffffffffffffff0 + adr x1, _start mov x0, x28 - adrp x1, _DYNAMIC - add x1, x1, #:lo12:_DYNAMIC - // ld_so_start(stack=x0, dynamic=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 @@ -78,13 +79,15 @@ global_asm!( .globl _start _start: + lea rsi, [rip + _start] + # Save original stack and align stack to 16 bytes mov rbp, rsp and rsp, 0xfffffffffffffff0 - # Call ld_so_start(stack=rdi, dynamic=rsi) + # Call ld_so_start(stack=rdi, ld_entry=rsi, dynamic=rdx) mov rdi, rbp - lea rsi, [rip + _DYNAMIC] + lea rdx, [rip + _DYNAMIC] call relibc_ld_so_start # Restore original stack, clear registers, and jump to new start function diff --git a/redox-rt/src/proc.rs b/redox-rt/src/proc.rs index 90a7c1c168..f094733896 100644 --- a/redox-rt/src/proc.rs +++ b/redox-rt/src/proc.rs @@ -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::
()..]; // 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> = 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::
())?; - 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, )); diff --git a/src/ld_so/dso.rs b/src/ld_so/dso.rs index 55c6c559f1..daaf5084f2 100644 --- a/src/ld_so/dso.rs +++ b/src/ld_so/dso.rs @@ -527,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; diff --git a/src/ld_so/linker.rs b/src/ld_so/linker.rs index f740f52dda..03380d25b9 100644 --- a/src/ld_so/linker.rs +++ b/src/ld_so/linker.rs @@ -348,7 +348,7 @@ bitflags::bitflags! { #[derive(Default)] pub struct Config { - debug_flags: DebugFlags, + pub debug_flags: DebugFlags, library_path: Option, /// Resolve symbols at program startup. bind_now: bool, diff --git a/src/ld_so/start.rs b/src/ld_so/start.rs index ac37af04ea..7265b9ec9a 100644 --- a/src/ld_so/start.rs +++ b/src/ld_so/start.rs @@ -11,17 +11,20 @@ use alloc::{ }; use object::{ NativeEndian, - elf::{self, PT_PHDR}, + elf::{self, PT_DYNAMIC, PT_PHDR}, read::elf::{Dyn as _, ProgramHeader as _, Rela as _}, }; use crate::{ c_str::CStr, header::{ - elf::{AT_BASE, AT_PHDR, AT_PHENT, AT_PHNUM, PT_DYNAMIC}, + elf::{AT_BASE, AT_ENTRY, AT_PHDR, AT_PHENT, AT_PHNUM}, unistd, }, - ld_so::dso::{DT_RELR, DT_RELRENT, DT_RELRSZ, Dyn, ProgramHeader, Rela, Relr, apply_relr}, + ld_so::{ + dso::{DT_RELR, DT_RELRENT, DT_RELRSZ, Dyn, ProgramHeader, Rela, Relr, apply_relr}, + linker::DebugFlags, + }, platform::{auxv_iter, get_auxvs, types::c_char}, start::Stack, sync::mutex::Mutex, @@ -157,17 +160,23 @@ fn resolve_path_name( } #[unsafe(no_mangle)] -pub unsafe extern "C" fn relibc_ld_so_start(sp: &'static mut Stack, dynamic: *const Dyn) -> usize { +pub unsafe extern "C" fn relibc_ld_so_start( + sp: &'static mut Stack, + ld_entry: usize, + dynamic: *const Dyn, +) -> usize { 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::()) } { 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), _ => {} } } @@ -178,24 +187,21 @@ pub unsafe extern "C" fn relibc_ld_so_start(sp: &'static mut Stack, dynamic: *co assert!(!at_phdr.is_null() && at_phnum != 0 && at_phent == size_of::()); let phdrs = unsafe { slice::from_raw_parts(at_phdr, at_phnum) }; - // [`AT_BASE`] is the base address at which the dynamic linker was loaded in memory. On Linux, - // this entry is always present. If the dynamic linker was not loaded (i.e. run as a command), - // its value is 0. On Redox, it is only present if the dynamic linker is loaded. + let at_entry = at_entry.unwrap(); let at_base = at_base.unwrap_or_default(); - let (is_manual, self_base) = if at_base != 0 { - (false, at_base) + let self_base = if at_base != 0 { + at_base } else { - // Dynamic linker was run as a command. let ph = phdrs .iter() .find(|ph| ph.p_type(NativeEndian) == PT_DYNAMIC as u32) .unwrap(); - (true, unsafe { - dynamic.byte_sub(ph.p_vaddr(NativeEndian) as usize) as usize - }) + 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; @@ -253,12 +259,8 @@ pub unsafe extern "C" fn relibc_ld_so_start(sp: &'static mut Stack, dynamic: *co apply_relr(self_base as *const u8, relr); } - println!( - "[ld.so]: relocated self at {self_base:#x} (DT_RELASZ={rela_len:?}, DT_RELRSZ={relr_len:?})" - ); - let mut base_addr = None; - if is_manual && cfg!(target_os = "linux") { + 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. @@ -383,7 +385,15 @@ pub unsafe extern "C" fn relibc_ld_so_start(sp: &'static mut Stack, dynamic: *co } }; - let mut linker = Linker::new(Config::from_env(&envs)); + 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?}"); + } + } + + let mut linker = Linker::new(config); let entry = match linker.load_program(&path, base_addr) { Ok(entry) => entry, Err(err) => { From 371540d2ae08b480c63e6405b1e9870233412b3a Mon Sep 17 00:00:00 2001 From: Anhad Singh Date: Mon, 16 Feb 2026 17:04:01 +1100 Subject: [PATCH 5/9] fix(ld.so/i586): build Signed-off-by: Anhad Singh --- src/ld_so/dso.rs | 10 +++++----- src/ld_so/start.rs | 36 ++++++++++++++++++++++++++---------- 2 files changed, 31 insertions(+), 15 deletions(-) diff --git a/src/ld_so/dso.rs b/src/ld_so/dso.rs index daaf5084f2..0533b3117a 100644 --- a/src/ld_so/dso.rs +++ b/src/ld_so/dso.rs @@ -175,11 +175,11 @@ unsafe impl Send for Dynamic<'_> {} unsafe impl Sync for Dynamic<'_> {} #[derive(Debug)] -struct Relocation { - offset: usize, - addend: Option, - sym: SymbolIndex, - kind: RelocationKind, +pub(super) struct Relocation { + pub(super) offset: usize, + pub(super) addend: Option, + pub(super) sym: SymbolIndex, + pub(super) kind: RelocationKind, } #[cfg(target_pointer_width = "32")] diff --git a/src/ld_so/start.rs b/src/ld_so/start.rs index 7265b9ec9a..2ac5a27bed 100644 --- a/src/ld_so/start.rs +++ b/src/ld_so/start.rs @@ -12,7 +12,7 @@ use alloc::{ use object::{ NativeEndian, elf::{self, PT_DYNAMIC, PT_PHDR}, - read::elf::{Dyn as _, ProgramHeader as _, Rela as _}, + read::elf::{Dyn as _, ProgramHeader as _}, }; use crate::{ @@ -22,7 +22,10 @@ use crate::{ unistd, }, ld_so::{ - dso::{DT_RELR, DT_RELRENT, DT_RELRSZ, Dyn, ProgramHeader, Rela, Relr, apply_relr}, + 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}, @@ -207,6 +210,8 @@ pub unsafe extern "C" fn relibc_ld_so_start( 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); @@ -218,6 +223,11 @@ pub unsafe extern "C" fn relibc_ld_so_start( elf::DT_RELAENT => { assert_eq!(val as usize, size_of::(),); } + elf::DT_REL => rel_ptr = Some(ptr.cast::()), + elf::DT_RELSZ => rel_len = Some(val as usize / size_of::()), + elf::DT_RELENT => { + assert_eq!(val as usize, size_of::()); + } DT_RELR => relr_ptr = Some(ptr.cast::()), DT_RELRSZ => relr_len = Some(val as usize / size_of::()), DT_RELRENT => { @@ -242,13 +252,19 @@ pub unsafe extern "C" fn relibc_ld_so_start( } } - for rela in unsafe { get_array(rela_ptr, rela_len, self_base) } { - let offset = rela.r_offset(NativeEndian); - let addend = rela.r_addend(NativeEndian) as isize; - let reloc_ptr = (offset + self_base as u64) as *mut usize; - match rela.r_type(NativeEndian, false) as u32 { - elf::R_X86_64_RELATIVE => { - unsafe { reloc_ptr.write((self_base as isize + addend) as usize) }; + for reloc in unsafe { get_array::(rela_ptr, rela_len, self_base) } + .iter() + .map(Relocation::from) + .chain( + unsafe { get_array::(rel_ptr, rel_len, self_base) } + .iter() + .map(Relocation::from), + ) + { + let ptr = (reloc.offset + self_base) as *mut usize; + match reloc.kind { + RelocationKind::RELATIVE => { + unsafe { ptr.write(self_base + reloc.addend.unwrap_or_default()) }; } _ => {} } @@ -386,7 +402,7 @@ pub unsafe extern "C" fn relibc_ld_so_start( }; let config = Config::from_env(&envs); - if config.debug_flags.contains(DebugFlags::LOAD) { + if config.debug_flags.contains(DebugFlags::LOAD) || true { 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?}"); From 0c3f88f46214ff520664f9643f03772a52451bad Mon Sep 17 00:00:00 2001 From: Anhad Singh Date: Mon, 16 Feb 2026 22:46:45 +1100 Subject: [PATCH 6/9] misc(ld.so): fix logging Signed-off-by: Anhad Singh --- src/ld_so/start.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ld_so/start.rs b/src/ld_so/start.rs index 2ac5a27bed..e3ba717cb7 100644 --- a/src/ld_so/start.rs +++ b/src/ld_so/start.rs @@ -402,7 +402,7 @@ pub unsafe extern "C" fn relibc_ld_so_start( }; let config = Config::from_env(&envs); - if config.debug_flags.contains(DebugFlags::LOAD) || true { + 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?}"); From f5ebd7882d9262f9b04a94b414bb4c32717263ab Mon Sep 17 00:00:00 2001 From: Anhad Singh Date: Mon, 16 Feb 2026 22:51:14 +1100 Subject: [PATCH 7/9] misc(ld.so): minor cleanup Signed-off-by: Anhad Singh --- src/ld_so/start.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/ld_so/start.rs b/src/ld_so/start.rs index e3ba717cb7..72eb54096b 100644 --- a/src/ld_so/start.rs +++ b/src/ld_so/start.rs @@ -293,6 +293,15 @@ pub unsafe extern "C" fn relibc_ld_so_start( } } + stage2(sp, self_base, is_manual, base_addr) +} + +fn stage2( + sp: &'static mut Stack, + self_base: usize, + is_manual: bool, + base_addr: Option, +) -> usize { // Setup TCB for ourselves. unsafe { #[cfg(target_os = "redox")] From d234e81fc9cd7e24be85d44e5fb41288eb31b6bf Mon Sep 17 00:00:00 2001 From: Anhad Singh Date: Tue, 17 Feb 2026 00:15:10 +1100 Subject: [PATCH 8/9] fix(ld.so): debug build Signed-off-by: Anhad Singh --- Makefile | 4 ++-- src/ld_so/start.rs | 53 +++++++++++++++++++++++++++------------------ src/platform/mod.rs | 6 ++--- 3 files changed, 37 insertions(+), 26 deletions(-) diff --git a/Makefile b/Makefile index 2bb04c48c1..bdb822d45c 100644 --- a/Makefile +++ b/Makefile @@ -149,7 +149,7 @@ $(BUILD)/debug/libc.a: $(BUILD)/debug/librelibc.a $(BUILD)/openlibm/libopenlibm. $(AR) -M < "$@.mri" $(BUILD)/debug/librelibc.a: $(SRC) - $(CARGO) rustc $(CARGOFLAGS) -- --emit link=$@ -g -C debug-assertions=no -C opt-level=2 $(RUSTCFLAGS) + $(CARGO) rustc $(CARGOFLAGS) -- --emit link=$@ -g -C debug-assertions=no $(RUSTCFLAGS) ./renamesyms.sh "$@" "$(BUILD)/debug/deps/" ./stripcore.sh "$@" touch $@ @@ -214,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/" diff --git a/src/ld_so/start.rs b/src/ld_so/start.rs index 72eb54096b..25d4fccc1c 100644 --- a/src/ld_so/start.rs +++ b/src/ld_so/start.rs @@ -168,6 +168,14 @@ pub unsafe extern "C" fn relibc_ld_so_start( 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 `__relibc_ld_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; @@ -184,13 +192,13 @@ pub unsafe extern "C" fn relibc_ld_so_start( } } - let at_phdr = at_phdr.unwrap(); - let at_phnum = at_phnum.expect("`AT_PHNUM` must be present if `AT_PHDR` is"); - let at_phent = at_phent.expect("`AT_PHENT` must be present if `AT_PHDR` is"); + 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::()); let phdrs = unsafe { slice::from_raw_parts(at_phdr, at_phnum) }; - let at_entry = at_entry.unwrap(); + 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 { @@ -244,32 +252,34 @@ pub unsafe extern "C" fn relibc_ld_so_start( base_addr: usize, ) -> &'a [T] { if let Some(ptr) = ptr { - let len = len.expect("dynamic entry was present without it's corresponding size"); + 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 { - assert!(len.is_none()); &[] } } - for reloc in unsafe { get_array::(rela_ptr, rela_len, self_base) } - .iter() - .map(Relocation::from) - .chain( - unsafe { get_array::(rel_ptr, rel_len, self_base) } - .iter() - .map(Relocation::from), - ) + fn do_relocs<'a, T>(relocs: &'a [T], self_base: usize) + where + Relocation: From<&'a T>, { - let ptr = (reloc.offset + self_base) as *mut usize; - match reloc.kind { - RelocationKind::RELATIVE => { - unsafe { ptr.write(self_base + reloc.addend.unwrap_or_default()) }; + 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_ptr, rela_len, self_base) }; + let rel = unsafe { get_array::(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); @@ -293,10 +303,11 @@ pub unsafe extern "C" fn relibc_ld_so_start( } } - stage2(sp, self_base, is_manual, base_addr) + __relibc_ld_stage2(sp, self_base, is_manual, base_addr) } -fn stage2( +#[unsafe(no_mangle)] +fn __relibc_ld_stage2( sp: &'static mut Stack, self_base: usize, is_manual: bool, diff --git a/src/platform/mod.rs b/src/platform/mod.rs index 19792ba194..c4250cfbd8 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -287,11 +287,11 @@ pub unsafe fn auxv_iter<'a>(ptr: *const usize) -> impl Iterator Option { 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]) From e3abf808f3ae9e141033bcdda54a2ef2b416591a Mon Sep 17 00:00:00 2001 From: Anhad Singh Date: Tue, 17 Feb 2026 00:46:57 +1100 Subject: [PATCH 9/9] misc(ld.so): remove `no_mangle` for `stage2` Signed-off-by: Anhad Singh --- src/ld_so/start.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/ld_so/start.rs b/src/ld_so/start.rs index 25d4fccc1c..355f1474fa 100644 --- a/src/ld_so/start.rs +++ b/src/ld_so/start.rs @@ -171,8 +171,8 @@ pub unsafe extern "C" fn relibc_ld_so_start( // Relocate ourselves. // // This function is very delicate as it must **not** contain relocations itself. References to - // external symbols **cannot** be made until `__relibc_ld_stage2()` so, this function might not - // be very elegant. + // 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`. @@ -303,11 +303,10 @@ pub unsafe extern "C" fn relibc_ld_so_start( } } - __relibc_ld_stage2(sp, self_base, is_manual, base_addr) + stage2(sp, self_base, is_manual, base_addr) } -#[unsafe(no_mangle)] -fn __relibc_ld_stage2( +fn stage2( sp: &'static mut Stack, self_base: usize, is_manual: bool,