1pub(crate) mod builtin;
2pub(crate) mod ehframe;
3#[cfg(feature = "tls")]
4pub(crate) mod tls;
5
6#[cfg(feature = "debug")]
7use super::debug::DebugInfo;
8use crate::{
9 OpenFlags, Result, find_lib_error, find_symbol_error,
10 register::{DylibState, MANAGER, register},
11};
12use alloc::{boxed::Box, format, sync::Arc, vec::Vec};
13use core::{any::Any, ffi::CStr, fmt::Debug};
14use ehframe::EhFrame;
15use elf_loader::{
16 CoreComponent, CoreComponentRef, ElfDylib, Loader, RelocatedDylib, Symbol, UserData,
17 abi::PT_GNU_EH_FRAME,
18 arch::{ElfPhdr, ElfRela},
19 mmap::MmapImpl,
20 object::{ElfBinary, ElfObject},
21 segment::ElfSegments,
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: &ElfPhdr,
59 segments: &ElfSegments,
60 data: &mut UserData,
61) -> core::result::Result<(), Box<dyn Any>> {
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: &CoreComponent,
99 mut deps: impl Iterator<Item = &'scope RelocatedDylib<'static>> + Clone,
100) -> core::result::Result<(), Box<dyn Any>> {
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().unwrap().symbol_idx(r_sym);
117 if dynsym.is_local() {
118 unsafe { ptr.write(cast(lib)) };
119 return Ok(());
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 Ok(());
128 };
129 };
130 } else {
131 unsafe { ptr.write(cast(lib)) };
132 return Ok(());
133 }
134 }
135 elf_loader::arch::REL_TPOFF => {
136 let r_sym = rela.r_symbol();
137 let r_off = rela.r_offset();
138 if r_sym != 0 {
139 let (dynsym, syminfo) = lib.symtab().unwrap().symbol_idx(r_sym);
140 if let Some(val) = tls::get_libc_tls_offset(syminfo.name()) {
141 let ptr = (lib.base() + r_off) as *mut usize;
142 unsafe { ptr.write(val) };
143 return Ok(());
144 }
145 }
146 }
147 _ => {}
148 }
149 log::error!("Relocating dylib [{}] failed!", lib.name());
150 Err(Box::new(()))
151}
152
153#[inline]
154pub(crate) fn create_lazy_scope(
155 deps: &[RelocatedDylib],
156 is_lazy: bool,
157) -> Option<Box<dyn for<'a> Fn(&'a str) -> Option<*const ()>>> {
158 if is_lazy {
159 let deps_weak: Vec<CoreComponentRef> = deps
160 .iter()
161 .map(|dep| unsafe { dep.core_component_ref().downgrade() })
162 .collect();
163 Some(Box::new(move |name: &str| {
164 deps_weak.iter().find_map(|dep| unsafe {
165 let lib = RelocatedDylib::from_core_component(dep.upgrade().unwrap());
166 lib.get::<()>(name).map(|sym| {
167 log::trace!(
168 "Lazy Binding: find symbol [{}] from [{}] in local scope ",
169 name,
170 lib.name()
171 );
172 sym.into_raw()
173 })
174 })
175 })
176 as Box<dyn Fn(&str) -> Option<*const ()> + 'static>)
177 } else {
178 None
179 }
180}
181
182fn from_impl(object: impl ElfObject, flags: OpenFlags) -> Result<ElfLibrary> {
183 let mut loader = Loader::<MmapImpl>::new();
184 #[cfg(feature = "std")]
185 unsafe {
186 loader.set_init_params(
187 crate::init::ARGC,
188 (*core::ptr::addr_of!(crate::init::ARGV)).as_ptr() as usize,
189 crate::init::ENVP,
190 )
191 };
192 let lazy_bind = if flags.contains(OpenFlags::RTLD_LAZY) {
193 Some(true)
194 } else if flags.contains(OpenFlags::RTLD_NOW) {
195 Some(false)
196 } else {
197 None
198 };
199 let dylib = loader.load_dylib(object, lazy_bind, parse_phdr)?;
200 log::debug!(
201 "Loading dylib [{}] at address [0x{:x}-0x{:x}]",
202 dylib.name(),
203 dylib.base(),
204 dylib.base() + dylib.map_len()
205 );
206 let lib = ElfLibrary { dylib, flags };
207 Ok(lib)
208}
209
210impl ElfLibrary {
211 #[cfg(feature = "std")]
227 #[inline]
228 pub fn from_file(path: impl AsRef<std::ffi::OsStr>, flags: OpenFlags) -> Result<Self> {
229 let path = path.as_ref().to_str().unwrap();
230 let file = std::fs::File::open(path)?;
231 Self::from_open_file(file, path, flags)
232 }
233
234 #[cfg(feature = "std")]
244 #[inline]
245 pub fn from_open_file(
246 file: std::fs::File,
247 path: impl AsRef<str>,
248 flags: OpenFlags,
249 ) -> Result<ElfLibrary> {
250 use std::os::fd::IntoRawFd;
251
252 use elf_loader::object;
253 let file = unsafe { object::ElfFile::from_owned_fd(path.as_ref(), file.into_raw_fd()) };
254 from_impl(file, flags)
255 }
256
257 #[inline]
269 pub fn from_binary(
270 bytes: impl AsRef<[u8]>,
271 path: impl AsRef<str>,
272 flags: OpenFlags,
273 ) -> Result<Self> {
274 let file = ElfBinary::new(path.as_ref(), bytes.as_ref());
275 from_impl(file, flags)
276 }
277
278 pub fn load_existing(shortname: &str) -> Result<Dylib> {
285 MANAGER
286 .read()
287 .all
288 .get(shortname)
289 .filter(|lib| lib.deps().is_some())
290 .map(|lib| lib.get_dylib())
291 .ok_or(find_lib_error(format!("{}: load fail", shortname)))
292 }
293
294 pub fn needed_libs(&self) -> &[&str] {
296 self.dylib.needed_libs()
297 }
298
299 pub fn name(&self) -> &str {
301 self.dylib.name()
302 }
303
304 fn relocate_impl<F>(self, libs: &[Dylib], find: &F) -> Result<Dylib>
305 where
306 F: for<'b> Fn(&'b str) -> Option<*const ()>,
307 {
308 let mut deps = Vec::new();
309 deps.push(unsafe { RelocatedDylib::from_core_component(self.dylib.core_component()) });
310 deps.extend(libs.iter().map(|lib| lib.inner.clone()));
311 let deps = Arc::new(deps.into_boxed_slice());
312 let lazy_scope = create_lazy_scope(&deps, self.dylib.is_lazy());
313 let cur_lib: RelocatedDylib<'static> = unsafe {
314 core::mem::transmute(self.dylib.relocate(
315 deps.iter(),
316 find,
317 deal_unknown,
318 lazy_scope,
319 )?)
320 };
321 if !self.flags.contains(OpenFlags::CUSTOM_NOT_REGISTER) {
322 register(
323 cur_lib.clone(),
324 self.flags,
325 Some(deps.clone()),
326 &mut MANAGER.write(),
327 *DylibState::default().set_relocated(),
328 );
329 Ok(Dylib {
330 inner: cur_lib,
331 flags: self.flags,
332 deps: Some(deps),
333 })
334 } else {
335 Ok(Dylib {
336 inner: cur_lib,
337 flags: self.flags,
338 deps: Some(deps),
339 })
340 }
341 }
342
343 #[inline]
354 pub fn relocate(self, libs: impl AsRef<[Dylib]>) -> Result<Dylib> {
355 self.relocate_impl(libs.as_ref(), &|name| builtin::BUILTIN.get(name).copied())
356 }
357
358 #[inline]
385 pub fn relocate_with<F>(mut self, libs: impl AsRef<[Dylib]>, func: F) -> Result<Dylib>
386 where
387 F: for<'b> Fn(&'b str) -> Option<*const ()> + 'static,
388 {
389 type Closure = Box<dyn Fn(&str) -> Option<*const ()> + 'static>;
390
391 self.dylib.user_data_mut().unwrap().insert(
392 CLOSURE,
393 Box::new(
394 Box::new(move |name: &str| func(name).or(builtin::BUILTIN.get(name).copied()))
395 as Closure,
396 ),
397 );
398 let func_ref: &Closure = unsafe {
399 core::mem::transmute(
400 self.dylib
401 .user_data()
402 .get(CLOSURE)
403 .unwrap()
404 .downcast_ref::<Closure>()
405 .unwrap(),
406 )
407 };
408 self.relocate_impl(libs.as_ref(), func_ref)
409 }
410}
411
412#[derive(Clone)]
414pub struct Dylib {
415 pub(crate) inner: RelocatedDylib<'static>,
416 pub(crate) flags: OpenFlags,
417 pub(crate) deps: Option<Arc<Box<[RelocatedDylib<'static>]>>>,
418}
419
420impl Debug for Dylib {
421 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
422 f.debug_struct("Dylib")
423 .field("inner", &self.inner)
424 .field("flags", &self.flags)
425 .finish()
426 }
427}
428
429impl Dylib {
430 #[inline]
432 pub fn name(&self) -> &str {
433 self.inner.name()
434 }
435
436 #[inline]
438 pub fn cname(&self) -> &CStr {
439 self.inner.cname()
440 }
441
442 #[inline]
444 pub fn base(&self) -> usize {
445 self.inner.base()
446 }
447
448 #[inline]
450 pub fn phdrs(&self) -> &[ElfPhdr] {
451 self.inner.phdrs()
452 }
453
454 #[inline]
456 pub fn needed_libs(&self) -> &[&str] {
457 self.inner.needed_libs()
458 }
459
460 #[inline]
488 pub unsafe fn get<'lib, T>(&'lib self, name: &str) -> Result<Symbol<'lib, T>> {
489 find_symbol(self.deps.as_ref().unwrap(), name)
490 }
491
492 #[cfg(feature = "version")]
499 #[inline]
500 pub unsafe fn get_version<'lib, T>(
501 &'lib self,
502 name: &str,
503 version: &str,
504 ) -> Result<Symbol<'lib, T>> {
505 unsafe {
506 self.inner
507 .get_version(name, version)
508 .ok_or(find_symbol_error(format!("can not find symbol:{}", name)))
509 }
510 }
511}