1use alloc::{
2 collections::BTreeMap,
3 rc::Rc,
4 string::{String, ToString},
5 sync::{Arc, Weak},
6 vec::Vec,
7};
8use object::elf;
9#[cfg(not(target_arch = "x86"))]
10use object::{
11 NativeEndian,
12 read::elf::{Rela as _, Sym},
13};
14
15use core::{
16 cell::RefCell,
17 ptr::{self, NonNull},
18};
19
20use crate::{
21 ALLOCATOR,
22 c_str::{CStr, CString},
23 error::Errno,
24 header::{
25 dl_tls::{__tls_get_addr, dl_tls_index},
26 fcntl, sys_mman,
27 unistd::F_OK,
28 },
29 ld_so::dso::SymbolBinding,
30 out::Out,
31 platform::{
32 Pal, Sys,
33 types::{c_int, c_void},
34 },
35 sync::rwlock::RwLock,
36};
37
38#[cfg(not(target_arch = "x86"))]
39use crate::{ld_so::dso::resolve_sym, platform::types::c_uint};
40
41#[cfg(not(target_arch = "x86"))]
42use super::dso::Rela;
43use super::{
44 PATH_SEP,
45 access::accessible,
46 callbacks::LinkerCallbacks,
47 debug::{_dl_debug_state, _r_debug, RTLDState},
48 dso::{DSO, ProgramHeader},
49 tcb::{Master, Tcb},
50};
51
52#[derive(Debug, Copy, Clone)]
53pub enum DlError {
54 NotFound,
56 Malformed,
58 InvalidHandle,
60 Oom,
62}
63
64impl DlError {
65 pub const fn repr(&self) -> &'static core::ffi::CStr {
67 match self {
68 DlError::NotFound => {
69 c"Failed to locate the requested DSO. Set `LD_DEBUG=all` for more information."
70 }
71
72 DlError::Malformed => {
73 c"The DSO is malformed somehow. Set `LD_DEBUG=all` for more information."
74 }
75
76 DlError::InvalidHandle => {
77 c"Invalid DSO handle. Set `LD_DEBUG=all` for more information."
78 }
79
80 DlError::Oom => c"Out of memory.",
81 }
82 }
83}
84
85pub type Result<T> = core::result::Result<T, DlError>;
86
87pub(super) static GLOBAL_SCOPE: RwLock<Scope> = RwLock::new(Scope::global());
88
89struct MmapFile {
90 fd: i32,
91 ptr: *mut c_void,
92 size: usize,
93}
94
95impl MmapFile {
96 fn open(path: CStr, oflag: c_int) -> core::result::Result<Self, Errno> {
97 let fd = Sys::open(path, oflag, 0 )?;
98 let mut stat = crate::header::sys_stat::stat::default();
99 Sys::fstat(fd, Out::from_mut(&mut stat))?;
100
101 Self::from_fd(fd, stat.st_size as usize)
102 }
103
104 fn from_fd(fd: i32, size: usize) -> core::result::Result<Self, Errno> {
105 let ptr = unsafe {
106 Sys::mmap(
107 ptr::null_mut(),
108 size,
109 sys_mman::PROT_READ,
110 sys_mman::MAP_PRIVATE,
111 fd,
112 0,
113 )
114 }?;
115
116 Ok(Self { fd, ptr, size })
117 }
118
119 fn anonymous(size: usize) -> core::result::Result<Self, Errno> {
120 let ptr = unsafe {
121 Sys::mmap(
122 ptr::null_mut(),
123 size,
124 sys_mman::PROT_READ | sys_mman::PROT_WRITE,
125 sys_mman::MAP_PRIVATE | sys_mman::MAP_ANONYMOUS,
126 -1,
127 0,
128 )
129 }?;
130
131 Ok(Self { fd: -1, ptr, size })
132 }
133
134 fn data(&self) -> &[u8] {
135 unsafe { core::slice::from_raw_parts(self.ptr.cast::<u8>(), self.size) }
136 }
137
138 fn as_mut_slice(&self) -> &mut [u8] {
139 unsafe { core::slice::from_raw_parts_mut(self.ptr.cast::<u8>(), self.size) }
140 }
141}
142
143impl Drop for MmapFile {
144 fn drop(&mut self) {
145 unsafe {
146 Sys::munmap(self.ptr, self.size).unwrap();
147 if self.fd != -1 {
148 Sys::close(self.fd).unwrap();
149 }
150 }
151 }
152}
153
154#[derive(Clone, Debug)]
155pub struct Symbol<'a> {
156 pub name: &'a str,
157 pub value: usize,
158 pub base: usize,
159 pub size: usize,
160 pub sym_type: u8,
161}
162
163impl Symbol<'_> {
164 pub fn as_ptr(&self) -> *mut c_void {
165 (self.base + self.value) as *mut c_void
166 }
167}
168
169#[derive(Debug, Default, Copy, Clone, PartialEq)]
170pub enum Resolve {
171 #[cfg_attr(not(target_arch = "x86_64"), default)]
173 Now,
174 #[cfg_attr(target_arch = "x86_64", default)]
177 Lazy,
178}
179
180#[derive(Debug, Copy, Clone, PartialEq)]
181pub enum ScopeKind {
182 Global,
183 Local,
184}
185
186pub enum Scope {
187 Global { objs: Vec<Weak<DSO>> },
191 Local {
192 owner: Option<Weak<DSO>>,
193 objs: Vec<Arc<DSO>>,
194 },
195}
196
197impl Scope {
198 #[inline]
199 const fn global() -> Self {
200 Self::Global { objs: Vec::new() }
201 }
202
203 #[inline]
204 const fn local() -> Self {
205 Self::Local {
206 owner: None,
207 objs: Vec::new(),
208 }
209 }
210
211 fn set_owner(&mut self, obj: Weak<DSO>) {
212 match self {
213 Self::Global { .. } => panic!("attempted to set global scope owner"),
214 Self::Local { owner, .. } => {
215 assert!(owner.is_none(), "attempted to change local scope owner");
216 *owner = Some(obj);
217 }
218 }
219 }
220
221 fn add(&mut self, target: &Arc<DSO>) {
222 match self {
223 Self::Global { objs } => {
224 let target = Arc::downgrade(target);
225 for obj in objs.iter() {
226 if Weak::ptr_eq(obj, &target) {
227 return;
228 }
229 }
230
231 objs.push(target);
232 }
233
234 Self::Local { objs, .. } => {
235 for obj in objs.iter() {
236 if Arc::ptr_eq(obj, target) {
237 return;
238 }
239 }
240
241 objs.push(target.clone());
242 }
243 }
244 }
245
246 pub(super) fn get_sym<'a>(
247 &self,
248 name: &'a str,
249 ) -> Option<(Symbol<'a>, SymbolBinding, Arc<DSO>)> {
250 self._get_sym(name, 0)
251 }
252
253 pub(super) fn _get_sym<'a>(
254 &self,
255 name: &'a str,
256 skip: usize,
257 ) -> Option<(Symbol<'a>, SymbolBinding, Arc<DSO>)> {
258 let mut res = None;
259
260 let get_sym = |obj: Arc<DSO>| {
261 if let Some((sym, binding)) = obj.get_sym(name) {
262 if binding.is_global() {
263 return Some((sym, binding, obj.clone()));
264 }
265
266 res = Some((sym, binding, obj.clone()));
267 }
268
269 None
270 };
271
272 match self {
273 Self::Global { objs } => objs
274 .iter()
275 .skip(skip)
276 .map(|o| o.upgrade().unwrap())
277 .find_map(get_sym),
278 Self::Local { owner, objs } => {
279 let owner = owner
280 .as_ref()
281 .expect("local scope without owner")
282 .upgrade()
283 .expect("local scope owner was dropped");
284
285 core::iter::once(owner)
286 .chain(objs.iter().cloned())
287 .skip(skip)
288 .find_map(get_sym)
289 }
290 }
291 .or(res)
292 }
293
294 fn copy_into(&self, other: &mut Self) {
295 match (self, other) {
296 (Self::Local { owner, objs }, Self::Global { objs: other_objs }) => {
297 let owner = owner.as_ref().expect("local scope without owner");
299 other_objs.push(owner.clone());
300 other_objs.extend(objs.iter().map(Arc::downgrade));
301 }
302
303 _ => unreachable!(),
304 }
305 }
306
307 fn debug(&self) {
308 match self {
309 Self::Global { objs } => {
310 println!(
311 "[@global] {:?}",
312 objs.iter()
313 .map(|x| x.upgrade().unwrap().name.clone())
314 .collect::<Vec<_>>()
315 );
316 }
317
318 Self::Local { owner, objs } => {
319 let owner = owner.as_ref().unwrap().upgrade().unwrap();
320 println!(
321 "[{}] {:?}",
322 owner.name,
323 objs.iter().map(|x| x.name.clone()).collect::<Vec<_>>()
324 )
325 }
326 }
327 }
328}
329
330pub struct ObjectHandle(*const DSO);
335
336impl ObjectHandle {
337 #[inline]
338 fn new(obj: Arc<DSO>) -> Self {
339 Self(Arc::into_raw(obj))
340 }
341
342 #[inline]
343 fn into_inner(self) -> Arc<DSO> {
344 unsafe { Arc::from_raw(self.0) }
345 }
346
347 #[inline]
348 pub fn as_ptr(&self) -> *const c_void {
349 self.0.cast()
350 }
351
352 #[inline]
353 pub fn from_ptr(ptr: *const c_void) -> Option<Self> {
354 NonNull::new(ptr as *mut DSO).map(|ptr| Self(ptr.as_ptr()))
355 }
356}
357
358impl AsRef<DSO> for ObjectHandle {
359 #[inline]
360 fn as_ref(&self) -> &DSO {
361 unsafe { &*self.0 }
362 }
363}
364
365bitflags::bitflags! {
366 #[derive(Debug, Default)]
367 pub struct DebugFlags: u32 {
368 const LOAD = 1 << 1;
370 const SEARCH = 1 << 2;
372 const SCOPES = 1 << 3;
374 }
375}
376
377#[derive(Default)]
378pub struct Config {
379 pub debug_flags: DebugFlags,
380 library_path: Option<String>,
381 bind_now: bool,
383}
384
385impl Config {
386 pub fn from_env(env: &BTreeMap<String, String>) -> Self {
387 let debug_flags = env
388 .get("LD_DEBUG")
389 .map(|value| {
390 let mut flags = DebugFlags::empty();
391 for opt in value.split(',') {
392 flags |= match opt {
393 "load" => DebugFlags::LOAD,
394 "search" => DebugFlags::SEARCH,
395 "scopes" => DebugFlags::SCOPES,
396 "all" => DebugFlags::all(),
397 _ => {
398 eprintln!("[ld.so]: unknown debug flag '{}'", opt);
399 DebugFlags::empty()
400 }
401 };
402 }
403
404 flags
405 })
406 .unwrap_or(DebugFlags::empty());
407
408 Self {
409 debug_flags,
410 library_path: env.get("LD_LIBRARY_PATH").cloned(),
411 bind_now: env
412 .get("LD_BIND_NOW")
413 .map(|value| !value.is_empty())
414 .unwrap_or_default(),
415 }
416 }
417}
418
419pub struct Linker {
420 config: Config,
421
422 next_object_id: usize,
423 next_tls_module_id: usize,
424 tls_size: usize,
425 objects: BTreeMap<usize, Arc<DSO>>,
426 name_to_object_id_map: BTreeMap<String, usize>,
427 pub cbs: Rc<RefCell<LinkerCallbacks>>,
428}
429
430const ROOT_ID: usize = 1;
431
432impl Linker {
433 pub fn new(config: Config) -> Self {
434 Self {
435 config,
436 next_object_id: ROOT_ID,
437 next_tls_module_id: 1,
438 tls_size: 0,
439 objects: BTreeMap::new(),
440 name_to_object_id_map: BTreeMap::new(),
441 cbs: Rc::new(RefCell::new(LinkerCallbacks::new())),
442 }
443 }
444
445 pub fn load_program(&mut self, path: &str, base_addr: Option<usize>) -> Result<usize> {
446 let dso = self.load_object(
447 path,
448 &None,
449 base_addr,
450 false,
451 if self.config.bind_now {
452 Resolve::Now
453 } else {
454 Resolve::default()
455 },
456 ScopeKind::Global,
457 )?;
458 Ok(dso.entry_point)
459 }
460
461 pub fn load_library(
462 &mut self,
463 name: Option<&str>,
464 resolve: Resolve,
465 scope: ScopeKind,
466 noload: bool,
467 ) -> Result<ObjectHandle> {
468 log::trace!(
469 "[ld.so] load_library(name={:?}, resolve={:#?}, scope={:#?}, noload={})",
470 name,
471 resolve,
472 scope,
473 noload
474 );
475
476 if noload && resolve == Resolve::Now {
477 todo!("resolve symbols now!");
481 }
482
483 match name {
484 Some(name) => {
485 if let Some(id) = self.name_to_object_id_map.get(name) {
486 let obj = self.objects.get(id).unwrap();
487
488 if scope == ScopeKind::Global {
491 if self.config.debug_flags.contains(DebugFlags::SCOPES) {
492 eprintln!("[ld.so]: moving {} into the global scope", obj.name);
493 }
494
495 {
496 let mut global_scope = GLOBAL_SCOPE.write();
497 obj.scope().copy_into(&mut global_scope);
498 }
499 self.scope_debug();
500 }
501
502 Ok(ObjectHandle::new(obj.clone()))
503 } else if !noload {
504 let parent_runpath = &self
505 .objects
506 .get(&ROOT_ID)
507 .and_then(|parent| parent.runpath().cloned());
508
509 Ok(ObjectHandle::new(self.load_object(
510 name,
511 parent_runpath,
512 None,
513 true,
514 if self.config.bind_now {
515 Resolve::Now
516 } else {
517 resolve
518 },
519 scope,
520 )?))
521 } else {
522 Ok(ObjectHandle(ptr::null()))
528 }
529 }
530
531 None => match self.objects.get(&ROOT_ID) {
532 Some(obj) => Ok(ObjectHandle::new(obj.clone())),
533 None => Err(DlError::NotFound),
534 },
535 }
536 }
537
538 pub fn get_sym(&self, handle: Option<ObjectHandle>, name: &str) -> Option<*mut c_void> {
539 let guard;
540
541 if let Some(handle) = handle.as_ref() {
542 handle.as_ref().scope()
543 } else {
544 guard = GLOBAL_SCOPE.read();
545 &guard
546 }
547 .get_sym(name)
548 .map(|(symbol, _, obj)| {
549 if symbol.sym_type != elf::STT_TLS {
550 symbol.as_ptr()
551 } else {
552 let mut tls_index = dl_tls_index {
553 ti_module: obj.tls_module_id,
554 ti_offset: symbol.value,
555 };
556
557 unsafe { __tls_get_addr(&raw mut tls_index) }
558 }
559 })
560 }
561
562 pub fn unload(&mut self, handle: ObjectHandle) {
563 let obj = handle.into_inner();
564 if !obj.dlopened {
565 return;
566 }
567
568 log::trace!(
569 "[ld.so] unloading {} (sc={}, wc={})",
570 obj.name,
571 Arc::strong_count(&obj),
572 Arc::weak_count(&obj)
573 );
574
575 if Arc::strong_count(&obj) == 2 {
578 match *GLOBAL_SCOPE.write() {
580 Scope::Global { ref mut objs } => {
581 objs.retain(|o| !Weak::ptr_eq(o, &Arc::downgrade(&obj)));
582 }
583
584 _ => unreachable!(),
585 }
586
587 let _ = self.objects.remove(&obj.id).unwrap();
588 for dep in obj.dependencies() {
589 if let Some(name) = self.name_to_object_id_map.get(*dep)
590 && let Some(object_name) = self.objects.get(name)
591 {
592 self.unload(ObjectHandle::new(object_name.clone()));
593 }
594 }
595 self.name_to_object_id_map.remove(&obj.name);
596 assert!(Arc::strong_count(&obj) == 1);
597 drop(obj);
598 }
599
600 }
602
603 pub fn fini(&self) {
604 for obj in self.objects.values() {
605 obj.run_fini();
606 }
607 }
608
609 fn load_object(
610 &mut self,
611 path: &str,
612 runpath: &Option<String>,
613 base_addr: Option<usize>,
614 dlopened: bool,
615 resolve: Resolve,
616 scope: ScopeKind,
617 ) -> Result<Arc<DSO>> {
618 let resolve = if cfg!(target_arch = "x86_64") {
619 resolve
620 } else {
621 Resolve::Now
623 };
624
625 _r_debug.lock().state = RTLDState::RtAdd;
626 _dl_debug_state();
627
628 let mut new_objects = Vec::new();
629 let mut objects_data = Vec::new();
630 let mut tcb_masters = Vec::new();
631 let loaded_dso = self.load_objects_recursive(
632 path,
633 runpath,
634 base_addr,
635 dlopened,
636 &mut new_objects,
637 &mut objects_data,
638 &mut tcb_masters,
639 None,
640 scope,
641 )?;
642
643 for (i, obj) in new_objects.iter().enumerate() {
644 obj.relocate(&objects_data[i], resolve).unwrap();
645 }
646
647 unsafe {
648 if !dlopened {
649 #[cfg(target_os = "redox")]
650 let (tcb, old_tcb, thr_fd) = {
651 use redox_rt::signal::tmp_disable_signals;
652
653 let old_tcb = Tcb::current().expect("failed to get bootstrap TCB");
654 let thr_fd = (&mut *old_tcb.os_specific.thr_fd.get())
655 .take()
656 .expect("no thread FD present");
657 let new_tcb = Tcb::new(self.tls_size)?; let new_tls_end = new_tcb.generic.tls_end;
661 let new_tls_len = new_tcb.generic.tls_len;
662 let new_tcb_ptr = new_tcb.generic.tcb_ptr;
663 let new_tcb_len = new_tcb.generic.tcb_len;
664
665 Sys::munmap(new_tcb as *mut Tcb as *mut c_void, syscall::PAGE_SIZE).unwrap();
667
668 let new_addr = ptr::addr_of!(*new_tcb) as usize;
669
670 assert_eq!(
671 syscall::syscall5(
672 syscall::SYS_MREMAP,
673 old_tcb as *mut Tcb as usize,
674 syscall::PAGE_SIZE,
675 new_addr,
676 syscall::PAGE_SIZE,
677 (syscall::MremapFlags::FIXED | syscall::MremapFlags::KEEP_OLD).bits()
678 | (syscall::MapFlags::PROT_READ | syscall::MapFlags::PROT_WRITE)
679 .bits(),
680 )
681 .expect("mremap: failed to alias TCB"),
682 new_addr,
683 );
684 let _guard = tmp_disable_signals();
687 new_tcb.generic.tls_end = new_tls_end;
689 new_tcb.generic.tls_len = new_tls_len;
690 new_tcb.generic.tcb_ptr = new_tcb_ptr;
691 new_tcb.generic.tcb_len = new_tcb_len;
692
693 drop(_guard);
694 (new_tcb, old_tcb as *mut Tcb as *mut c_void, thr_fd)
695 };
696
697 #[cfg(not(target_os = "redox"))]
698 let tcb = Tcb::new(self.tls_size)?;
699
700 tcb.setup_dtv(tcb_masters.len());
706
707 for obj in new_objects.iter() {
708 if obj.tls_module_id == 0 {
709 continue;
711 }
712
713 let dtv_idx = obj.tls_module_id - 1;
714
715 if cfg!(any(target_arch = "x86", target_arch = "x86_64")) {
716 tcb.dtv_mut()[dtv_idx] = tcb.tls_end.sub(obj.tls_offset);
718 } else {
719 let tls_start = tcb.tls_end.sub(tcb.tls_len);
726 tcb.dtv_mut()[dtv_idx] = tls_start.add(obj.tls_offset);
727 }
728 }
729
730 tcb.append_masters(tcb_masters);
731 tcb.copy_masters().map_err(|_| DlError::Malformed)?;
733 tcb.activate(
734 #[cfg(target_os = "redox")]
735 Some(thr_fd),
736 );
737 tcb.mspace = ALLOCATOR.get();
738
739 #[cfg(target_os = "redox")]
740 {
741 Sys::munmap(old_tcb, syscall::PAGE_SIZE).unwrap();
743 }
744 } else {
745 let tcb = Tcb::current().expect("failed to get current tcb");
746
747 tcb.append_masters(tcb_masters);
749 }
750 }
751
752 for obj in new_objects.into_iter() {
753 obj.mark_ready();
754 self.run_init(&obj);
755 self.register_object(obj);
756 }
757
758 _r_debug.lock().state = RTLDState::RtConsistent;
759 _dl_debug_state();
760
761 Ok(loaded_dso)
762 }
763
764 fn register_object(&mut self, obj: Arc<DSO>) {
765 self.name_to_object_id_map.insert(obj.name.clone(), obj.id);
766 self.objects.insert(obj.id, obj);
767 }
768
769 #[allow(clippy::too_many_arguments)]
783 fn load_objects_recursive(
784 &mut self,
785 name: &str,
786 parent_runpath: &Option<String>,
787 base_addr: Option<usize>,
788 dlopened: bool,
789 new_objects: &mut Vec<Arc<DSO>>,
790 objects_data: &mut Vec<Vec<ProgramHeader>>,
791 tcb_masters: &mut Vec<Master>,
792 dependent_scope: Option<&mut Scope>,
794 scope_kind: ScopeKind,
795 ) -> Result<Arc<DSO>> {
796 if let Some(id) = self.name_to_object_id_map.get(name) {
798 if let Some(obj) = self.objects.get(id) {
799 if let Some(scope) = dependent_scope {
800 match scope_kind {
801 ScopeKind::Local => scope.add(obj),
802 ScopeKind::Global => GLOBAL_SCOPE.write().add(obj),
803 }
804 } else if scope_kind == ScopeKind::Global {
805 GLOBAL_SCOPE.write().add(obj);
806 }
807 return Ok(obj.clone());
808 }
809 } else if let Some(obj) = new_objects.iter().find(|o| o.name == name) {
810 if let Some(scope) = dependent_scope {
811 match scope_kind {
812 ScopeKind::Local => scope.add(obj),
813 ScopeKind::Global => GLOBAL_SCOPE.write().add(obj),
814 }
815 } else if scope_kind == ScopeKind::Global {
816 GLOBAL_SCOPE.write().add(obj);
817 }
818 return Ok(obj.clone());
819 }
820
821 let debug = self.config.debug_flags.contains(DebugFlags::LOAD);
822
823 let path = self.search_object(name, parent_runpath)?;
824 let file = self.read_file(&path)?;
825 let data = file.data();
826 let (obj, tcb_master, elf) = DSO::new(
827 &path,
828 data,
829 base_addr,
830 dlopened,
831 self.next_object_id,
832 self.next_tls_module_id,
833 self.tls_size.next_multiple_of(16),
835 )
836 .map_err(|err| {
837 if debug {
838 eprintln!("[ld.so]: failed to load '{}': {}", name, err)
839 }
840
841 DlError::Malformed
842 })?;
843
844 if debug {
845 eprintln!(
846 "[ld.so]: loading object: {} at {:#x}:{:#x} (pie: {})",
847 name,
848 obj.mmap.as_ptr() as usize,
849 obj.mmap.as_ptr() as usize + obj.mmap.len(),
850 obj.pie,
851 );
852 }
853
854 self.next_object_id += 1;
855
856 if let Some(master) = tcb_master {
857 if !dlopened {
858 self.tls_size = master.offset; }
860
861 tcb_masters.push(master);
862 self.next_tls_module_id += 1;
863 }
864
865 let runpath = obj.runpath().cloned();
866 let dependencies = obj
867 .dependencies()
868 .iter()
869 .map(|dep| dep.to_string())
870 .collect::<Vec<_>>();
871
872 let obj = Arc::new(obj);
873 let mut scope = Scope::local();
874
875 if let Some(dependent_scope) = dependent_scope {
876 match scope_kind {
877 ScopeKind::Local => dependent_scope.add(&obj),
878 ScopeKind::Global => GLOBAL_SCOPE.write().add(&obj),
879 }
880 } else if let ScopeKind::Global = scope_kind {
881 GLOBAL_SCOPE.write().add(&obj);
882 }
883
884 for dep_name in dependencies.iter() {
885 self.load_objects_recursive(
886 dep_name,
887 &runpath,
888 None,
889 dlopened,
890 new_objects,
891 objects_data,
892 tcb_masters,
893 Some(&mut scope),
894 scope_kind,
895 )?;
896 }
897
898 objects_data.push(elf);
899 new_objects.push(obj.clone());
900
901 scope.set_owner(Arc::downgrade(&obj));
902 obj.scope.call_once(|| scope);
903
904 Ok(obj)
905 }
906
907 fn search_object(&self, name: &str, parent_runpath: &Option<String>) -> Result<String> {
908 let debug = self.config.debug_flags.contains(DebugFlags::SEARCH);
909 if debug {
910 eprintln!("[ld.so]: looking for '{}'", name);
911 }
912
913 let mut full_path = name.to_string();
914 if accessible(&full_path, F_OK).is_ok() {
915 if debug {
916 eprintln!("[ld.so]: found at '{}'!", full_path);
917 }
918 return Ok(full_path);
919 } else {
920 let mut search_paths = Vec::new();
921 if let Some(runpath) = parent_runpath {
922 search_paths.extend(runpath.split(PATH_SEP));
923 }
924 if let Some(ld_path) = self.config.library_path.as_ref() {
925 search_paths.extend(ld_path.split(PATH_SEP));
926 }
927 search_paths.push("/lib");
928 for part in search_paths.iter() {
929 full_path = format!("{}/{}", part, name);
930 if debug {
931 eprintln!("[ld.so]: trying path '{}'", full_path);
932 }
933 if accessible(&full_path, F_OK).is_ok() {
934 if debug {
935 eprintln!("[ld.so]: found at '{}'!", full_path);
936 }
937 return Ok(full_path);
938 }
939 }
940 }
941
942 if debug {
943 eprintln!("[ld.so]: failed to locate '{}'", name);
944 }
945
946 Err(DlError::NotFound)
947 }
948
949 fn read_file(&self, path: &str) -> Result<MmapFile> {
950 let debug = self.config.debug_flags.contains(DebugFlags::SEARCH);
951
952 let path_c = CString::new(path).map_err(|err| {
953 if debug {
954 eprintln!("[ld.so]: invalid path '{}': {}", path, err)
955 }
956
957 DlError::NotFound
958 })?;
959
960 let file = {
961 let flags = fcntl::O_RDONLY | fcntl::O_CLOEXEC;
962 MmapFile::open(CStr::borrow(&path_c), flags).map_err(|err| {
963 if debug {
964 eprintln!("[ld.so]: failed to open '{}': {}", path, err)
965 }
966
967 DlError::NotFound
968 })?
969 };
970
971 Ok(file)
972 }
973
974 fn run_init(&self, obj: &DSO) {
975 use crate::platform::{self, types::*};
976
977 if let Some((symbol, SymbolBinding::Global)) = obj.get_sym("__relibc_init_environ") {
978 unsafe {
979 symbol
980 .as_ptr()
981 .cast::<*mut *mut c_char>()
982 .write(platform::environ);
983 }
984 }
985
986 obj.run_init();
987 }
988
989 fn scope_debug(&self) {
990 if self.config.debug_flags.contains(DebugFlags::SCOPES) {
991 println!("[ld.so]: =========== SCOPES ==========");
992 GLOBAL_SCOPE.read().debug();
993 for obj in self.objects.values() {
994 obj.scope().debug();
995 }
996 println!("[ld.so]: ==============================");
997 }
998 }
999}
1000
1001#[cfg(target_pointer_width = "64")]
1011extern "C" fn __plt_resolve_inner(obj: *const DSO, relocation_index: c_uint) -> *mut c_void {
1012 let obj = unsafe { &*obj };
1013 let obj_base = obj.mmap.as_ptr() as usize;
1014 let jmprel = obj.dynamic.jmprel;
1015
1016 let rela = unsafe { &*(jmprel as *const Rela).add(relocation_index as usize) };
1017 assert_eq!(rela.r_type(NativeEndian, false), elf::R_X86_64_JUMP_SLOT);
1018
1019 let sym = obj
1020 .dynamic
1021 .symbol(rela.symbol(NativeEndian, false).unwrap())
1022 .expect("symbol not found");
1023 assert_ne!(sym.st_name(NativeEndian), 0);
1024
1025 let name = core::str::from_utf8(
1026 obj.dynamic
1027 .dynstrtab
1028 .get(sym.st_name(NativeEndian))
1029 .unwrap(),
1030 )
1031 .expect("non utf8 symbol name");
1032
1033 let resolved = resolve_sym(name, &[&GLOBAL_SCOPE.read(), obj.scope()])
1034 .map(|(sym, _, _)| sym)
1035 .unwrap_or_else(|| panic!("symbol '{name}' not found"))
1036 .as_ptr();
1037
1038 let ptr = if obj.pie {
1039 (obj_base as u64 + rela.r_offset(NativeEndian)) as *mut u64
1040 } else {
1041 rela.r_offset(NativeEndian) as *mut u64
1042 };
1043 #[cfg(feature = "trace_tls")]
1044 log::trace!("@plt: {} -> *mut {:p}", name, ptr);
1045
1046 unsafe { *ptr = resolved as u64 }
1047 resolved
1048}
1049
1050unsafe extern "C" {
1051 pub(super) fn __plt_resolve_trampoline() -> usize;
1052}
1053
1054#[cfg(target_arch = "x86_64")]
1055core::arch::global_asm!(
1056 "
1057.global __plt_resolve_trampoline
1058.hidden __plt_resolve_trampoline
1059__plt_resolve_trampoline:
1060 push rsi
1061 push rdi
1062
1063 mov rdi, qword ptr [rsp + 0x10]
1064 mov rsi, qword ptr [rsp + 0x18]
1065
1066 // stash the floating point argument registers
1067 sub rsp, 128
1068 movdqu [rsp + 0x00], xmm0
1069 movdqu [rsp + 0x10], xmm1
1070 movdqu [rsp + 0x20], xmm2
1071 movdqu [rsp + 0x30], xmm3
1072 movdqu [rsp + 0x40], xmm4
1073 movdqu [rsp + 0x50], xmm5
1074 movdqu [rsp + 0x60], xmm6
1075 movdqu [rsp + 0x70], xmm7
1076
1077 push rax
1078 push rcx
1079 push rdx
1080 push r8
1081 push r9
1082 push r10
1083
1084 push rbp
1085 mov rbp, rsp
1086 and rsp, 0xfffffffffffffff0
1087 call {__plt_resolve_inner}
1088 mov r11, rax
1089 mov rsp, rbp
1090 pop rbp
1091
1092 pop r10
1093 pop r9
1094 pop r8
1095 pop rdx
1096 pop rcx
1097 pop rax
1098
1099 movdqu xmm7, [rsp + 0x70]
1100 movdqu xmm6, [rsp + 0x60]
1101 movdqu xmm5, [rsp + 0x50]
1102 movdqu xmm4, [rsp + 0x40]
1103 movdqu xmm3, [rsp + 0x30]
1104 movdqu xmm2, [rsp + 0x20]
1105 movdqu xmm1, [rsp + 0x10]
1106 movdqu xmm0, [rsp + 0x00]
1107 add rsp, 128
1108
1109 pop rdi
1110 pop rsi
1111
1112 add rsp, 0x10
1113 jmp r11
1114
1115 ud2
1116.size __plt_resolve_trampoline, . - __plt_resolve_trampoline
1117",
1118 __plt_resolve_inner = sym __plt_resolve_inner
1119);
1120
1121#[cfg(target_arch = "x86")]
1122core::arch::global_asm!(
1123 "
1124.global __plt_resolve_trampoline
1125.hidden __plt_resolve_trampoline
1126__plt_resolve_trampoline:
1127 ud2
1128.size __plt_resolve_trampoline, . - __plt_resolve_trampoline
1129 "
1130);
1131
1132#[cfg(target_arch = "aarch64")]
1133core::arch::global_asm!(
1134 "
1135.global __plt_resolve_trampoline
1136.hidden __plt_resolve_trampoline
1137__plt_resolve_trampoline:
1138 udf #0
1139.size __plt_resolve_trampoline, . - __plt_resolve_trampoline
1140 "
1141);
1142
1143#[cfg(target_arch = "riscv64")]
1144core::arch::global_asm!(
1145 "
1146.global __plt_resolve_trampoline
1147.hidden __plt_resolve_trampoline
1148__plt_resolve_trampoline:
1149 unimp
1150.size __plt_resolve_trampoline, . - __plt_resolve_trampoline
1151 "
1152);