1use core::slice;
4
5use alloc::{
6 borrow::ToOwned,
7 boxed::Box,
8 collections::BTreeMap,
9 string::{String, ToString},
10 vec::Vec,
11};
12use object::{
13 NativeEndian,
14 elf::{self, PT_DYNAMIC, PT_PHDR},
15 read::elf::{Dyn as _, ProgramHeader as _},
16};
17
18use crate::{
19 c_str::CStr,
20 header::{
21 elf::{AT_BASE, AT_ENTRY, AT_PHDR, AT_PHENT, AT_PHNUM},
22 unistd,
23 },
24 ld_so::{
25 dso::{
26 DT_RELR, DT_RELRENT, DT_RELRSZ, Dyn, ProgramHeader, Rel, Rela, Relocation,
27 RelocationKind, Relr, apply_relr,
28 },
29 linker::DebugFlags,
30 },
31 platform::{auxv_iter, get_auxvs, types::c_char},
32 start::Stack,
33 sync::mutex::Mutex,
34};
35
36use super::{
37 PATH_SEP,
38 access::accessible,
39 debug::_r_debug,
40 linker::{Config, Linker},
41 tcb::Tcb,
42};
43
44use generic_rt::ExpectTlsFree;
45
46#[cfg(target_pointer_width = "32")]
47pub const SIZEOF_EHDR: usize = 52;
48
49#[cfg(target_pointer_width = "64")]
50pub const SIZEOF_EHDR: usize = 64;
51
52unsafe fn get_argv(mut ptr: *const usize) -> (Vec<String>, *const usize) {
53 let mut argv = Vec::new();
55 while unsafe { *ptr != 0 } {
56 let arg = unsafe { *ptr };
57 match unsafe { CStr::from_ptr(arg as *const c_char).to_str() } {
58 Ok(arg_str) => argv.push(arg_str.to_owned()),
59 _ => {
60 eprintln!("ld.so: failed to parse argv[{}]", argv.len());
61 unistd::_exit(1);
62 }
63 }
64 ptr = unsafe { ptr.add(1) };
65 }
66
67 (argv, ptr)
68}
69
70unsafe fn get_env(mut ptr: *const usize) -> (BTreeMap<String, String>, *const usize) {
71 let mut envs = BTreeMap::new();
73 while unsafe { *ptr != 0 } {
74 let env = unsafe { *ptr };
75 if let Ok(arg_str) = unsafe { CStr::from_ptr(env as *const c_char).to_str() } {
76 let mut parts = arg_str.splitn(2, '=');
77 if let Some(key) = parts.next()
78 && let Some(value) = parts.next()
79 {
80 envs.insert(key.to_owned(), value.to_owned());
81 }
82 }
83 ptr = unsafe { ptr.add(1) };
84 }
85
86 (envs, ptr)
87}
88
89#[allow(unsafe_op_in_unsafe_fn)]
90unsafe fn adjust_stack(sp: &'static mut Stack) {
91 let mut argv = sp.argv() as *mut usize;
92
93 loop {
95 let next_argv = argv.add(1);
96 let arg = *next_argv;
97 *argv = arg;
98 argv = next_argv;
99 if arg == 0 {
100 break;
101 }
102 }
103
104 loop {
106 let next_argv = argv.add(1);
107 let arg = *next_argv;
108 *argv = arg;
109 argv = next_argv;
110 if arg == 0 {
111 break;
112 }
113 }
114
115 loop {
117 let next_argv = argv.add(1);
118 let kind = *next_argv;
119 *argv = kind;
120 argv = next_argv;
121 let next_argv = argv.add(1);
122 let value = *next_argv;
123 *argv = value;
124 argv = next_argv;
125 if kind == 0 {
126 break;
127 }
128 }
129 sp.argc -= 1;
130}
131
132fn resolve_path_name(
133 name_or_path: &str,
134 envs: &BTreeMap<String, String>,
135) -> Option<(String, String)> {
136 if accessible(name_or_path, unistd::F_OK).is_ok() {
137 return Some((
138 name_or_path.to_string(),
139 name_or_path
140 .split("/")
141 .collect::<Vec<&str>>()
142 .last()
143 .unwrap()
144 .to_string(),
145 ));
146 }
147 if name_or_path.split("/").collect::<Vec<&str>>().len() != 1 {
148 return None;
149 }
150
151 let env_path = envs.get("PATH")?;
152 for part in env_path.split(PATH_SEP) {
153 let path = if part.is_empty() {
154 format!("./{}", name_or_path)
155 } else {
156 format!("{}/{}", part, name_or_path)
157 };
158 if accessible(&path, unistd::F_OK).is_ok() {
159 return Some((path.clone(), name_or_path.to_string()));
160 }
161 }
162 None
163}
164
165#[unsafe(no_mangle)]
166pub unsafe extern "C" fn relibc_ld_so_start(
167 sp: &'static mut Stack,
168 ld_entry: usize,
169 dynamic: *const Dyn,
170) -> usize {
171 let mut at_phdr = None;
180 let mut at_phnum = None;
181 let mut at_phent = None;
182 let mut at_base = None;
183 let mut at_entry = None;
184 for [kind, value] in unsafe { auxv_iter(sp.auxv().cast::<usize>()) } {
185 match kind {
186 AT_PHDR => at_phdr = Some(value as *const ProgramHeader),
187 AT_PHNUM => at_phnum = Some(value),
188 AT_PHENT => at_phent = Some(value),
189 AT_BASE => at_base = Some(value),
190 AT_ENTRY => at_entry = Some(value),
191 _ => {}
192 }
193 }
194
195 let at_phdr = at_phdr.expect_notls("`AT_PHDR` must be present");
196 let at_phnum = at_phnum.expect_notls("`AT_PHNUM` must be present if `AT_PHDR` is");
197 let at_phent = at_phent.expect_notls("`AT_PHENT` must be present if `AT_PHDR` is");
198 assert!(!at_phdr.is_null() && at_phnum != 0 && at_phent == size_of::<ProgramHeader>());
199 let phdrs = unsafe { slice::from_raw_parts(at_phdr, at_phnum) };
200
201 let at_entry = at_entry.expect_notls("`AT_ENTRY` must be present");
202 let at_base = at_base.unwrap_or_default();
203
204 let self_base = if at_base != 0 {
205 at_base
206 } else {
207 let ph = phdrs
208 .iter()
209 .find(|ph| ph.p_type(NativeEndian) == PT_DYNAMIC)
210 .unwrap();
211 unsafe { dynamic.byte_sub(ph.p_vaddr(NativeEndian) as usize) as usize }
212 };
213
214 let is_manual = at_entry == ld_entry; let mut i = dynamic;
217 let mut rela_ptr = None;
218 let mut rela_len = None;
219 let mut relr_ptr = None;
220 let mut relr_len = None;
221 let mut rel_ptr = None;
222 let mut rel_len = None;
223 loop {
224 let entry = unsafe { &*i };
225 let val = entry.d_val(NativeEndian);
226 let ptr = val as *const u8;
227 match entry.d_tag(NativeEndian) as u32 {
228 elf::DT_NULL => break,
229 elf::DT_RELA => rela_ptr = Some(ptr.cast::<Rela>()),
230 elf::DT_RELASZ => rela_len = Some(val as usize / size_of::<Rela>()),
231 elf::DT_RELAENT => {
232 assert_eq!(val as usize, size_of::<Rela>(),);
233 }
234 elf::DT_REL => rel_ptr = Some(ptr.cast::<Rel>()),
235 elf::DT_RELSZ => rel_len = Some(val as usize / size_of::<Rel>()),
236 elf::DT_RELENT => {
237 assert_eq!(val as usize, size_of::<Rel>());
238 }
239 DT_RELR => relr_ptr = Some(ptr.cast::<Relr>()),
240 DT_RELRSZ => relr_len = Some(val as usize / size_of::<Relr>()),
241 DT_RELRENT => {
242 assert_eq!(val as usize, size_of::<Relr>());
243 }
244 _ => {}
245 }
246 i = unsafe { i.add(1) };
247 }
248
249 unsafe fn get_array<'a, T>(
250 ptr: Option<*const T>,
251 len: Option<usize>,
252 base_addr: usize,
253 ) -> &'a [T] {
254 if let Some(ptr) = ptr {
255 let len = len.expect_notls("dynamic entry was present without it's corresponding size");
256 unsafe { core::slice::from_raw_parts(ptr.byte_add(base_addr), len) }
257 } else {
258 &[]
259 }
260 }
261
262 fn do_relocs<'a, T>(relocs: &'a [T], self_base: usize)
263 where
264 Relocation: From<&'a T>,
265 {
266 for reloc in relocs {
267 let reloc: Relocation = reloc.into();
268 let ptr = (reloc.offset + self_base) as *mut usize;
269 if reloc.kind == RelocationKind::RELATIVE {
270 unsafe { *ptr = self_base + reloc.addend.unwrap_or_default() };
271 }
272 }
273 }
274
275 let rela = unsafe { get_array::<Rela>(rela_ptr, rela_len, self_base) };
276 let rel = unsafe { get_array::<Rel>(rel_ptr, rel_len, self_base) };
277 do_relocs(rela, self_base);
278 do_relocs(rel, self_base);
279
280 unsafe {
281 let relr = get_array(relr_ptr, relr_len, self_base);
282 apply_relr(self_base as *const u8, relr);
283 }
284
285 let mut base_addr = None;
286 if !is_manual {
287 for ph in phdrs.iter() {
291 if ph.p_type(NativeEndian) == PT_PHDR {
292 assert!(base_addr.is_none(), "`PT_PHDR` cannot occur more than once");
293 base_addr = Some(unsafe {
294 phdrs
295 .as_ptr()
296 .cast::<u8>()
297 .sub(ph.p_vaddr(NativeEndian) as usize)
298 } as usize);
299 }
300 }
301 }
302
303 stage2(sp, self_base, is_manual, base_addr)
304}
305
306fn stage2(
307 sp: &'static mut Stack,
308 self_base: usize,
309 is_manual: bool,
310 base_addr: Option<usize>,
311) -> usize {
312 unsafe {
314 #[cfg(target_os = "redox")]
315 let auxv = sp.auxv().cast();
316 #[cfg(target_os = "redox")]
317 let thr_fd = crate::platform::get_auxv_raw(auxv, redox_rt::auxv_defs::AT_REDOX_THR_FD)
318 .expect_notls("no thread fd present");
319
320 #[cfg(target_os = "redox")]
321 {
322 if redox_rt::current_filetable().fd().is_none() {
323 let filetable_fd = crate::platform::get_auxv_raw(
324 sp.auxv().cast(),
325 redox_rt::auxv_defs::AT_REDOX_FILETABLE_FD,
326 )
327 .expect_notls("no filetable fd present");
328 let filetable_guard = redox_rt::proc::FdGuard::new(filetable_fd)
329 .to_upper()
330 .expect_notls("failed to move filetable fd to upper table");
331 *redox_rt::current_filetable() =
332 redox_rt::sys::FdTbl::from_binary_fd(filetable_guard)
333 .expect_notls("failed to initialize FILETABLE");
334 }
335 }
336
337 let tcb = Tcb::new(0).expect_notls("[ld.so]: failed to allocate bootstrap TCB");
338 tcb.activate(
339 #[cfg(target_os = "redox")]
340 Some(
341 redox_rt::proc::FdGuard::new(thr_fd)
342 .to_upper()
343 .expect_notls("failed to move thread fd to upper table"),
344 ),
345 );
346 #[cfg(target_os = "redox")]
347 {
348 let proc_fd =
349 crate::platform::get_auxv_raw(auxv, redox_rt::auxv_defs::AT_REDOX_PROC_FD)
350 .expect_notls("no proc fd present");
351
352 let ns_fd = crate::platform::get_auxv_raw(auxv, redox_rt::auxv_defs::AT_REDOX_NS_FD)
353 .filter(|&fd| fd != usize::MAX)
354 .map(|fd| {
355 redox_rt::proc::FdGuard::new(fd)
356 .to_upper()
357 .expect_notls("failed to move ns fd to upper table")
358 });
359
360 redox_rt::initialize(
361 redox_rt::proc::FdGuard::new(proc_fd)
362 .to_upper()
363 .expect_notls("failed to move proc fd to upper table"),
364 ns_fd,
365 );
366 redox_rt::signal::setup_sighandler(&tcb.os_specific, true);
367 }
368 }
369
370 let (argv, envs, auxv) = unsafe {
372 let argv_start = sp.argv() as *mut usize;
373 let (argv, argv_end) = get_argv(argv_start);
374 let (envs, envs_end) = get_env(argv_end.add(1));
375 let auxv = get_auxvs(envs_end.add(1));
376 (argv, envs, auxv)
377 };
378
379 unsafe {
380 crate::platform::OUR_ENVIRON.unsafe_set(
381 envs.iter()
382 .map(|(k, v)| {
383 let mut var = Vec::with_capacity(k.len() + v.len() + 2);
384 var.extend(k.as_bytes());
385 var.push(b'=');
386 var.extend(v.as_bytes());
387 var.push(b'\0');
388 let mut var = var.into_boxed_slice();
389 let ptr = var.as_mut_ptr();
390 core::mem::forget(var);
391 ptr.cast()
392 })
393 .chain(core::iter::once(core::ptr::null_mut()))
394 .collect::<Vec<_>>(),
395 );
396
397 crate::platform::environ = crate::platform::OUR_ENVIRON.unsafe_mut().as_mut_ptr();
398 }
399
400 _r_debug.lock().r_ldbase = self_base;
402
403 let name_or_path = if is_manual {
404 println!("argv: {:#?}", argv);
406 println!("envs: {:#?}", envs);
407 println!("auxv: {:#x?}", auxv);
408
409 if sp.argc < 2 {
410 eprintln!("ld.so [executable] [arguments...]");
411 unistd::_exit(1);
412 }
413 unsafe { adjust_stack(sp) };
414 argv[1].clone()
415 } else {
416 argv[0].clone()
417 };
418
419 #[cfg(target_os = "redox")]
421 unsafe {
422 crate::platform::init_inner(auxv);
423 }
424
425 let (path, _name) = match resolve_path_name(&name_or_path, &envs) {
426 Some((p, n)) => (p, n),
427 None => {
428 eprintln!("[ld.so]: failed to locate '{name_or_path}'");
429 unistd::_exit(1);
430 }
431 };
432
433 let config = Config::from_env(&envs);
434 if config.debug_flags.contains(DebugFlags::LOAD) {
435 println!("[ld.so]: relocated self at {self_base:#x}!");
436 if let Some(base_addr) = base_addr {
437 println!("[ld.so]: executable has been already loaded at {base_addr:#x?}");
438 }
439 }
440
441 let mut linker = Linker::new(config);
442 let entry = match linker.load_program(&path, base_addr) {
443 Ok(entry) => entry,
444 Err(err) => {
445 eprintln!("[ld.so]: failed to link '{path}': {err:?}");
446 eprintln!("[ld.so]: enable debug output with `LD_DEBUG=all` for more information");
447 unistd::_exit(1);
448 }
449 };
450 if let Some(tcb) = unsafe { Tcb::current() } {
451 tcb.linker_ptr = Box::into_raw(Box::new(Mutex::new(linker)));
452 }
453 if is_manual {
454 eprintln!("[ld.so]: entry '{path}': {entry:#x}");
455 }
456 entry
457}