Static Initializers

Some background on the original decision to ban static initializers:

http://neugierig.org/software/chromium/notes/2011/08/static-initializers.html

Note: Another name for static initializers is “global constructors”.

How Static Initializers are Checked

Removing Static Initializers

Common fixes include:

  • Add constexpr.
  • Move global variable to be a static variable within a function that returns it, often wrapped in base::NoDestructor.

Listing Static Initializers

Step 1 - Use objdump to report them

For Linux:

tools/linux/dump-static-initializers.py out/Release/chrome

For Android (from easiest to hardest):

# Build with: is_official_build=true is_chrome_branded=true
# This will dump the list of SI's only when they don't match the expected
# number in static_initializers.gni (this is what the bots use).
ninja chrome/android:monochrome_static_initializers
# or:
tools/binary_size/diagnose_bloat.py HEAD  # See README.md for flags.
# or (the other two use this under the hood):
tools/linux/dump-static-initializers.py --toolchain-prefix third_party/android_ndk/toolchains/llvm/prebuilt/linux-x86_64/bin/arm-linux-androideabi- out/Release/lib.unstripped/libmonochrome.so
# arm32 ^^ vv arm64
tools/linux/dump-static-initializers.py --toolchain-prefix third_party/android_ndk/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android- out/Release/lib.unstripped/libmonochrome.so
# Note: For arm64, having use_thin_lto=true seems to dump a couple extra
#     initializers that don't actually exist.

The last one may actually be the easiest if you've already properly built libmonochrome.so with is_official_build=true.

Step 2 - Ask compiler to report them

If the source of the new initializers is not obvious from Step 1, you can ask the compiler to pinpoint the exact source line.

  1. Edit //build/config/BUILDCONFIG.gn and add "//build/config/compiler:wglobal_constructors" to default_compiler_configs
  2. Remove the config from the configs in //base:base
  3. Set GN arg treat_warnings_as_errors=false
  4. Compile and look for warnings from the files identified by step 1 (may want to pipe ninja output to a file).
The compiler warning triggers for every static initializer that exists before optimization. We care only about those that survive optimization. More details in crbug/1136086.

Step 3 - Manual Verification

If the source of the new initializers is not revealed with dump-static-initializers.py (e.g. for static initializers introduced in compiler-rt), there's a manual option.

  1. Locate the address range of the .init_array section with:
$ third_party/llvm-build/Release+Asserts/bin/llvm-readelf \
    --hex-dump=.init_array out/Release/lib.unstripped/libmonochrome.so
Hex dump of section '.init_array':
0x04064624 294a1a02 154acb00 79d3be01 894c1a02 )J...J..y....L..
  • 0x04064624 is the location of .init_array.
  • The other four entries are addresses of functions in little endian.
  1. Convert the address into a function name with:
# Reverse hex pairs to account for endianness.
$ third_party/llvm-build/Release+Asserts/bin/llvm-symbolizer \
    --functions -e out/Release/lib.unstripped/libmonochrome.so 0x021a4a29
_GLOBAL__I_000101
./../../buildtools/third_party/libc++/trunk/src/iostream.cpp:0:0
  1. If any .init_array slots are zero, that means they their address is exists within the relocation table. To find the address:
# Use the location of ".init_array" printed in step 1, plus an offset for subsequent slots.
$ third_party/llvm-build/Release+Asserts/bin/llvm-readelf \
    --relocations out/Release/lib.unstripped/libmonochrome.so | grep 0x04064624
03dfb7b0  00000017 R_ARM_RELATIVE                    0

Step 4 - Compiler Naming Heuristics

You might be able to find the static initialzer functions by listing symbols:

nm out/Release/lib.unstripped/libmonochrome.so | grep " _GLOBAL__"

This currently yields:

0214ea45 t _GLOBAL__I_000101
00cb2315 t _GLOBAL__sub_I_base_logging.cc
0214eca5 t _GLOBAL__sub_I_iostream.cpp
01c01219 t _GLOBAL__sub_I_token.cc