elf_loader/lib.rs
1//! # Relink
2//!
3//! Relink is a high-performance, `no_std`-friendly ELF loader and runtime linker for Rust.
4//! It maps ELF images from files or memory, performs relocations at runtime, and exposes
5//! typed symbol lookups with Rust lifetimes.
6//!
7//! ## Start with [`Loader`]
8//!
9//! - Use [`Loader::load`] to auto-detect whether the input is a dylib, executable, or
10//! relocatable object.
11//! - Use [`Loader::scan`] to classify executable or dynamic ELF metadata without mapping it.
12//! - Use [`Loader::load_dylib`] or [`Loader::load_exec`] when you want strict type checks.
13//! - Use [`Loader::load_dynamic`] when you want any `PT_DYNAMIC` image, including a dynamic
14//! `ET_EXEC`.
15//! - Use [`Loader::scan`] and [`Loader::load_scanned_dynamic`] to split dynamic metadata
16//! discovery from mapping.
17//! - Use `Loader::load_object` to load `ET_REL` object files when the `object` feature is enabled.
18//! - Inputs can come from file paths, [`input::Path`] / [`input::PathBuf`], raw bytes,
19//! [`input::ElfFile`], or [`input::ElfBinary`].
20//!
21//! ## Highlights
22//!
23//! - Safer symbol lifetimes. Typed symbols borrow the loaded image, so they cannot outlive
24//! the library that produced them.
25//! - Hybrid linking. Compose `.so`, `.o`, and synthetic modules at runtime with `scope()` and
26//! `extend_scope()`.
27//! - Explicit dependency loading. Build your own dependency policy with an
28//! actual [`Loader`], [`linker::KeyResolver`], [`Linker`], and [`LinkContext`].
29//! - Deep customization. Inject host or bridge symbols with
30//! [`image::SyntheticModule`] and intercept relocations with handlers.
31//! - Optional advanced features. TLS relocation handling, lazy binding, relocatable object
32//! loading, logging, and versioned symbol lookup are feature-gated.
33//!
34//! ## Example
35//!
36//! ```rust,no_run
37//! use elf_loader::{
38//! Loader, Relocator, Result,
39//! image::{SyntheticSymbol, SyntheticModule},
40//! };
41//!
42//! extern "C" fn host_double(value: i32) -> i32 {
43//! value * 2
44//! }
45//!
46//! fn main() -> Result<()> {
47//! let host = SyntheticModule::new(
48//! "__host",
49//! [SyntheticSymbol::function("host_double", host_double as *const ())],
50//! );
51//!
52//! let lib = Relocator::new()
53//! .run(Loader::new().load_dylib("path/to/plugin.so")?)
54//! .scope([host])
55//! .relocate()?;
56//!
57//! let run = unsafe {
58//! lib.get::<extern "C" fn(i32) -> i32>("run")
59//! .expect("symbol `run` not found")
60//! };
61//! assert_eq!(run(21), 42);
62//! Ok(())
63//! }
64//! ```
65//!
66//! ## Loading Dependencies With [`Linker`]
67//!
68//! Use [`Linker::load`] when you want a reusable [`LinkContext`]
69//! and resolver-driven `DT_NEEDED` dependency loading. The built-in
70//! [`linker::SearchPathResolver`] covers the common filesystem search-path case;
71//! implement [`linker::KeyResolver`] when dependencies come from memory,
72//! package stores, or another registry.
73//!
74//! ```rust,no_run
75//! use elf_loader::{
76//! LinkContext, Linker, Result,
77//! input::PathBuf,
78//! linker::SearchPathResolver,
79//! };
80//!
81//! fn main() -> Result<()> {
82//! let root = PathBuf::from("path/to/plugin.so");
83//! let mut context: LinkContext<PathBuf, ()> = LinkContext::new();
84//!
85//! let loaded = Linker::new()
86//! .resolver(SearchPathResolver::new())
87//! .load(&mut context, root)?;
88//!
89//! let run = unsafe {
90//! loaded
91//! .get::<extern "C" fn() -> i32>("run")
92//! .expect("symbol `run` not found")
93//! };
94//! let _ = run();
95//!
96//! Ok(())
97//! }
98//! ```
99//!
100//! ## Observer Hooks
101//!
102//! Observers are attached to a single loader or linker run, so reusable
103//! [`Loader`] and [`Linker`] configuration can stay immutable while each run
104//! decides which events to inspect or override.
105//!
106//! ```rust,no_run
107//! use elf_loader::{
108//! Loader, Result,
109//! arch::NativeArch,
110//! observer::{BeforeLoadEvent, LoadObserver},
111//! relocation::RelocationArch,
112//! };
113//!
114//! struct TraceLoads;
115//!
116//! impl LoadObserver for TraceLoads {
117//! fn on_before_load(
118//! &mut self,
119//! event: BeforeLoadEvent<'_, (), <NativeArch as RelocationArch>::Layout>,
120//! ) -> Result<()> {
121//! let _path = event.path();
122//! let _is_dynamic = event.is_dynamic();
123//! Ok(())
124//! }
125//! }
126//!
127//! fn main() -> Result<()> {
128//! let _raw = Loader::new()
129//! .run()
130//! .with_observer(TraceLoads)
131//! .load_dylib("path/to/lib.so")?;
132//!
133//! Ok(())
134//! }
135//! ```
136//!
137//! ## Feature Flags
138//!
139//! - `tls` (default): enables TLS relocation handling. For TLS-using modules, start from
140//! `Loader::with_default_tls_resolver` or provide a custom TLS resolver.
141//! - `lazy-binding`: enables `Relocator::lazy` and PLT/GOT lazy binding.
142//! - `object`: enables `Loader::load_object` and relocatable object (`ET_REL`) loading.
143//! - `version`: enables version-aware symbol lookup via `ElfCore::get_version`.
144//! - `log`, `portable-atomic`, and `use-syscall`: optional integrations for diagnostics and
145//! specialized targets.
146//!
147//! ## More
148//!
149//! - The [`examples`](https://github.com/weizhiao/Relink/tree/main/examples) directory
150//! covers loading from memory, `Linker::load`, scan-first linking, observer hooks,
151//! and object loading.
152//! - The crate currently targets `x86_64`, `x86`, `aarch64`, `arm`, `riscv64`, `riscv32`,
153//! and `loongarch64`.
154//! - Relocatable object support is currently centered on `x86_64` and `riscv64`.
155#![cfg_attr(docsrs, feature(doc_cfg))]
156#![no_std]
157#![warn(
158 clippy::unnecessary_wraps,
159 clippy::unnecessary_lazy_evaluations,
160 clippy::collapsible_if,
161 clippy::cast_lossless,
162 clippy::explicit_iter_loop,
163 clippy::manual_assert,
164 clippy::needless_question_mark,
165 clippy::needless_return,
166 clippy::needless_update,
167 clippy::redundant_clone,
168 clippy::redundant_else,
169 clippy::redundant_static_lifetimes
170)]
171#![allow(
172 clippy::len_without_is_empty,
173 clippy::unnecessary_cast,
174 clippy::uninit_vec
175)]
176extern crate alloc;
177
178/// Compile-time check for supported architectures
179#[cfg(not(any(
180 target_arch = "x86_64",
181 target_arch = "aarch64",
182 target_arch = "riscv64",
183 target_arch = "riscv32",
184 target_arch = "loongarch64",
185 target_arch = "x86",
186 target_arch = "arm",
187)))]
188compile_error!(
189 "Unsupported target architecture. Supported architectures: x86_64, aarch64, riscv64, riscv32, loongarch64, x86, arm"
190);
191
192mod aligned_bytes;
193pub mod arch;
194mod const_builder;
195pub mod elf;
196mod entity;
197pub mod error;
198mod hint;
199pub mod image;
200pub mod input;
201pub mod lazy;
202pub mod linker;
203pub mod loader;
204mod logging;
205pub mod memory;
206#[cfg(feature = "object")]
207pub mod object;
208pub mod observer;
209pub mod os;
210pub mod relocation;
211pub mod runtime;
212mod segment;
213mod sync;
214pub mod tls;
215
216pub(crate) use aligned_bytes::{AlignedBytes, try_cast_bytes};
217pub(crate) use error::*;
218
219pub use aligned_bytes::ByteRepr;
220pub use error::Error;
221pub use linker::{LinkContext, Linker, LinkerRun};
222pub use loader::{Loader, LoaderRun};
223pub use relocation::{Relocator, RelocatorRun};
224
225/// A type alias for `Result`s returned by `elf_loader` functions.
226///
227/// This is a convenience alias that eliminates the need to repeatedly specify
228/// the `Error` type in function signatures.
229pub type Result<T> = core::result::Result<T, Error>;