xref: /relibc/src/ld_so/start.rs (revision 7d27737c3ffa5bbdf5d00592bb95abf1f9703aee)
1 // Start code adapted from https://gitlab.redox-os.org/redox-os/relibc/blob/master/src/start.rs
2 
3 use alloc::{
4     borrow::ToOwned,
5     boxed::Box,
6     collections::BTreeMap,
7     string::{String, ToString},
8     vec::Vec,
9 };
10 
11 use crate::{
12     c_str::CStr,
13     header::unistd,
14     platform::{get_auxv, get_auxvs, new_mspace, types::c_char},
15     start::Stack,
16     sync::mutex::Mutex,
17     ALLOCATOR,
18 };
19 
20 use super::{access::accessible, debug::_r_debug, linker::Linker, tcb::Tcb, PATH_SEP};
21 use crate::header::sys_auxv::{AT_ENTRY, AT_PHDR};
22 use goblin::elf::header::header64::SIZEOF_EHDR;
23 
24 unsafe fn get_argv(mut ptr: *const usize) -> (Vec<String>, *const usize) {
25     //traverse the stack and collect argument vector
26     let mut argv = Vec::new();
27     while *ptr != 0 {
28         let arg = *ptr;
29         match CStr::from_ptr(arg as *const c_char).to_str() {
30             Ok(arg_str) => argv.push(arg_str.to_owned()),
31             _ => {
32                 eprintln!("ld.so: failed to parse argv[{}]", argv.len());
33                 unistd::_exit(1);
34                 loop {}
35             }
36         }
37         ptr = ptr.add(1);
38     }
39     return (argv, ptr);
40 }
41 
42 unsafe fn get_env(mut ptr: *const usize) -> (BTreeMap<String, String>, *const usize) {
43     //traverse the stack and collect argument environment variables
44     let mut envs = BTreeMap::new();
45     while *ptr != 0 {
46         let env = *ptr;
47         if let Ok(arg_str) = CStr::from_ptr(env as *const c_char).to_str() {
48             let mut parts = arg_str.splitn(2, '=');
49             if let Some(key) = parts.next() {
50                 if let Some(value) = parts.next() {
51                     envs.insert(key.to_owned(), value.to_owned());
52                 }
53             }
54         }
55         ptr = ptr.add(1);
56     }
57     return (envs, ptr);
58 }
59 
60 unsafe fn adjust_stack(sp: &'static mut Stack) {
61     let mut argv = sp.argv() as *mut usize;
62 
63     // Move arguments
64     loop {
65         let next_argv = argv.add(1);
66         let arg = *next_argv;
67         *argv = arg;
68         argv = next_argv;
69         if arg == 0 {
70             break;
71         }
72     }
73 
74     // Move environment
75     loop {
76         let next_argv = argv.add(1);
77         let arg = *next_argv;
78         *argv = arg;
79         argv = next_argv;
80         if arg == 0 {
81             break;
82         }
83         if let Ok(arg_str) = CStr::from_ptr(arg as *const c_char).to_str() {
84             let mut parts = arg_str.splitn(2, '=');
85             if let Some(key) = parts.next() {
86                 if let Some(value) = parts.next() {
87                     if let "LD_LIBRARY_PATH" = key {
88                         //library_path = value
89                     }
90                 }
91             }
92         }
93     }
94 
95     // Move auxiliary vectors
96     loop {
97         let next_argv = argv.add(1);
98         let kind = *next_argv;
99         *argv = kind;
100         argv = next_argv;
101         let next_argv = argv.add(1);
102         let value = *next_argv;
103         *argv = value;
104         argv = next_argv;
105         if kind == 0 {
106             break;
107         }
108     }
109     sp.argc -= 1;
110 }
111 
112 fn resolve_path_name(
113     name_or_path: &str,
114     envs: &BTreeMap<String, String>,
115 ) -> Option<(String, String)> {
116     if accessible(name_or_path, unistd::F_OK) == 0 {
117         return Some((
118             name_or_path.to_string(),
119             name_or_path
120                 .split("/")
121                 .collect::<Vec<&str>>()
122                 .last()
123                 .unwrap()
124                 .to_string(),
125         ));
126     }
127     if name_or_path.split("/").collect::<Vec<&str>>().len() != 1 {
128         return None;
129     }
130 
131     let env_path = envs.get("PATH")?;
132     for part in env_path.split(PATH_SEP) {
133         let path = if part.is_empty() {
134             format!("./{}", name_or_path)
135         } else {
136             format!("{}/{}", part, name_or_path)
137         };
138         if accessible(&path, unistd::F_OK) == 0 {
139             return Some((path.to_string(), name_or_path.to_string()));
140         }
141     }
142     None
143 }
144 #[no_mangle]
145 pub extern "C" fn relibc_ld_so_start(sp: &'static mut Stack, ld_entry: usize) -> usize {
146     // First thing we initialize the mspace
147     ALLOCATOR.set_book_keeper(new_mspace());
148     // next we get the arguments, the environment, and the auxilary vector
149     let (argv, envs, auxv) = unsafe {
150         let argv_start = sp.argv() as *mut usize;
151         let (argv, argv_end) = get_argv(argv_start);
152         let (envs, envs_end) = get_env(argv_end.add(1));
153         let auxv = get_auxvs(envs_end.add(1));
154         (argv, envs, auxv)
155     };
156 
157     unsafe {
158         crate::platform::OUR_ENVIRON = envs
159             .iter()
160             .map(|(k, v)| {
161                 let mut var = Vec::with_capacity(k.len() + v.len() + 2);
162                 var.extend(k.as_bytes());
163                 var.push(b'=');
164                 var.extend(v.as_bytes());
165                 var.push(b'\0');
166                 let mut var = var.into_boxed_slice();
167                 let ptr = var.as_mut_ptr();
168                 core::mem::forget(var);
169                 ptr.cast()
170             })
171             .chain(core::iter::once(core::ptr::null_mut()))
172             .collect::<Vec<_>>();
173 
174         crate::platform::environ = crate::platform::OUR_ENVIRON.as_mut_ptr();
175     }
176 
177     let is_manual = if let Some(img_entry) = get_auxv(&auxv, AT_ENTRY) {
178         img_entry == ld_entry
179     } else {
180         true
181     };
182 
183     // we might need global lock for this kind of stuff
184     unsafe {
185         _r_debug.r_ldbase = ld_entry;
186     }
187 
188     // TODO: Fix memory leak, although minimal.
189     crate::platform::init(auxv.clone());
190 
191     // Some variables that will be overridden by environment and auxiliary vectors
192     let ld_library_path = envs.get("LD_LIBRARY_PATH").map(|s| s.to_owned());
193 
194     let name_or_path = if is_manual {
195         // ld.so is run directly by user and not via execve() or similar systemcall
196         println!("argv: {:#?}", argv);
197         println!("envs: {:#?}", envs);
198         println!("auxv: {:#x?}", auxv);
199 
200         if sp.argc < 2 {
201             eprintln!("ld.so [executable] [arguments...]");
202             unistd::_exit(1);
203             loop {}
204         }
205         unsafe { adjust_stack(sp) };
206         argv[1].to_string()
207     } else {
208         argv[0].to_string()
209     };
210 
211     let (path, name) = match resolve_path_name(&name_or_path, &envs) {
212         Some((p, n)) => (p, n),
213         None => {
214             eprintln!("ld.so: failed to locate '{}'", name_or_path);
215             unistd::_exit(1);
216             loop {}
217         }
218     };
219 
220     // if we are not running in manual mode, then the main
221     // program is already loaded by the kernel and we want
222     // to use it. on redox, we treat it the same.
223     let base_addr = {
224         let mut base = None;
225         if !is_manual && cfg!(not(target_os = "redox")) {
226             let phdr = get_auxv(&auxv, AT_PHDR).unwrap();
227             if phdr != 0 {
228                 base = Some(phdr - SIZEOF_EHDR);
229             }
230         }
231         base
232     };
233     let mut linker = Linker::new(ld_library_path);
234     let entry = match linker.load_program(&path, base_addr) {
235         Ok(entry) => entry,
236         Err(err) => {
237             eprintln!("ld.so: failed to link '{}': {}", path, err);
238             unistd::_exit(1);
239             loop {}
240         }
241     };
242     if let Some(tcb) = unsafe { Tcb::current() } {
243         tcb.linker_ptr = Box::into_raw(Box::new(Mutex::new(linker)));
244         tcb.mspace = ALLOCATOR.get_book_keeper();
245     }
246     if is_manual {
247         eprintln!("ld.so: entry '{}': {:#x}", path, entry);
248     }
249     entry
250 }
251