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