Understanding 64-bit Builds

Overview

From the build system’s perspective, the most noticeable change is that now it supports building binaries for two target CPU architectures (64-bit and 32-bit) in the same build. That’s also known as Multilib build.

For native static libraries and shared libraries, the build system sets up rules to build binaries for both architectures. The product configuration (PRODUCT_PACKAGES), together with the dependency graph, determines which binaries are built and installed to the system image.

For executables and apps, the build system builds only the 64-bit version by default, but you can override this setting by using a global BoardConfig.mk variable or a module-scoped variable.

Caution: If an app exposes an API to other apps that can be either 32- or 64-bit, the app must have the android:multiarch property set to a value of true within its manifest to avoid potential errors.

Product Configuration

In BoardConfig.mk, we added the following variables to configure the second CPU architecture and ABI:

TARGET_2ND_ARCH
TARGET_2ND_ARCH_VARIANT
TARGET_2ND_CPU_VARIANT
TARGET_2ND_CPU_ABI
TARGET_2ND_CPU_ABI2

You can see an example in build/target/board/generic_arm64/BoardConfig.mk.

If you want the build system to build 32-bit executables and apps by default, set the following variable:

TARGET_PREFER_32_BIT := true

However, you can override this setting by using module-specific variables in Android.mk.

In a Multilib build, module names in PRODUCT_PACKAGES cover both the 32-bit and 64-bit binaries, as long as they are defined by the build system. For libraries pulled in by dependency, a 32-bit library is installed only if it’s required by another 32-bit library or executable. The same is true for 64-bit libraries.

However, module names on the make command line cover only the 64-bit version. For example, after running lunch aosp_arm64-eng,make libc builds only the 64-bit libc. To build the 32-bit libc, you need to run make libc_32.

Module Definition in Android.mk

You can use the LOCAL_MULTILIB variable to configure your build for 32-bit and/or 64-bit and override the global TARGET_PREFER_32_BIT.

Set LOCAL_MULTILIB to one of the following:

  • "both”: build both 32-bit and 64-bit.
  • “32”: build only 32-bit.
  • “64”: build only 64-bit.
  • “first”: build for only the first arch (32-bit in 32-bit devices and 64-bit in 64-bit devices).
  • “”: the default; the build system decides what arch to build based on the module class and other LOCAL_ variables, such as LOCAL_MODULE_TARGET_ARCH, LOCAL_32_BIT_ONLY, etc.

In a Multilib build, conditionals like ifeq $(TARGET_ARCH) don’t work any more.

If you want to build your module for some specific arch(s), the following variables can help you:

  • LOCAL_MODULE_TARGET_ARCH
    It can be set to a list of archs, something like “arm x86 arm64”. Only if the arch being built is among that list will the current module be included by the build system.
  • LOCAL_MODULE_UNSUPPORTED_TARGET_ARCH
    The opposite of LOCAL_MODULE_TARGET_ARCH. Only if the arch being built is not among the list, the current module will be included.

There are minor variants of the above two variables:

  • LOCAL_MODULE_TARGET_ARCH_WARN
  • LOCAL_MODULE_UNSUPPORTED_TARGET_ARCH_WARN

The build system will give warning if the current module is skipped due to archs limited by them.

To set up arch-specific build flags, use the arch-specific LOCAL_ variables. An arch-specific LOCAL_ variable is a normal LOCAL_ variable with an arch suffix, for example:

  • LOCAL_SRC_FILES_arm, LOCAL_SRC_FILES_x86,
  • LOCAL_CFLAGS_arm, LOCAL_CFLAGS_arm64,
  • LOCAL_LDFLAGS_arm, LOCAL_LDFLAGS_arm64,

Those variables will be applied only if a binary is currently being built for that arch.

Sometimes it’s more convenient to set up flags based on whether the binary is currently being built for 32-bit or 64-bit. In that case you can use the LOCAL_ variable with a _32 or _64 suffix, for example:

  • LOCAL_SRC_FILES_32, LOCAL_SRC_FILES_64,
  • LOCAL_CFLAGS_32, LOCAL_CFLAGS_64,
  • LOCAL_LDFLAGS_32, LOCAL_LDFLAGS_64,

Note that not all of the LOCAL_ variables support the arch-specific variants. For an up-to-date list of such variables, refer to build/core/clear_vars.mk.

Install path

In the past, you could use LOCAL_MODULE_PATH to install a library to a location other than the default one. For example, LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw.

In Multilib build, use LOCAL_MODULE_RELATIVE_PATH instead:

LOCAL_MODULE_RELATIVE_PATH := hw

so that both the 64-bit and 32-bit libraries can be installed to the right place.

If you build an executable as both 32-bit and 64-bit, you’ll need to use one of the following variables to distinguish the install path:

  • LOCAL_MODULE_STEM_32, LOCAL_MODULE_STEM_64
    Specifies the installed file name.
  • LOCAL_MODULE_PATH_32, LOCAL_MODULE_PATH_64
    Specifies the install path.

Generated sources

In a Multilib build, if you generate source files to $(local-intermediates-dir) (or $(intermediates-dir-for) with explicit variables), it won’t reliably work any more. That’s because the intermediate generated sources will be required by both 32-bit and 64-bit build, but $(local-intermediates-dir) only points to one of the two intermediate directories.

Happily, the build system now provides a dedicated, Multilib-friendly, intermediate directory for generating sources. You can call $(local-generated-sources-dir) or $(generated-sources-dir-for) to get the directory’s path. Their usages are similar to $(local-intermediates-dir) and $(intermediates-dir-for).

If a source file is generated to the new dedicated directory and picked up by LOCAL_GENERATED_SOURCES, it is built for both 32-bit and 64-bit in multilib build.

Prebuilts

In Multilib, you can’t use TARGET_ARCH (or together with TARGET_2ND_ARCH) to tell the build system what arch the prebuilt binary is targeted for. Use the aforementioned LOCAL_ variable LOCAL_MODULE_TARGET_ARCH or LOCAL_MODULE_UNSUPPORTED_TARGET_ARCH instead.

With these variables, the build system can choose the corresponding 32-bit prebuilt binary even if it’s currently doing a 64-bit Multilib build.

If you want to use the chosen arch to compute the source path for the prebuilt binary , you can call $(get-prebuilt-src-arch).

Dex-preopt

For 64-bit devices, by default we generate both 32-bit and 64-bit odex files for the boot image and any Java libraries. For APKs, by default we generate odex only for the primary 64-bit arch. If an app will be launched in both 32-bit and 64-bit processes, please use LOCAL_MULTILIB := both to make sure both 32-bit and 64-bit odex files are generated. That flag also tells the build system to include both 32-bit and 64-bit JNI libraries, if the app has any.