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