Browse Source

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.
oddcoder 5 years ago
parent
commit
cc305fc574
3 changed files with 80 additions and 21 deletions
  1. 1 1
      src/header/dlfcn/mod.rs
  2. 53 16
      src/ld_so/linker.rs
  3. 26 4
      src/ld_so/start.rs

+ 1 - 1
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);

+ 53 - 16
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<Option<usize>> {
+    pub fn link(&mut self, primary_opt: Option<&str>, dso: Option<DSO>) -> Result<Option<usize>> {
         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,

+ 26 - 4
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<String>, *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 => {