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