diff --git a/src/libstd/env.rs b/src/libstd/env.rs index b8989369269..651010d1366 100644 --- a/src/libstd/env.rs +++ b/src/libstd/env.rs @@ -702,6 +702,11 @@ pub struct ArgsOs { inner: sys::args::Args } /// (such as `*` and `?`). On Windows this is not done, and such arguments are /// passed as-is. /// +/// On glibc Linux, arguments are retrieved by placing a function in .init_array. +/// 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` or `staticlib`, as it does on macOS +/// and Windows. +/// /// # Panics /// /// The returned iterator will panic during iteration if any argument to the @@ -732,6 +737,11 @@ pub fn args() -> Args { /// set to arbitrary text, and it may not even exist, so this property should /// not be relied upon for security purposes. /// +/// On glibc Linux, arguments are retrieved by placing a function in .init_array. +/// 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` or `staticlib`, as it does on macOS +/// and Windows. +/// /// # Examples /// /// ``` diff --git a/src/libstd/sys/unix/args.rs b/src/libstd/sys/unix/args.rs index 82ef35ea7b5..15dafb1bcf9 100644 --- a/src/libstd/sys/unix/args.rs +++ b/src/libstd/sys/unix/args.rs @@ -72,12 +72,40 @@ mod imp { // acquire this mutex reentrantly! static LOCK: Mutex = Mutex::new(); - pub unsafe fn init(argc: isize, argv: *const *const u8) { + unsafe fn really_init(argc: isize, argv: *const *const u8) { let _guard = LOCK.lock(); ARGC = argc; ARGV = argv; } + #[inline(always)] + pub unsafe fn init(_argc: isize, _argv: *const *const u8) { + #[cfg(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( + 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 unsafe fn cleanup() { let _guard = LOCK.lock(); ARGC = 0; diff --git a/src/test/run-make-fulldeps/glibc-staticlib-args/Makefile b/src/test/run-make-fulldeps/glibc-staticlib-args/Makefile new file mode 100644 index 00000000000..ad841ec6101 --- /dev/null +++ b/src/test/run-make-fulldeps/glibc-staticlib-args/Makefile @@ -0,0 +1,12 @@ +# only-gnu +# only-linux + +-include ../tools.mk + +# This ensures that std::env::args works in a library called from C on glibc Linux. + +all: + $(RUSTC) --crate-type=staticlib library.rs + $(CC) program.c $(call STATICLIB,library) $(call OUT_EXE,program) \ + $(EXTRACFLAGS) $(EXTRACXXFLAGS) + $(call RUN,program) diff --git a/src/test/run-make-fulldeps/glibc-staticlib-args/library.rs b/src/test/run-make-fulldeps/glibc-staticlib-args/library.rs new file mode 100644 index 00000000000..991981dc096 --- /dev/null +++ b/src/test/run-make-fulldeps/glibc-staticlib-args/library.rs @@ -0,0 +1,4 @@ +#[no_mangle] +pub extern fn args_check() { + assert_ne!(std::env::args_os().count(), 0); +} diff --git a/src/test/run-make-fulldeps/glibc-staticlib-args/program.c b/src/test/run-make-fulldeps/glibc-staticlib-args/program.c new file mode 100644 index 00000000000..d704c39d5c4 --- /dev/null +++ b/src/test/run-make-fulldeps/glibc-staticlib-args/program.c @@ -0,0 +1,7 @@ +// ignore-license +void args_check(); + +int main() { + args_check(); + return 0; +}