blob: 656dba704ae1c0d7f77d3f5602b2cbc618ddb2b8 [file] [log] [blame] [view]
Andrew Grieved2ec82d2018-05-22 14:28:431# Shared Libraries on Android
Christopher Grantdb575e9c2019-11-28 16:24:022This doc outlines some tricks / gotchas / features of how we ship native code in
3Chrome on Android.
Andrew Grieved2ec82d2018-05-22 14:28:434
5[TOC]
6
7## Library Packaging
Andrew Grieve84bf1dd2020-07-06 20:31:508 * Android N, O & P (MonochromePublic.aab):
9 * `libmonochrome.so` is stored uncompressed within the apk (an
10 AndroidManifest.xml attribute disables extraction).
11 * It is loaded directly from the apk by the system linker.
12 * It exports all JNI symbols and does not use explicit JNI registration.
13 * It is not loaded by `libchromium_android_linker.so` and relies on the
14 system's webview zygote for RELRO sharing.
15 * Android Q (TrichromeChrome.aab + TrichromeLibrary.apk):
16 * Trichrome uses the exact same native library as Monochrome:
17 `libmonochrome.so`.
Egor Paskoace6f1c2020-11-13 15:38:4818 * `libmonochrome.so` is stored in the shared APK (TrichromeLibrary.apk)
Andrew Grieve84bf1dd2020-07-06 20:31:5019 so that it can be shared with TrichromeWebView.
20 * It is loaded by `libchromium_android_linker.so` using
21 `android_dlopen_ext()` to enable RELRO sharing.
22
Christopher Grantdb575e9c2019-11-28 16:24:0223## Build Variants (eg. monochrome_64_32_apk)
24The packaging above extends to cover both 32-bit and 64-bit device
25configurations.
26
Andrew Grievecdbc63e82020-07-03 16:16:5727Chrome support 64-bit builds, but these do not ship to Stable.
Nate Fischer80c1af7df2024-01-31 01:29:4028The system WebView APK that ships to those devices contains a 32-bit library,
29and for 64-bit devices, a 64-bit library as well (32-bit WebView client apps
Christopher Grantdb575e9c2019-11-28 16:24:0230will use the 32-bit library, and vice-versa).
31
32### Monochrome
33Monochrome's intent was to eliminate the duplication between the 32-bit Chrome
Nate Fischer80c1af7df2024-01-31 01:29:4034and WebView libraries (most of the library is identical). In 32-bit Monochrome,
35a single combined library serves both Chrome and WebView needs. The 64-bit
36version adds an extra WebView-only library.
Christopher Grantdb575e9c2019-11-28 16:24:0237
38More recently, additional Monochrome permutations have arrived. First, Google
39Play will eventually require that apps offer a 64-bit version to compatible
40devices. In Monochrome, this implies swapping the architecture of the Chrome and
Nate Fischer80c1af7df2024-01-31 01:29:4041WebView libraries (64-bit combined lib, and extra 32-bit WebView lib). Further
Christopher Grantdb575e9c2019-11-28 16:24:0242down the road, silicon vendors may drop 32-bit support from their chips, after
43which a pure 64-bit version of Monochrome will apply. In each of these cases,
Nate Fischer80c1af7df2024-01-31 01:29:4044the library name of the combined and WebView-only libraries must match (an
Christopher Grantdb575e9c2019-11-28 16:24:0245Android platform requirement), so both libs are named libmonochrome.so (or
46libmonochrome_64.so in the 64-bit browser case).
47
48Since 3 of these variations require a 64-bit build config, it makes sense to
49also support the 4th variant on 64-bit, thus allowing a single builder to build
50all variants (if desired). Further, a naming scheme must exist to disambiguate
51the various targets:
52
53**monochrome_(browser ABI)_(extra_webview ABI)**
54
Nate Fischer80c1af7df2024-01-31 01:29:4055For example, the 64-bit browser version with extra 32-bit WebView is
Christopher Grantdb575e9c2019-11-28 16:24:0256**monochrome_64_32_apk**. The combinations are as follows:
57
58Builds on | Variant | Description
59--- | --- | ---
6032-bit | monochrome | The original 32-bit-only version
Nate Fischer80c1af7df2024-01-31 01:29:406164-bit | monochrome | The original 64-bit version, with 32-bit combined lib and 64-bit WebView. This would be named monochrome_32_64_apk if not for legacy naming.
6264-bit | monochrome_64_32 | 64-bit combined lib with 32-bit WebView library.
Christopher Grantdb575e9c2019-11-28 16:24:026364-bit | monochrome_64 | 64-bit combined lib only, for eventual pure 64-bit hardware.
6464-bit | monochrome_32 | A mirror of the original 32-bit-only version on 64-bit, to allow building all products on one builder. The result won't be bit-identical to the original, since there are subtle compilation differences.
65
66### Trichrome
67Trichrome has the same 4 permutations as Monochrome, but adds another dimension.
Nate Fischer80c1af7df2024-01-31 01:29:4068Trichrome returns to separate apps for Chrome and WebView, but places shared
Christopher Grantdb575e9c2019-11-28 16:24:0269resources in a third shared-library APK. The table below shows which native
70libraries are packaged where. Note that **dummy** placeholder libraries are
71inserted where needed, since Android determines supported ABIs from the presence
72of native libraries, and the ABIs of a shared library APK must match its client
73app.
74
Nate Fischer80c1af7df2024-01-31 01:29:4075Builds on | Variant | Chrome | Library | WebView
Christopher Grantdb575e9c2019-11-28 16:24:0276--- | --- | --- | --- | ---
7732-bit | trichrome | `32/dummy` | `32/combined` | `32/dummy`
7864-bit | trichrome | `32/dummy`, `64/dummy` | `32/combined`, `64/dummy` | `32/dummy`, `64/webview`
7964-bit | trichrome_64_32 | `32/dummy`, `64/dummy` | `32/dummy`, `64/combined` | `32/webview`, `64/dummy`
8064-bit | trichrome_64 | `64/dummy` | `64/combined` | `64/dummy`
8164-bit | trichrome_32 | `32/dummy` | `32/combined` | `32/dummy`
82
Joshua Peraza8be635b2019-02-25 21:51:0083## Crashpad Packaging
84 * Crashpad is a native library providing out-of-process crash dumping. When a
85 dump is requested (e.g. after a crash), a Crashpad handler process is started
86 to produce a dump.
Andrew Grievecdbc63e82020-07-03 16:16:5787 * Chrome (Android L through M):
Vlad Tsyrklevich0656f2c2019-07-30 18:16:3288 * libchrome_crashpad_handler.so is a standalone executable containing all of
89 the crash dumping code. It is stored compressed and extracted automatically
90 by the system, allowing it to be directly executed to produce a crash dump.
Torne (Richard Coles)d8bc2922019-05-01 21:26:5291 * Monochrome (N through P) and SystemWebView (L through P):
Joshua Peraza8be635b2019-02-25 21:51:0092 * All of the Crashpad code is linked into the package's main native library
93 (e.g. libmonochrome.so). When a dump is requested, /system/bin/app_process
94 is executed, loading CrashpadMain.java which in turn uses JNI to call into
95 the native crash dumping code. This approach requires building CLASSPATH
96 and LD_LIBRARY_PATH variables to ensure app_process can locate
97 CrashpadMain.java and any native libraries (e.g. system libraries, shared
98 libraries, split apks, etc.) the package's main native library depends on.
99 * Monochrome, Trichrome, and SystemWebView (Q+):
100 * All of the Crashpad handler code is linked into the package's native
101 library. libcrashpad_handler_trampoline.so is a minimal executable
102 packaged with the main native library, stored uncompressed and left
103 unextracted. When a dump is requested, /system/bin/linker is executed to
104 load the trampoline from the APK, which in turn `dlopen()`s the main
105 native library to load the remaining Crashpad handler code. A trampoline
106 is used to de-duplicate shared code between Crashpad and the main native
107 library packaged with it. This approach isn't used for P- because the
108 linker doesn't support loading executables on its command line until Q.
109 This approach also requires building a suitable LD_LIBRARY_PATH to locate
110 any shared libraries Chrome/WebView depends on.
111
Andrew Grieved2ec82d2018-05-22 14:28:43112## Debug Information
113**What is it?**
114 * Sections of an ELF that provide debugging and symbolization information (e.g. ability convert addresses to function & line numbers).
115
116**How we use it:**
117 * ELF debug information is too big to push to devices, even for local development.
118 * All of our APKs include `.so` files with debug information removed via `strip`.
119 * Unstripped libraries are stored at `out/Default/lib.unstripped`.
120 * Many of our scripts are hardcoded to look for them there.
121
122## Unwind Info & Frame Pointers
123**What are they:**
124 * Unwind info is data that describes how to unwind the stack. It is:
125 * It is required to support C++ exceptions (which Chrome doesn't use).
126 * It can also be used to produce stack traces.
127 * It is generally stored in an ELF section called `.eh_frame` & `.eh_frame_hdr`, but arm32 stores it in `.ARM.exidx` and `.ARM.extab`.
128 * You can see these sections via: `readelf -S libchrome.so`
129 * "Frame Pointers" is a calling convention that ensures every function call has the return address pushed onto the stack.
130 * Frame Pointers can also be used to produce stack traces (but without entries for inlined functions).
131
132**How we use them:**
133 * We disable unwind information (search for [`exclude_unwind_tables`](https://cs.chromium.org/search/?q=exclude_unwind_tables+file:%5C.gn&type=cs)).
134 * For all architectures except arm64, we disable frame pointers in order to reduce binary size (search for [`enable_frame_pointers`](https://cs.chromium.org/search/?q=enable_frame_pointers+file:%5C.gn&type=cs)).
135 * Crashes are unwound offline using `minidump_stackwalk`, which can create a stack trace given a snapshot of stack memory and the unstripped library (see [//docs/testing/using_breakpad_with_content_shell.md](testing/using_breakpad_with_content_shell.md))
136 * To facilitate heap profiling, we ship unwind information to arm32 canary & dev channels as a separate file: `assets/unwind_cfi_32`
137
138## JNI Native Methods Resolution
Andrew Grievecdbc63e82020-07-03 16:16:57139 * For ChromePublic.apk:
Andrew Grieved2ec82d2018-05-22 14:28:43140 * `JNI_OnLoad()` is the only exported symbol (enforced by a linker script).
141 * Native methods registered explicitly during start-up by generated code.
Peter Wenbe712e642019-11-14 21:36:58142 * For MonochromePublic.apk and TrichromeChrome.aab:
Andrew Grieved2ec82d2018-05-22 14:28:43143 * `JNI_OnLoad()` and `Java_*` symbols are exported by linker script.
144 * No manual JNI registration is done. Symbols are resolved lazily by the runtime.
145
146## Packed Relocations
147 * All flavors of `lib(mono)chrome.so` enable "packed relocations", or "APS2 relocations" in order to save binary size.
148 * Refer to [this source file](https://android.googlesource.com/platform/bionic/+/refs/heads/master/tools/relocation_packer/src/delta_encoder.h) for an explanation of the format.
149 * To process these relocations:
150 * Pre-M Android: Our custom linker must be used.
151 * M+ Android: The system linker understands the format.
152 * To see if relocations are packed, look for `LOOS+#` when running: `readelf -S libchrome.so`
153 * Android P+ [supports an even better format](https://android.googlesource.com/platform/bionic/+/8b14256/linker/linker.cpp#2620) known as RELR.
154 * We'll likely switch non-Monochrome apks over to using it once it is implemented in `lld`.
155
156## RELRO Sharing
157**What is it?**
158 * RELRO refers to the ELF segment `GNU_RELRO`. It contains data that the linker marks as read-only after it applies relocations.
159 * To inspect the size of the segment: `readelf --segments libchrome.so`
Egor Pasko9783f922021-03-30 16:41:01160 * For `lib(mono)chrome.so` the region occupies about 2.4MiB on arm32 and 4.7 MiB on arm64
Andrew Grieved2ec82d2018-05-22 14:28:43161 * If two processes map this segment to the same virtual address space, then pages of memory within the segment which contain only relative relocations (99% of them) will be byte-for-byte identical.
Egor Pasko9783f922021-03-30 16:41:01162 * "RELRO sharing" is when this segment is moved into shared memory and shared by multiple processes.
163 * Processes `fork()`ed from the app zygote (where the library is loaded) share RELRO (via `fork()`'s copy-on-write semantics), but this region is not shared with other process types (privileged, utility, GPU)
Andrew Grieved2ec82d2018-05-22 14:28:43164
165**How does it work?**
Andrew Grieved2ec82d2018-05-22 14:28:43166 * For a more detailed description, refer to comments in [Linker.java](https://cs.chromium.org/chromium/src/base/android/java/src/org/chromium/base/library_loader/Linker.java).
Torne (Richard Coles)d8bc2922019-05-01 21:26:52167 * For Android N-P:
Andrew Grieved2ec82d2018-05-22 14:28:43168 * The OS maintains a RELRO file on disk with the contents of the GNU_RELRO segment.
169 * All Android apps that contain a WebView load `libmonochrome.so` at the same virtual address and apply RELRO sharing against the memory-mapped RELRO file.
Clark DuValld68549332021-01-29 21:20:02170 * Chrome uses `WebViewLibraryPreloader` to call into the same WebView library loading code.
Andrew Grieved2ec82d2018-05-22 14:28:43171 * When Monochrome is the WebView provider, `libmonochrome.so` is loaded with the system's cached RELRO's applied.
172 * `System.loadLibrary()` is called afterwards.
173 * When Monochrome is the WebView provider, this only calls JNI_OnLoad, since the library is already loaded. Otherwise, this loads the library and no RELRO sharing occurs.
Torne (Richard Coles)d8bc2922019-05-01 21:26:52174 * For non-low-end Android O-P (where there's a WebView zygote):
Andrew Grieved2ec82d2018-05-22 14:28:43175 * For non-renderer processes, the above Android N+ logic applies.
176 * For renderer processes, the OS starts all Monochrome renderer processes by `fork()`ing the WebView zygote rather than the normal application zygote.
177 * In this case, RELRO sharing would be redundant since the entire process' memory is shared with the zygote with copy-on-write semantics.
Egor Pasko56343f42021-06-15 16:17:29178 * For Android Q+ (Trichrome):
Andrew Grieve84bf1dd2020-07-06 20:31:50179 * TrichromeWebView works the same way as on Android N-P.
180 * TrichromeChrome uses `android_dlopen_ext()` and `ASharedMemory_create()` to
181 perform RELRO sharing, and then relies on a subsequent call to
182 `System.loadLibrary()` to enable JNI method resolution without loading the
183 library a second time.
184 * For renderer processes, TrichromeChrome `fork()`s from a chrome-specific
185 app zygote. `libmonochrome.so` is loaded in the zygote before `fork()`.
186 * Similar to O-P, app zygote provides copy-on-write memory semantics so
187 RELRO sharing is redundant.
Egor Pasko438cf4e2022-12-01 14:14:10188 * For Android R+ (still Trichrome)
189 * The RELRO region is created in the App Zygote, picked up by the Browser
190 process, which then redistributes the region to all other processes. The
191 receiving of the region and remapping it on top of the non-shared RELRO
192 happens asynchronously after the library has been loaded. Native code is
193 generally already running at this point. Hence the replacement must be
194 atomic.
Andrew Grieved2ec82d2018-05-22 14:28:43195
Christopher Grant8fea5a12019-07-31 19:12:31196## Partitioned libraries
197Some Chrome code is placed in feature-specific libraries and delivered via
198[Dynamic Feature Modules](android_dynamic_feature_modules.md).
199
200A linker-assisted partitioning system automates the placement of code into
201either the main Chrome library or feature-specific .so libraries. Feature code
202may continue to make use of core Chrome code (eg. base::) without modification,
203but Chrome must call feature code through a virtual interface.
204
205**How partitioning works**
206
207The lld linker is now capable of producing a [partitioned
208library](https://lld.llvm.org/Partitions.html), which is effectively an
209intermediate single file containing multiple libraries. A separate tool
210*(llvm-objcopy)* then splits the file into standalone .so files, invoked through
211a [partitioned shared library](https://cs.chromium.org/chromium/src/build/partitioned_shared_library.gni)
212GN template.
213
214The primary partition is Chrome's main library (eg. libchrome.so), and other
215partitions may contain feature code (eg. libvr.so). By specifying a list of
216C/C++ symbols to use as entrypoints, the linker can collect all code used only
217through these entrypoints, and place it in a particular partition.
218
219To facilitate partitioning, all references from Chrome to the feature
220entrypoints must be indirect. That is, Chrome must obtain a symbol from the
221feature library through dlsym(), cast the pointer to its actual type, and call
222through the resulting pointer.
223
224Feature code retains the ability to freely call back into Chrome's core code.
225When loading the library, the feature module system uses the feature name to
226look up a partition name *(libfoo.so)* in an address offset table built into the
227main library. The resulting offset is supplied to android_dlopen_ext(), which
228instructs Android to load the library in a particular reserved address region.
229This allows the feature library's relative references back to the main library
230to work, as if the feature code had been linked into the main library
231originally. No dynamic symbol resolution is required here.
232
233**Implications on code placement**
234
235* Any symbol referenced by multiple partitions ends up in the main library (even
236 if all calling libraries are feature partitions).
237* Symbols that aren't feature code (eg. base::) will be pulled into the
238 feature's library if only that feature uses the code. This is a benefit, but
239 can be unexpected.
240
241**Builds that support partitioned libraries**
242
243Partitioned libraries are usable when all of the following are true:
244* Component build is disabled (component build splits code across GN component
245 target boundaries instead).
246* The compiler is Clang.
247* The linker is lld.
248
Andrew Grieved2ec82d2018-05-22 14:28:43249## Library Prefetching
250 * During start-up, we `fork()` a process that reads a byte from each page of the library's memory (or just the ordered range of the library).
251 * See [//base/android/library_loader/](../base/android/library_loader/).
252
253## Historical Tidbits
254 * We used to use the system linker on M (`ModernLinker.java`).
255 * This was removed due to [poor performance](https://bugs.chromium.org/p/chromium/issues/detail?id=719977).
256 * We used to use `relocation_packer` to pack relocations after linking, which complicated our build system and caused many problems for our tools because it caused logical addresses to differ from physical addresses.
257 * We now link with `lld`, which supports packed relocations natively and doesn't have these problems.
Egor Pasko438cf4e2022-12-01 14:14:10258 * We used to use the Crazy Linker until Android M was deprecated
259 * It allowed storing `libchrome.so` uncompressed within the apk before the
260 system linker allowed it (with the name `crazy.libchrome.so` to avoid extraction).
261 * It was loaded directly from the apk via `libchromium_android_linker.so`.
262 * Only JNI_OnLoad was exported. Explicit JNI registration was required
263 because the Android runtime uses the system's `dlsym()`, which doesn't know
Sam Maiere1df6f22023-08-11 14:20:40264 about Crazy-Linker-opened libraries. (see [JNI README](/third_party/jni_zero/README.md)).
Andrew Grieved2ec82d2018-05-22 14:28:43265
266## See Also
267 * [//docs/android_build_instructions.md#Multiple-Chrome-APK-Targets](android_build_instructions.md#Multiple-Chrome-APK-Targets)
268 * [//third_party/android_crazy_linker/README.chromium](../third_party/android_crazy_linker/README.chromium)
269 * [//base/android/linker/BUILD.gn](../base/android/linker/BUILD.gn)