Skip to main content

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};