]> git.lizzy.rs Git - rust.git/blob - library/std/src/sys/unix/args.rs
Rollup merge of #100445 - krasimirgg:llvm-16-msan, r=tmiasko
[rust.git] / library / std / src / sys / unix / args.rs
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::OsString;
9 use crate::fmt;
10 use crate::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     imp::args()
20 }
21
22 pub struct Args {
23     iter: vec::IntoIter<OsString>,
24 }
25
26 impl !Send for Args {}
27 impl !Sync for Args {}
28
29 impl fmt::Debug for Args {
30     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31         self.iter.as_slice().fmt(f)
32     }
33 }
34
35 impl Iterator for Args {
36     type Item = OsString;
37     fn next(&mut self) -> Option<OsString> {
38         self.iter.next()
39     }
40     fn size_hint(&self) -> (usize, Option<usize>) {
41         self.iter.size_hint()
42     }
43 }
44
45 impl ExactSizeIterator for Args {
46     fn len(&self) -> usize {
47         self.iter.len()
48     }
49 }
50
51 impl DoubleEndedIterator for Args {
52     fn next_back(&mut self) -> Option<OsString> {
53         self.iter.next_back()
54     }
55 }
56
57 #[cfg(any(
58     target_os = "linux",
59     target_os = "android",
60     target_os = "freebsd",
61     target_os = "dragonfly",
62     target_os = "netbsd",
63     target_os = "openbsd",
64     target_os = "solaris",
65     target_os = "illumos",
66     target_os = "emscripten",
67     target_os = "haiku",
68     target_os = "l4re",
69     target_os = "fuchsia",
70     target_os = "redox",
71     target_os = "vxworks",
72     target_os = "horizon"
73 ))]
74 mod imp {
75     use super::Args;
76     use crate::ffi::{CStr, OsString};
77     use crate::os::unix::prelude::*;
78     use crate::ptr;
79     use crate::sync::atomic::{AtomicIsize, AtomicPtr, Ordering};
80
81     // The system-provided argc and argv, which we store in static memory
82     // here so that we can defer the work of parsing them until its actually
83     // needed.
84     //
85     // Note that we never mutate argv/argc, the argv array, or the argv
86     // strings, which allows the code in this file to be very simple.
87     static ARGC: AtomicIsize = AtomicIsize::new(0);
88     static ARGV: AtomicPtr<*const u8> = AtomicPtr::new(ptr::null_mut());
89
90     unsafe fn really_init(argc: isize, argv: *const *const u8) {
91         // These don't need to be ordered with each other or other stores,
92         // because they only hold the unmodified system-provide argv/argc.
93         ARGC.store(argc, Ordering::Relaxed);
94         ARGV.store(argv as *mut _, Ordering::Relaxed);
95     }
96
97     #[inline(always)]
98     pub unsafe fn init(_argc: isize, _argv: *const *const u8) {
99         // On Linux-GNU, we rely on `ARGV_INIT_ARRAY` below to initialize
100         // `ARGC` and `ARGV`. But in Miri that does not actually happen so we
101         // still initialize here.
102         #[cfg(any(miri, not(all(target_os = "linux", target_env = "gnu"))))]
103         really_init(_argc, _argv);
104     }
105
106     /// glibc passes argc, argv, and envp to functions in .init_array, as a non-standard extension.
107     /// This allows `std::env::args` to work even in a `cdylib`, as it does on macOS and Windows.
108     #[cfg(all(target_os = "linux", target_env = "gnu"))]
109     #[used]
110     #[link_section = ".init_array.00099"]
111     static ARGV_INIT_ARRAY: extern "C" fn(
112         crate::os::raw::c_int,
113         *const *const u8,
114         *const *const u8,
115     ) = {
116         extern "C" fn init_wrapper(
117             argc: crate::os::raw::c_int,
118             argv: *const *const u8,
119             _envp: *const *const u8,
120         ) {
121             unsafe {
122                 really_init(argc as isize, argv);
123             }
124         }
125         init_wrapper
126     };
127
128     pub fn args() -> Args {
129         Args { iter: clone().into_iter() }
130     }
131
132     fn clone() -> Vec<OsString> {
133         unsafe {
134             // Load ARGC and ARGV, which hold the unmodified system-provided
135             // argc/argv, so we can read the pointed-to memory without atomics
136             // or synchronization.
137             //
138             // If either ARGC or ARGV is still zero or null, then either there
139             // really are no arguments, or someone is asking for `args()`
140             // before initialization has completed, and we return an empty
141             // list.
142             let argv = ARGV.load(Ordering::Relaxed);
143             let argc = if argv.is_null() { 0 } else { ARGC.load(Ordering::Relaxed) };
144             (0..argc)
145                 .map(|i| {
146                     let cstr = CStr::from_ptr(*argv.offset(i) as *const libc::c_char);
147                     OsStringExt::from_vec(cstr.to_bytes().to_vec())
148                 })
149                 .collect()
150         }
151     }
152 }
153
154 #[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))]
155 mod imp {
156     use super::Args;
157     use crate::ffi::CStr;
158
159     pub unsafe fn init(_argc: isize, _argv: *const *const u8) {}
160
161     #[cfg(target_os = "macos")]
162     pub fn args() -> Args {
163         use crate::os::unix::prelude::*;
164         extern "C" {
165             // These functions are in crt_externs.h.
166             fn _NSGetArgc() -> *mut libc::c_int;
167             fn _NSGetArgv() -> *mut *mut *mut libc::c_char;
168         }
169
170         let vec = unsafe {
171             let (argc, argv) =
172                 (*_NSGetArgc() as isize, *_NSGetArgv() as *const *const libc::c_char);
173             (0..argc as isize)
174                 .map(|i| {
175                     let bytes = CStr::from_ptr(*argv.offset(i)).to_bytes().to_vec();
176                     OsStringExt::from_vec(bytes)
177                 })
178                 .collect::<Vec<_>>()
179         };
180         Args { iter: vec.into_iter() }
181     }
182
183     // As _NSGetArgc and _NSGetArgv aren't mentioned in iOS docs
184     // and use underscores in their names - they're most probably
185     // are considered private and therefore should be avoided
186     // Here is another way to get arguments using Objective C
187     // runtime
188     //
189     // In general it looks like:
190     // res = Vec::new()
191     // let args = [[NSProcessInfo processInfo] arguments]
192     // for i in (0..[args count])
193     //      res.push([args objectAtIndex:i])
194     // res
195     #[cfg(any(target_os = "ios", target_os = "watchos"))]
196     pub fn args() -> Args {
197         use crate::ffi::OsString;
198         use crate::mem;
199         use crate::str;
200
201         extern "C" {
202             fn sel_registerName(name: *const libc::c_uchar) -> Sel;
203             fn objc_getClass(class_name: *const libc::c_uchar) -> NsId;
204         }
205
206         #[cfg(target_arch = "aarch64")]
207         extern "C" {
208             fn objc_msgSend(obj: NsId, sel: Sel) -> NsId;
209             #[allow(clashing_extern_declarations)]
210             #[link_name = "objc_msgSend"]
211             fn objc_msgSend_ul(obj: NsId, sel: Sel, i: libc::c_ulong) -> NsId;
212         }
213
214         #[cfg(not(target_arch = "aarch64"))]
215         extern "C" {
216             fn objc_msgSend(obj: NsId, sel: Sel, ...) -> NsId;
217             #[allow(clashing_extern_declarations)]
218             #[link_name = "objc_msgSend"]
219             fn objc_msgSend_ul(obj: NsId, sel: Sel, ...) -> NsId;
220         }
221
222         type Sel = *const libc::c_void;
223         type NsId = *const libc::c_void;
224
225         let mut res = Vec::new();
226
227         unsafe {
228             let process_info_sel = sel_registerName("processInfo\0".as_ptr());
229             let arguments_sel = sel_registerName("arguments\0".as_ptr());
230             let utf8_sel = sel_registerName("UTF8String\0".as_ptr());
231             let count_sel = sel_registerName("count\0".as_ptr());
232             let object_at_sel = sel_registerName("objectAtIndex:\0".as_ptr());
233
234             let klass = objc_getClass("NSProcessInfo\0".as_ptr());
235             let info = objc_msgSend(klass, process_info_sel);
236             let args = objc_msgSend(info, arguments_sel);
237
238             let cnt: usize = mem::transmute(objc_msgSend(args, count_sel));
239             for i in 0..cnt {
240                 let tmp = objc_msgSend_ul(args, object_at_sel, i as libc::c_ulong);
241                 let utf_c_str: *const libc::c_char = mem::transmute(objc_msgSend(tmp, utf8_sel));
242                 let bytes = CStr::from_ptr(utf_c_str).to_bytes();
243                 res.push(OsString::from(str::from_utf8(bytes).unwrap()))
244             }
245         }
246
247         Args { iter: res.into_iter() }
248     }
249 }
250
251 #[cfg(target_os = "espidf")]
252 mod imp {
253     use super::Args;
254
255     #[inline(always)]
256     pub unsafe fn init(_argc: isize, _argv: *const *const u8) {}
257
258     pub fn args() -> Args {
259         Args { iter: Vec::new().into_iter() }
260     }
261 }