xref: /relibc/src/ld_so/start.rs (revision 3a923aa62d168d05c45d02c8e53804d7fdfe7f05)
1 // Start code adapted from https://gitlab.redox-os.org/redox-os/relibc/blob/master/src/start.rs
2 
3 use alloc::{borrow::ToOwned, boxed::Box, collections::BTreeMap, string::String, vec::Vec};
4 
5 use crate::{
6     c_str::CStr, header::unistd, platform::types::c_char, start::Stack, sync::mutex::Mutex,
7 };
8 
9 use super::{
10     debug::_r_debug,
11     linker::{Linker, DSO},
12     tcb::Tcb,
13 };
14 use crate::header::sys_auxv::{AT_ENTRY, AT_PHDR};
15 
16 unsafe fn get_argv(mut ptr: *const usize) -> (Vec<String>, *const usize) {
17     //traverse the stack and collect argument vector
18     let mut argv = Vec::new();
19     while *ptr != 0 {
20         let arg = *ptr;
21         match CStr::from_ptr(arg as *const c_char).to_str() {
22             Ok(arg_str) => argv.push(arg_str.to_owned()),
23             _ => {
24                 eprintln!("ld.so: failed to parse argv[{}]", argv.len());
25                 unistd::_exit(1);
26                 loop {}
27             }
28         }
29         ptr = ptr.add(1);
30     }
31     return (argv, ptr);
32 }
33 
34 unsafe fn get_env(mut ptr: *const usize) -> (BTreeMap<String, String>, *const usize) {
35     //traverse the stack and collect argument environment variables
36     let mut envs = BTreeMap::new();
37     while *ptr != 0 {
38         let env = *ptr;
39         if let Ok(arg_str) = CStr::from_ptr(env as *const c_char).to_str() {
40             let mut parts = arg_str.splitn(2, '=');
41             if let Some(key) = parts.next() {
42                 if let Some(value) = parts.next() {
43                     envs.insert(key.to_owned(), value.to_owned());
44                 }
45             }
46         }
47         ptr = ptr.add(1);
48     }
49     return (envs, ptr);
50 }
51 
52 unsafe fn get_auxv(mut ptr: *const usize) -> BTreeMap<usize, usize> {
53     //traverse the stack and collect argument environment variables
54     let mut auxv = BTreeMap::new();
55     while *ptr != 0 {
56         let kind = *ptr;
57         ptr = ptr.add(1);
58         let value = *ptr;
59         ptr = ptr.add(1);
60         auxv.insert(kind, value);
61     }
62     return auxv;
63 }
64 
65 unsafe fn adjust_stack(sp: &'static mut Stack) {
66     let mut argv = sp.argv() as *mut usize;
67 
68     // Move arguments
69     loop {
70         let next_argv = argv.add(1);
71         let arg = *next_argv;
72         *argv = arg;
73         argv = next_argv;
74         if arg == 0 {
75             break;
76         }
77     }
78 
79     // Move environment
80     loop {
81         let next_argv = argv.add(1);
82         let arg = *next_argv;
83         *argv = arg;
84         argv = next_argv;
85         if arg == 0 {
86             break;
87         }
88         if let Ok(arg_str) = CStr::from_ptr(arg as *const c_char).to_str() {
89             let mut parts = arg_str.splitn(2, '=');
90             if let Some(key) = parts.next() {
91                 if let Some(value) = parts.next() {
92                     if let "LD_LIBRARY_PATH" = key {
93                         //library_path = value
94                     }
95                 }
96             }
97         }
98     }
99 
100     // Move auxiliary vectors
101     loop {
102         let next_argv = argv.add(1);
103         let kind = *next_argv;
104         *argv = kind;
105         argv = next_argv;
106         let next_argv = argv.add(1);
107         let value = *next_argv;
108         *argv = value;
109         argv = next_argv;
110         if kind == 0 {
111             break;
112         }
113     }
114 
115     sp.argc -= 1;
116 }
117 #[no_mangle]
118 pub extern "C" fn relibc_ld_so_start(sp: &'static mut Stack, ld_entry: usize) -> usize {
119     // first we get the arguments, the environment, and the auxilary vector
120     let (argv, envs, auxv) = unsafe {
121         let argv_start = sp.argv() as *mut usize;
122         let (argv, argv_end) = get_argv(argv_start);
123         let (envs, envs_end) = get_env(argv_end.add(1));
124         let auxv = get_auxv(envs_end.add(1));
125         (argv, envs, auxv)
126     };
127 
128     let is_manual = if let Some(img_entry) = auxv.get(&AT_ENTRY) {
129         *img_entry == ld_entry
130     } else {
131         true
132     };
133 
134     // we might need global lock for this kind of stuff
135     unsafe {
136         _r_debug.r_ldbase = ld_entry;
137     }
138 
139     // Some variables that will be overridden by environment and auxiliary vectors
140     let library_path = match envs.get("LD_LIBRARY_PATH") {
141         Some(lib_path) => lib_path,
142         None => "/lib",
143     };
144 
145     let path = if is_manual {
146         // ld.so is run directly by user and not via execve() or similar systemcall
147         println!("argv: {:#?}", argv);
148         println!("envs: {:#?}", envs);
149         println!("auxv: {:#x?}", auxv);
150 
151         if sp.argc < 2 {
152             eprintln!("ld.so [executable] [arguments...]");
153             unistd::_exit(1);
154             loop {}
155         }
156         unsafe { adjust_stack(sp) };
157         &argv[1]
158     } else {
159         &argv[0]
160     };
161     // if we are not running in manual mode, then the main
162     // program is already loaded by the kernel and we want
163     // to use it.
164     let program = {
165         let mut pr = None;
166         if !is_manual {
167             let phdr = *auxv.get(&AT_PHDR).unwrap();
168             if phdr != 0 {
169                 let p = DSO {
170                     name: path.to_owned(),
171                     entry_point: *auxv.get(&AT_ENTRY).unwrap(),
172                     // The 0x40 is the size of Elf header not a good idea for different bit size
173                     // compatiablility but it will always work on 64 bit systems,
174                     base_addr: phdr - 0x40,
175                 };
176                 pr = Some(p);
177             }
178         }
179         pr
180     };
181     let mut linker = Linker::new(library_path, false);
182     match linker.load(&path, &path) {
183         Ok(()) => (),
184         Err(err) => {
185             eprintln!("ld.so: failed to load '{}': {}", path, err);
186             unistd::_exit(1);
187             loop {}
188         }
189     }
190 
191     let entry = match linker.link(Some(&path), program) {
192         Ok(ok) => match ok {
193             Some(some) => some,
194             None => {
195                 eprintln!("ld.so: failed to link '{}': missing entry", path);
196                 unistd::_exit(1);
197                 loop {}
198             }
199         },
200         Err(err) => {
201             eprintln!("ld.so: failed to link '{}': {}", path, err);
202             unistd::_exit(1);
203             loop {}
204         }
205     };
206     if let Err(e) = linker.run_init() {
207         eprintln!("ld.so: failed to run .init_array");
208         unistd::_exit(1);
209         loop {}
210     }
211     if let Some(tcb) = unsafe { Tcb::current() } {
212         tcb.linker_ptr = Box::into_raw(Box::new(Mutex::new(linker)));
213     }
214     if is_manual {
215         eprintln!("ld.so: entry '{}': {:#x}", path, entry);
216     }
217     entry
218 }
219