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) => {