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
64pub mod arch;
65pub mod dynamic;
66mod format;
67mod loader;
68mod macros;
69pub mod mmap;
70pub mod object;
71mod os;
72mod relocation;
73pub mod segment;
74mod symbol;
75#[cfg(feature = "version")]
76mod version;
77
78use alloc::{
79    boxed::Box,
80    string::{String, ToString},
81};
82use core::{
83    any::Any,
84    fmt::{Debug, Display},
85};
86use object::*;
87use relocation::{ElfRelocation, GLOBAL_SCOPE};
88use segment::ELFRelro;
89
90pub use elf::abi;
91pub use format::dylib::{ElfDylib, RelocatedDylib, Symbol};
92pub use format::exec::{ElfExec, RelocatedExec};
93pub use format::{CoreComponent, CoreComponentRef, Elf, UserData};
94pub use loader::Loader;
95pub use relocation::find_symdef;
96
97/// elf_loader error types
98#[derive(Debug)]
99pub enum Error {
100    /// An error occurred while opening or reading or writing elf files.
101    IOError { msg: String },
102    /// An error occurred while memory mapping.
103    MmapError { msg: String },
104    /// An error occurred during dynamic library relocation.
105    RelocateError {
106        msg: String,
107        custom_err: Box<dyn Any + Send + Sync>,
108    },
109    /// An error occurred while parsing dynamic section.
110    ParseDynamicError { msg: &'static str },
111    /// An error occurred while parsing elf header.
112    ParseEhdrError { msg: String },
113    /// An error occurred while parsing program header.
114    ParsePhdrError {
115        msg: String,
116        custom_err: Box<dyn Any + Send + Sync>,
117    },
118}
119
120impl Display for Error {
121    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
122        match self {
123            Error::IOError { msg } => write!(f, "{msg}"),
124            Error::MmapError { msg } => write!(f, "{msg}"),
125            Error::RelocateError { msg, .. } => write!(f, "{msg}"),
126            Error::ParseDynamicError { msg } => write!(f, "{msg}"),
127            Error::ParseEhdrError { msg } => write!(f, "{msg}"),
128            Error::ParsePhdrError { msg, .. } => write!(f, "{msg}"),
129        }
130    }
131}
132
133impl core::error::Error for Error {}
134
135#[cold]
136#[inline(never)]
137#[allow(unused)]
138fn io_error(msg: impl ToString) -> Error {
139    Error::IOError {
140        msg: msg.to_string(),
141    }
142}
143
144#[cold]
145#[inline(never)]
146fn relocate_error(msg: impl ToString, custom_err: Box<dyn Any + Send + Sync>) -> Error {
147    Error::RelocateError {
148        msg: msg.to_string(),
149        custom_err,
150    }
151}
152
153#[cold]
154#[inline(never)]
155fn parse_dynamic_error(msg: &'static str) -> Error {
156    Error::ParseDynamicError { msg }
157}
158
159#[cold]
160#[inline(never)]
161fn parse_ehdr_error(msg: impl ToString) -> Error {
162    Error::ParseEhdrError {
163        msg: msg.to_string(),
164    }
165}
166
167#[cold]
168#[inline(never)]
169fn parse_phdr_error(msg: impl ToString, custom_err: Box<dyn Any + Send + Sync>) -> Error {
170    Error::ParsePhdrError {
171        msg: msg.to_string(),
172        custom_err,
173    }
174}
175
176/// Set the global scope, lazy binding will look for the symbol in the global scope.
177///
178/// # Safety
179/// This function is marked as unsafe because it directly interacts with raw pointers,
180/// and it also requires the user to ensure thread safety.  
181/// It is the caller's responsibility to ensure that the provided function `f` behaves correctly.
182///
183/// # Parameters
184/// - `f`: A function that takes a symbol name as a parameter and returns an optional raw pointer.
185///   If the symbol is found in the global scope, the function should return `Some(raw_pointer)`,
186///   otherwise, it should return `None`.
187///
188/// # Return
189/// This function does not return a value. It sets the global scope for lazy binding.
190pub unsafe fn set_global_scope(f: fn(&str) -> Option<*const ()>) {
191    GLOBAL_SCOPE.store(f as usize, core::sync::atomic::Ordering::Release);
192}
193
194pub type Result<T> = core::result::Result<T, Error>;