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