Andrew Grieve | 860b155 | 2017-09-06 14:50:06 | [diff] [blame] | 1 | # Static Initializers |
| 2 | |
| 3 | [TOC] |
| 4 | |
| 5 | Some background on the original decision to ban static initializers: |
| 6 | |
| 7 | http://neugierig.org/software/chromium/notes/2011/08/static-initializers.html |
| 8 | |
Andrew Grieve | 5e70468 | 2021-02-19 23:35:24 | [diff] [blame] | 9 | Note: Another name for static initializers is "global constructors". |
| 10 | |
Andrew Grieve | 860b155 | 2017-09-06 14:50:06 | [diff] [blame] | 11 | # How Static Initializers are Checked |
| 12 | |
| 13 | * For Linux and Mac: |
Matthew Denton | 43cafd5 | 2021-05-26 15:42:14 | [diff] [blame] | 14 | * The expected count is stored in [//testing/scripts/check_static_initializers.py](https://source.chromium.org/chromium/chromium/src/+/main:testing/scripts/check_static_initializers.py) |
Andrew Grieve | 860b155 | 2017-09-06 14:50:06 | [diff] [blame] | 15 | * For Android: |
| 16 | * The expected count is stored in the build target [//chrome/android:monochrome_static_initializers](https://cs.chromium.org/chromium/src/chrome/android/BUILD.gn) |
| 17 | |
| 18 | ## Removing Static Initializers |
| 19 | |
| 20 | Common fixes include: |
| 21 | |
Lei Zhang | fafa2b7 | 2020-09-29 21:58:46 | [diff] [blame] | 22 | * Add constexpr. |
| 23 | * Move global variable to be a static variable within a function that returns |
| 24 | it, often wrapped in `base::NoDestructor`. |
Andrew Grieve | 860b155 | 2017-09-06 14:50:06 | [diff] [blame] | 25 | |
| 26 | ## Listing Static Initializers |
| 27 | |
Andrew Grieve | 5e70468 | 2021-02-19 23:35:24 | [diff] [blame] | 28 | ### Step 1 - Use objdump to report them |
Andrew Grieve | 860b155 | 2017-09-06 14:50:06 | [diff] [blame] | 29 | For Linux: |
| 30 | |
| 31 | tools/linux/dump-static-initializers.py out/Release/chrome |
| 32 | |
Andrew Grieve | 3c2c420 | 2020-04-16 01:55:05 | [diff] [blame] | 33 | For Android (from easiest to hardest): |
Andrew Grieve | 860b155 | 2017-09-06 14:50:06 | [diff] [blame] | 34 | |
Andrew Grieve | 3c2c420 | 2020-04-16 01:55:05 | [diff] [blame] | 35 | # Build with: is_official_build=true is_chrome_branded=true |
| 36 | # This will dump the list of SI's only when they don't match the expected |
| 37 | # number in static_initializers.gni (this is what the bots use). |
| 38 | ninja chrome/android:monochrome_static_initializers |
| 39 | # or: |
| 40 | tools/binary_size/diagnose_bloat.py HEAD # See README.md for flags. |
Andrew Grieve | 5e70468 | 2021-02-19 23:35:24 | [diff] [blame] | 41 | # or (the other two use this under the hood): |
| 42 | 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 |
| 43 | # arm32 ^^ vv arm64 |
| 44 | 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 |
| 45 | # Note: For arm64, having use_thin_lto=true seems to dump a couple extra |
| 46 | # initializers that don't actually exist. |
| 47 | |
| 48 | The last one may actually be the easiest if you've already properly built |
| 49 | `libmonochrome.so` with `is_official_build=true`. |
| 50 | |
| 51 | ### Step 2 - Ask compiler to report them |
| 52 | |
Adenilson Cavalcanti | 9b15683 | 2021-10-27 00:50:14 | [diff] [blame] | 53 | If the source of the new initializers is not obvious from Step 1, you can ask the |
Andrew Grieve | 5e70468 | 2021-02-19 23:35:24 | [diff] [blame] | 54 | compiler to pinpoint the exact source line. |
| 55 | |
| 56 | 1. Edit [//build/config/BUILDCONFIG.gn](https://cs.chromium.org/chromium/src/build/config/BUILDCONFIG.gn) |
| 57 | and add `"//build/config/compiler:wglobal_constructors"` to `default_compiler_configs` |
| 58 | 2. Remove the config from the `configs` in `//base:base` |
| 59 | 3. Set GN arg `treat_warnings_as_errors=false` |
| 60 | 4. Compile and look for warnings **from the files identified by step 1** (may want to pipe ninja output to a file). |
| 61 | |
| 62 | *** note |
| 63 | The compiler warning triggers for every static initializer that exists |
| 64 | *before optimization*. We care only about those that survive optimization. |
| 65 | More details in [crbug/1136086](https://bugs.chromium.org/p/chromium/issues/detail?id=1136086). |
| 66 | *** |
Andrew Grieve | 860b155 | 2017-09-06 14:50:06 | [diff] [blame] | 67 | |
Andrew Grieve | 3c2c420 | 2020-04-16 01:55:05 | [diff] [blame] | 68 | * For more information about `diagnose_bloat.py`, refer to its [README.md](/tools/binary_size/README.md#diagnose_bloat.py) |
| 69 | * List of existing static initializers documented in [static_initializers.gni](/chrome/android/static_initializers.gni) |
Adenilson Cavalcanti | 9b15683 | 2021-10-27 00:50:14 | [diff] [blame] | 70 | |
Andrew Grieve | 7c58e2e | 2022-04-22 02:00:10 | [diff] [blame] | 71 | ### Step 3 - Manual Verification |
Adenilson Cavalcanti | 9b15683 | 2021-10-27 00:50:14 | [diff] [blame] | 72 | |
| 73 | If the source of the new initializers is not revealed with |
| 74 | `dump-static-initializers.py` (e.g. for static initializers introduced in |
| 75 | compiler-rt), there's a manual option. |
| 76 | |
Andrew Grieve | 7c58e2e | 2022-04-22 02:00:10 | [diff] [blame] | 77 | 1. Locate the address range of the .init_array section with: |
Adenilson Cavalcanti | 9b15683 | 2021-10-27 00:50:14 | [diff] [blame] | 78 | ``` |
Andrew Grieve | 7c58e2e | 2022-04-22 02:00:10 | [diff] [blame] | 79 | $ third_party/llvm-build/Release+Asserts/bin/llvm-readelf \ |
| 80 | --hex-dump=.init_array out/Release/lib.unstripped/libmonochrome.so |
| 81 | Hex dump of section '.init_array': |
| 82 | 0x04064624 294a1a02 154acb00 79d3be01 894c1a02 )J...J..y....L.. |
Adenilson Cavalcanti | 9b15683 | 2021-10-27 00:50:14 | [diff] [blame] | 83 | ``` |
Andrew Grieve | 7c58e2e | 2022-04-22 02:00:10 | [diff] [blame] | 84 | |
| 85 | * `0x04064624` is the location of `.init_array`. |
| 86 | * The other four entries are addresses of functions **in little endian**. |
| 87 | |
| 88 | 2. Convert the address into a function name with: |
| 89 | |
Adenilson Cavalcanti | 9b15683 | 2021-10-27 00:50:14 | [diff] [blame] | 90 | ``` |
Andrew Grieve | 7c58e2e | 2022-04-22 02:00:10 | [diff] [blame] | 91 | # Reverse hex pairs to account for endianness. |
| 92 | $ third_party/llvm-build/Release+Asserts/bin/llvm-symbolizer \ |
| 93 | --functions -e out/Release/lib.unstripped/libmonochrome.so 0x021a4a29 |
| 94 | _GLOBAL__I_000101 |
| 95 | ./../../buildtools/third_party/libc++/trunk/src/iostream.cpp:0:0 |
| 96 | ``` |
| 97 | |
| 98 | 3. If any `.init_array` slots are zero, that means they their address is exists |
| 99 | within the relocation table. To find the address: |
| 100 | |
| 101 | ``` |
| 102 | # Use the location of ".init_array" printed in step 1, plus an offset for subsequent slots. |
| 103 | $ third_party/llvm-build/Release+Asserts/bin/llvm-readelf \ |
| 104 | --relocations out/Release/lib.unstripped/libmonochrome.so | grep 0x04064624 |
| 105 | 03dfb7b0 00000017 R_ARM_RELATIVE 0 |
| 106 | ``` |
| 107 | |
| 108 | ### Step 4 - Compiler Naming Heuristics |
| 109 | |
| 110 | You might be able to find the static initialzer functions by listing symbols: |
| 111 | |
| 112 | ```sh |
| 113 | nm out/Release/lib.unstripped/libmonochrome.so | grep " _GLOBAL__" |
| 114 | ``` |
| 115 | |
| 116 | This currently yields: |
| 117 | ``` |
| 118 | 0214ea45 t _GLOBAL__I_000101 |
| 119 | 00cb2315 t _GLOBAL__sub_I_base_logging.cc |
| 120 | 0214eca5 t _GLOBAL__sub_I_iostream.cpp |
| 121 | 01c01219 t _GLOBAL__sub_I_token.cc |
Adenilson Cavalcanti | 9b15683 | 2021-10-27 00:50:14 | [diff] [blame] | 122 | ``` |