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