From cc305fc574843d963eed01e40849606bc40567f0 Mon Sep 17 00:00:00 2001 From: oddcoder Date: Tue, 7 Apr 2020 21:26:22 +0200 Subject: [PATCH] Use Kernel mapped binaries when available. At least in linux kernel, assuming that a.out is an elf that is linked against relibc's own ld.so. When a user attempts `./a.out`, Linux kernel will map `./a.out`, then map `ld.so` and jump into ld.so entry point. In relibc ld.so will simply ignore the kernel mapped a.out and create its own mapping. This patch forces relic ld.so to use the already mapped `a.out` when ever possible. This would normally have slight performance improvement (especially that currently relibc doesn't map a.out but instead copy the data into empty mmaped memory). The real motivation behind this patch is while impelemnting Runtime linker debugging protocol for relibc. part of the protocol is ld.so inseting address of some ld.so managed data structure into .dynamic seciton of a.out then the debugger would check it there. The thing is that debuggers have information about the kernel loaded ./a.out and they check that one specifically which is in our case totally ignored by relibc. --- src/header/dlfcn/mod.rs | 2 +- src/ld_so/linker.rs | 69 +++++++++++++++++++++++++++++++---------- src/ld_so/start.rs | 30 +++++++++++++++--- 3 files changed, 80 insertions(+), 21 deletions(-) diff --git a/src/header/dlfcn/mod.rs b/src/header/dlfcn/mod.rs index cc10390c07..f9c3bc8149 100644 --- a/src/header/dlfcn/mod.rs +++ b/src/header/dlfcn/mod.rs @@ -66,7 +66,7 @@ pub unsafe extern "C" fn dlopen(filename: *const c_char, flags: c_int) -> *mut c } } - match linker.link(None) { + match linker.link(None, None) { Ok(ok) => (), Err(err) => { eprintln!("dlopen: failed to link '{}': {}", filename, err); diff --git a/src/ld_so/linker.rs b/src/ld_so/linker.rs index 8486451460..15c666711c 100644 --- a/src/ld_so/linker.rs +++ b/src/ld_so/linker.rs @@ -29,6 +29,12 @@ const PATH_SEP: char = ';'; #[cfg(target_os = "linux")] const PATH_SEP: char = ':'; +pub struct DSO { + pub name: String, + pub base_addr: usize, + pub entry_point: usize, +} + pub struct Linker { // Used by load /// Library path to search when loading library by name @@ -190,7 +196,7 @@ impl Linker { } } - pub fn link(&mut self, primary_opt: Option<&str>) -> Result> { + pub fn link(&mut self, primary_opt: Option<&str>, dso: Option) -> Result> { let elfs = { let mut elfs = BTreeMap::new(); for (name, data) in self.objects.iter() { @@ -262,22 +268,41 @@ impl Linker { // Allocate memory let mmap = unsafe { let size = bounds.1 /* - bounds.0 */; - let ptr = sys_mman::mmap( - ptr::null_mut(), - size, - //TODO: Make it possible to not specify PROT_EXEC on Redox - sys_mman::PROT_READ | sys_mman::PROT_WRITE, - sys_mman::MAP_ANONYMOUS | sys_mman::MAP_PRIVATE, - -1, - 0, - ); - if ptr as usize == !0 - /* MAP_FAILED */ - { - return Err(Error::Malformed(format!("failed to map {}", elf_name))); + let same_elf = if let Some(prog) = dso.as_ref() { + if prog.name == *elf_name { + true + } else { + false + } + } else { + false + }; + if same_elf { + let addr = dso.as_ref().unwrap().base_addr; + sys_mman::mprotect( + addr as *mut c_void, + size, + sys_mman::PROT_READ | sys_mman::PROT_WRITE, + ); + slice::from_raw_parts_mut(addr as *mut u8, size) + } else { + let ptr = sys_mman::mmap( + ptr::null_mut(), + size, + //TODO: Make it possible to not specify PROT_EXEC on Redox + sys_mman::PROT_READ | sys_mman::PROT_WRITE, + sys_mman::MAP_ANONYMOUS | sys_mman::MAP_PRIVATE, + -1, + 0, + ); + if ptr as usize == !0 + /* MAP_FAILED */ + { + return Err(Error::Malformed(format!("failed to map {}", elf_name))); + } + ptr::write_bytes(ptr as *mut u8, 0, size); + slice::from_raw_parts_mut(ptr as *mut u8, size) } - ptr::write_bytes(ptr as *mut u8, 0, size); - slice::from_raw_parts_mut(ptr as *mut u8, size) }; if self.verbose { println!(" mmap {:p}, {:#x}", mmap.as_mut_ptr(), mmap.len()); @@ -308,6 +333,18 @@ impl Linker { }); let mut tls_ranges = BTreeMap::new(); for (elf_name, elf) in elfs.iter() { + let same_elf = if let Some(prog) = dso.as_ref() { + if prog.name == *elf_name { + true + } else { + false + } + } else { + false + }; + if same_elf { + continue; + } let object = match self.objects.get(*elf_name) { Some(some) => some, None => continue, diff --git a/src/ld_so/start.rs b/src/ld_so/start.rs index 28856a46fe..1607547992 100644 --- a/src/ld_so/start.rs +++ b/src/ld_so/start.rs @@ -6,8 +6,11 @@ use crate::{ c_str::CStr, header::unistd, platform::types::c_char, start::Stack, sync::mutex::Mutex, }; -use super::{linker::Linker, tcb::Tcb}; -use crate::header::sys_auxv::AT_ENTRY; +use super::{ + linker::{Linker, DSO}, + tcb::Tcb, +}; +use crate::header::sys_auxv::{AT_ENTRY, AT_PHDR}; unsafe fn get_argv(mut ptr: *const usize) -> (Vec, *const usize) { //traverse the stack and collect argument vector @@ -149,7 +152,26 @@ pub extern "C" fn relibc_ld_so_start(sp: &'static mut Stack, ld_entry: usize) -> } else { &argv[0] }; - + // if we are not running in manual mode, then the main + // program is already loaded by the kernel and we want + // to use it. + let program = { + let mut pr = None; + if !is_manual { + let phdr = *auxv.get(&AT_PHDR).unwrap(); + if phdr != 0 { + let p = DSO { + name: path.to_owned(), + entry_point: *auxv.get(&AT_ENTRY).unwrap(), + // The 0x40 is the size of Elf header not a good idea for different bit size + // compatiablility but it will always work on 64 bit systems, + base_addr: phdr - 0x40, + }; + pr = Some(p); + } + } + pr + }; let mut linker = Linker::new(library_path, false); match linker.load(&path, &path) { Ok(()) => (), @@ -160,7 +182,7 @@ pub extern "C" fn relibc_ld_so_start(sp: &'static mut Stack, ld_entry: usize) -> } } - let entry = match linker.link(Some(&path)) { + let entry = match linker.link(Some(&path), program) { Ok(ok) => match ok { Some(some) => some, None => {