xref: /drstd/dlibc/src/unix/ld_so/dso.rs (revision 0fe3ff0054d3aec7fbf9bddecfecb10bc7d23a51)
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};
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