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