| 1 | //! Global initialization and retrieval of command line arguments. |
| 2 | //! |
| 3 | //! On some platforms these are stored during runtime startup, |
| 4 | //! and on some they are retrieved from the system on demand. |
| 5 | |
| 6 | #![allow (dead_code)] // runtime init functions not used during testing |
| 7 | |
| 8 | pub use super::common::Args; |
| 9 | use crate::ffi::CStr; |
| 10 | #[cfg (target_os = "hermit" )] |
| 11 | use crate::os::hermit::ffi::OsStringExt; |
| 12 | #[cfg (not(target_os = "hermit" ))] |
| 13 | use crate::os::unix::ffi::OsStringExt; |
| 14 | |
| 15 | /// One-time global initialization. |
| 16 | pub unsafe fn init(argc: isize, argv: *const *const u8) { |
| 17 | unsafe { imp::init(argc, argv) } |
| 18 | } |
| 19 | |
| 20 | /// Returns the command line arguments |
| 21 | pub fn args() -> Args { |
| 22 | let (argc, argv) = imp::argc_argv(); |
| 23 | |
| 24 | let mut vec = Vec::with_capacity(argc as usize); |
| 25 | |
| 26 | for i in 0..argc { |
| 27 | // SAFETY: `argv` is non-null if `argc` is positive, and it is |
| 28 | // guaranteed to be at least as long as `argc`, so reading from it |
| 29 | // should be safe. |
| 30 | let ptr = unsafe { argv.offset(i).read() }; |
| 31 | |
| 32 | // Some C commandline parsers (e.g. GLib and Qt) are replacing already |
| 33 | // handled arguments in `argv` with `NULL` and move them to the end. |
| 34 | // |
| 35 | // Since they can't directly ensure updates to `argc` as well, this |
| 36 | // means that `argc` might be bigger than the actual number of |
| 37 | // non-`NULL` pointers in `argv` at this point. |
| 38 | // |
| 39 | // To handle this we simply stop iterating at the first `NULL` |
| 40 | // argument. `argv` is also guaranteed to be `NULL`-terminated so any |
| 41 | // non-`NULL` arguments after the first `NULL` can safely be ignored. |
| 42 | if ptr.is_null() { |
| 43 | // NOTE: On Apple platforms, `-[NSProcessInfo arguments]` does not |
| 44 | // stop iterating here, but instead `continue`, always iterating |
| 45 | // up until it reached `argc`. |
| 46 | // |
| 47 | // This difference will only matter in very specific circumstances |
| 48 | // where `argc`/`argv` have been modified, but in unexpected ways, |
| 49 | // so it likely doesn't really matter which option we choose. |
| 50 | // See the following PR for further discussion: |
| 51 | // <https://github.com/rust-lang/rust/pull/125225> |
| 52 | break; |
| 53 | } |
| 54 | |
| 55 | // SAFETY: Just checked that the pointer is not NULL, and arguments |
| 56 | // are otherwise guaranteed to be valid C strings. |
| 57 | let cstr = unsafe { CStr::from_ptr(ptr) }; |
| 58 | vec.push(OsStringExt::from_vec(cstr.to_bytes().to_vec())); |
| 59 | } |
| 60 | |
| 61 | Args::new(vec) |
| 62 | } |
| 63 | |
| 64 | #[cfg (any( |
| 65 | target_os = "linux" , |
| 66 | target_os = "android" , |
| 67 | target_os = "freebsd" , |
| 68 | target_os = "dragonfly" , |
| 69 | target_os = "netbsd" , |
| 70 | target_os = "openbsd" , |
| 71 | target_os = "cygwin" , |
| 72 | target_os = "solaris" , |
| 73 | target_os = "illumos" , |
| 74 | target_os = "emscripten" , |
| 75 | target_os = "haiku" , |
| 76 | target_os = "hermit" , |
| 77 | target_os = "l4re" , |
| 78 | target_os = "fuchsia" , |
| 79 | target_os = "redox" , |
| 80 | target_os = "vxworks" , |
| 81 | target_os = "horizon" , |
| 82 | target_os = "aix" , |
| 83 | target_os = "nto" , |
| 84 | target_os = "hurd" , |
| 85 | target_os = "rtems" , |
| 86 | target_os = "nuttx" , |
| 87 | ))] |
| 88 | mod imp { |
| 89 | use crate::ffi::c_char; |
| 90 | use crate::ptr; |
| 91 | use crate::sync::atomic::{Atomic, AtomicIsize, AtomicPtr, Ordering}; |
| 92 | |
| 93 | // The system-provided argc and argv, which we store in static memory |
| 94 | // here so that we can defer the work of parsing them until its actually |
| 95 | // needed. |
| 96 | // |
| 97 | // Note that we never mutate argv/argc, the argv array, or the argv |
| 98 | // strings, which allows the code in this file to be very simple. |
| 99 | static ARGC: Atomic<isize> = AtomicIsize::new(0); |
| 100 | static ARGV: Atomic<*mut *const u8> = AtomicPtr::new(ptr::null_mut()); |
| 101 | |
| 102 | unsafe fn really_init(argc: isize, argv: *const *const u8) { |
| 103 | // These don't need to be ordered with each other or other stores, |
| 104 | // because they only hold the unmodified system-provided argv/argc. |
| 105 | ARGC.store(argc, Ordering::Relaxed); |
| 106 | ARGV.store(argv as *mut _, Ordering::Relaxed); |
| 107 | } |
| 108 | |
| 109 | #[inline (always)] |
| 110 | pub unsafe fn init(argc: isize, argv: *const *const u8) { |
| 111 | // on GNU/Linux if we are main then we will init argv and argc twice, it "duplicates work" |
| 112 | // BUT edge-cases are real: only using .init_array can break most emulators, dlopen, etc. |
| 113 | unsafe { really_init(argc, argv) }; |
| 114 | } |
| 115 | |
| 116 | /// glibc passes argc, argv, and envp to functions in .init_array, as a non-standard extension. |
| 117 | /// This allows `std::env::args` to work even in a `cdylib`, as it does on macOS and Windows. |
| 118 | #[cfg (all(target_os = "linux" , target_env = "gnu" ))] |
| 119 | #[used ] |
| 120 | #[unsafe(link_section = ".init_array.00099" )] |
| 121 | static ARGV_INIT_ARRAY: extern "C" fn( |
| 122 | crate::os::raw::c_int, |
| 123 | *const *const u8, |
| 124 | *const *const u8, |
| 125 | ) = { |
| 126 | extern "C" fn init_wrapper( |
| 127 | argc: crate::os::raw::c_int, |
| 128 | argv: *const *const u8, |
| 129 | _envp: *const *const u8, |
| 130 | ) { |
| 131 | unsafe { really_init(argc as isize, argv) }; |
| 132 | } |
| 133 | init_wrapper |
| 134 | }; |
| 135 | |
| 136 | pub fn argc_argv() -> (isize, *const *const c_char) { |
| 137 | // Load ARGC and ARGV, which hold the unmodified system-provided |
| 138 | // argc/argv, so we can read the pointed-to memory without atomics or |
| 139 | // synchronization. |
| 140 | // |
| 141 | // If either ARGC or ARGV is still zero or null, then either there |
| 142 | // really are no arguments, or someone is asking for `args()` before |
| 143 | // initialization has completed, and we return an empty list. |
| 144 | let argv = ARGV.load(Ordering::Relaxed); |
| 145 | let argc = if argv.is_null() { 0 } else { ARGC.load(Ordering::Relaxed) }; |
| 146 | |
| 147 | // Cast from `*mut *const u8` to `*const *const c_char` |
| 148 | (argc, argv.cast()) |
| 149 | } |
| 150 | } |
| 151 | |
| 152 | // Use `_NSGetArgc` and `_NSGetArgv` on Apple platforms. |
| 153 | // |
| 154 | // Even though these have underscores in their names, they've been available |
| 155 | // since the first versions of both macOS and iOS, and are declared in |
| 156 | // the header `crt_externs.h`. |
| 157 | // |
| 158 | // NOTE: This header was added to the iOS 13.0 SDK, which has been the source |
| 159 | // of a great deal of confusion in the past about the availability of these |
| 160 | // APIs. |
| 161 | // |
| 162 | // NOTE(madsmtm): This has not strictly been verified to not cause App Store |
| 163 | // rejections; if this is found to be the case, the previous implementation |
| 164 | // of this used `[[NSProcessInfo processInfo] arguments]`. |
| 165 | #[cfg (target_vendor = "apple" )] |
| 166 | mod imp { |
| 167 | use crate::ffi::{c_char, c_int}; |
| 168 | |
| 169 | pub unsafe fn init(_argc: isize, _argv: *const *const u8) { |
| 170 | // No need to initialize anything in here, `libdyld.dylib` has already |
| 171 | // done the work for us. |
| 172 | } |
| 173 | |
| 174 | pub fn argc_argv() -> (isize, *const *const c_char) { |
| 175 | unsafe extern "C" { |
| 176 | // These functions are in crt_externs.h. |
| 177 | fn _NSGetArgc() -> *mut c_int; |
| 178 | fn _NSGetArgv() -> *mut *mut *mut c_char; |
| 179 | } |
| 180 | |
| 181 | // SAFETY: The returned pointer points to a static initialized early |
| 182 | // in the program lifetime by `libdyld.dylib`, and as such is always |
| 183 | // valid. |
| 184 | // |
| 185 | // NOTE: Similar to `_NSGetEnviron`, there technically isn't anything |
| 186 | // protecting us against concurrent modifications to this, and there |
| 187 | // doesn't exist a lock that we can take. Instead, it is generally |
| 188 | // expected that it's only modified in `main` / before other code |
| 189 | // runs, so reading this here should be fine. |
| 190 | let argc = unsafe { _NSGetArgc().read() }; |
| 191 | // SAFETY: Same as above. |
| 192 | let argv = unsafe { _NSGetArgv().read() }; |
| 193 | |
| 194 | // Cast from `*mut *mut c_char` to `*const *const c_char` |
| 195 | (argc as isize, argv.cast()) |
| 196 | } |
| 197 | } |
| 198 | |