From 1182d1200603c9f33ec9968a1a00df5ebd3aa7d8 Mon Sep 17 00:00:00 2001 From: oddcoder Date: Sun, 3 May 2020 09:48:55 +0200 Subject: [PATCH 1/3] Fill gaps in binary memory image Some ELFs can have gaps between the segments, this results in problems when mprotecting or when converting (addr + size) into rust slice. Motivated by this This patch will fill these gaps with mmaped addresses. In theory no real memory should be allocated because mmap is lazy allocator. --- src/ld_so/linker.rs | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/src/ld_so/linker.rs b/src/ld_so/linker.rs index fbd7dd0abd..118122f6bc 100644 --- a/src/ld_so/linker.rs +++ b/src/ld_so/linker.rs @@ -383,7 +383,6 @@ impl Linker { } // Allocate memory let mmap = unsafe { - let size = bounds.1 /* - bounds.0 */; let same_elf = if let Some(prog) = dso.as_ref() { if prog.name == *elf_name { true @@ -395,6 +394,36 @@ impl Linker { }; if same_elf { let addr = dso.as_ref().unwrap().base_addr; + let mut size = bounds.1; + // Fill the gaps i the binary + let mut ranges = Vec::new(); + for ph in elf.program_headers.iter() { + if ph.p_type == program_header::PT_LOAD { + let voff = ph.p_vaddr as usize % PAGE_SIZE; + let vaddr = ph.p_vaddr as usize - voff; + let vsize = ((ph.p_memsz as usize + voff + PAGE_SIZE - 1) / PAGE_SIZE) + * PAGE_SIZE; + ranges.push((vaddr, vsize)); + } + } + ranges.sort(); + let mut start = addr; + for (vaddr, vsize) in ranges.iter() { + if start < addr + vaddr { + let gap_size = addr + vaddr - start; + size += gap_size; + sys_mman::mmap( + start as *mut c_void, + addr + vaddr - start, + //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, + ); + } + start = addr + vaddr + vsize + } sys_mman::mprotect( addr as *mut c_void, size, @@ -403,6 +432,7 @@ impl Linker { _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 size = bounds.1; let ptr = sys_mman::mmap( ptr::null_mut(), size, From 67be05d3a350a9a4d4db0c07366cad9b93d66610 Mon Sep 17 00:00:00 2001 From: oddcoder Date: Sun, 3 May 2020 12:33:55 +0200 Subject: [PATCH 2/3] Use segments instead of sections for calculating which offset to write, I noticed that the implementation is noth precise although it worked. and instead of using the sections to identify memory addresses of DT_DEBUG. I used segements --- src/ld_so/linker.rs | 49 ++++++++++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/src/ld_so/linker.rs b/src/ld_so/linker.rs index 118122f6bc..8fc7bc876b 100644 --- a/src/ld_so/linker.rs +++ b/src/ld_so/linker.rs @@ -689,29 +689,36 @@ 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; + // overwrite DT_DEBUG if exist in DYNAMIC segment + // first we identify the location of DYNAMIC segment + let mut dyn_start = None; + let mut debug_start = None; + for ph in elf.program_headers.iter() { + if ph.p_type == program_header::PT_DYNAMIC { + dyn_start = Some(ph.p_vaddr as usize); } - 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::() / 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::() + size_of::() / 2; - mmap[start..start + size_of::() / 2].clone_from_slice(&bytes); - } - i += 1; + } + // next we identify the location of DT_DEBUG in .dynamic section + if let Some(dynamic) = elf.dynamic.as_ref() { + let mut i = 0; + for entry in &dynamic.dyns { + if entry.d_tag == DT_DEBUG { + debug_start = Some(i as usize); + break; } + i += 1; + } + } + if let Some(dyn_start_addr) = dyn_start { + if let Some(i) = debug_start { + let mmap = match self.mmaps.get_mut(*elf_name) { + Some(some) => some, + None => continue, + }; + let bytes: [u8; size_of::() / 2] = + unsafe { transmute((&_r_debug) as *const RTLDDebug as usize) }; + let start = dyn_start_addr + i * size_of::() + size_of::() / 2; + mmap[start..start + size_of::() / 2].clone_from_slice(&bytes); } } From 850dfd971b54d1864331478e56b202d691fa83b2 Mon Sep 17 00:00:00 2001 From: oddcoder Date: Sun, 3 May 2020 12:35:01 +0200 Subject: [PATCH 3/3] Make "./a.out" entry the first in rmap list This fixes a regression in gdb where sometimes it decides to ignore the first entry in the list. --- src/ld_so/debug.rs | 15 ++++++++++++++- src/ld_so/linker.rs | 2 +- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/ld_so/debug.rs b/src/ld_so/debug.rs index 8780cc1f26..d6838e08a7 100644 --- a/src/ld_so/debug.rs +++ b/src/ld_so/debug.rs @@ -52,6 +52,16 @@ impl RTLDDebug { } return; } + pub fn insert_first(&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 { + let tmp = self.r_map; + self.r_map = LinkMap::new_with_args(l_addr, name, l_ld); + unsafe { (*self.r_map).link(&mut *tmp) }; + } + return; + } } #[repr(C)] @@ -80,7 +90,10 @@ impl LinkMap { }); Box::into_raw(map) } - + fn link(&mut self, map: &mut LinkMap) { + map.l_prev = self as *mut LinkMap; + self.l_next = map as *mut LinkMap; + } fn new_with_args(l_addr: usize, name: &str, l_ld: usize) -> *mut Self { let map = LinkMap::new(); unsafe { diff --git a/src/ld_so/linker.rs b/src/ld_so/linker.rs index 8fc7bc876b..1b47d5c38e 100644 --- a/src/ld_so/linker.rs +++ b/src/ld_so/linker.rs @@ -429,7 +429,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); + _r_debug.insert_first(addr as usize, &elf_name, addr + l_ld as usize); slice::from_raw_parts_mut(addr as *mut u8, size) } else { let size = bounds.1;