rustld/lib.rs
1//! Rustld is a modern (and working) ELF x86_64 & aarch64 loader (static & dynamic linker + compatible glibc & musl) written in Rust
2//! You can run (almost) **everything** with it.
3//!
4//! ## Rust SDK:
5//! ```rust
6//! // With syscall trampoline obfuscation:
7//! rustld::ElfLoader::new_with_obf(true).execute_from_bytes(target_bytes, target_argv, None, None, false);
8//! // Without obfuscation:
9//! rustld::ElfLoader::new().execute_from_bytes(target_bytes, target_argv, None, None, false);
10//! // Optional explicit entrypoint:
11//! rustld::ElfLoader::new_with_obf(true).execute_from_bytes_with_entry(
12//! target_bytes, target_argv,
13//! Some("hello"), // or None
14//! None, // or Some(0x399)
15//! None, None, false,
16//! );
17//! ```
18//!
19//! ## Basic usage example:
20//! ```rust
21//! use std::{ffi::OsStr, os::fd::AsRawFd, os::unix::ffi::OsStrExt};
22//! use rustld::ElfLoader;
23//!
24//! fn main() {
25//! unsafe {
26//! let argv_storage = collect_process_arguments();
27//! if argv_storage.len() < 2 {
28//! eprintln!(
29//! "Usage: rustld [--entry-symbol <name> | --entry-addr <addr>] <program> [args...]"
30//! );
31//! std::process::exit(1);
32//! }
33//! let mut args = argv_storage.into_iter();
34//! let _self_name = args.next();
35//! let mut entry_symbol: Option<String> = None;
36//! let mut entry_address: Option<usize> = None;
37//! let mut target_argv: Vec<String> = Vec::new();
38//!
39//! while let Some(arg) = args.next() {
40//! if arg == "--entry-symbol" {
41//! let Some(symbol) = args.next() else {
42//! eprintln!("Error: --entry-symbol expects a value");
43//! std::process::exit(1);
44//! };
45//! entry_symbol = Some(symbol);
46//! continue;
47//! }
48//! if arg == "--entry-addr" {
49//! let Some(raw_addr) = args.next() else {
50//! eprintln!("Error: --entry-addr expects a value");
51//! std::process::exit(1);
52//! };
53//! let parsed = parse_address(&raw_addr);
54//! entry_address = Some(parsed);
55//! continue;
56//! }
57//! target_argv.push(arg);
58//! target_argv.extend(args);
59//! break;
60//! }
61//!
62//! if target_argv.is_empty() {
63//! eprintln!("Error: missing target program path");
64//! std::process::exit(1);
65//! }
66//! if entry_symbol.is_some() && entry_address.is_some() {
67//! eprintln!("Error: --entry-symbol and --entry-addr are mutually exclusive");
68//! std::process::exit(1);
69//! }
70//!
71//! let target_bytes = map_file_readonly(OsStr::from_bytes(target_argv[0].as_bytes()));
72//!
73//! #[cfg(debug_assertions)]
74//! eprintln!("Executing target binary: {}", target_argv[0]);
75//! if entry_symbol.is_some() || entry_address.is_some() {
76//! ElfLoader::new_with_obf(true).execute_from_bytes_with_entry(
77//! target_bytes,
78//! target_argv,
79//! entry_symbol.as_deref(),
80//! entry_address,
81//! None,
82//! None,
83//! false,
84//! );
85//! } else {
86//! ElfLoader::new_with_obf(true).execute_from_bytes(target_bytes, target_argv, None, None, false);
87//! }
88//! }
89//! }
90//!
91//! fn parse_address(raw: &str) -> usize {
92//! let (digits, radix) = if let Some(hex) = raw.strip_prefix("0x") {
93//! (hex, 16)
94//! } else if let Some(hex) = raw.strip_prefix("0X") {
95//! (hex, 16)
96//! } else {
97//! (raw, 10)
98//! };
99//!
100//! match usize::from_str_radix(digits, radix) {
101//! Ok(value) => value,
102//! Err(_) => {
103//! eprintln!("Error: invalid entry address: {raw}");
104//! std::process::exit(1);
105//! }
106//! }
107//! }
108//!
109//! fn collect_process_arguments() -> Vec<String> {
110//! std::env::args_os()
111//! .map(|arg| arg.to_string_lossy().into_owned())
112//! .collect()
113//! }
114//!
115//! unsafe fn map_file_readonly(path: &OsStr) -> &'static [u8] {
116//! use rustld::syscall::mmap::{self, MAP_PRIVATE, PROT_READ};
117//!
118//! let file = match std::fs::File::open(path) {
119//! Ok(file) => file,
120//! Err(error) => {
121//! eprintln!("Error: could not open target binary: {error}");
122//! std::process::exit(1);
123//! }
124//! };
125//!
126//! let length = match file.metadata() {
127//! Ok(metadata) => metadata.len() as usize,
128//! Err(error) => {
129//! eprintln!("Error: could not stat target binary: {error}");
130//! std::process::exit(1);
131//! }
132//! };
133//!
134//! if length == 0 {
135//! eprintln!("Error: target binary is empty");
136//! std::process::exit(1);
137//! }
138//!
139//! let mapped = mmap::mmap(
140//! core::ptr::null_mut(),
141//! length,
142//! PROT_READ,
143//! MAP_PRIVATE,
144//! file.as_raw_fd() as isize,
145//! 0,
146//! );
147//! let mapped_addr = mapped as isize;
148//! if mapped_addr < 0 {
149//! eprintln!("Error: could not map target binary");
150//! std::process::exit(1);
151//! }
152//!
153//! core::mem::drop(file);
154//! core::slice::from_raw_parts(mapped as *const u8, length)
155//! }
156//! ```
157//!
158
159#![cfg_attr(docsrs, feature(doc_cfg))]
160#![feature(impl_trait_in_assoc_type)]
161#![feature(c_variadic)]
162#![feature(type_changing_struct_update)]
163#![feature(thread_id_value)]
164#![feature(thread_local)]
165#![allow(dead_code)]
166
167pub(crate) mod arch;
168#[cfg_attr(target_arch = "x86_64", path = "arch/x86_64/syscall/mod.rs")]
169#[cfg_attr(target_arch = "aarch64", path = "arch/aarch64/syscall/mod.rs")]
170pub mod syscall;
171
172pub mod runtime_loader;
173
174mod c_api;
175mod elf;
176mod global_allocator;
177mod io_macros;
178mod ld_stubs;
179mod libc;
180mod linking;
181mod page_size;
182mod shared_object;
183mod start;
184mod tls;
185mod utils;
186
187pub use runtime_loader::{host_environment_pointer, ElfLoader};
188pub use start::auxiliary_vector::{AuxiliaryVectorItem, AuxiliaryVectorIter};