### argv_addrs() Source: https://docs.rs/killmyargv/latest/killmyargv/fn.argv_addrs.html Get the argv start address and end address. ```APIDOC ## Function argv_addrs ### Description Get the argv start address and end address. ### Signature rust pub fn argv_addrs() -> Result<(*mut u8, *mut u8), EnvError> ### Parameters None. ### Return Value - **Ok((*mut u8, *mut u8))** - A tuple containing the start address and end address of the argv array. - **Err(EnvError)** - An error if the addresses cannot be obtained. ``` -------------------------------- ### Setting Command Line Arguments Source: https://docs.rs/killmyargv/latest/src/killmyargv/lib.rs.html Method to set new command line arguments, handling memory safety and null termination. ```rust 252 /// set a new args/cmdline. 253 pub fn set(&self, chars: &[u8]) { 254 trace!( 255 "set len: {:?}, need not null byte: {:?}, String: {:?}, bytes hex: {chars:02x?}", 256 chars.len(), 257 self.nonul_byte, 258 OsStr::from_bytes(chars) 259 ); 260 unsafe { 261 if chars.len() < self.max_len { 262 slice::from_raw_parts_mut(self.begin_addr, chars.len()).copy_from_slice(chars); 263 264 ptr::write_bytes( 265 self.begin_addr.add(chars.len()), 266 0x00, 267 self.max_len - chars.len(), 268 ); 269 } else { 270 slice::from_raw_parts_mut(self.begin_addr, self.max_len) 271 .copy_from_slice(&chars[..self.max_len]); 272 } 273 // It should be handled by advanced packaging or users, ``` -------------------------------- ### argv_addr.rs: init and addr for retrieving argc/argv Source: https://docs.rs/killmyargv/latest/src/killmyargv/argv_addr.rs.html The complete source of argv_addr.rs, defining the public init and addr functions and the platform-specific imp module. It stores the system-provided argc/argv in atomics, uses a .init_array hook on Linux-GNU, and provides a stack-walking fallback to locate argv when not initialized. ```rust use super::EnvError; use std::ffi::c_char; // init() copied from https://doc.rust-lang.org/src/std/sys/unix/args.rs.html#12-15 // But what's the point? #[allow(unused)] /// One-time global initialization. /// The above statement was made by rust std, and no one here is responsible for that statement. pub unsafe fn init(argc: isize, argv: *const *const c_char) { imp::init(argc, argv) } /// stack walking: /// https://github.com/rust-lang/rust/pull/66547#issuecomment-556013952 /// and https://github.com/leo60228/libargs /// some links: /// https://github.com/rust-lang/rust/issues/105999 /// https://github.com/rust-lang/rust/pull/106001 /// https://github.com/rust-lang/rust/commit/e97203c3f893893611818997bbeb0116ded2605f pub(super) fn addr() -> Result<(usize, *const *const c_char), EnvError> { imp::addr() } #[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" ))] mod imp { use std::{ ffi::{c_char, c_int}, ptr, sync::atomic::{AtomicIsize, AtomicPtr, Ordering}, }; #[cfg(not(feature = "force_walking"))] use std::{ ffi::{CStr, OsStr}, os::unix::ffi::OsStrExt, }; use super::EnvError; use log::trace; // 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 c_char> = AtomicPtr::new(ptr::null_mut()); unsafe fn really_init(argc: isize, argv: *const *const c_char) { // 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); } #[cfg_attr(all(target_os = "linux", target_env = "gnu"), allow(unused))] #[inline(always)] pub unsafe fn init(_argc: isize, _argv: *const *const c_char) { // On Linux-GNU, we rely on `ARGV_INIT_ARRAY` below to initialize // `ARGC` and `ARGV`. But in Miri that does not actually happen so we // still initialize here. #[cfg(any(miri, not(all(target_os = "linux", target_env = "gnu"))))] 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(c_int, *const *const c_char, *const *const c_char) = { extern "C" fn init_wrapper( argc: c_int, argv: *const *const c_char, _envp: *const *const c_char, ) { unsafe { really_init(argc as isize, argv); } } init_wrapper }; // from: https://github.com/leo60228/libargs/blob/master/src/lib.rs#L16-L30 // or `fn from_backtrace(...) -> (...)`? fn from_stack_walking(environ: *const *const c_char) -> (usize, *const *const c_char) { let mut walk_environ = environ as *const usize; walk_environ = walk_environ.wrapping_sub(1); let mut i = 0; loop { let argc_ptr = walk_environ.wrapping_sub(1) as *const c_int; let argc = unsafe { *argc_ptr }; if argc == i { break (argc as usize, walk_environ as *const *const c_char); } walk_environ = walk_environ.wrapping_sub(1); i += 1; } } pub(super) fn addr() -> Result<(usize, *const *const c_char), EnvError> { unsafe { // 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 ``` -------------------------------- ### Public API Methods Source: https://docs.rs/killmyargv/latest/src/killmyargv/lib.rs.html Public methods for retrieving metadata and managing argument state. ```rust 230 /// Gets the maximum byte length for which the cmdline can be set. 231 pub fn max_len(&self) -> usize { 232 self.max_len 233 } 234 235 /// origin argv length, containing the terminating NUL byte. 236 /// This bit is written to a non-nul value requiring attention to os behavior. 237 pub fn nonul_byte(&self) -> Option { 238 self.nonul_byte 239 } 240 241 /// Undo the args/cmdline changes. 242 pub fn revert(&self) { 243 let backup_chars: Vec = self 244 .saved_argv 245 .iter() 246 .flat_map(|s| s.as_bytes_with_nul()) 247 .cloned() 248 .collect(); 249 Self::set(self, &backup_chars) 250 } ``` -------------------------------- ### Rust: environ() function for Apple platforms Source: https://docs.rs/killmyargv/latest/src/killmyargv/env_addr.rs.html Defines the `environ()` function for Apple platforms using `libc::_NSGetEnviron()`. This is the documented alternative to the `environ` static and is available since early macOS/iOS versions. The function is marked `unsafe` and returns a pointer to the environment. ```rust #[cfg(target_vendor = "apple")] pub unsafe fn environ() -> *mut *const *const c_char { libc::_NSGetEnviron() as *mut *const *const c_char } ``` -------------------------------- ### Retrieve argv/argc with feature-gated fallbacks Source: https://docs.rs/killmyargv/latest/src/killmyargv/argv_addr.rs.html Loads ARGV and ARGC atomically. If null and any of comp_argv, stack_walking, or force_walking features are enabled, it attempts to compute argv from the environment pointer, comparing against std::env::args_os. Returns errors like EnvError::FailedToGetArgvPointer or EnvError::InvalidArgvPointer on failure. ```rust let argv = ARGV.load(Ordering::Relaxed); let argc = ARGC.load(Ordering::Relaxed); trace!("argc: {argc}, argv: {argv:?}"); #[cfg(any( feature = "comp_argv", feature = "stack_walking", feature = "force_walking" ))] if argv.is_null() || (*argv).is_null() { use crate::env_addr::envptr; #[cfg(not(feature = "force_walking"))] use std::env::args_os; let envp = envptr().ok_or(EnvError::FailedToGetArgvPointer)?; #[cfg(feature = "force_walking")] return Ok(from_stack_walking(envp)); #[cfg(not(feature = "force_walking"))] { let mut args = args_os(); let mut args_is_empty = false; trace!("args: {:#?}", &args); if args.len() == 0 { args_is_empty = true; trace!("std args is empty"); } if args_is_empty { #[cfg(feature = "stack_walking")] return Ok(from_stack_walking(envp)); #[cfg(not(feature = "stack_walking"))] return Err(EnvError::FailedToGetArgvPointer); } let std_argc = args.len(); // *environ[] == *argv[] + argc + 1, aka // *argv[] = *environ[] - (argc + 1) let comp_argv = envp.sub(std_argc + 1); trace!("environ ptr: {envp:?}, argc from std: {std_argc:?}, computed argv: {comp_argv:?}"); if comp_argv.is_null() || (*comp_argv).is_null() { return Err(EnvError::InvalidArgvPointer); } let frist = args.next().ok_or(EnvError::InvalidArgvPointer)?; let argv_frist = OsStr::from_bytes(CStr::from_ptr(*comp_argv).to_bytes()); trace!("comp argv[0]: {argv_frist:?}, std argv[0]: {frist:?}"); if argv_frist == frist { Ok((std_argc, comp_argv)) } else { Err(EnvError::InvalidArgvPointer) } } } else { Ok((argc as usize, argv)) } #[cfg(all( not(feature = "comp_argv"), not(feature = "stack_walking"), not(feature = "force_walking") ))] if argv.is_null() || (*argv).is_null() { Err(EnvError::InvalidArgvPointer) } else { Ok((argc as usize, argv)) } ``` -------------------------------- ### Argv and Environment Replacement Logic Source: https://docs.rs/killmyargv/latest/src/killmyargv/lib.rs.html Internal logic for replacing argv elements and environment variables when specific features are enabled. ```rust 151 .iter() 152 .map(|s| s.as_ptr()) 153 .collect::>(); 154 155 #[cfg(feature = "replace_argv_element")] 156 for i in (0..argv_mem.element).rev() { 157 if let Some(new_ptr) = new_argvp.pop() { 158 trace!("processing argc: {i}, ptr: {new_ptr:?}"); 159 unsafe { 160 let ptr = argv_mem.pointer_addr as *mut *const c_char; 161 trace!("ptrs: {:?}, {:?}, {new_ptr:?}", *ptr.add(i), ptr.add(i)); 162 ptr.add(i).write(new_ptr); 163 } 164 } else { 165 trace!("new_argvp as none"); 166 } 167 } 168 169 #[allow(unused)] 170 #[cfg(all(feature = "clobber_environ", feature = "replace_environ_element"))] 171 // I haven't decided if I want to remove it or not, 172 // since setenv makes it probably unnecessary. 173 let mut new_envp = env_mem 174 .saved 175 .leak() 176 .iter() 177 .map(|s| s.as_ptr()) 178 .collect::>(); 179 180 // Using std instead of manually replacing each element in environ 181 // is just being lazy. 182 #[cfg(all(feature = "clobber_environ", feature = "replace_environ_element"))] 183 for (key, value) in vars_os() { 184 remove_var(&key); 185 set_var(key, value); // Expected: libc::setenv(key, value, 1) 186 } 187 188 Ok(KillMyArgv { 189 begin_addr: argv_mem.begin_addr as *mut u8, 190 end_addr: env_mem.end_addr as *mut u8, 191 max_len: argv_mem.byte_len + env_mem.byte_len - 1, 192 saved_argv: argv_mem.saved, 193 nonul_byte: Some(argv_mem.byte_len), 194 }) ``` -------------------------------- ### Rust: environ() function for non-Apple platforms Source: https://docs.rs/killmyargv/latest/src/killmyargv/env_addr.rs.html Defines the `environ()` function for non-Apple platforms using the POSIX `environ` static. The function is marked `#[cfg(not(target_vendor = "apple"))]` and returns a pointer to the environment. ```rust #[cfg(not(target_vendor = "apple"))] pub unsafe fn environ() -> *mut *const *const c_char { extern "C" { static mut environ: *const *const c_char; } &raw mut environ } ``` -------------------------------- ### killmyargv lib.rs source code Source: https://docs.rs/killmyargv/latest/src/killmyargv/lib.rs.html The complete source code of the killmyargv crate's lib.rs file, defining the EnvError enum, KillMyArgv struct, MemInfo struct, and functions for handling argv and environ memory. ```rust 1mod argv_addr; 2mod env_addr; 3 4#[cfg(all(feature = "clobber_environ", feature = "replace_environ_element"))] 5use std::env::{remove_var, set_var, vars_os}; 6use std::{ 7 ffi::{c_char, CStr, CString, OsStr}, 8 os::unix::ffi::OsStrExt, 9 ptr, slice, 10}; 11 12use log::{error, trace, warn}; 13use thiserror::Error; 14 15#[derive(Error, Debug)] 16pub enum EnvError { 17 #[error("The *argv[] points to an invalid memory address.")] 18 InvalidArgvPointer, 19 #[error("Failed to get *argv[] pointer.")] 20 FailedToGetArgvPointer, 21 #[error("Failed to get a string from pointer `{ptr:?}`.")] 22 FailedToGetString { ptr: *const *const c_char }, 23 #[error("The pointer `{ptr:?}` points to null.")] 24 PonitToNull { ptr: *const *const c_char }, 25 #[error("The pointer as null.")] 26 NullPointer, 27} 28 29#[derive(Debug)] 30pub struct KillMyArgv { 31 begin_addr: *mut u8, 32 end_addr: *mut u8, 33 max_len: usize, 34 saved_argv: Vec, 35 nonul_byte: Option, 36} 37 38#[derive(Debug)] 39struct MemInfo { 40 begin_addr: *const c_char, 41 end_addr: *const c_char, 42 byte_len: usize, 43 #[allow(unused)] 44 // argv[element] or environ[element] 45 element: usize, 46 saved: Vec, 47 #[allow(unused)] 48 pointer_addr: *const *const c_char, 49} 50 51unsafe fn from_addr(count: usize, ptr: *const *const c_char) -> Result { 52 if ptr.is_null() { 53 return Err(EnvError::NullPointer); 54 } else if (*ptr).is_null() { 55 return Err(EnvError::PonitToNull { ptr }); 56 } 57 58 let mut byte_len = 0; 59 let mut end_addr = *ptr; 60 let mut saved: Vec = Vec::with_capacity(count); 61 let cstr_ptrs = slice::from_raw_parts(ptr, count); 62 let cstr_ptrs_len = cstr_ptrs.len(); 63 if cstr_ptrs_len != count { 64 warn!( 65 "The actual length of the array ({cstr_ptrs_len}) does not match the argument ({count})." 66 ); 67 } 68 for (i, cstr_ptr) in cstr_ptrs.into_iter().enumerate() { 69 trace!( 70 "string[{i}] ptr info: current ptr={:?}, point to={cstr_ptr:?}", 71 cstr_ptr as *const _ 72 ); 73 if cstr_ptr.is_null() { 74 warn!("the string[{i}] is null, pls check"); 75 break; 76 } 77 let cstr_len = CStr::from_ptr(*cstr_ptr).to_bytes_with_nul().len(); 78 saved.push(CStr::from_ptr(*cstr_ptr).into()); 79 // Decide elsewhere whether to exclude nul. 80 byte_len += cstr_len; 81 82 trace!( 83 "string[{i}] collect: recorded len={byte_len}, start addr={cstr_ptr:?}, len={cstr_len}, end addr (with null)={:?}", 84 cstr_ptr.add(cstr_len - 1) 85 ); 86 if i == count - 1 { 87 // Perhaps it would be better to add 1 byte manually when 88 // calculating the length. 89 // Avoid overstepping the bounds. 90 end_addr = cstr_ptr.add(cstr_len - 1); 91 } 92 } 93 trace!( 94 "collect count: {count}, string ptr: {ptr:?}, addr: {:?} -> {end_addr:?}, len: {byte_len}, vec_cap: {}", 95 *ptr, saved.capacity() 96 ); 97 if byte_len != 0 { 98 Ok(MemInfo { 99 begin_addr: *ptr, 100 end_addr, 101 byte_len, 102 element: count, 103 saved, 104 pointer_addr: ptr, 105 }) 106 } else { 107 Err(EnvError::FailedToGetString { ptr }) 108 } 109} 110 111// Get environ address, ignore errors 112fn from_env() -> Option { 113 let (count, ptr) = env_addr::addr()?; 114 unsafe { from_addr(count, ptr) } 115 .map_err(|e| { 116 trace!("env err: {e:?}"); 117 e 118 }) 119 .ok() 120} 121 122fn from_argv() -> Result { 123 let (count, ptr) = argv_addr::addr()?; 124 unsafe { from_addr(count, ptr) }.map_err(|e| { 125 trace!("argv err: {e:?}"); 126 e 127 }) 128} 129 130/// Get the argv start address and end address. 131pub fn argv_addrs() -> Result<(*mut u8, *mut u8), EnvError> { 132 from_argv().map(|m| (m.begin_addr as *mut u8, m.end_addr as *mut u8)) 133} 134 135impl KillMyArgv { 136 /// Get the argv start address and end address. 137 pub fn argv_addrs(&self) -> (*mut u8, *mut u8) { 138 (self.begin_addr, self.end_addr) 139 } 140 141 pub fn new() -> Result { 142 let argv_mem = from_argv()?; 143 144 if let Some(env_mem) = from_env() { 145 trace!("argv struct: {argv_mem:#?}, env struct: {env_mem:#?}"); 146 #[cfg(feature = "replace_argv_element")] 147 let mut new_argvp = argv_mem 148 .saved 149 .clone() .leak() ``` -------------------------------- ### Read process arguments via Objective-C runtime on macOS Source: https://docs.rs/killmyargv/latest/src/killmyargv/argv_addr.rs.html Use this when standard argv parsing is not viable on macOS. It declares Objective-C runtime functions (sel_registerName, objc_getClass, objc_msgSend) and iterates NSProcessInfo arguments. Requires unsafe code and uses different objc_msgSend signatures for aarch64 and other architectures. ```rust use crate::ffi::OsString; use crate::mem; use crate::str; extern "C" { fn sel_registerName(name: *const libc::c_uchar) -> Sel; fn objc_getClass(class_name: *const libc::c_uchar) -> NsId; } #[cfg(target_arch = "aarch64")] extern "C" { fn objc_msgSend(obj: NsId, sel: Sel) -> NsId; #[allow(clashing_extern_declarations)] #[link_name = "objc_msgSend"] fn objc_msgSend_ul(obj: NsId, sel: Sel, i: libc::c_ulong) -> NsId; } #[cfg(not(target_arch = "aarch64"))] extern "C" { fn objc_msgSend(obj: NsId, sel: Sel, ...) -> NsId; #[allow(clashing_extern_declarations)] #[link_name = "objc_msgSend"] fn objc_msgSend_ul(obj: NsId, sel: Sel, ...) -> NsId; } type Sel = *mut libc::c_void; type NsId = *mut libc::c_void; let mut res = Vec::new(); unsafe { let process_info_sel = sel_registerName("processInfo\0".as_ptr()); let arguments_sel = sel_registerName("arguments\0".as_ptr()); let utf8_sel = sel_registerName("UTF8String\0".as_ptr()); let count_sel = sel_registerName("count\0".as_ptr()); let object_at_sel = sel_registerName("objectAtIndex:\0".as_ptr()); let klass = objc_getClass("NSProcessInfo\0".as_ptr()); let info = objc_msgSend(klass, process_info_sel); let args = objc_msgSend(info, arguments_sel); let cnt: usize = mem::transmute(objc_msgSend(args, count_sel)); for i in 0..cnt { let tmp = objc_msgSend_ul(args, object_at_sel, i as libc::c_ulong); let utf_c_str: *mut libc::c_char = mem::transmute(objc_msgSend(tmp, utf8_sel)); let bytes = CStr::from_ptr(utf_c_str).to_bytes(); res.push(OsString::from(str::from_utf8(bytes).unwrap())) } } Args { iter: res.into_iter() } ``` -------------------------------- ### KillMyArgv Methods Source: https://docs.rs/killmyargv/latest/killmyargv/struct.KillMyArgv.html Methods provided by the KillMyArgv struct for managing argv and cmdline. ```APIDOC ## KillMyArgv::argv_addrs ### Description Returns the start and end addresses of the argv array. ### Method public method ### Signature pub fn argv_addrs(&self) -> (*mut u8, *mut u8) ### Parameters None ### Return Value - `(*mut u8, *mut u8)` - Tuple of raw pointers to the argv start and end addresses. ### Example let (start, end) = killmyargv.argv_addrs(); ## KillMyArgv::new ### Description Creates a new KillMyArgv instance, which may fail due to environment errors. ### Method public constructor ### Signature pub fn new() -> Result ### Parameters None ### Return Value - `Result` - Ok containing a new KillMyArgv instance, or Err with an EnvError. ### Example let killmyargv = KillMyArgv::new()?; ## KillMyArgv::max_len ### Description Gets the maximum byte length for which the cmdline can be set. ### Method public method ### Signature pub fn max_len(&self) -> usize ### Parameters None ### Return Value - `usize` - The maximum byte length for the cmdline. ### Example let max = killmyargv.max_len(); ## KillMyArgv::nonul_byte ### Description Returns the origin argv length, containing the terminating NUL byte. This bit is written to a non-nul value requiring attention to OS behavior. ### Method public method ### Signature pub fn nonul_byte(&self) -> Option ### Parameters None ### Return Value - `Option` - Some with the origin argv length (including NUL) or None if not available. ### Example if let Some(len) = killmyargv.nonul_byte() { ... } ## KillMyArgv::revert ### Description Undo the args/cmdline changes made by previous set operations. ### Method public method ### Signature pub fn revert(&self) ### Parameters None ### Return Value None ### Example killmyargv.revert(); ## KillMyArgv::set ### Description Sets a new args/cmdline for the process. ### Method public method ### Signature pub fn set(&self, chars: &[u8]) ### Parameters - `chars` (`&[u8]`) - Required - The new arguments as a byte slice. ### Return Value None ### Example let new_args = b"new args"; killmyargv.set(new_args); ``` -------------------------------- ### Rust: envptr() function to derive argv pointer Source: https://docs.rs/killmyargv/latest/src/killmyargv/env_addr.rs.html Defines the `envptr()` function that returns an `Option<*const *const c_char>` by dereferencing the `environ()` pointer. It returns `None` if the pointer is null. This function is `pub(super)` and is used to derive the argv pointer. ```rust #[allow(unused)] pub(super) unsafe fn envptr() -> Option<*const *const c_char> { let envp = *environ(); if envp.is_null() { None } else { Some(envp) } } ``` -------------------------------- ### Argv Replacement Fallback Logic Source: https://docs.rs/killmyargv/latest/src/killmyargv/lib.rs.html Fallback logic for replacing argv elements when environment replacement is not performed. ```rust 195 } else { 196 trace!("argv struct: {argv_mem:#?}"); 197 #[cfg(feature = "replace_argv_element")] 198 let mut new_argvp = argv_mem 199 .saved 200 .clone() 201 .leak() 202 .iter() 203 .map(|s| s.as_ptr()) 204 .collect::>(); 205 206 #[cfg(feature = "replace_argv_element")] 207 for i in (0..argv_mem.element).rev() { 208 if let Some(new_ptr) = new_argvp.pop() { 209 trace!("processing argc: {i}, ptr: {new_ptr:?}"); 210 unsafe { 211 let ptr = argv_mem.pointer_addr as *mut *const c_char; 212 ptr.add(i).write(new_ptr); 213 trace!("ptrs: {:?}, {:?}, {new_ptr:?}", *ptr.add(i), ptr.add(i)); 214 } 215 } else { 216 trace!("argv ptr as empty"); 217 } 218 } 219 220 Ok(KillMyArgv { 221 begin_addr: argv_mem.begin_addr as *mut u8, 222 end_addr: argv_mem.end_addr as *mut u8, 223 max_len: argv_mem.byte_len - 1, 224 saved_argv: argv_mem.saved, 225 nonul_byte: None, 226 }) 227 } ``` -------------------------------- ### Rust: addr() function when clobber_environ is disabled Source: https://docs.rs/killmyargv/latest/src/killmyargv/env_addr.rs.html Defines the `addr()` function that returns `None` when the `clobber_environ` feature is not enabled. This is the default behavior. ```rust #[cfg(not(feature = "clobber_environ"))] pub(super) fn addr() -> Option<(usize, *const *const c_char)> { None } ``` -------------------------------- ### macOS-specific addr() using _NSGetArgc and _NSGetArgv Source: https://docs.rs/killmyargv/latest/src/killmyargv/argv_addr.rs.html For macOS, this function uses the private functions _NSGetArgc and _NSGetArgv from crt_externs.h to obtain the argument count and pointer. Returns EnvError::InvalidArgvPointer if the pointer is null. ```rust #[cfg(target_os = "macos")] pub fn addr() -> Result { extern "C" { // These functions are in crt_externs.h. fn _NSGetArgc() -> *mut c_int; fn _NSGetArgv() -> *mut *mut *mut c_char; } unsafe { let (argc, argv) = ( *_NSGetArgc() as isize, *_NSGetArgv() as *const *const c_char, ); if argv.is_null() || (*argv).is_null() { Err(EnvError::InvalidArgvPointer) } else { Ok((argc as usize, argv)) } }; } ``` -------------------------------- ### Public API Functions Source: https://docs.rs/killmyargv/latest/src/killmyargv/lib.rs.html Public functions and methods for the killmyargv library that can be called directly by users. ```APIDOC ## `argv_addrs() -> Result<(*mut u8, *mut u8), EnvError>` ### Description Returns the start and end addresses of the argv array as a tuple of mutable pointers. ### Signature ```rust pub fn argv_addrs() -> Result<(*mut u8, *mut u8), EnvError> ``` ### Parameters None. ### Returns - `Ok((*mut u8, *mut u8))` - A tuple containing the start and end addresses of the argv array. - `Err(EnvError)` - An error if the argv pointer cannot be obtained or is invalid. ### Errors - `EnvError::InvalidArgvPointer` - The argv pointer points to an invalid memory address. - `EnvError::FailedToGetArgvPointer` - Failed to get the argv pointer. - `EnvError::FailedToGetString` - Failed to get a string from a pointer. - `EnvError::PonitToNull` - The pointer points to null. - `EnvError::NullPointer` - The pointer is null. ``` ```APIDOC ## `KillMyArgv::argv_addrs(&self) -> (*mut u8, *mut u8)` ### Description Returns the start and end addresses of the argv array stored in the `KillMyArgv` instance. ### Signature ```rust pub fn argv_addrs(&self) -> (*mut u8, *mut u8) ``` ### Parameters - `&self` - A reference to the `KillMyArgv` instance. ### Returns - `(*mut u8, *mut u8)` - A tuple containing the start and end addresses of the argv array. ``` ```APIDOC ## `KillMyArgv::new() -> Result` ### Description Creates a new `KillMyArgv` instance by collecting the argv and environ memory information. ### Signature ```rust pub fn new() -> Result ``` ### Parameters None. ### Returns - `Ok(KillMyArgv)` - A new `KillMyArgv` instance. - `Err(EnvError)` - An error if the argv pointer cannot be obtained or is invalid. ### Errors - `EnvError::InvalidArgvPointer` - The argv pointer points to an invalid memory address. - `EnvError::FailedToGetArgvPointer` - Failed to get the argv pointer. - `EnvError::FailedToGetString` - Failed to get a string from a pointer. - `EnvError::PonitToNull` - The pointer points to null. - `EnvError::NullPointer` - The pointer is null. ``` -------------------------------- ### Rust: addr() function when clobber_environ is enabled Source: https://docs.rs/killmyargv/latest/src/killmyargv/env_addr.rs.html Defines the `addr()` function that iterates through the environment pointer to count elements and return the count and pointer. This is used when the `clobber_environ` feature is enabled. ```rust #[cfg(feature = "clobber_environ")] // copied from https://github.com/rust-lang/rust/blob/1.84.0/library/src/std/sys/pal/unix/os.rs.html#L626-L658 pub(super) fn addr() -> Option<(usize, *const *const c_char)> { unsafe { let envp = *environ(); let mut environ = envp; // I often forget: Where did the number of elements go? trace!("environ ptr: {environ:?} {:?}", *environ); if !environ.is_null() && !(*environ).is_null() { let mut element = 0; trace!( "frist env: element: {element}, ptr: {environ:?} {:?}", *environ ); while !(*environ).is_null() { trace!( "currter env: element: {element}, ptr: {environ:?} {:?}", *environ ); environ = environ.add(1); element += 1; if (*environ).is_null() { trace!( "end env: element: {element}, ptr: {environ:?} {:?}", *environ ); environ = envp; break; } } Some((element as usize, environ)) } else { None } } } ``` -------------------------------- ### iOS/watchOS addr() stub returning InvalidArgvPointer Source: https://docs.rs/killmyargv/latest/src/killmyargv/argv_addr.rs.html For iOS and watchOS, this function is a stub that always returns EnvError::InvalidArgvPointer. The code comments note that _NSGetArgc and _NSGetArgv are private and should be avoided, and an alternative using Objective-C runtime is suggested but not implemented. ```rust #[cfg(any(target_os = "ios", target_os = "watchos"))] pub fn addr() -> Result { EnvError(EnvError::InvalidArgvPointer) } ``` -------------------------------- ### Overwrite null byte in process arguments Source: https://docs.rs/killmyargv/latest/src/killmyargv/lib.rs.html This snippet demonstrates the logic for identifying and overwriting a null byte at a specific memory address to prevent argument truncation. ```rust // and is difficultto dispose of properly here. if let Some(nonul_byte) = self.nonul_byte { if chars.len() > nonul_byte && dbg!(ptr::read(self.begin_addr.add(nonul_byte - 1))) == 0x00 { dbg!(ptr::write_bytes( self.begin_addr.add(nonul_byte - 1), 0x01, 1 )); } } let end = ptr::read(self.end_addr); if end != 0x00 { error!("BUG! Unexpected non-null value: {end:?}"); } } } } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.