dlopen_rs/
lib.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
//!A Rust library that implements a series of interfaces such as `dlopen` and `dlsym`, consistent with the behavior of libc,
//!providing robust support for dynamic library loading and symbol resolution.
//!
//!This library serves four purposes:
//!1. Provide a pure Rust alternative to musl ld.so or glibc ld.so.
//!2. Provide loading ELF dynamic libraries support for `#![no_std]` targets.
//!3. Easily swap out symbols in shared libraries with your own custom symbols at runtime
//!4. Faster than `ld.so` in most cases (loading dynamic libraries and getting symbols)
//!
//!Currently, it supports `x86_64`, `RV64`, and `AArch64` architectures.
//!
//! # Examples
//! ```
//! use dlopen_rs::ElfLibrary;
//! use std::path::Path;
//!
//! fn main(){
//!     dlopen_rs::init();
//!     let path = Path::new("./target/release/libexample.so");
//!     let libexample = ElfLibrary::dlopen(path, OpenFlags::RTLD_LOCAL | OpenFlags::RTLD_LAZY).unwrap();
//!
//!     let add = unsafe {
//! 	    libexample.get::<fn(i32, i32) -> i32>("add").unwrap()
//!     };
//!     println!("{}", add(1,1));
//! }
//! ```
#![cfg_attr(not(feature = "std"), no_std)]
extern crate alloc;

#[cfg(feature = "debug")]
mod debug;
pub mod dlopen;
#[cfg(feature = "std")]
mod init;
mod loader;
mod register;
use alloc::string::{String, ToString};
use bitflags::bitflags;
use core::fmt::Display;

pub use elf_loader::Symbol;
#[cfg(feature = "std")]
pub use init::init;
pub use loader::{Dylib, ElfLibrary};

#[cfg(not(any(
    target_arch = "x86_64",
    target_arch = "aarch64",
    target_arch = "riscv64",
)))]
compile_error!("unsupport arch");

bitflags! {
    /// Control how dynamic libraries are loaded.
    #[derive(Clone, Copy, Debug)]
    pub struct OpenFlags:u32{
        /// This is the converse of RTLD_GLOBAL, and the default if neither flag is specified.
        /// Symbols defined in this shared object are not made available to resolve references in subsequently loaded shared objects.
        const RTLD_LOCAL = 0;
        /// Perform lazy binding. Resolve symbols only as the code that references them is executed.
        /// If the symbol is never referenced, then it is never resolved.
        const RTLD_LAZY = 1;
        /// If this value is specified, or the environment variable LD_BIND_NOW is set to a nonempty string,
        /// all undefined symbols in the shared object are resolved before dlopen() returns.
        const RTLD_NOW= 2;
        /// Not supported
        const RTLD_NOLOAD = 4;
        /// Not supported
        const RTLD_DEEPBIND =8;
        /// The symbols defined by this shared object will be made available for symbol resolution of subsequently loaded shared objects.
        const RTLD_GLOBAL = 256;
        /// Do not unload the shared object during dlclose(). Consequently,
        /// the object's static and global variables are not reinitialized if the object is reloaded with dlopen() at a later time.
        const RTLD_NODELETE = 4096;
        /// dlopen-rs custom flag, true local loading, does not involve any global variable operations, no lock, and has the fastest loading speed.
        const CUSTOM_NOT_REGISTER = 1024;
    }
}

/// dlopen-rs error type
#[derive(Debug)]
pub enum Error {
    /// Returned when encountered an io error.
    #[cfg(feature = "std")]
    IOError { err: std::io::Error },
    /// Returned when encountered a loader error.
    LoaderError { err: elf_loader::Error },
    /// Returned when failed to find a library.
    FindLibError { msg: String },
    /// Returned when failed to find a symbol.
    FindSymbolError { msg: String },
}

impl Display for Error {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        match self {
            #[cfg(feature = "std")]
            Error::IOError { err } => write!(f, "{err}"),
            Error::LoaderError { err } => write!(f, "{err}"),
            Error::FindLibError { msg } => write!(f, "{msg}"),
            Error::FindSymbolError { msg } => write!(f, "{msg}"),
        }
    }
}

#[cfg(feature = "std")]
impl std::error::Error for Error {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        match self {
            Error::IOError { err } => Some(err),
            _ => None,
        }
    }
}

impl From<elf_loader::Error> for Error {
    #[cold]
    fn from(value: elf_loader::Error) -> Self {
        Error::LoaderError { err: value }
    }
}

#[cfg(feature = "std")]
impl From<std::io::Error> for Error {
    #[cold]
    fn from(value: std::io::Error) -> Self {
        Error::IOError { err: value }
    }
}

#[cold]
#[inline(never)]
fn find_lib_error(msg: impl ToString) -> Error {
    Error::FindLibError {
        msg: msg.to_string(),
    }
}

#[cold]
#[inline(never)]
fn find_symbol_error(msg: impl ToString) -> Error {
    Error::FindSymbolError {
        msg: msg.to_string(),
    }
}

pub type Result<T> = core::result::Result<T, Error>;