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 `modules()` or an
26//! explicit [`LocalScope`].
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 built-ins. The native lazy binder, relocatable object loading, logging, and
32//! versioned symbol lookup are feature-gated; TLS and custom lazy binders remain available.
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//! .modules([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::run`] and [`LinkerRun::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//! runtime::DomainId,
80//! };
81//!
82//! fn main() -> Result<()> {
83//! let root = PathBuf::from("path/to/plugin.so");
84//! let mut context = LinkContext::<()>::new(DomainId::PROCESS);
85//! let mut resolver = SearchPathResolver::new();
86//! resolver.push_rpath();
87//! resolver.push_runpath();
88//!
89//! let loaded = Linker::new()
90//! .resolver(resolver)
91//! .run()
92//! .load(&mut context, root)?;
93//!
94//! let run = unsafe {
95//! context
96//! .module(loaded.root())?
97//! .get::<extern "C" fn() -> i32>("run")
98//! .expect("symbol `run` not found")
99//! };
100//! let _ = run();
101//! drop(loaded.release(&mut context)?);
102//!
103//! Ok(())
104//! }
105//! ```
106//!
107//! ## Observer Hooks
108//!
109//! Observers are attached to a single loader or linker run, so reusable
110//! [`Loader`] and [`Linker`] configuration can stay immutable while each run
111//! decides which events to inspect or override.
112//!
113//! ```rust,no_run
114//! use elf_loader::{
115//! Loader, Result,
116//! arch::NativeArch,
117//! observer::{BeforeLoadEvent, LoadObserver},
118//! relocation::RelocationArch,
119//! };
120//!
121//! struct TraceLoads;
122//!
123//! impl LoadObserver for TraceLoads {
124//! fn on_before_load(
125//! &mut self,
126//! event: BeforeLoadEvent<'_, (), <NativeArch as RelocationArch>::Layout>,
127//! ) -> Result<()> {
128//! let _path = event.path();
129//! let _is_dynamic = event.is_dynamic();
130//! Ok(())
131//! }
132//! }
133//!
134//! fn main() -> Result<()> {
135//! let _raw = Loader::new()
136//! .run()
137//! .with_observer(TraceLoads)
138//! .load_dylib("path/to/lib.so")?;
139//!
140//! Ok(())
141//! }
142//! ```
143//!
144//! ## Feature Flags
145//!
146//! - TLS relocation handling is always available. For TLS-using modules, start from
147//! `Loader::with_default_tls_resolver` or provide a custom TLS resolver.
148//! - `lazy-binding`: enables the built-in `NativeLazyBinder`. Custom
149//! [`lazy::LazyBinder`] implementations and [`RelocatorRun::lazy`] are always available;
150//! [`Relocator::new`] remains eager until a binder is configured.
151//! - `object`: enables `Loader::load_object` and relocatable object (`ET_REL`) loading.
152//! - `version`: enables version-aware `get_version` symbol lookups.
153//! - `log`, `portable-atomic`, and `use-syscall`: optional integrations for diagnostics and
154//! specialized targets.
155//!
156//! ## More
157//!
158//! - The [`examples`](https://github.com/weizhiao/Relink/tree/main/examples) directory
159//! covers loading from memory, `LinkerRun::load`, scan-first linking, observer hooks,
160//! and object loading.
161//! - The crate currently targets `x86_64`, `x86`, `aarch64`, `arm`, `riscv64`, `riscv32`,
162//! and `loongarch64`.
163//! - Little-endian Xtensa ELF32 images have basic cross-architecture dynamic
164//! relocation support; lazy binding, native runtime hooks, and TLS relocation
165//! support are pending.
166//! - Relocatable object support is currently centered on `x86_64` and `riscv64`.
167#![cfg_attr(docsrs, feature(doc_cfg))]
168#![no_std]
169#![warn(
170 missing_docs,
171 unreachable_pub,
172 clippy::unnecessary_wraps,
173 clippy::unnecessary_lazy_evaluations,
174 clippy::collapsible_if,
175 clippy::cast_lossless,
176 clippy::explicit_iter_loop,
177 clippy::manual_assert,
178 clippy::needless_question_mark,
179 clippy::needless_return,
180 clippy::needless_update,
181 clippy::redundant_clone,
182 clippy::redundant_else,
183 clippy::redundant_static_lifetimes
184)]
185#![allow(
186 clippy::len_without_is_empty,
187 clippy::unnecessary_cast,
188 clippy::uninit_vec
189)]
190extern crate alloc;
191
192/// Compile-time check for supported architectures
193#[cfg(not(any(
194 target_arch = "x86_64",
195 target_arch = "aarch64",
196 target_arch = "riscv64",
197 target_arch = "riscv32",
198 target_arch = "loongarch64",
199 target_arch = "x86",
200 target_arch = "arm",
201 target_arch = "xtensa",
202)))]
203compile_error!(
204 "Unsupported target architecture. Supported architectures: x86_64, aarch64, riscv64, riscv32, loongarch64, x86, arm, xtensa"
205);
206
207mod aligned_bytes;
208pub mod arch;
209mod const_builder;
210pub mod elf;
211mod entity;
212pub mod error;
213mod hint;
214pub mod image;
215pub mod input;
216pub mod lazy;
217pub mod linker;
218pub mod loader;
219mod logging;
220pub mod memory;
221#[cfg(feature = "object")]
222pub mod object;
223pub mod observer;
224pub mod os;
225pub mod relocation;
226pub mod runtime;
227mod segment;
228mod sync;
229pub mod tls;
230
231pub(crate) use aligned_bytes::{AlignedBytes, try_cast_bytes};
232pub(crate) use error::*;
233
234pub use aligned_bytes::ByteRepr;
235pub use error::Error;
236pub use image::{
237 ElfModule, GlobalScope, LocalScope, Module, ModuleInstanceId, ModuleScope, ModuleSearch,
238 ModuleState, SearchPathPool,
239};
240pub use input::ModuleSourceId;
241pub use linker::{GraphModule, LinkContext, Linker, LinkerRun, ModuleKey};
242pub use loader::{Loader, LoaderRun};
243pub use relocation::{Relocator, RelocatorRun};
244
245/// A type alias for `Result`s returned by `elf_loader` functions.
246///
247/// This is a convenience alias that eliminates the need to repeatedly specify
248/// the `Error` type in function signatures.
249pub type Result<T> = core::result::Result<T, Error>;