-
-
Notifications
You must be signed in to change notification settings - Fork 14.2k
Description
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 Initializers are a problem
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.
Removing the Static Initializer
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.
Alternatives
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.
Proposed Change
PR is here: #111920