diff --git a/src/ld_so/debug.rs b/src/ld_so/debug.rs new file mode 100644 index 0000000000000000000000000000000000000000..8780cc1f268f6b5e15170363834a2909e770f145 --- /dev/null +++ b/src/ld_so/debug.rs @@ -0,0 +1,117 @@ +use crate::{c_str::CString, platform::types::*}; +use alloc::boxed::Box; + +#[repr(C)] +pub enum RTLDState { + /// Mapping change is complete. + RT_CONSISTENT, + /// Beginning to add a new object. + RT_ADD, + /// Beginning to remove an object mapping. + RT_DELETE, +} + +/// Data structure for sharing debugging information from the +/// run-time dynamic linker for loaded ELF shared objects. +#[repr(C)] +pub struct RTLDDebug { + /// Version number for this protocol. + r_version: i32, + /// Head of the chain of loaded objects. + r_map: *mut LinkMap, + //struct link_map *r_map; + /// This is the address of a function internal to the run-time linker, + /// that will always be called when the linker begins to map in a + /// library or unmap it, and again when the mapping change is complete. + /// The debugger can set a breakpoint at this address if it wants to + /// notice shared object mapping changes. + pub r_brk: extern "C" fn(), + + /// This state value describes the mapping change taking place when + /// the `r_brk' address is called. + pub state: RTLDState, + + /// Base address the linker is loaded at. + pub r_ldbase: usize, +} + +impl RTLDDebug { + const NEW: Self = RTLDDebug { + r_version: 1, + r_map: 0 as *mut LinkMap, + r_brk: _dl_debug_state, + state: RTLDState::RT_CONSISTENT, + r_ldbase: 0, + }; + + pub fn insert(&mut self, l_addr: usize, name: &str, l_ld: usize) { + if self.r_map.is_null() { + self.r_map = LinkMap::new_with_args(l_addr, name, l_ld); + } else { + unsafe { (*self.r_map).add_object(l_addr, name, l_ld) }; + } + return; + } +} + +#[repr(C)] +struct LinkMap { + /* These members are part of the protocol with the debugger. + This is the same format used in SVR4. */ + /// Difference between the address in the ELF + /// file and the addresses in memory. + l_addr: usize, + /// Absolute file name object was found in. + l_name: *const c_char, + /// Dynamic section of the shared object. + l_ld: usize, + l_next: *mut LinkMap, + l_prev: *mut LinkMap, +} + +impl LinkMap { + fn new() -> *mut Self { + let map = Box::new(LinkMap { + l_addr: 0, + l_name: 0 as *const c_char, + l_ld: 0, + l_next: 0 as *mut LinkMap, + l_prev: 0 as *mut LinkMap, + }); + Box::into_raw(map) + } + + fn new_with_args(l_addr: usize, name: &str, l_ld: usize) -> *mut Self { + let map = LinkMap::new(); + unsafe { + (*map).l_addr = l_addr; + (*map).l_ld = l_ld; + let c_name = CString::new(name).unwrap(); + (*map).l_name = c_name.into_raw() as *const c_char; + } + map + } + + fn add_object(&mut self, l_addr: usize, name: &str, l_ld: usize) { + let node = LinkMap::new_with_args(l_addr, name, l_ld); + let mut last = self; + while !last.l_next.is_null() { + last = unsafe { last.l_next.as_mut() }.unwrap(); + } + unsafe { + (*node).l_prev = last; + (*last).l_next = node; + } + } +} + +/* + * Gdb may be looking for this fuction with that exact name and set + * break point there + */ +#[linkage = "weak"] +#[no_mangle] +pub extern "C" fn _dl_debug_state() {} + +#[no_mangle] +pub static mut _r_debug: RTLDDebug = RTLDDebug::NEW; diff --git a/src/ld_so/linker.rs b/src/ld_so/linker.rs index 15c666711c436468b224c25e2754a6f9e8418665..818a5feb483b0e73e22f164f3c715e7026682334 100644 --- a/src/ld_so/linker.rs +++ b/src/ld_so/linker.rs @@ -4,9 +4,16 @@ use alloc::{ string::{String, ToString}, vec::Vec, }; -use core::{mem, ptr, slice}; +use core::{ + mem::{size_of, transmute}, + ptr, slice, +}; use goblin::{ - elf::{program_header, reloc, sym, Elf}, + elf::{ + program_header, + r#dyn::{Dyn, DT_DEBUG}, + reloc, sym, Elf, + }, error::{Error, Result}, }; @@ -19,6 +26,7 @@ use crate::{ }; use super::{ + debug::{RTLDDebug, RTLDState, _dl_debug_state, _r_debug}, tcb::{Master, Tcb}, PAGE_SIZE, }; @@ -197,6 +205,8 @@ impl Linker { } pub fn link(&mut self, primary_opt: Option<&str>, dso: Option<DSO>) -> Result<Option<usize>> { + unsafe { _r_debug.state = RTLDState::RT_ADD }; + _dl_debug_state(); let elfs = { let mut elfs = BTreeMap::new(); for (name, data) in self.objects.iter() { @@ -219,7 +229,8 @@ impl Linker { Some(some) => some, None => continue, }; - + // data for struct LinkMap + let mut l_ld = 0; // Calculate virtual memory bounds let bounds = { let mut bounds_opt: Option<(usize, usize)> = None; @@ -230,6 +241,9 @@ impl Linker { ((ph.p_memsz as usize + voff + PAGE_SIZE - 1) / PAGE_SIZE) * PAGE_SIZE; match ph.p_type { + program_header::PT_DYNAMIC => { + l_ld = ph.p_vaddr; + } program_header::PT_LOAD => { if self.verbose { println!(" load {:#x}, {:#x}: {:x?}", vaddr, vsize, ph); @@ -284,6 +298,7 @@ impl Linker { size, sys_mman::PROT_READ | sys_mman::PROT_WRITE, ); + _r_debug.insert(addr as usize, &elf_name, addr + l_ld as usize); slice::from_raw_parts_mut(addr as *mut u8, size) } else { let ptr = sys_mman::mmap( @@ -301,6 +316,7 @@ impl Linker { return Err(Error::Malformed(format!("failed to map {}", elf_name))); } ptr::write_bytes(ptr as *mut u8, 0, size); + _r_debug.insert(ptr as usize, &elf_name, ptr as usize + l_ld as usize); slice::from_raw_parts_mut(ptr as *mut u8, size) } }; @@ -541,6 +557,32 @@ impl Linker { } } + // overwrite DT_DEBUG if exist in .dynamic section + for section in &elf.section_headers { + // we won't bother with half corrupted elfs. + let name = elf.shdr_strtab.get(section.sh_name).unwrap().unwrap(); + if name != ".dynamic" { + continue; + } + let mmap = match self.mmaps.get_mut(*elf_name) { + Some(some) => some, + None => continue, + }; + let dyn_start = section.sh_addr as usize; + let bytes: [u8; size_of::<Dyn>() / 2] = + unsafe { transmute((&_r_debug) as *const RTLDDebug as usize) }; + if let Some(dynamic) = elf.dynamic.as_ref() { + let mut i = 0; + for entry in &dynamic.dyns { + if entry.d_tag == DT_DEBUG { + let start = dyn_start + i * size_of::<Dyn>() + size_of::<Dyn>() / 2; + mmap[start..start + size_of::<Dyn>() / 2].clone_from_slice(&bytes); + } + i += 1; + } + } + } + // Protect pages for ph in elf.program_headers.iter() { if ph.p_type == program_header::PT_LOAD { @@ -629,7 +671,7 @@ impl Linker { if rel.r_type == reloc::R_X86_64_IRELATIVE { unsafe { - let f: unsafe extern "C" fn() -> u64 = mem::transmute(b + a); + let f: unsafe extern "C" fn() -> u64 = transmute(b + a); set_u64(f()); } } @@ -670,7 +712,8 @@ impl Linker { } } } - + unsafe { _r_debug.state = RTLDState::RT_CONSISTENT }; + _dl_debug_state(); Ok(entry_opt) } } diff --git a/src/ld_so/mod.rs b/src/ld_so/mod.rs index cbc27567bc503bffb8645fd90e8d9b23cb72ceba..1d06541ac15dfc6fcbeaac4a7498d1e503fb0486 100644 --- a/src/ld_so/mod.rs +++ b/src/ld_so/mod.rs @@ -5,10 +5,10 @@ use crate::start::Stack; pub const PAGE_SIZE: usize = 4096; +pub mod debug; pub mod linker; pub mod start; pub mod tcb; - pub fn static_init(sp: &'static Stack) { let mut phdr_opt = None; let mut phent_opt = None; diff --git a/src/ld_so/start.rs b/src/ld_so/start.rs index 16075479925ddb0a794f37adf2968137953dc89d..1d60b714fd09efec0224f3ca52ebca64c0666a57 100644 --- a/src/ld_so/start.rs +++ b/src/ld_so/start.rs @@ -7,6 +7,7 @@ use crate::{ }; use super::{ + debug::_r_debug, linker::{Linker, DSO}, tcb::Tcb, }; @@ -130,6 +131,11 @@ pub extern "C" fn relibc_ld_so_start(sp: &'static mut Stack, ld_entry: usize) -> true }; + // we might need global lock for this kind of stuff + unsafe { + _r_debug.r_ldbase = ld_entry; + } + // Some variables that will be overridden by environment and auxiliary vectors let library_path = match envs.get("LD_LIBRARY_PATH") { Some(lib_path) => lib_path, diff --git a/src/platform/test/mod.rs b/src/platform/test/mod.rs index 3ac5a17166659584384f2661af8051a3849e8dcc..01889bb3588b6f58ea662a474529eef9429a8e68 100644 --- a/src/platform/test/mod.rs +++ b/src/platform/test/mod.rs @@ -8,10 +8,7 @@ mod epoll; #[test] fn access() { - use crate::header::{ - errno, - unistd, - }; + use crate::header::{errno, unistd}; //TODO: create test files assert_eq!(Sys::access(c_str!("not a file!"), unistd::F_OK), !0); @@ -44,9 +41,7 @@ fn chdir() { #[test] fn clock_gettime() { - use crate::header::{ - time - }; + use crate::header::time; { let mut timespec = time::timespec {