1 use super::{ 2 debug::{RTLDDebug, _r_debug}, 3 linker::Symbol, 4 tcb::{round_up, Master}, 5 }; 6 use crate::{ 7 header::{errno::STR_ERROR, sys_mman}, 8 platform::{errno, types::c_void}, 9 }; 10 use alloc::{ 11 collections::BTreeMap, 12 string::{String, ToString}, 13 vec::Vec, 14 }; 15 use core::{ 16 mem::{size_of, transmute}, 17 ptr, slice, 18 }; 19 #[cfg(target_pointer_width = "32")] 20 use goblin::elf32::{ 21 header::ET_DYN, 22 program_header, 23 r#dyn::{Dyn, DT_DEBUG, DT_RUNPATH}, 24 section_header::{SHN_UNDEF, SHT_FINI_ARRAY, SHT_INIT_ARRAY}, 25 sym, 26 }; 27 #[cfg(target_pointer_width = "64")] 28 use goblin::elf64::{ 29 header::ET_DYN, 30 program_header, 31 r#dyn::{Dyn, DT_DEBUG, DT_RUNPATH}, 32 section_header::{SHN_UNDEF, SHT_FINI_ARRAY, SHT_INIT_ARRAY}, 33 sym, 34 }; 35 use goblin::{ 36 elf::Elf, 37 error::{Error, Result}, 38 }; 39 40 /// Use to represent a library as well as all the symbols that is loaded withen it. 41 #[derive(Default)] 42 pub struct DSO { 43 pub name: String, 44 pub id: usize, 45 pub dlopened: bool, 46 pub entry_point: usize, 47 pub runpath: Option<String>, 48 /// Loaded library in-memory data 49 pub mmap: &'static mut [u8], 50 pub global_syms: BTreeMap<String, Symbol>, 51 pub weak_syms: BTreeMap<String, Symbol>, 52 pub dependencies: Vec<String>, 53 /// .init_array addr and len 54 pub init_array: (usize, usize), 55 /// .fini_array addr and len 56 pub fini_array: (usize, usize), 57 pub tls_module_id: usize, 58 pub tls_offset: usize, 59 pub use_count: usize, 60 } 61 62 impl DSO { 63 pub fn new( 64 path: &str, 65 data: &Vec<u8>, 66 base_addr: Option<usize>, 67 dlopened: bool, 68 id: usize, 69 tls_module_id: usize, 70 tls_offset: usize, 71 ) -> Result<(DSO, Option<Master>)> { 72 let elf = Elf::parse(data)?; 73 let (mmap, tcb_master) = DSO::mmap_and_copy(&path, &elf, &data, base_addr, tls_offset)?; 74 let (global_syms, weak_syms) = DSO::collect_syms(&elf, &mmap)?; 75 let (init_array, fini_array) = DSO::init_fini_arrays(&elf, mmap.as_ptr() as usize); 76 77 let name = match elf.soname { 78 Some(soname) => soname.to_string(), 79 _ => basename(&path), 80 }; 81 let tls_offset = match tcb_master { 82 Some(ref master) => master.offset, 83 _ => 0, 84 }; 85 let entry_point = if is_pie_enabled(&elf) { 86 mmap.as_ptr() as usize + elf.header.e_entry as usize 87 } else { 88 elf.header.e_entry as usize 89 }; 90 let dso = DSO { 91 name: name, 92 id: id, 93 use_count: 1, 94 dlopened: dlopened, 95 entry_point: entry_point, 96 runpath: DSO::get_runpath(&path, &elf)?, 97 mmap: mmap, 98 global_syms: global_syms, 99 weak_syms: weak_syms, 100 dependencies: elf.libraries.iter().map(|s| s.to_string()).collect(), 101 init_array: init_array, 102 fini_array: fini_array, 103 tls_module_id: tls_module_id, 104 tls_offset: tls_offset, 105 }; 106 return Ok((dso, tcb_master)); 107 } 108 109 pub fn get_sym(&self, name: &str) -> Option<(Symbol, bool)> { 110 if let Some(value) = self.global_syms.get(name) { 111 Some((*value, true)) 112 } else if let Some(value) = self.weak_syms.get(name) { 113 Some((*value, false)) 114 } else { 115 None 116 } 117 } 118 119 pub fn run_init(&self) { 120 unsafe { 121 let (addr, size) = self.init_array; 122 for i in (0..size).step_by(8) { 123 let func = transmute::<usize, *const Option<extern "C" fn()>>(addr + i); 124 (*func).map(|x| x()); 125 } 126 } 127 } 128 129 pub fn run_fini(&self) { 130 unsafe { 131 let (addr, size) = self.fini_array; 132 for i in (0..size).step_by(8).rev() { 133 let func = transmute::<usize, *const Option<extern "C" fn()>>(addr + i); 134 (*func).map(|x| x()); 135 } 136 } 137 } 138 139 fn get_runpath(path: &str, elf: &Elf) -> Result<Option<String>> { 140 if let Some(dynamic) = &elf.dynamic { 141 let entry = dynamic.dyns.iter().find(|d| d.d_tag == DT_RUNPATH); 142 match entry { 143 Some(entry) => { 144 let runpath = elf 145 .dynstrtab 146 .get(entry.d_val as usize) 147 .ok_or(Error::Malformed("Missing RUNPATH in dynstrtab".to_string()))??; 148 let base = dirname(path); 149 return Ok(Some(runpath.replace("$ORIGIN", &base))); 150 } 151 _ => return Ok(None), 152 } 153 } 154 return Ok(None); 155 } 156 157 fn mmap_and_copy( 158 path: &str, 159 elf: &Elf, 160 data: &Vec<u8>, 161 base_addr: Option<usize>, 162 tls_offset: usize, 163 ) -> Result<(&'static mut [u8], Option<Master>)> { 164 trace!("# {}", path); 165 // data for struct LinkMap 166 let mut l_ld = 0; 167 // Calculate virtual memory bounds 168 let bounds = { 169 let mut bounds_opt: Option<(usize, usize)> = None; 170 for ph in elf.program_headers.iter() { 171 let voff = ph.p_vaddr % ph.p_align; 172 let vaddr = (ph.p_vaddr - voff) as usize; 173 let vsize = round_up((ph.p_memsz + voff) as usize, ph.p_align as usize); 174 175 match ph.p_type { 176 program_header::PT_DYNAMIC => { 177 l_ld = ph.p_vaddr; 178 } 179 program_header::PT_LOAD => { 180 trace!(" load {:#x}, {:#x}: {:x?}", vaddr, vsize, ph); 181 if let Some(ref mut bounds) = bounds_opt { 182 if vaddr < bounds.0 { 183 bounds.0 = vaddr; 184 } 185 if vaddr + vsize > bounds.1 { 186 bounds.1 = vaddr + vsize; 187 } 188 } else { 189 bounds_opt = Some((vaddr, vaddr + vsize)); 190 } 191 } 192 _ => (), 193 } 194 } 195 bounds_opt.ok_or(Error::Malformed( 196 "Unable to find PT_LOAD section".to_string(), 197 ))? 198 }; 199 trace!(" bounds {:#x}, {:#x}", bounds.0, bounds.1); 200 // Allocate memory 201 let mmap = unsafe { 202 if let Some(addr) = base_addr { 203 let size = if is_pie_enabled(&elf) { 204 bounds.1 205 } else { 206 bounds.1 - bounds.0 207 }; 208 _r_debug.insert_first(addr as usize, path, addr + l_ld as usize); 209 slice::from_raw_parts_mut(addr as *mut u8, size) 210 } else { 211 let (start, end) = bounds; 212 let size = end - start; 213 let mut flags = sys_mman::MAP_ANONYMOUS | sys_mman::MAP_PRIVATE; 214 if start != 0 { 215 flags |= sys_mman::MAP_FIXED_NOREPLACE; 216 } 217 trace!(" mmap({:#x}, {:x}, {:x})", start, size, flags); 218 let ptr = sys_mman::mmap( 219 start as *mut c_void, 220 size, 221 //TODO: Make it possible to not specify PROT_EXEC on Redox 222 sys_mman::PROT_READ | sys_mman::PROT_WRITE, 223 flags, 224 -1, 225 0, 226 ); 227 if ptr as usize == !0 228 /* MAP_FAILED */ 229 { 230 return Err(Error::Malformed(format!( 231 "failed to map {}. errno: {}", 232 path, STR_ERROR[errno as usize] 233 ))); 234 } 235 if start as *mut c_void != ptr::null_mut() { 236 assert_eq!( 237 ptr, start as *mut c_void, 238 "mmap must always map on the destination we requested" 239 ); 240 } 241 trace!(" = {:p}", ptr); 242 ptr::write_bytes(ptr as *mut u8, 0, size); 243 _r_debug.insert(ptr as usize, path, ptr as usize + l_ld as usize); 244 slice::from_raw_parts_mut(ptr as *mut u8, size) 245 } 246 }; 247 248 let skip_load_segment_copy = base_addr.is_some(); 249 let mut tcb_master = None; 250 251 // Copy data 252 for ph in elf.program_headers.iter() { 253 let voff = ph.p_vaddr % ph.p_align; 254 let vaddr = (ph.p_vaddr - voff) as usize; 255 let vsize = round_up((ph.p_memsz + voff) as usize, ph.p_align as usize); 256 257 match ph.p_type { 258 program_header::PT_LOAD => { 259 if skip_load_segment_copy { 260 continue; 261 } 262 let obj_data = { 263 let range = ph.file_range(); 264 match data.get(range.clone()) { 265 Some(some) => some, 266 None => { 267 return Err(Error::Malformed(format!( 268 "failed to read {:x?}", 269 range 270 ))) 271 } 272 } 273 }; 274 275 let mmap_data = { 276 let range = if is_pie_enabled(elf) { 277 let addr = ph.p_vaddr as usize; 278 addr..addr + obj_data.len() 279 } else { 280 let addr = ph.p_vaddr as usize - mmap.as_ptr() as usize; 281 addr..addr + obj_data.len() 282 }; 283 match mmap.get_mut(range.clone()) { 284 Some(some) => some, 285 None => { 286 return Err(Error::Malformed(format!( 287 "failed to write {:x?}", 288 range 289 ))); 290 } 291 } 292 }; 293 trace!( 294 " copy {:#x}, {:#x}: {:#x}, {:#x}", 295 vaddr, 296 vsize, 297 voff, 298 obj_data.len() 299 ); 300 mmap_data.copy_from_slice(obj_data); 301 } 302 program_header::PT_TLS => { 303 let ptr = unsafe { 304 if is_pie_enabled(elf) { 305 mmap.as_ptr().add(ph.p_vaddr as usize) 306 } else { 307 ph.p_vaddr as *const u8 308 } 309 }; 310 tcb_master = Some(Master { 311 ptr: ptr, 312 len: ph.p_filesz as usize, 313 offset: tls_offset + vsize, 314 }); 315 trace!(" tcb master {:x?}", tcb_master); 316 } 317 program_header::PT_DYNAMIC => { 318 // overwrite DT_DEBUG if exist in DYNAMIC segment 319 // first we identify the location of DYNAMIC segment 320 let dyn_start = ph.p_vaddr as usize; 321 let mut debug_start = None; 322 // next we identify the location of DT_DEBUG in .dynamic section 323 if let Some(dynamic) = elf.dynamic.as_ref() { 324 let mut i = 0; 325 for entry in &dynamic.dyns { 326 if entry.d_tag == DT_DEBUG { 327 debug_start = Some(i as usize); 328 break; 329 } 330 i += 1; 331 } 332 } 333 if let Some(i) = debug_start { 334 let bytes: [u8; size_of::<Dyn>() / 2] = 335 unsafe { transmute((&_r_debug) as *const RTLDDebug as usize) }; 336 let start = if is_pie_enabled(elf) { 337 dyn_start + i * size_of::<Dyn>() + size_of::<Dyn>() / 2 338 } else { 339 dyn_start + i * size_of::<Dyn>() + size_of::<Dyn>() / 2 340 - mmap.as_mut_ptr() as usize 341 }; 342 mmap[start..start + size_of::<Dyn>() / 2].clone_from_slice(&bytes); 343 } 344 } 345 _ => (), 346 } 347 } 348 return Ok((mmap, tcb_master)); 349 } 350 351 fn collect_syms( 352 elf: &Elf, 353 mmap: &[u8], 354 ) -> Result<(BTreeMap<String, Symbol>, BTreeMap<String, Symbol>)> { 355 let mut globals = BTreeMap::new(); 356 let mut weak_syms = BTreeMap::new(); 357 for sym in elf.dynsyms.iter() { 358 let bind = sym.st_bind(); 359 if sym.st_shndx == SHN_UNDEF as usize 360 || ![sym::STB_GLOBAL, sym::STB_WEAK].contains(&bind) 361 { 362 continue; 363 } 364 let name: String; 365 let value: Symbol; 366 if let Some(name_res) = elf.dynstrtab.get(sym.st_name) { 367 name = name_res?.to_string(); 368 value = if is_pie_enabled(elf) { 369 Symbol { 370 base: mmap.as_ptr() as usize, 371 value: sym.st_value as usize, 372 size: sym.st_size as usize, 373 sym_type: sym::st_type(sym.st_info), 374 } 375 } else { 376 Symbol { 377 base: 0, 378 value: sym.st_value as usize, 379 size: sym.st_size as usize, 380 sym_type: sym::st_type(sym.st_info), 381 } 382 }; 383 } else { 384 continue; 385 } 386 match sym.st_bind() { 387 sym::STB_GLOBAL => { 388 trace!(" global {}: {:x?} = {:p}", &name, sym, value.as_ptr()); 389 globals.insert(name, value); 390 } 391 sym::STB_WEAK => { 392 trace!(" weak {}: {:x?} = {:p}", &name, sym, value.as_ptr()); 393 weak_syms.insert(name, value); 394 } 395 _ => unreachable!(), 396 } 397 } 398 return Ok((globals, weak_syms)); 399 } 400 401 fn init_fini_arrays(elf: &Elf, mmap_addr: usize) -> ((usize, usize), (usize, usize)) { 402 let mut init_array: (usize, usize) = (0, 0); 403 let mut fini_array: (usize, usize) = (0, 0); 404 for section in elf 405 .section_headers 406 .iter() 407 .filter(|s| s.sh_type == SHT_INIT_ARRAY || s.sh_type == SHT_FINI_ARRAY) 408 { 409 let addr = if is_pie_enabled(&elf) { 410 mmap_addr + section.vm_range().start 411 } else { 412 section.vm_range().start 413 }; 414 if section.sh_type == SHT_INIT_ARRAY { 415 init_array = (addr, section.sh_size as usize); 416 } else { 417 fini_array = (addr, section.sh_size as usize); 418 } 419 } 420 return (init_array, fini_array); 421 } 422 } 423 424 impl Drop for DSO { 425 fn drop(&mut self) { 426 self.run_fini(); 427 unsafe { sys_mman::munmap(self.mmap.as_mut_ptr() as *mut c_void, self.mmap.len()) }; 428 } 429 } 430 431 pub fn is_pie_enabled(elf: &Elf) -> bool { 432 return elf.header.e_type == ET_DYN; 433 } 434 435 fn basename(path: &str) -> String { 436 return path.split("/").last().unwrap_or(path).to_string(); 437 } 438 439 fn dirname(path: &str) -> String { 440 let mut parts: Vec<&str> = path.split("/").collect(); 441 parts.truncate(parts.len() - 1); 442 return parts.join("/"); 443 } 444