1pub(crate) mod builtin;
2pub(crate) mod ehframe;
3pub(crate) mod tls;
4
5#[cfg(feature = "debug")]
6use super::debug::DebugInfo;
7use crate::{
8 find_lib_error, find_symbol_error,
9 register::{register, DylibState, MANAGER},
10 OpenFlags, Result,
11};
12use alloc::{boxed::Box, format, sync::Arc, vec::Vec};
13use core::{ffi::CStr, fmt::Debug};
14use ehframe::EhFrame;
15use elf_loader::{
16 abi::PT_GNU_EH_FRAME,
17 arch::{ElfRela, Phdr},
18 mmap::MmapImpl,
19 object::{ElfBinary, ElfObject},
20 segment::ElfSegments,
21 CoreComponentRef, ElfDylib, Loader, RelocatedDylib, Symbol, UserData,
22};
23
24pub(crate) const EH_FRAME_ID: u8 = 0;
25#[cfg(feature = "debug")]
26pub(crate) const DEBUG_INFO_ID: u8 = 1;
27#[cfg(feature = "tls")]
28const TLS_ID: u8 = 2;
29const CLOSURE: u8 = 3;
30
31#[inline]
32pub(crate) fn find_symbol<'lib, T>(
33 libs: &'lib [RelocatedDylib<'static>],
34 name: &str,
35) -> Result<Symbol<'lib, T>> {
36 log::info!("Get the symbol [{}] in [{}]", name, libs[0].shortname());
37 libs.iter()
38 .find_map(|lib| unsafe { lib.get::<T>(name) })
39 .ok_or(find_symbol_error(format!("can not find symbol:{}", name)))
40}
41
42pub struct ElfLibrary {
44 pub(crate) dylib: ElfDylib,
45 pub(crate) flags: OpenFlags,
46}
47
48impl Debug for ElfLibrary {
49 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
50 self.dylib.fmt(f)
51 }
52}
53
54#[inline(always)]
55#[allow(unused)]
56fn parse_phdr(
57 cname: &CStr,
58 phdr: &Phdr,
59 segments: &ElfSegments,
60 data: &mut UserData,
61) -> elf_loader::Result<()> {
62 match phdr.p_type {
63 PT_GNU_EH_FRAME => {
64 data.insert(
65 EH_FRAME_ID,
66 Box::new(EhFrame::new(
67 phdr,
68 segments.base()..segments.base() + segments.len(),
69 )),
70 );
71 }
72 #[cfg(feature = "debug")]
73 elf_loader::abi::PT_DYNAMIC => {
74 data.insert(
75 DEBUG_INFO_ID,
76 Box::new(unsafe {
77 DebugInfo::new(
78 segments.base(),
79 cname.as_ptr() as _,
80 segments.base() + phdr.p_vaddr as usize,
81 )
82 }),
83 );
84 }
85 #[cfg(feature = "tls")]
86 elf_loader::abi::PT_TLS => {
87 data.insert(TLS_ID, Box::new(tls::ElfTls::new(phdr, segments.base())));
88 }
89 _ => {}
90 }
91 Ok(())
92}
93
94#[inline(always)]
95#[allow(unused)]
96pub(crate) fn deal_unknown<'scope>(
97 rela: &ElfRela,
98 lib: &ElfDylib,
99 mut deps: impl Iterator<Item = &'scope RelocatedDylib<'static>> + Clone,
100) -> bool {
101 #[cfg(feature = "tls")]
102 match rela.r_type() as _ {
103 elf_loader::arch::REL_DTPMOD => {
104 let r_sym = rela.r_symbol();
105 let r_off = rela.r_offset();
106 let ptr = (lib.base() + r_off) as *mut usize;
107 let cast = |core: &elf_loader::CoreComponent| unsafe {
108 core.user_data()
109 .get(TLS_ID)
110 .unwrap()
111 .downcast_ref::<tls::ElfTls>()
112 .unwrap_unchecked()
113 .module_id()
114 };
115 if r_sym != 0 {
116 let (dynsym, syminfo) = lib.symtab().symbol_idx(r_sym);
117 if dynsym.is_local() {
118 unsafe { ptr.write(cast(lib.core_component_ref())) };
119 return true;
120 } else {
121 if let Some(id) = deps.find_map(|lib| unsafe {
122 lib.symtab()
123 .lookup_filter(&syminfo)
124 .map(|_| cast(lib.core_component_ref()))
125 }) {
126 unsafe { ptr.write(id) };
127 return true;
128 };
129 };
130 } else {
131 unsafe { ptr.write(cast(lib.core_component_ref())) };
132 return true;
133 }
134 }
135 _ => {}
136 }
137 log::error!("Relocating dylib [{}] failed!", lib.name());
138 false
139}
140
141#[inline]
142pub(crate) fn create_lazy_scope(
143 deps: &[RelocatedDylib],
144 is_lazy: bool,
145) -> Option<Box<dyn for<'a> Fn(&'a str) -> Option<*const ()>>> {
146 if is_lazy {
147 let deps_weak: Vec<CoreComponentRef> = deps
148 .iter()
149 .map(|dep| unsafe { dep.core_component_ref().downgrade() })
150 .collect();
151 Some(Box::new(move |name: &str| {
152 deps_weak.iter().find_map(|dep| unsafe {
153 RelocatedDylib::from_core_component(dep.upgrade().unwrap())
154 .get::<()>(name)
155 .map(|sym| sym.into_raw())
156 })
157 })
158 as Box<dyn Fn(&str) -> Option<*const ()> + 'static>)
159 } else {
160 None
161 }
162}
163
164fn from_impl(object: impl ElfObject, flags: OpenFlags) -> Result<ElfLibrary> {
165 #[allow(unused_mut)]
166 let mut loader = Loader::<MmapImpl>::new();
167 #[cfg(feature = "std")]
168 unsafe {
169 loader.set_init_params(
170 crate::init::ARGC,
171 (*core::ptr::addr_of!(crate::init::ARGV)).as_ptr() as usize,
172 crate::init::ENVP,
173 )
174 };
175 let lazy_bind = if flags.contains(OpenFlags::RTLD_LAZY) {
176 Some(true)
177 } else if flags.contains(OpenFlags::RTLD_NOW) {
178 Some(false)
179 } else {
180 None
181 };
182 let dylib = loader.load_dylib(object, lazy_bind, parse_phdr)?;
183 log::debug!(
184 "Loading dylib [{}] at address [0x{:x}-0x{:x}]",
185 dylib.name(),
186 dylib.base(),
187 dylib.base() + dylib.map_len()
188 );
189 let lib = ElfLibrary { dylib, flags };
190 Ok(lib)
191}
192
193impl ElfLibrary {
194 #[cfg(feature = "std")]
210 #[inline]
211 pub fn from_file(path: impl AsRef<std::ffi::OsStr>, flags: OpenFlags) -> Result<Self> {
212 let path = path.as_ref().to_str().unwrap();
213 let file = std::fs::File::open(path)?;
214 Self::from_open_file(file, path, flags)
215 }
216
217 #[cfg(feature = "std")]
225 #[inline]
226 pub fn from_open_file(
227 file: std::fs::File,
228 path: impl AsRef<str>,
229 flags: OpenFlags,
230 ) -> Result<ElfLibrary> {
231 use std::os::fd::IntoRawFd;
232
233 use elf_loader::object;
234 let file = unsafe { object::ElfFile::from_owned_fd(path.as_ref(), file.into_raw_fd()) };
235 from_impl(file, flags)
236 }
237
238 #[inline]
249 pub fn from_binary(
250 bytes: impl AsRef<[u8]>,
251 path: impl AsRef<str>,
252 flags: OpenFlags,
253 ) -> Result<Self> {
254 let file = ElfBinary::new(path.as_ref(), bytes.as_ref());
255 from_impl(file, flags)
256 }
257
258 pub fn load_existing(shortname: &str) -> Result<Dylib> {
265 MANAGER
266 .read()
267 .all
268 .get(shortname)
269 .filter(|lib| lib.deps().is_some())
270 .map(|lib| lib.get_dylib())
271 .ok_or(find_lib_error(format!("{}: load fail", shortname)))
272 }
273
274 pub fn needed_libs(&self) -> &[&'static str] {
276 self.dylib.needed_libs()
277 }
278
279 pub fn name(&self) -> &str {
281 self.dylib.name()
282 }
283
284 fn relocate_impl<F>(self, libs: &[Dylib], find: &'static F) -> Result<Dylib>
285 where
286 F: for<'b> Fn(&'b str) -> Option<*const ()>,
287 {
288 let mut deps = Vec::new();
289 deps.push(unsafe { RelocatedDylib::from_core_component(self.dylib.core_component()) });
290 deps.extend(libs.iter().map(|lib| lib.inner.clone()));
291 let deps = Arc::new(deps.into_boxed_slice());
292 let lazy_scope = create_lazy_scope(&deps, self.dylib.is_lazy());
293 let cur_lib = self
294 .dylib
295 .relocate(deps.clone().iter(), find, deal_unknown, lazy_scope)?;
296 if !self.flags.contains(OpenFlags::CUSTOM_NOT_REGISTER) {
297 register(
298 cur_lib.clone(),
299 self.flags,
300 Some(deps.clone()),
301 &mut MANAGER.write(),
302 *DylibState::default().set_relocated(),
303 );
304 Ok(Dylib {
305 inner: cur_lib,
306 flags: self.flags,
307 deps: Some(deps),
308 })
309 } else {
310 Ok(Dylib {
311 inner: cur_lib,
312 flags: self.flags,
313 deps: Some(deps),
314 })
315 }
316 }
317
318 #[inline]
329 pub fn relocate(self, libs: impl AsRef<[Dylib]>) -> Result<Dylib> {
330 self.relocate_impl(libs.as_ref(), &|name| builtin::BUILTIN.get(name).copied())
331 }
332
333 #[inline]
358 pub fn relocate_with<F>(mut self, libs: impl AsRef<[Dylib]>, func: F) -> Result<Dylib>
359 where
360 F: for<'b> Fn(&'b str) -> Option<*const ()> + 'static,
361 {
362 type Closure = Box<dyn Fn(&str) -> Option<*const ()> + 'static>;
363
364 self.dylib.user_data_mut().unwrap().insert(
365 CLOSURE,
366 Box::new(
367 Box::new(move |name: &str| func(name).or(builtin::BUILTIN.get(name).copied()))
368 as Closure,
369 ),
370 );
371 let func_ref: &Closure = unsafe {
372 core::mem::transmute(
373 self.dylib
374 .user_data()
375 .get(CLOSURE)
376 .unwrap()
377 .downcast_ref::<Closure>()
378 .unwrap(),
379 )
380 };
381 self.relocate_impl(libs.as_ref(), func_ref)
382 }
383}
384
385#[derive(Clone)]
387pub struct Dylib {
388 pub(crate) inner: RelocatedDylib<'static>,
389 pub(crate) flags: OpenFlags,
390 pub(crate) deps: Option<Arc<Box<[RelocatedDylib<'static>]>>>,
391}
392
393impl Debug for Dylib {
394 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
395 f.debug_struct("Dylib")
396 .field("inner", &self.inner)
397 .field("flags", &self.flags)
398 .finish()
399 }
400}
401
402impl Dylib {
403 #[inline]
405 pub fn name(&self) -> &str {
406 self.inner.name()
407 }
408
409 #[inline]
411 pub fn cname(&self) -> &CStr {
412 self.inner.cname()
413 }
414
415 #[inline]
417 pub fn base(&self) -> usize {
418 self.inner.base()
419 }
420
421 #[inline]
423 pub fn phdrs(&self) -> &[Phdr] {
424 self.inner.phdrs()
425 }
426
427 #[inline]
429 pub fn needed_libs(&self) -> &[&str] {
430 self.inner.needed_libs()
431 }
432
433 #[inline]
457 pub unsafe fn get<'lib, T>(&'lib self, name: &str) -> Result<Symbol<'lib, T>> {
458 find_symbol(self.deps.as_ref().unwrap(), name)
459 }
460
461 #[cfg(feature = "version")]
468 #[inline]
469 pub unsafe fn get_version<'lib, T>(
470 &'lib self,
471 name: &str,
472 version: &str,
473 ) -> Result<Symbol<'lib, T>> {
474 self.inner
475 .get_version(name, version)
476 .ok_or(find_symbol_error(format!("can not find symbol:{}", name)))
477 }
478}