1# Android linker changes for NDK developers 2 3This document details important changes related to native code 4loading in various Android releases. 5 6See also [bionic status](docs/status.md) for general libc/libm/libdl 7behavior changes. 8 9See also the 10[unwinder documentation](https://android.googlesource.com/platform/system/unwinding/+/refs/heads/main/libunwindstack/AndroidVersions.md) 11for details about changes in stack unwinding (crash dumps) between 12different releases. 13 14Required tools: the NDK has an `llvm-readelf` binary that understands all the 15architecture-specific details of all Android's supported architectures. Recent 16versions of Android also have toybox readelf on the device. 17 18 19## How we manage incompatible changes 20 21Our general practice with dynamic linker behavior changes is that they 22will be tied to an app's target API level: 23 24* Below the affected API level we'll preserve the old behavior or issue 25a warning, as appropriate. 26 27* At the affected API level and above, we’ll refuse to load the library. 28 29* Warnings about any behavior change that will affect a library if you 30increase your target API level will appear in logcat when that library 31is loaded, even if you're not yet targeting that API level. 32 33* On a developer preview build, dynamic linker warnings will also show up 34as toasts. Experience has shown that many developers don’t habitually 35check logcat for warnings until their app stops functioning, so the 36toasts help bring some visibility to the issues before it's too late. 37 38 39## Changes to library dependency resolution 40 41Until it was [fixed](https://issuetracker.google.com/36950617) in 42API level 18, Android didn't include the application library directory 43on the dynamic linker's search path. This meant that apps 44had to call `dlopen` or `System.loadLibrary` on all transitive 45dependencies before loading their main library. Worse, until it was 46[fixed](https://issuetracker.google.com/36935779) in API level 18, the 47dynamic linker's caching code cached failures too, so it was necessary 48to topologically sort your libraries and load them in reverse order. 49 50If you need to support Android devices running OS versions older than 51API level 23, you might want to consider 52[ReLinker](https://github.com/KeepSafe/ReLinker) which claims to solve 53these and other problems automatically. 54 55Alternatively, if you don't have too many dependencies, it can be easiest to 56simply link all of your code into one big library and sidestep the details of 57library and symbol lookup changes on all past (and future) Android versions. 58 59 60## Changes to library search order 61 62We have made various fixes to library search order when resolving symbols. 63 64With API level 22, load order switched from depth-first to breadth-first to 65fix dlsym(3). 66 67Before API level 23, the default search order was to try the main executable, 68LD_PRELOAD libraries, the library itself, and its DT_NEEDED libraries 69in that order. For API level 23 and later, for any given library, the dynamic 70linker divides other libraries into the global group and the local 71group. The global group is shared by all libraries and contains the main 72executable, LD_PRELOAD libraries, and any library with the DF_1_GLOBAL 73flag set (by passing “-z global” to ld(1)). The local group is 74the breadth-first transitive closure of the library and its DT_NEEDED 75libraries. The API level 23 dynamic linker searches the global group followed by 76the local group. This allows ASAN, for example, to ensure that it can 77intercept any symbol. 78 79 80## LD_PRELOAD and 32/64 bit 81 82LD_PRELOAD applies to both 32- and 64-bit processes. This means that you 83should avoid saying something like `/system/lib/libfoo.so` and just say 84`libfoo.so` instead, letting the dynamic linker find the correct library 85on its search path. 86 87 88## RTLD_LOCAL (Available in API level >= 23) 89 90The dlopen(3) RTLD_LOCAL flag used to be ignored but is implemented 91correctly in API level 23 and later. Note that RTLD_LOCAL is the default, 92so even calls to dlopen(3) that didn’t explicitly use RTLD_LOCAL will 93be affected (unless they explicitly used RTLD_GLOBAL). With RTLD_LOCAL, 94symbols will not be made available to libraries loaded by later calls 95to dlopen(3) (as opposed to being referenced by DT_NEEDED entries). 96 97 98## GNU hashes (Availible in API level >= 23) 99 100The GNU hash style available with `--hash-style=gnu` allows faster 101symbol lookup and is supported by Android's dynamic linker in API level 23 and 102above. Use `--hash-style=both` if you want to build code that uses this 103feature in new enough releases but still works on older releases. 104If you're using the NDK, clang chooses the right option 105(automatically)[https://github.com/android/ndk/issues/2005]. 106 107 108## Correct soname/path handling (Available in API level >= 23) 109 110The dynamic linker now understands the difference 111between a library’s soname and its path (public bug 112https://code.google.com/p/android/issues/detail?id=6670). API level 23 113is the first release where search by soname is implemented. Earlier 114releases would assume that the basename of the library was the soname, 115and used that to search for already-loaded libraries. For example, 116`dlopen("/this/directory/does/not/exist/libc.so", RTLD_NOW)` would 117find `/system/lib/libc.so` because it’s already loaded. This also meant 118that it was impossible to have two libraries `"dir1/libx.so"` and 119`"dir2/libx.so"` --- the dynamic linker couldn’t tell the difference 120and would always use whichever was loaded first, even if you explicitly 121tried to load both. This also applied to DT_NEEDED entries. 122 123Some apps have bad DT_NEEDED entries (usually absolute paths on the build 124machine’s file system) that used to work because we ignored everything 125but the basename. These apps will fail to load on API level 23 and above. 126 127 128## Symbol versioning (Available in API level >= 23) 129 130Symbol versioning allows libraries to provide better backwards 131compatibility. For example, if a library author knowingly changes 132the behavior of a function, they can provide two versions in the same 133library so that old code gets the old version and new code gets the new 134version. This is supported in API level 23 and above. 135 136 137## Opening shared libraries directly from an APK 138 139In API level 23 and above, it’s possible to open a .so file directly from 140your APK. Just use `System.loadLibrary("foo")` exactly as normal but set 141`android:extractNativeLibs="false"` in your `AndroidManifest.xml`. In 142older releases, the .so files were extracted from the APK file 143at install time. This meant that they took up space in your APK and 144again in your installation directory (and this was counted against you 145and reported to the user as space taken up by your app). Any .so file 146that you want to load directly from your APK must be page aligned 147(on a 4096-byte boundary) in the zip file and stored uncompressed. 148Current versions of the zipalign tool take care of alignment. 149 150Note that in API level 23 and above dlopen(3) can open a library from 151any zip file, not just an APK. Just give dlopen(3) a path of the form 152"my_zip_file.zip!/libs/libstuff.so". As with APKs, the library must be 153page-aligned and stored uncompressed for this to work. 154 155 156## Private API (Enforced for API level >= 24) 157 158Native libraries must use only public API, and must not link against 159non-NDK platform libraries. On devices running API level 24 or later, 160this rule is enforced and applications are no longer able to load all 161non-NDK platform libraries. This was to prevent future issues similar 162to the disruption caused when Android switched from OpenSSL to BoringSSL 163at API level 23. 164 165The rule is enforced by the dynamic linker, so non-public libraries 166are not accessible regardless of the way code tries to load them: 167System.loadLibrary(), DT_NEEDED entries, and direct calls to dlopen(3) 168will all work exactly the same. 169 170In order to reduce the user impact of this transition, we identified 171a set of libraries that saw significant use from Google Play's 172most-installed apps and were feasible for us to support in the 173short term (including libandroid_runtime.so, libcutils.so, libcrypto.so, 174and libssl.so). In order to give app developers more time to transition, 175we allowed access to these libraries for apps with a target API level < 24. 176On devices running API level 26 to API level 30, this compatibility mode could be 177disabled by setting a system property (`debug.ld.greylist_disabled`). 178This property is ignored on devices running API level 31 and later. 179 180``` 181$ readelf --dynamic libBroken.so | grep NEEDED 182 0x00000001 (NEEDED) Shared library: [libnativehelper.so] 183 0x00000001 (NEEDED) Shared library: [libutils.so] 184 0x00000001 (NEEDED) Shared library: [libstagefright_foundation.so] 185 0x00000001 (NEEDED) Shared library: [libmedia_jni.so] 186 0x00000001 (NEEDED) Shared library: [liblog.so] 187 0x00000001 (NEEDED) Shared library: [libdl.so] 188 0x00000001 (NEEDED) Shared library: [libz.so] 189 0x00000001 (NEEDED) Shared library: [libstdc++.so] 190 0x00000001 (NEEDED) Shared library: [libm.so] 191 0x00000001 (NEEDED) Shared library: [libc.so] 192``` 193 194*Potential problems*: starting from API level 24 the dynamic linker will not 195load private libraries, preventing the application from loading. 196 197*Resolution*: rewrite your native code to rely only on public API. As a 198short term workaround, platform libraries without complex dependencies 199(libcutils.so) can be copied to the project. As a long term solution 200the relevant code must be copied to the project tree. SSL/Media/JNI 201internal/binder APIs should not be accessed from the native code. When 202necessary, native code should call appropriate public Java API methods. 203 204A complete list of public libraries is available within the NDK, under 205platforms/android-API/usr/lib. 206 207Note: SSL/crypto is a special case, applications must NOT use platform 208libcrypto and libssl libraries directly, even on older platforms. All 209applications should use GMS Security Provider to ensure they are protected 210from known vulnerabilities. 211 212 213## Missing Section Headers (Enforced for API level >= 24) 214 215Each ELF file has additional information contained in the section 216headers. These headers must be present now, because the dynamic linker 217uses them for validity checking. Some developers strip them in an 218attempt to obfuscate the binary and prevent reverse engineering. (This 219doesn't really help because it is possible to reconstruct the stripped 220information using widely-available tools.) 221 222``` 223$ readelf --headers libBroken.so | grep 'section headers' 224 Start of section headers: 0 (bytes into file) 225 Size of section headers: 0 (bytes) 226 Number of section headers: 0 227``` 228 229*Resolution*: remove the extra steps from your build that strip section 230headers. 231 232 233## Text Relocations (Enforced for API level >= 23) 234 235Apps with a target API level >= 23 cannot load shared objects that contain text 236relocations. Such an approach reduces load time and improves security. This was 237only a change for 32-bit, because 64-bit never supported text relocations. 238 239The usual reason for text relocations was non-position independent 240hand-written assembler. This is not common. You can use the scanelf tool 241from the pax-utils debian package for further diagnostics: 242 243``` 244$ scanelf -qT libTextRel.so 245 libTextRel.so: (memory/data?) [0x15E0E2] in (optimized out: previous simd_broken_op1) [0x15E0E0] 246 libTextRel.so: (memory/data?) [0x15E3B2] in (optimized out: previous simd_broken_op2) [0x15E3B0] 247 ... 248``` 249 250If you have no scanelf tool available, it is possible to do a basic 251check with readelf instead. Look for either a TEXTREL entry or the 252TEXTREL flag. Either alone is sufficient. (The value corresponding to the 253TEXTREL entry is irrelevant and typically 0 --- simply the presence of 254the TEXTREL entry declares that the .so contains text relocations.) This 255example has both indicators present: 256 257``` 258$ readelf --dynamic libTextRel.so | grep TEXTREL 259 0x00000016 (TEXTREL) 0x0 260 0x0000001e (FLAGS) SYMBOLIC TEXTREL BIND_NOW 261``` 262 263Note: it is technically possible to have a shared object with the TEXTREL 264entry/flag but without any actual text relocations. This doesn't happen 265with the NDK, but if you're generating ELF files yourself make sure 266you're not generating ELF files that claim to have text relocations, 267because the Android dynamic linker trusts the entry/flag. 268 269*Potential problems*: Relocations enforce code pages being writable, and 270wastefully increase the number of dirty pages in memory. The dynamic 271linker issued warnings about text relocations from API level 19, but on API 272level 23 and above refuses to load code with text relocations. 273 274*Resolution*: rewrite assembler to be position independent to ensure 275no text relocations are necessary. The 276[Gentoo Textrels guide](https://wiki.gentoo.org/wiki/Hardened/Textrels_Guide) 277has instructions for fixing text relocations, and more detailed 278[scanelf documentation](https://wiki.gentoo.org/wiki/Hardened/PaX_Utilities). 279 280 281## Invalid DT_NEEDED Entries (Enforced for API level >= 23) 282 283While library dependencies (DT_NEEDED entries in the ELF headers) can be 284absolute paths, that doesn't make sense on Android because you have 285no control over where your library will be installed by the system. A 286DT_NEEDED entry should be the same as the needed library's SONAME, 287leaving the business of finding the library at runtime to the dynamic 288linker. 289 290Before API level 23, Android's dynamic linker ignored the full path, and 291used only the basename (the part after the last ‘/') when looking 292up the required libraries. Since API level 23 the runtime linker will honor 293the DT_NEEDED exactly and so it won't be able to load the library if 294it is not present in that exact location on the device. 295 296Even worse, some build systems have bugs that cause them to insert 297DT_NEEDED entries that point to a file on the build host, something that 298cannot be found on the device. 299 300``` 301$ readelf --dynamic libSample.so | grep NEEDED 302 0x00000001 (NEEDED) Shared library: [libm.so] 303 0x00000001 (NEEDED) Shared library: [libc.so] 304 0x00000001 (NEEDED) Shared library: [libdl.so] 305 0x00000001 (NEEDED) Shared library: 306[C:\Users\build\Android\ci\jni\libBroken.so] 307``` 308 309*Potential problems*: before API level 23 the DT_NEEDED entry's basename was 310used, but starting from API level 23 the Android runtime will try to load the 311library using the path specified, and that path won't exist on the 312device. There are broken third-party toolchains/build systems that use 313a path on a build host instead of the SONAME. 314 315*Resolution*: make sure all required libraries are referenced by SONAME 316only. It is better to let the runtime linker to find and load those 317libraries as the location may change from device to device. 318 319 320## Missing SONAME (Enforced for API level >= 23) 321 322Each ELF shared object (“native library”) must have a SONAME 323(Shared Object Name) attribute. The NDK build systems add this 324attribute by default, so its absence (or an incorrect soname) indicates 325a misconfiguration in your build system. A missing SONAME may lead to 326runtime issues such as the wrong library being loaded: the filename is 327used instead when this attribute is missing. 328 329``` 330$ readelf --dynamic libWithSoName.so | grep SONAME 331 0x0000000e (SONAME) Library soname: [libWithSoName.so] 332``` 333 334*Potential problems*: namespace conflicts may lead to the wrong library 335being loaded at runtime, which leads to crashes when required symbols 336are not found, or you try to use an ABI-incompatible library that isn't 337the library you were expecting. 338 339*Resolution*: the current NDK generates the correct SONAME by 340default. Ensure you're using the current NDK and that you haven't 341configured your build system to generate incorrect SONAME entries (using 342the `-soname` linker option). 343 344 345## `__register_atfork` (Available in API level >= 23) 346 347To allow `atfork` and `pthread_atfork` handlers to be unregistered on 348`dlclose`, API level 23 added a new libc function `__register_atfork`. 349This means that code using `atfork` or `pthread_atfork` functions that is 350built with a `minSdkVersion` >= 23 will not load on earlier versions of 351Android, with an error referencing `__register_atfork`. 352 353*Resolution*: build your code with `minSdkVersion` that matches the minimum 354API level you actually support, or avoid using `atfork`/`pthread_atfork`. 355 356 357## DT_RUNPATH support (Available in API level >= 24) 358 359If an ELF file contains a DT_RUNPATH entry, the directories listed there 360will be searched to resolve DT_NEEDED entries. The string `${ORIGIN}` will 361be rewritten at runtime to the directory containing the ELF file. This 362allows the use of relative paths. The `${LIB}` and `${PLATFORM}` 363substitutions supported on some systems are not currently implemented on 364Android. 365 366 367## Writable and Executable Segments (Enforced for API level >= 26) 368 369Each segment in an ELF file has associated flags that tell the 370dynamic linker what permissions to give the corresponding page in 371memory. For security, data shouldn't be executable and code shouldn't be 372writable. This means that the W (for Writable) and E (for Executable) 373flags should be mutually exclusive. This wasn't historically enforced, 374but is now. 375 376``` 377$ readelf --program-headers -W libBadFlags.so | grep WE 378 LOAD 0x000000 0x00000000 0x00000000 0x4c01d 0x4c01d RWE 0x1000 379``` 380 381*Resolution*: we're aware of one middleware product that introduces these 382into your app. The middleware vendor is aware of the problem and has a fix 383available. 384 385 386## Invalid ELF header/section headers (Enforced for API level >= 26) 387 388In API level 26 and above the dynamic linker checks more values in 389the ELF header and section headers and fails if they are invalid. 390 391*Example error* 392``` 393dlopen failed: "/data/data/com.example.bad/lib.so" has unsupported e_shentsize: 0x0 (expected 0x28) 394``` 395 396*Resolution*: don't use tools that produce invalid/malformed 397ELF files. Note that using them puts application under high risk of 398being incompatible with future versions of Android. 399 400 401## Enable logging of dlopen/dlsym and library loading errors for apps (Available for API level >= 26) 402 403On devices running API level 26 or later you can enable logging of dynamic 404linker activity for debuggable apps by setting a property corresponding 405to the fully-qualified name of the specific app: 406``` 407adb shell setprop debug.ld.app.com.example.myapp dlerror,dlopen,dlsym 408adb logcat 409``` 410 411Any combination of `dlerror`, `dlopen`, and `dlsym` can be used. There's 412no separate `dlclose` option: `dlopen` covers both loading and unloading 413of libraries. Note also that `dlerror` doesn't correspond to actual 414calls of dlerror(3) but to any time the dynamic linker writes to its 415internal error buffer, so you'll see any errors the dynamic linker would 416have reported, even if the code you're debugging doesn't actually call 417dlerror(3) itself. 418 419On userdebug and eng builds it is possible to enable tracing for the 420whole system by using the `debug.ld.all` system property instead of 421app-specific one. For example, to enable logging of all dlopen(3) 422(and thus dclose(3)) calls, and all failures, but not dlsym(3) calls: 423``` 424adb shell setprop debug.ld.all dlerror,dlopen 425``` 426 427 428## dlclose interacts badly with thread local variables with non-trivial destructors 429 430Android allows `dlclose` to unload a library even if there are still 431thread-local variables with non-trivial destructors. This leads to 432crashes when a thread exits and attempts to call the destructor, the 433code for which has been unloaded (as in [issue 360], fixed in API level 28). 434 435[issue 360]: https://github.com/android-ndk/ndk/issues/360 436 437Not calling `dlclose` or ensuring that your library has `RTLD_NODELETE` 438set (so that calls to `dlclose` don't actually unload the library) 439are possible workarounds. 440 441| | API level < 23 | >= 23 | >= 28 | 442| ----------------- | -------------------------- | ------- | ----- | 443| No workaround | Works for static STL | Broken | Works | 444| `-Wl,-z,nodelete` | Works for static STL | Works | Works | 445| No `dlclose` | Works | Works | Works | 446 447 448## Use of IFUNC in libc (True for all API levels on devices running Android 10) 449 450On devices running API level 29, libc uses 451[IFUNC](https://sourceware.org/glibc/wiki/GNU_IFUNC) 452functionality in the dynamic linker to choose optimized assembler routines at 453run time rather than at build time. This lets us use the same `libc.so` on all 454devices, and is similar to what other OSes already did. Because the zygote 455uses the C library, this decision is made long before we know what API 456level an app targets, so all code sees the new IFUNC-using C library. 457Most apps should be unaffected by this change, but apps that hook or try to 458detect hooking of C library functions might need to fix their code to cope 459with IFUNC relocations. The affected functions are from `<string.h>`, but 460may expand to include more functions (and more libraries) in future. 461 462 463## Relative relocations (RELR) 464 465Android added experimental support for RELR relative relocations 466in API level 28, but using `SHT_` and `DT_` constants in the space 467reserved for OS private use. 468 469API level 30 added support for ELF files using the official `SHT_` and 470`DT_` constants. 471 472The RELR encoding is unrelated to the earlier "packed relocations" 473format available from API level 23. 474 475There are no plans to remove support for ELF files using the older 476OS private use constants for RELR, nor for ELF files using packed 477relocations. 478 479Prior to API level 35, there was a bug that caused RELR relocations to 480be applied after packed relocations. This meant that ifunc resolvers 481referenced by `R_*_IRELATIVE` relocations in the packed relocation 482section would have been able to read globals with RELR relocations 483before they were relocated. The version of `lld` in the NDK has never 484produced binaries affected by this bug, but third-party toolchains 485should make sure not to store `R_*_IRELATIVE` relocations in packed 486relocation sections in order to maintain compatibility with API levels 487below 35. 488 489You can read more about relative relocations 490and their long and complicated history at 491https://maskray.me/blog/2021-10-31-relative-relocations-and-relr. 492 493 494## No more sentinels in .preinit_array/.init_array/.fini_array sections of executables (in All API levels) 495 496In Android <= API level 34 and NDK <= r26, Android used sentinels in the 497`.preinit_array`/`.init_array`/`.fini_array` sections of executables to locate 498the start and end of these arrays. When building with LTO, the function pointers 499in the arrays can be reordered, making sentinels no longer work. This prevents 500constructors for global C++ variables from being called in static executables 501when using LTO. 502 503To fix this, in Android >= API level 35 and NDK >= r27, we removed sentinels 504and switched to using symbols inserted by LLD (like `__init_array_start`, 505`__init_array_end`) to locate the arrays. This also avoids the need for an 506empty section when there are no corresponding functions. 507 508For dynamic executables, we kept sentinel support in `crtbegin_dynamic.o` and 509`libc.so`. This ensures that executables built with newer `crtbegin_dynamic.o` 510(in NDK >= r27) work with older `libc.so` (in Android <= API level 34), and 511vice versa. 512