elf_loader/
lib.rs

1//! # elf_loader
2//! A `safe`, `lightweight`, `extensible`, and `high-performance` library for loading ELF files.
3//! ## Usage
4//! `elf_loader` can load various ELF files and provides interfaces for extended functionality. It can be used in the following areas:
5//! * Use it as an ELF file loader in operating system kernels
6//! * Use it to implement a Rust version of the dynamic linker
7//! * Use it to load ELF dynamic libraries on embedded devices
8//! ## Example
9//! ```rust, ignore
10//! use elf_loader::{Loader, mmap::MmapImpl, object::ElfFile};
11//! use std::collections::HashMap;
12//!
13//! fn print(s: &str) {
14//!     println!("{}", s);
15//! }
16//! // Symbols required by dynamic library liba.so
17//! let mut map = HashMap::new();
18//! map.insert("print", print as _);
19//! let pre_find = |name: &str| -> Option<*const ()> { map.get(name).copied() };
20//! // Load dynamic library liba.so
21//! let mut loader = Loader::<MmapImpl>::new();
22//! let liba = loader
23//!     .easy_load_dylib(ElfFile::from_path("target/liba.so").unwrap())
24//!     .unwrap();
25//!     // Relocate symbols in liba.so
26//! let a = liba.easy_relocate([].iter(), &pre_find).unwrap();
27//! // Call function a in liba.so
28//! let f = unsafe { a.get::<fn() -> i32>("a").unwrap() };
29//! f();
30//! ```
31#![no_std]
32#![warn(
33    clippy::unnecessary_wraps,
34    clippy::unnecessary_lazy_evaluations,
35    clippy::collapsible_if,
36    clippy::cast_lossless,
37    clippy::explicit_iter_loop,
38    clippy::manual_assert,
39    clippy::needless_question_mark,
40    clippy::needless_return,
41    clippy::needless_update,
42    clippy::redundant_clone,
43    clippy::redundant_else,
44    clippy::redundant_static_lifetimes
45)]
46#![allow(
47    clippy::len_without_is_empty,
48    clippy::unnecessary_cast,
49    clippy::uninit_vec
50)]
51extern crate alloc;
52
53#[cfg(not(any(
54    target_arch = "x86_64",
55    target_arch = "aarch64",
56    target_arch = "riscv64",
57    target_arch = "riscv32",
58    target_arch = "loongarch64",
59    target_arch = "x86",
60    target_arch = "arm",
61)))]
62compile_error!("unsupport arch");
63
64#[cfg(all(
65    any(feature = "fs", feature = "mmap"),
66    not(any(feature = "use-libc", feature = "use-syscall"))
67))]
68compile_error!("use at least one of libc and syscall");
69
70#[cfg(all(feature = "use-libc", feature = "use-syscall"))]
71compile_error!("only one of use-libc and use-syscall can be used");
72
73pub mod arch;
74pub mod dynamic;
75mod format;
76mod loader;
77mod macros;
78pub mod mmap;
79pub mod object;
80mod relocation;
81pub mod segment;
82mod symbol;
83#[cfg(feature = "version")]
84mod version;
85
86use alloc::{
87    boxed::Box,
88    string::{String, ToString},
89};
90use core::{
91    any::Any,
92    fmt::{Debug, Display},
93};
94use object::*;
95use relocation::{ElfRelocation, GLOBAL_SCOPE};
96use segment::ELFRelro;
97
98pub use elf::abi;
99pub use format::dylib::{ElfDylib, RelocatedDylib, Symbol};
100pub use format::exec::{ElfExec, RelocatedExec};
101pub use format::{CoreComponent, CoreComponentRef, Elf, UserData};
102pub use loader::Loader;
103pub use relocation::find_symdef;
104
105/// elf_loader error types
106#[derive(Debug)]
107pub enum Error {
108    /// An error occurred while opening or reading or writing elf files.
109    #[cfg(feature = "fs")]
110    IOError { msg: String },
111    /// An error occurred while memory mapping.
112    MmapError { msg: String },
113    /// An error occurred during dynamic library relocation.
114    RelocateError {
115        msg: String,
116        custom_err: Box<dyn Any + Send + Sync>,
117    },
118    /// An error occurred while parsing dynamic section.
119    ParseDynamicError { msg: &'static str },
120    /// An error occurred while parsing elf header.
121    ParseEhdrError { msg: String },
122    /// An error occurred while parsing program header.
123    ParsePhdrError {
124        msg: String,
125        custom_err: Box<dyn Any + Send + Sync>,
126    },
127}
128
129impl Display for Error {
130    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
131        match self {
132            #[cfg(feature = "fs")]
133            Error::IOError { msg } => write!(f, "{msg}"),
134            Error::MmapError { msg } => write!(f, "{msg}"),
135            Error::RelocateError { msg, .. } => write!(f, "{msg}"),
136            Error::ParseDynamicError { msg } => write!(f, "{msg}"),
137            Error::ParseEhdrError { msg } => write!(f, "{msg}"),
138            Error::ParsePhdrError { msg, .. } => write!(f, "{msg}"),
139        }
140    }
141}
142
143impl core::error::Error for Error {}
144
145#[cfg(feature = "fs")]
146#[cold]
147#[inline(never)]
148fn io_error(msg: impl ToString) -> Error {
149    Error::IOError {
150        msg: msg.to_string(),
151    }
152}
153
154#[cold]
155#[inline(never)]
156fn relocate_error(msg: impl ToString, custom_err: Box<dyn Any + Send + Sync>) -> Error {
157    Error::RelocateError {
158        msg: msg.to_string(),
159        custom_err,
160    }
161}
162
163#[cold]
164#[inline(never)]
165fn parse_dynamic_error(msg: &'static str) -> Error {
166    Error::ParseDynamicError { msg }
167}
168
169#[cold]
170#[inline(never)]
171fn parse_ehdr_error(msg: impl ToString) -> Error {
172    Error::ParseEhdrError {
173        msg: msg.to_string(),
174    }
175}
176
177#[cold]
178#[inline(never)]
179fn parse_phdr_error(msg: impl ToString, custom_err: Box<dyn Any + Send + Sync>) -> Error {
180    Error::ParsePhdrError {
181        msg: msg.to_string(),
182        custom_err,
183    }
184}
185
186/// Set the global scope, lazy binding will look for the symbol in the global scope.
187///
188/// # Safety
189/// This function is marked as unsafe because it directly interacts with raw pointers,
190/// and it also requires the user to ensure thread safety.  
191/// It is the caller's responsibility to ensure that the provided function `f` behaves correctly.
192///
193/// # Parameters
194/// - `f`: A function that takes a symbol name as a parameter and returns an optional raw pointer.
195///   If the symbol is found in the global scope, the function should return `Some(raw_pointer)`,
196///   otherwise, it should return `None`.
197///
198/// # Return
199/// This function does not return a value. It sets the global scope for lazy binding.
200pub unsafe fn set_global_scope(f: fn(&str) -> Option<*const ()>) {
201    GLOBAL_SCOPE.store(f as usize, core::sync::atomic::Ordering::Release);
202}
203
204pub type Result<T> = core::result::Result<T, Error>;