dlopen_rs/
lib.rs

1//!A Rust library that implements a series of interfaces such as `dlopen` and `dlsym`, consistent with the behavior of libc,
2//!providing robust support for dynamic library loading and symbol resolution.
3//!
4//!This library serves four purposes:
5//!1. Provide a pure Rust alternative to musl ld.so or glibc ld.so.
6//!2. Provide loading ELF dynamic libraries support for `#![no_std]` targets.
7//!3. Easily swap out symbols in shared libraries with your own custom symbols at runtime
8//!4. Faster than `ld.so` in most cases (loading dynamic libraries and getting symbols)
9//!
10//!Currently, it supports `x86_64`, `RV64`, and `AArch64` architectures.
11//!
12//! # Examples
13//! ```
14//! # use dlopen_rs::{ElfLibrary, OpenFlags};
15//! use std::path::Path;
16//!
17//! fn main(){
18//!     dlopen_rs::init();
19//!     let path = Path::new("./target/release/libexample.so");
20//!     let libexample = ElfLibrary::dlopen(path, OpenFlags::RTLD_LOCAL | OpenFlags::RTLD_LAZY).unwrap();
21//!
22//!     let add = unsafe {
23//! 	    libexample.get::<fn(i32, i32) -> i32>("add").unwrap()
24//!     };
25//!     println!("{}", add(1,1));
26//! }
27//! ```
28#![cfg_attr(not(feature = "std"), no_std)]
29extern crate alloc;
30
31pub mod abi;
32#[cfg(feature = "debug")]
33mod debug;
34mod dl_iterate_phdr;
35mod dladdr;
36mod dlopen;
37mod dlsym;
38#[cfg(feature = "std")]
39mod init;
40mod loader;
41mod register;
42use alloc::{
43    boxed::Box,
44    string::{String, ToString},
45};
46use bitflags::bitflags;
47use core::{any::Any, fmt::Display};
48
49pub use elf_loader::Symbol;
50#[cfg(feature = "std")]
51pub use init::init;
52pub use loader::{Dylib, ElfLibrary};
53
54#[cfg(not(any(
55    target_arch = "x86_64",
56    target_arch = "aarch64",
57    target_arch = "riscv64",
58)))]
59compile_error!("unsupport arch");
60
61bitflags! {
62    /// Control how dynamic libraries are loaded.
63    #[derive(Clone, Copy, Debug)]
64    pub struct OpenFlags:u32{
65        /// This is the converse of RTLD_GLOBAL, and the default if neither flag is specified.
66        /// Symbols defined in this shared object are not made available to resolve references in subsequently loaded shared objects.
67        const RTLD_LOCAL = 0;
68        /// Perform lazy binding. Resolve symbols only as the code that references them is executed.
69        /// If the symbol is never referenced, then it is never resolved.
70        const RTLD_LAZY = 1;
71        /// If this value is specified, or the environment variable LD_BIND_NOW is set to a nonempty string,
72        /// all undefined symbols in the shared object are resolved before dlopen() returns.
73        const RTLD_NOW= 2;
74        /// Not supported
75        const RTLD_NOLOAD = 4;
76        /// Not supported
77        const RTLD_DEEPBIND =8;
78        /// The symbols defined by this shared object will be made available for symbol resolution of subsequently loaded shared objects.
79        const RTLD_GLOBAL = 256;
80        /// Do not unload the shared object during dlclose(). Consequently,
81        /// the object's static and global variables are not reinitialized if the object is reloaded with dlopen() at a later time.
82        const RTLD_NODELETE = 4096;
83        /// dlopen-rs custom flag, true local loading, does not involve any global variable operations, no lock, and has the fastest loading speed.
84        const CUSTOM_NOT_REGISTER = 1024;
85    }
86}
87
88/// dlopen-rs error type
89#[derive(Debug)]
90pub enum Error {
91    /// Returned when encountered an io error.
92    #[cfg(feature = "std")]
93    IOError { err: std::io::Error },
94    /// Returned when encountered a loader error.
95    LoaderError { err: elf_loader::Error },
96    /// Returned when failed to find a library.
97    FindLibError { msg: String },
98    /// Returned when failed to find a symbol.
99    FindSymbolError { msg: String },
100    /// Returned when failed to iterate phdr.
101    IteratorPhdrError { err: Box<dyn Any> },
102}
103
104impl Display for Error {
105    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
106        match self {
107            #[cfg(feature = "std")]
108            Error::IOError { err } => write!(f, "{err}"),
109            Error::LoaderError { err } => write!(f, "{err}"),
110            Error::FindLibError { msg } => write!(f, "{msg}"),
111            Error::FindSymbolError { msg } => write!(f, "{msg}"),
112            Error::IteratorPhdrError { err } => write!(f, "{:?}", err),
113        }
114    }
115}
116
117#[cfg(feature = "std")]
118impl std::error::Error for Error {
119    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
120        match self {
121            Error::IOError { err } => Some(err),
122            _ => None,
123        }
124    }
125}
126
127impl From<elf_loader::Error> for Error {
128    #[cold]
129    fn from(value: elf_loader::Error) -> Self {
130        Error::LoaderError { err: value }
131    }
132}
133
134#[cfg(feature = "std")]
135impl From<std::io::Error> for Error {
136    #[cold]
137    fn from(value: std::io::Error) -> Self {
138        Error::IOError { err: value }
139    }
140}
141
142#[cold]
143#[inline(never)]
144fn find_lib_error(msg: impl ToString) -> Error {
145    Error::FindLibError {
146        msg: msg.to_string(),
147    }
148}
149
150#[cold]
151#[inline(never)]
152fn find_symbol_error(msg: impl ToString) -> Error {
153    Error::FindSymbolError {
154        msg: msg.to_string(),
155    }
156}
157
158pub type Result<T> = core::result::Result<T, Error>;