Ask questionsStdlib contains a static initializer on Linux, without any way to opt out
The Rust stdlib has a single static initializer on the cfg #[cfg(all(target_os = "linux", target_env = "gnu"))]
.
In general the stdlib is already avoiding static initializers, and this one is added to support interaction with glibc from inside a cdylib. Typically, args::init() is called during startup which initializes the std::env::args() data structures. But in a cdylib, there’s no startup call that gets through, whereas glibc will call the function pointer in this static initializer.
We would like to provide a mechanism to remove this static initializer, and accept that std::env::args() will be empty as a result inside a cdylib. Since Chromium (as in chrome.exe) does not use argument parsing in Rust, we are fine with the result being, and prefer, that the argument/environment are empty on Linux with glibc with the static initializer being removed if we have Rust code in a cdylib.
While this particular static initializer is not hugely problematic, it is a much better engineering position to work from if there are none at all.
Chromium bug: https://bugs.chromium.org/p/chromium/issues/detail?id=1445935
Static initialization is known to be a problem, due to the Static Initialization Order Fiasco. They also have a measurable negative impact on startup speed, even on modern computers. The Chromium project bans static initializers to avoid these problems.
Many software shops try to avoid static initializers. The Google C++ Style guide tries to ban them but hedges a little.
LLVM provides link-time mechanisms to override a symbol and remove static initializers, such as with __llvm_pgo_register_write_atexit()
in compiler-rt.
We propose to add a feature explicit-init-args-with-glibc
which defaults off, and gate the static initializer’s presence on this feature.
As well, when the feature is enabled, allow the args::imp::init()
function to call really_init() directly. Basically, this feature disables the “linux + gnu” cfg check.
When this feature is enabled and the stdlib is linked statically into a Rust executable or Rust dylib, the init() path will be properly called, explicitly.
When statically linked into a C executable or a C shared-library, the init() path is already not called. When the feature is enabled, it will also not be called in Rust cdylibs, and the std::env::args()
will not be available.
A colleague suggested we could change the cfg guard in sys/unix/args.rs to be a single check like #[cfg(use-glibc-static-initializer)]
and then have the build.rs
file add that cfg flag when on Linux/GNU and the explicit-init-args-with-glibc
feature is not enabled. This moves the combination of these things to a single place in build.rs
at the cost of adding a layer of abstraction.
PR is here: https://github.com/rust-lang/rust/pull/111920
Answer
questions
the8472
In the long term we could also avoid the static initializer if a way to get argv would be added to glibc or the kernel. But considering backwards compatibility we would only be able to remove it once support for that becomes widespread.
Related questions
No questions were found.