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 | |