• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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