了解 64 位版本

概览

从编译系统的角度来看,最显著的变化是现在支持在同一次编译中为两种目标 CPU 架构(64 位和 32 位)编译二进制文件。这也称为“多库编译”。

对于本机静态库和共享库,编译系统设置了为两种架构编译二进制文件的规则。产品配置 (PRODUCT_PACKAGES) 与依赖关系图共同决定了编译哪些二进制文件并安装到系统映像中。

对于可执行文件和应用,编译系统默认仅编译 64 位版本,但您可以使用一个全局 BoardConfig.mk 变量或针对特定模块的变量来替换此设置。

注意:如果某个应用提供了一个可供其他应用(可以是 32 位,也可以是 64 位)使用的 API,那么在该应用的清单中,android:multiarch 属性的值必须设为 true,以避免可能出现的错误。

产品配置

BoardConfig.mk 中,我们添加了以下变量来配置第二个 CPU 架构和 ABI:

TARGET_2ND_ARCH
TARGET_2ND_ARCH_VARIANT
TARGET_2ND_CPU_VARIANT
TARGET_2ND_CPU_ABI
TARGET_2ND_CPU_ABI2

您可以在 build/target/board/generic_arm64/BoardConfig.mk 中查看示例。

如果您希望编译系统默认编译 32 位可执行文件和应用,请设置以下变量:

TARGET_PREFER_32_BIT := true

不过,您可以在 Android.mk 中使用针对特定模块的变量来替换此设置。

在多库编译中,PRODUCT_PACKAGES 中的模块名称同时涵盖了 32 位和 64 位二进制文件,只要这些名称是由编译系统定义的。对于通过依赖关系提取而来的库,只有在另一个 32 位库或可执行文件要求使用时,系统才会安装 32 位库。64 位库也遵循同样的规则。

但是,make 命令行中的模块名称仅涵盖 64 位版本。例如,在运行 lunch aosp_arm64-eng 之后,make libc 仅编译 64 位库。要编译 32 位库,您需要运行 make libc_32

Android.mk 中的模块定义

您可以使用 LOCAL_MULTILIB 变量来配置您是要编译 32 位还是 64 位架构,或是同时编译二者,并可以替换全局 TARGET_PREFER_32_BIT 变量。

LOCAL_MULTILIB 设为以下任一值:

  • “both”(二者):同时编译 32 位和 64 位架构。
  • “32”:仅编译 32 位架构。
  • “64”:仅编译 64 位架构。
  • “first”(第一个):仅编译第一个架构(在 32 位设备中编译 32 位架构,在 64 位设备中编译 64 位架构)。
  • “”:默认值;编译系统根据模块类和其他 LOCAL_ 变量(如 LOCAL_MODULE_TARGET_ARCHLOCAL_32_BIT_ONLY 等)决定要编译哪种架构。

在多库编译中,ifeq $(TARGET_ARCH) 等条件不再起作用。

如果您想为某些特定架构编译模块,以下变量可为您提供帮助:

  • LOCAL_MODULE_TARGET_ARCH
    该变量可设为一个架构列表,类似于“arm x86 arm64”。只有正在编译的架构位于该列表中,编译系统才会添加当前模块。
  • LOCAL_MODULE_UNSUPPORTED_TARGET_ARCH
    LOCAL_MODULE_TARGET_ARCH 的相反变量。只有正在编译的架构不在相应列表中,编译系统才会添加当前模块。

上述两个变量有两个小变体:

  • LOCAL_MODULE_TARGET_ARCH_WARN
  • LOCAL_MODULE_UNSUPPORTED_TARGET_ARCH_WARN

如果当前模块由于架构受到这两个变量的限制而被跳过,编译系统将发出警告。

要设置针对特定架构的编译标记,请使用针对特定架构的 LOCAL_ 变量。针对特定架构的 LOCAL_ 变量由普通 LOCAL_ 变量加架构后缀构成,例如:

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

只有当前正在为相应架构编译二进制文件时,才能使用这些变量。

有时,根据当前正在为 32 位还是 64 位架构编译二进制文件来设置标记会更方便。在这种情况下,您可以使用带有 _32_64 后缀的 LOCAL_ 变量,例如:

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

请注意,并非所有 LOCAL_ 变量都支持针对特定架构的变体。如需了解此类变量的最新列表,请参阅 build/core/clear_vars.mk

安装路径

在过去,您可以使用 LOCAL_MODULE_PATH 将库安装到默认位置以外的位置。例如:LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw

在多库编译中,请改用 LOCAL_MODULE_RELATIVE_PATH

LOCAL_MODULE_RELATIVE_PATH := hw

这样就可以将 64 位和 32 位库安装到正确的位置。

如果您要将某个可执行文件编译为同时适用于 32 位和 64 位架构,则需要使用以下变量之一来区分安装路径:

  • LOCAL_MODULE_STEM_32, LOCAL_MODULE_STEM_64
    指定已安装文件的名称。
  • LOCAL_MODULE_PATH_32, LOCAL_MODULE_PATH_64
    指定安装路径。

生成的源代码

在多库编译中,在 $(local-intermediates-dir)(或通过明确的变量在 $(intermediates-dir-for) 中生成)中生成源代码文件这种方法会变得不再可靠。这是因为 32 位和 64 位版本都需要用到中间目录中生成的源代码,而 $(local-intermediates-dir) 仅指向两个中间目录中的一个。

值得高兴的是,编译系统现在提供了一个适合多库编译的、用于生成源代码的专用中间目录。您可以调用 $(local-generated-sources-dir)$(generated-sources-dir-for) 来获取该目录的路径。它们的用法与 $(local-intermediates-dir)$(intermediates-dir-for) 类似。

如果源代码文件在新的专用目录中生成并由 LOCAL_GENERATED_SOURCES 调用,那么就意味着它在多库编译中是同时为 32 位和 64 位架构编译的。

预编译

在多库编译中,您无法使用 TARGET_ARCH(或加上 TARGET_2ND_ARCH)来告知编译系统,预编译的二进制文件是以哪种架构为目标。请改用上述 LOCAL_ 变量 LOCAL_MODULE_TARGET_ARCHLOCAL_MODULE_UNSUPPORTED_TARGET_ARCH

利用这些变量,即使编译系统目前正在进行 64 位多库编译,也可以选择对应的 32 位预编译二进制文件。

如果您想使用所选的架构来计算预编译二进制文件的源路径,则可以调用 $(get-prebuilt-src-arch)

Dex-preopt

对于 64 位设备,我们会默认为启动映像及任何 Java 库同时生成 32 位和 64 位 odex 文件。对于 APK,我们默认仅为主要的 64 位架构生成 odex。如果某个应用将同时在 32 位和 64 位进程中启动,请使用 LOCAL_MULTILIB := both 确保同时生成 32 位和 64 位 odex 文件。该标记还会指示编译系统同时添加 32 位和 64 位 JNI 库(如果应用中包含任何此类库)。