| # Build System Maintainers Guide |
| |
| The latest version of this document is available at |
| https://android.googlesource.com/platform/ndk/+/mirror-goog-main-ndk/docs/BuildSystemMaintainers.md. |
| Ensure that you are using the version that corresponds to your NDK. Replace |
| `master` in the URL with the appropriate NDK release branch. For example, the |
| NDK r28 version of this document is located at |
| https://android.googlesource.com/platform/ndk/+/ndk-r28-release/docs/BuildSystemMaintainers.md. |
| For r23 and older, the branch name follows a different pattern. The r23 version |
| of this document is located at |
| https://android.googlesource.com/platform/ndk/+/ndk-release-r23/docs/BuildSystemMaintainers.md. |
| |
| The purpose of this guide is to instruct third-party build system maintainers in |
| adding NDK support to their build systems. This guide will not be useful to most |
| NDK users. NDK users should start with [Building Your Project]. |
| |
| Note: This guide is written assuming Linux is the host OS. Mac should be no |
| different, and the only difference on Windows is that file extensions for |
| executables and scripts will differ. |
| |
| [Building Your Project]: https://developer.android.com/ndk/guides/build |
| |
| [TOC] |
| |
| ## Introduction |
| |
| The NDK uses the [LLVM] family of tools for building C/C++ code. These include |
| [Clang] for compilation, [LLD] for linking, and other [LLVM tools] for other |
| tasks. |
| |
| [Clang]: https://clang.llvm.org/ |
| [LLD]: https://lld.llvm.org/ |
| [LLVM tools]: https://llvm.org/docs/CommandGuide/ |
| [LLVM]: https://llvm.org/ |
| |
| ### Architectures |
| [Architectures]: #architectures |
| |
| Note: In general an architecture may have multiple ABIs. An ABI (application |
| binary interface) is different from an architecture in that it also specifies a |
| calling convention, size and alignment of types, and other implementation |
| details. For Android, each architecture supports only one ABI. |
| |
| Android supports multiple architectures: ARM32, ARM64, x86, and x86_64. NDK |
| applications must build libraries for every architecture they support. 64-bit |
| devices usually also support the 32-bit variant of their architecture, but this |
| may not always be the case. While in general this means that an app with only |
| 32-bit libraries can run on 64-bit capable devices, the 64-bit ABI will have |
| improved performance. |
| |
| This document will make use of `<arch>`, `<ABI>`, and `<triple>` in describing |
| paths and arguments. The values of these variables for each architecture are as |
| follows except where otherwise noted: |
| |
| | Name | arch | ABI | triple | |
| | ------------ | ------- | ----------- | --------------------- | |
| | 32-bit ARMv7 | arm | armeabi-v7a | arm-linux-androideabi | |
| | 64-bit ARMv8 | aarch64 | aarch64-v8a | aarch64-linux-android | |
| | 32-bit Intel | x86 | x86 | i686-linux-android | |
| | 64-bit Intel | x86_64 | x86_64 | x86_64-linux-android | |
| |
| Note: Strictly speaking ARMv7 with [NEON] is a different ABI from ARMv7 without |
| NEON, but it is not a *system* ABI. Both NEON and non-NEON ARMv7 code uses the |
| ARMv7 system and toolchains. |
| |
| To programatically determine the list of supported ABIs, their bitness, as well |
| as their deprecation status and whether or not it is recommended to build them |
| by default, use `<NDK>/meta/abis.json`. |
| |
| ### Thumb |
| |
| 32-bit ARM can be built using either the [Thumb] or ARM instruction sets. Thumb |
| code is smaller but may perform worse than ARM. However, smaller code makes more |
| effective use of a processor's instruction cache, so benchmarking is necessary |
| to determine which is more effective for a given application. ndk-build and the |
| NDK's CMake toolchain file generate Thumb code by default. |
| |
| The ARM or Thumb instruction sets are selected by passing `-marm` or `-mthumb` |
| to Clang respectively. By default, Clang will generate ARM code as opposed to |
| Thumb for the `armv7a-linux-androideabi` target. |
| |
| Note: For ARMv7, Thumb-2 is used. Android no longer supports ARMv5, but if your |
| build system mistakenly targets ARMv5 the less efficient Thumb-1 will be used. |
| |
| [Thumb]: https://en.wikipedia.org/wiki/ARM_architecture#Thumb-2 |
| |
| ### NEON |
| |
| Most ARM Android devices support [NEON]. This is supported by all 64-bit ARM |
| devices and nearly all 32-bit ARM devices running at least Android Marshmallow |
| (API 23). The [Android CDD] has required NEON support since that version, but it |
| is possible that extant devices that were upgraded to Marshmallow do not include |
| NEON support. |
| |
| NEON can significantly improve application performance. |
| |
| Clang automatically enables NEON for all API levels. ARM devices without NEON |
| are uncommon. To support non-NEON devices, pass `-mfpu=vfpv3-d16` when |
| compiling. Alternatively, use the Play Console to [exclude CPUs] without NEON |
| to disallow your app from being installed on those devices. |
| |
| [Android CDD]: https://source.android.com/compatibility/cdd |
| [NEON]: https://developer.arm.com/technologies/neon |
| [exclude CPUs]: https://support.google.com/googleplay/android-developer/answer/7353455?hl=en |
| |
| ### OS Versions |
| [OS Versions]: #os-versions |
| |
| As users are distributed over a wide variety of Android OS versions (see the |
| [Distribution dashboard]), applications have a minimum and maximum supported |
| version, as well as a targeted version. These are `minSdkVersion`, |
| `maxSdkVersion`, and `targetSdkVersion` respectively. See the [uses-sdk] |
| documentation for more information. |
| |
| For NDK code, the only relevant value is the minimum supported version. Any time |
| this doc refers to an API level, OS version, or target version, it is referring |
| to the application's `minSdkVersion`. |
| |
| The API level targeted by an NDK application determines which APIs will be |
| exposed for use by the application. By default, APIs that are not present in the |
| targeted API level cannot be linked directly, but may be accessed via `dlsym`. |
| An NDK application running on a device with an API level lower than the target |
| will often not load at all. If it does load, it may not behave as expected. This |
| is not a supported configuration. This behavior can be altered by following the |
| section about [weak symbols]. Be sure your users understand the implications of |
| doing so. |
| |
| The major/minor version number given to an Android OS has no meaning when it |
| comes to determining its API level. See the table in the [Build numbers] |
| document to map Android code names and version numbers to API levels. |
| |
| Note: Not every API level includes new NDK APIs. If there were no new NDK APIs |
| for the given API level, there is no library directory for that API level. In |
| that case, the build system should select the closest available API that is |
| below the target API level. For example, applications with a `minSdkVersion` of |
| 20 should use API 19 for their NDK target. |
| |
| To programatically determine the list of supported API levels as well as aliases |
| that are accepted by ndk-build and CMake, see `<NDK>/meta/platforms.json`. For |
| ABI specific minimum supported API levels, see `<NDK>/meta/abis.json`. |
| |
| Note: In some contexts the API level may be referred to as a platform. In this |
| document an API level is always an integer, and a platform takes the form of |
| `android-<API level>`. The latter format is not specifically used anywhere in |
| the NDK toolchain, but is used to specify target API levels for ndk-build and |
| CMake. |
| |
| Note: As a new version of the Android OS approaches release, previews and betas |
| of that OS will be released and an NDK will be released that can make use of the |
| new APIs. Targeting a preview API level is no different than targeting a |
| released API level, with the exception that applications built targeting preview |
| releases should not be shipped to production. Consult |
| `<NDK>/meta/platforms.json` to determine the API level for a preview release. |
| |
| [Build numbers]: https://source.android.com/setup/start/build-numbers |
| [Distribution dashboard]: https://developer.android.com/about/dashboards/ |
| [uses-sdk]: https://developer.android.com/guide/topics/manifest/uses-sdk-element |
| [weak symbols]: #weak-symbols-for-api-definitions |
| |
| ### Page sizes |
| |
| Android V will allow OEMs to ship arm64-v8a and x86_64 devices with 16KiB page |
| sizes. Devices that use this configuration will not be able to run existing apps |
| that use native code. To be compatible with these devices, applications will |
| need to rebuild all their native code to be 16KiB aligned, and rewrite any code |
| which assumes a specific page size. See [Support 16 KB page sizes] for details. |
| |
| Note: 16KiB compatible binaries are also compatible with 4KiB page devices. You |
| do not need to build both 16KiB and 4KiB variants of your libraries. |
| |
| To minimize disruption, the default configuration for NDK r27 remains 4KiB page |
| sizes. A future NDK (likely r28) will change the defaults. To support building |
| 16KiB compatible apps in your build system, do the following: |
| |
| 1. When linking arm64-v8a or x86_64 code, set the linker's max-page-size to |
| 16384: `-Wl,-z,max-page-size=16384`. This will increase the size of the |
| binaries. |
| 2. Define `__BIONIC_NO_PAGE_SIZE_MACRO` to configure libc to hide the |
| declaration of `PAGE_SIZE` from the build: `-D__BIONIC_NO_PAGE_SIZE_MACRO`. |
| There is no valid build-time constant for the page size in a world where |
| devices have varying page sizes. Runtime checks with `getpagesize()` are |
| required. |
| |
| Note that, for the time being, this only needs to be done for arm64-v8a. The |
| x86_64 emulator (see [Support 16 KB page sizes] for details) will support larger |
| page sizes for testing purposes, but there are no plans to change the page size |
| for the 32-bit ABIs, and riscv64 does not support 16KiB page sizes at all. |
| |
| [Support 16 KB page sizes]: https://developer.android.com/guide/practices/page-sizes |
| |
| ## Clang |
| |
| Clang is installed to `<NDK>/toolchains/llvm/prebuilt/<host-tag>/bin/clang`. |
| The C++ compiler is installed as `clang++` in the same directory. `clang++` will |
| make C++ headers available when compiling and will automatically link the C++ |
| runtime libraries when linking. |
| |
| `clang` should be used when compiling C source files, and `clang++` should be |
| used when compiling C++ source files. When linking, `clang` should be used if |
| the binary being linked contains no C++ code (i.e. none of the object files |
| being linked were generated from C++ files) and `clang++` should be used |
| otherwise. Using `clang++` ensures that the C++ standard library is linked. |
| |
| When linking a shared library, the `-Wl,-soname,$NAME_OF_LIBRARY` argument is |
| required. This is necessary to avoid the problems described in [this stack |
| overflow post](https://stackoverflow.com/a/48291044/632035). For example, when |
| building `libapp.so`, `-Wl,-soname,libapp.so` must be used. |
| |
| ### Target Selection |
| |
| [Cross-compilation] targets can be selected in one of two ways: by using |
| the `--target` flag, or by using target-specific wrapper scripts. |
| |
| If possible, we recommend using the `--target` flag, which is described more |
| fully in the [Clang User Manual]. The value passed is a Clang target |
| triple suffixed with an Android API level. For example, to target API 26 for |
| 32-bit ARM, use `--target armv7a-linux-androideabi26`. |
| |
| Note: "armv7a" should be used rather than simply "arm" when specifying targets |
| for Clang to generate ARMv7 code rather than the slower ARMv5 code. Specifying |
| ARMv5 and thumb code generation will result in Thumb-1 being generated rather |
| than Thumb-2, which is less efficient. |
| |
| If not possible to use the `--target` flag, we supply wrapper scripts alongside |
| the `clang` and `clang++` binaries, named `<triple><API-level>-clang` and |
| `<triple><API-level>-clang++`. For example, to target API 26 32-bit ARM, |
| invoke `armv7a-linux-androideabi26-clang` or |
| `armv7a-linux-androideabi26-clang++` instead of `clang` or `clang++`. These |
| wrappers come in two forms: Bash scripts (for Mac, Linux, Cygwin, and WSL) and |
| Windows batch files (with `.cmd` extensions). |
| |
| Note: For projects with many source files, the wrapper scripts may cause |
| noticeable overhead, which is why we recommend using `--target`. The overhead |
| is most significant on Windows, as `CreateProcess` is slower than `fork`. |
| |
| For more information on Android targets, see the [Architectures] and [OS |
| Versions] sections. |
| |
| [Clang User Manual]: https://clang.llvm.org/docs/UsersManual.html |
| [Cross-compilation]: https://en.wikipedia.org/wiki/Cross_compiler |
| |
| ## Linkers |
| |
| The NDK uses LLD for linking. The linker is installed to |
| `<NDK>/toolchains/llvm/prebuilt/<host-tag>/bin/<triple>-ld`. |
| |
| Note: It is usually not necessary to invoke the linkers directly since Clang |
| will do so automatically. Clang will also automatically link CRT objects and |
| default libraries and set up other target-specific options, so it is generally |
| better to use Clang for linking. |
| |
| [Issue 70838247]: https://issuetracker.google.com/70838247 |
| |
| ## Binutils |
| |
| LLVM's binutils tools are installed to the NDK at |
| `<NDK>/toolchains/llvm/prebuilt/<host-tag>/bin/llvm-<tool>`. These include but |
| are not limited to: |
| |
| * llvm-ar |
| * llvm-objcopy |
| * llvm-objdump |
| * llvm-readelf |
| * llvm-strip |
| |
| All LLVM tools are capable of handling every target architecture. Unlike Clang, |
| no `-target` argument is required for these tools, so they should behave |
| correctly when used as drop-in replacements for their GNU equivalents. Some |
| tools may optionally accept a `-target` argument, but if omitted they will |
| select the correct target based on the input files. |
| |
| Note that `llvm-as` is **not** an equivalent of GNU `as`, but rather a tool for |
| assembling LLVM IR. If you are currently using `as` directly, you will need to |
| migrate to using `clang` as a driver for building assembly. See [Clang |
| Migration Notes] for advice on fixing assembly to be LLVM compatible. |
| |
| Note that by default `/usr/bin/as` is used by Clang if the |
| `-fno-integrated-as` argument is used, which is almost certainly not |
| what you want! |
| |
| [Clang Migration Notes]: ClangMigration.md |
| |
| ## Sysroot |
| |
| The Android sysroot is installed to |
| `<NDK>/toolchains/llvm/prebuilt/<host-tag>/sysroot` and contains the headers, |
| libraries, and CRT object files for each Android target. |
| |
| Headers can be found in the `usr/include` directory of the sysroot. Target |
| specific include files are installed to `usr/include/<triple>`. When using |
| Clang, it is not necessary to include these directories explicitly; Clang will |
| automatically select the sysroot. If using a compiler other than Clang, ensure |
| that the target-specific include directory takes precedence over the |
| target-generic directory. |
| |
| Libraries are found in the `usr/lib/<triple>` directory of the sysroot. |
| Version-specific libraries are installed to `usr/lib/<triple>/<API-level>`. As |
| with the header files, when using Clang it is not necessary to include these |
| directories explicitly; the sysroot will be automatically selected. If using a |
| compiler other than Clang, ensure that the version-specific library directory |
| takes precedence over the version-generic directory. |
| |
| ## Libraries |
| |
| The NDK contains three types of libraries. Static libraries have a .a file |
| extension and are linked directly into app binaries. Shared libraries have a .so |
| file extension and must be included in the app's APK if used. System stub |
| libraries are a special type of shared library that should not be included in |
| the APK. The system stub libraries define the interface of a library that is |
| provided by the Android OS but contain no implementation. They can be identified |
| by their .so file extension and their presence in `<NDK>/meta/system_libs.json`. |
| The entries in this file are a key/value pair that maps library names to the |
| first API level the library is introduced. |
| |
| ## Weak symbols for API definitions |
| |
| See [Issue 837]. |
| |
| The Android APIs are exposed as strong symbols by default. This means that apps |
| must not directly refer to any APIs that were not available in their |
| `minSdkVersion`, even if they will not be called at runtime. The loader will |
| reject any library with strong references to symbols that are not present at |
| load time. |
| |
| It is possible to expose Android APIs as weak symbols to alter this behavior to |
| more closely match the Java behavior, which many app developers are more |
| familiar with. The loader will allow libraries with unresolved references to |
| weak symbols to load, allowing those APIs to be safely called as long as they |
| are only called when the API is available on the device. Absent APIs will have a |
| `nullptr` address, so calling an unavailable API will segfault. |
| |
| Note: APIs that are guaranteed to be available in the `minSdkVersion` (the API |
| level passed to Clang with `-target`) will always be strong references, even |
| with this option enabled. |
| |
| This is not enabled by default because, unless used cautiously, this method is |
| prone to deferring build failures to run-time (and only on older devices, since |
| newer devices will have the API). The loader not prevent the library from |
| loading, but the function's address will be `nullptr` if the API is not |
| available (if the API is newer than the OS). The API availability should be |
| checked with `__builtin_available` before making the call: |
| |
| ```c++ |
| if (__builtin_available(android 33, *)) { |
| // Call some API that's only available in API 33+. |
| } else { |
| // Use some fallback behavior, perhaps doing nothing. |
| } |
| ``` |
| |
| Clang offers some protections for this approach via `-Wunguarded-availability`, |
| which will emit a warning unless the call to the API is guarded with |
| `__builtin_available`. |
| |
| To enable this functionality, pass `-D__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__` |
| to Clang when compiling. We **strongly** recommend forcing |
| `-Werror=unguarded-availability` when using this option. |
| |
| We recommend making the choice of weak or strong APIs an option in your build |
| system. Most developers will likely prefer weak APIs as they are simpler than |
| using `dlopen`/`dlsym`, and as long as `-Werror=unguarded-availability` is used, |
| it should be safe. At the time of writing, the NDK's own build systems |
| (ndk-build and CMake) use strong API references by default, but that may change |
| in the future. |
| |
| Known issues and limitations: |
| |
| * Only symbols are affected, not libraries. The only way to conditionally depend |
| on a library that is not available in the app's `minSdkVersion` is with |
| `dlopen`. We do not know how to solve this in a backwards compatible manner. |
| * APIs in bionic (libc, libm, libdl) are not currently supported. See the bug |
| for more information. If the source compatibility issues can be resolved, that |
| will change in a future NDK release. |
| * Headers authored by third-parties (e.g. `vulkan.h`, which comes directly from |
| Khronos) are not supported. The implementation of this feature requires |
| annotation of all function declarations, and the upstream headers likely do |
| not contain those annotations. Solutions to this problem are being |
| investigated. |
| |
| [Issue 837]: https://github.com/android/ndk/issues/837 |
| |
| ## STL |
| |
| ### libc++ |
| |
| The STL provided by the NDK is [libc++]. Its headers are installed to |
| `<NDK>/sysroot/usr/include/c++/v1`. This STL is used by default. This STL comes |
| in both a static and shared variant. The shared variant is used by default. To |
| use the static variant, pass `-static-libstdc++` when linking. If using the |
| shared variant, libc++_shared.so must be included in the APK. This library is |
| installed to `<NDK>/sysroot/usr/lib/<triple>`. |
| |
| Warning: There are a number of things to consider when selecting between the |
| shared and static STLs. See the [Important Considerations] section of the C++ |
| Support document for more details. |
| |
| There are version-specific libc++.so and libc++.a libraries installed to |
| `<NDK>/sysroot/usr/lib/<triple>/<version>`. These are not true libraries but |
| [implicit linker scripts]. They inform the linker how to properly link the STL |
| for the given version. These scripts handle the inclusion of any libc++ |
| dependencies if necessary. Linker scripts should not be included in the APK. |
| |
| Build systems should prefer to let Clang link the STL. If not using Clang, the |
| version scripts should be used. Linking libc++ and its dependencies manually |
| should only be used as a last resort. |
| |
| Note: Linking libc++ and its dependencies explicitly may be necessary to defend |
| against exception unwinding bugs caused by improperly built dependencies (see |
| [Issue 379]). If not dependent on stack unwinding (the usual reason being that |
| the application does not make use of C++ exceptions) or if no dependencies were |
| improperly built, this is not necessary. If needed, link the libraries as listed |
| in the linker script and be sure to follow the instructions in [Unwinding]. |
| |
| [Important Considerations]: https://developer.android.com/ndk/guides/cpp-support#important_considerations |
| [Issue 379]: https://github.com/android-ndk/ndk/issues/379 |
| [implicit linker scripts]: https://sourceware.org/binutils/docs/ld/Scripts.html |
| [libc++]: https://libcxx.llvm.org/ |
| |
| ### System STL |
| |
| The legacy "system STL" is also included, but it will be removed in a future NDK |
| release. It is not in fact an STL; it contains only the barest C++ library |
| support: the C++ versions of the C library headers and basic C++ runtime support |
| like `new` and `delete`. Its headers are installed to |
| `<NDK>/toolchains/llvm/prebuilt/<host-tag>/include/c++/4.9.x` and its library is |
| the libstdc++.so system stub library. To use this STL, use the |
| `-stdlib=libstdc++` flag. |
| |
| TODO: Shouldn't it be installed to sysroot like libc++? |
| |
| Note: The system STL will likely be removed in a future NDK release. |
| |
| ### No STL |
| |
| To avoid using the STL at all, pass `-nostdinc++` when compiling and |
| `-nostdlib++` when linking. This is not necessary when using `clang`, only when |
| using `clang++`. |
| |
| ## Sanitizers |
| |
| The NDK supports [Address Sanitizer] (ASan). This tool is similar to Valgrind in |
| that it diagnoses memory bugs in a running application, but ASan is much faster |
| than Valgrind (roughly 50% performance compared to an unsanitized application). |
| |
| To use ASan, pass `-fsanitize=address` when both compiling and linking. The |
| sanitizer runtime libraries are installed to `<clang resource dir>/lib/linux`. |
| The Clang resource directory is given by `clang -print-resource-dir`. The |
| library is named `libclang_rt.asan-<arch>-android.so`. This library must be |
| included in the APK. A [wrap.sh] file must also be included in the APK. A |
| premade wrap.sh file for ASan is installed to `<NDK>/wrap.sh`. |
| |
| Note: wrap.sh is only available for [debuggable] APKs running on Android Oreo |
| (API 26) or higher. ASan can still be used devices prior to Oreo but at least |
| Lollipop (API 21) if the device has been rooted. Direct users to the |
| [AddressSanitizerOnAndroid] document for instructions on using this method. |
| |
| [Address Sanitizer]: https://clang.llvm.org/docs/AddressSanitizer.html |
| [AddressSanitizerOnAndroid]: https://github.com/google/sanitizers/wiki/AddressSanitizerOnAndroid#run-time-flags |
| [debuggable]: https://developer.android.com/guide/topics/manifest/application-element#debug |
| [wrap.sh]: https://developer.android.com/ndk/guides/wrap-script |
| |
| ## Additional Required Arguments |
| |
| Note: It is a bug that any of these need to be specified by the build system. |
| All flags discussed in this section should be automatically selected by Clang, |
| but they are not yet. Check back in a future NDK release to see if any can be |
| removed from your build system. |
| |
| For x86 targets prior to Android Nougat (API 24), `-mstackrealign` is needed to |
| properly align stacks for global constructors. See [Issue 635]. |
| |
| Android requires [Position-independent executables] beginning with API 21. Clang |
| builds PIE executables by default. If invoking the linker directly or not using |
| Clang, use `-pie` when linking. |
| |
| Android Studio's LLDB debugger uses a binary's build ID to locate debug |
| information. To ensure that LLDB works with a binary, pass an option like |
| `-Wl,--build-id=sha1` to Clang when linking. Other `--build-id=` modes are OK, |
| but avoid a plain `--build-id` argument when using LLD, because Android Studio's |
| version of LLDB doesn't recognize LLD's default 8-byte build ID. See [Issue |
| 885]. |
| |
| The unwinder used for crash handling on Android devices prior to API 29 cannot |
| correctly unwind binaries built with `-Wl,--rosegment`. This flag is enabled by |
| default when using LLD, so if using LLD and targeting devices older than API 29 |
| you must pass `-Wl,--no-rosegment` when linking for correct stack traces in |
| logcat. See [Issue 1196]. |
| |
| [Issue 635]: https://github.com/android-ndk/ndk/issues/635 |
| [Issue 885]: https://github.com/android-ndk/ndk/issues/885 |
| [Issue 906]: https://github.com/android-ndk/ndk/issues/906 |
| [Issue 1196]: https://github.com/android/ndk/issues/1196 |
| [Position-independent executables]: https://en.wikipedia.org/wiki/Position-independent_code#Position-independent_executables |
| |
| ## Useful Arguments |
| |
| ### Dependency Management |
| |
| It is recommended that `-Wl,--exclude-libs,<library file name>` be used for each |
| static library linked. This causes the linker to give symbols imported from a |
| static library hidden [visibility]. This prevents a binary from unintentionally |
| re-exporting an API other than its own. If the intent is to re-export all the |
| symbols in a static library, `-Wl,--whole-archive <library> |
| -Wl,--no-whole-archive` should be used to ensure that the whole archive is |
| preserved. By default, only symbols in used sections will be included in the |
| linked binary. |
| |
| [visibility]: https://gcc.gnu.org/wiki/Visibility |
| |
| ### Controlling Binary Size |
| |
| To minimize the size of an APK, it may be desirable to use the `-Oz` |
| optimization mode. This will generate somewhat slower code than `-O2` or `-O3`, |
| but it will be smaller. |
| |
| Note: `-Os` behavior is not the same with Clang as it is with GCC. Clang's `-Oz` |
| behaves similarly to GCC's `-Os`. `-Os` with Clang is a middle ground between |
| size and speed optimizations. |
| |
| To aid the linker in removing as much unused code as possible, the compiler |
| flags `-ffunction-sections` and `-fdata-sections` may be used. These flags |
| should only be used in conjunction with the `-Wl,--gc-sections` linker flag. |
| Failing to use `-Wl,--gc-sections` will cause the former flags to *increase* |
| output size. The linker is only able to discard unused sections, so it can only |
| discard at per-function or per-variable granularity if each is in its own |
| section. |
| |
| While `-Wl,--gc-sections` should always be used, whether or not to enable |
| `-ffunction-sections` and `-fdata-sections` depends on how the object file being |
| compiled is expected to be used. If it will be used in a shared library then all |
| of its [public symbols] will be preserved and the additional overhead of placing |
| each item in its own section may make the shared library *larger* rather than |
| smaller. If it will be used only in a static library or an executable then it |
| will depend on how much of the resulting object file is expected to be unused. |
| |
| [public symbols]: #dependency-management |
| |
| #### RELR and relocation packing |
| |
| Note that each of the flags below will prevent the library or executable from |
| loading on older devices. If your `minSdkVersion` is at least the supported API |
| level, these flags are typically beneficial. A future release of the NDK will |
| likely enable this by default based on the `minSdkVersion` passed to Clang. See |
| [Issue 909] for more information. |
| |
| Beginning with API level 23 it is possible to compress the relation data in |
| libraries and executables. Libraries with large numbers of relocations will |
| benefit from this. Enable with `-Wl,--pack-dyn-relocs=android` at link time. |
| |
| API level 28 adds support for relative relocations (RELR) which can further |
| reduce the size of relocations. Enable with `-Wl,--pack-dyn-relocs=android+relr` |
| at link time. API levels 28 and 29 predate the standardization of this feature |
| in ELF, so for those API levels also pass `-Wl,--use-android-relr-tags` at link |
| time. |
| |
| [Issue 909]: https://github.com/android/ndk/issues/909 |
| |
| ### Helpful Warnings |
| |
| It is recommended that build systems promote the following warnings to errors. |
| These warnings indicate either a bug or undefined behavior, the latter of which |
| Clang will usually turn into a bug. |
| |
| * `-Werror=return-type`: A non-void function is missing a return statement. |
| Clang may "optimize" this function to fall through into the next one. |
| * `-Werror=int-to-pointer-cast` and `-Werror=pointer-to-int-cast`: These |
| indicate bugs that will affect the 64-bit version of the application. |
| * `-Werror=implicit-function-declaration`: Undeclared functions may be inferred |
| to have a return type of `int` in C. For functions that return a pointer, the |
| return type will be silently truncated to a 32-bit `int`, resulting in bugs |
| that will affect the 64-bit version of the application. |
| |
| For more information on Clang's supported arguments, see the [Clang User |
| Manual]. |
| |
| ### Hardening |
| |
| #### Stack protectors |
| |
| It is recommented to build all code with `-fstack-protector-strong`. This causes |
| the compiler to emit stack guards to protect against security vulnerabilities |
| caused by buffer overruns. |
| |
| Note: ndk-build and the NDK's CMake toolchain file enable this option by |
| default. |
| |
| #### Fortify |
| |
| FORTIFY is a set of extensions to the C standard library that tries to catch the |
| incorrect use of standard functions, such as `memset`, `sprintf`, and `open`. |
| Where possible, runtime bugs will be diagnosed as errors at compile-time. If not |
| provable at compile-time, a run-time check is used. Note that the specific set |
| of APIs checked depends on the `minSdkVersion` used, since run-time support is |
| required. See [FORTIFY in Android] for more details. |
| |
| To enable this feature in your build define `_FORTIFY_SOURCE=2` when compiling. |
| |
| Note: ndk-build and the NDK's CMake toolchain file enable this option by |
| default. |
| |
| [FORTIFY in Android]: https://android-developers.googleblog.com/2017/04/fortify-in-android.html |
| |
| ### Version script validation |
| |
| LLD will not raise any errors for symbols named in version scripts that are |
| absent from the library. This is either a mistake in the version script, or a |
| missing definition in the library. To have LLD diagnose these errors, pass |
| `-Wl,--no-undefined-version` when linking. |
| |
| ## Common Issues |
| |
| ### Unwinding |
| [Unwinding]: #unwinding |
| |
| The NDK uses LLVM's libunwind. libunwind is needed to provide C++ exception |
| handling support and C's `__attribute__((cleanup))`. The unwinder is linked |
| automatically by Clang, and is built with hidden visibility to avoid shared |
| libraries re-exporting the unwind interface. |
| |
| Until NDK r23, libgcc was the unwinder for all architectures other than 32-bit |
| ARM, and even 32-bit ARM used libgcc to provide compiler runtime support. |
| Libraries built with NDKs older than r23 by build systems that did not follow |
| the advice in this document may re-export that incompatible unwinder. In this |
| case those libraries can prevent the correct unwinder from being used by your |
| build, resulting in crashes or incorrect behavior at runtime. |
| |
| The best way to avoid this problem is to ensure all libraries in the application |
| were built with NDK r23 or newer, but even libraries built by older NDKs are |
| unlikely to have this problem. |
| |
| For build systems that want to protect their users against improperly built |
| libraries, read on. **Neither ndk-build nor CMake make this effort.** |
| |
| To protect against improperly built libraries, build systems can ensure that |
| shared libraries are always linked **after** static libraries, and explicitly |
| link the unwinder between each group. The linker will prefer definitions that |
| appear sooner in the link order, so libunwind appearing **before** the shared |
| libraries will prevent the linker from considering the incompatible unwinder |
| provided by the broken library. libunwind must be linked after other static |
| libraries to provide the unwind interface to those static libraries. |
| |
| The following link order will protect against incorrectly built dependencies: |
| |
| 1. crtbegin |
| 2. object files |
| 3. static libraries |
| 4. libunwind |
| 5. shared libraries |
| 6. crtend |
| |
| Unless using `-nostdlib` when linking, crtend and crtbegin will be linked |
| automatically by Clang. libunwind can be manually linked with `-lunwind`. |
| |
| ## Windows Specific Issues |
| |
| ### Command Line Length Limits |
| |
| Command line length limits on Windows are short enough that they can pose |
| problems when building large projects. Commands executed via cmd.exe are limited |
| to [8,191 characters] and commands executed with `CreateProcess` are limited to |
| [32,768 characters]. |
| |
| To work around these issues, Clang, the linkers, and the archiver all accept a |
| response file that specifies the input files in place of specifying each input |
| explicitly on the command line. Response files are identified on the command |
| line with a "@" prefix and are formatted as space separated arguments. For |
| example: |
| |
| $ ar crsD liba.a @inputs.rsp |
| |
| If the contents of `inputs.rsp` are `a.o b.o c.o` then `ar` will insert `a.o`, |
| `b.o`, and `c.o` into `liba.a`. |
| |
| [8,191 characters]: https://support.microsoft.com/en-us/help/830473/command-prompt-cmd-exe-command-line-string-limitation |
| [32,768 characters]: https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-createprocessa |
| |
| ### Path Length Limits |
| |
| Windows paths are limited to 260 characters, including the drive letter, colon, |
| backslash, and terminating null. See Microsoft's documentation on [path length |
| limits] for possible solutions. |
| |
| [path length limits]: https://docs.microsoft.com/en-us/windows/desktop/fileio/naming-a-file#maximum-path-length-limitation |
| |
| ### Performance Differences |
| |
| Our experience shows that builds on Windows are generally slower than they are |
| on Linux. The cost of `CreateProcess` in comparison to `fork` accounts for much |
| of the difference, so it is best to minimize process creation in your build |
| system. |
| |
| File system performance can also make a large difference. This also appears to |
| be the reason that Mac, while it has better build performance than Windows, |
| still underperforms Linux. |
| |
| Windows and Mac users will see optimum build performance in a Linux VM. |