Add prebuilt ELF binaries checker
This commit introduces a prebuilt ELF binaries checker. The checker
will check:
1. Whether all DT_NEEDED shared libraries are specified in
`shared_libs` (Android.bp) or `LOCAL_SHARED_LIBRARIES` (Android.mk).
2. Whether all undefined symbols in the prebuilt binary can be resolved
to defined symbols exported by its dependencies.
This ensures that prebuilt binaries won't silently become ABI
incompatible.
To check the prebuilt binaries, all of the dependencies must be
specified in `shared_libs` (Android.bp) or `LOCAL_SHARED_LIBRARIES`
(Android.mk).
If your prebuilt binaries cannot be checked for some reason, you may add
the following property to Android.bp:
check_elf_files: false,
Or, add the following setting to Android.mk:
LOCAL_CHECK_ELF_FILES := false
Bug: 119084334
Test: CHECK_ELF_FILES=true make check-elf-files
Change-Id: I523d3083f22fd4053c096d26f61f8375800281c8
diff --git a/core/check_elf_file.mk b/core/check_elf_file.mk
new file mode 100644
index 0000000..69d891c
--- /dev/null
+++ b/core/check_elf_file.mk
@@ -0,0 +1,45 @@
+# Check the correctness of the prebuilt ELF files
+#
+# This check ensures that DT_SONAME matches with the filename, DT_NEEDED
+# matches the shared libraries specified in LOCAL_SHARED_LIBRARIES, and all
+# undefined symbols in the prebuilt binary can be found in one of the shared
+# libraries specified in LOCAL_SHARED_LIBRARIES.
+#
+# Inputs:
+# - LOCAL_ALLOW_UNDEFINED_SYMBOLS
+# - LOCAL_BUILT_MODULE
+# - LOCAL_IS_HOST_MODULE
+# - LOCAL_MODULE_CLASS
+# - intermediates
+# - my_installed_module_stem
+# - my_prebuilt_src_file
+
+ifndef LOCAL_IS_HOST_MODULE
+ifneq ($(filter $(LOCAL_MODULE_CLASS),SHARED_LIBRARIES EXECUTABLES NATIVE_TESTS),)
+check_elf_files_stamp := $(intermediates)/check_elf_files.timestamp
+$(check_elf_files_stamp): PRIVATE_SONAME := $(if $(filter $(LOCAL_MODULE_CLASS),SHARED_LIBRARIES),$(my_installed_module_stem))
+$(check_elf_files_stamp): PRIVATE_ALLOW_UNDEFINED_SYMBOLS := $(LOCAL_ALLOW_UNDEFINED_SYMBOLS)
+$(check_elf_files_stamp): PRIVATE_SHARED_LIBRARY_FILES := # This variable will be set by `core/main.mk`
+$(check_elf_files_stamp): $(my_prebuilt_src_file) $(CHECK_ELF_FILE) $(LLVM_READOBJ)
+ @echo Check prebuilt ELF binary: $<
+ $(hide) mkdir -p $(dir $@)
+ $(hide) rm -f $@
+ $(hide) $(CHECK_ELF_FILE) \
+ --skip-bad-elf-magic \
+ --skip-unknown-elf-machine \
+ $(if $(PRIVATE_SONAME),--soname $(PRIVATE_SONAME)) \
+ $(foreach l,$(PRIVATE_SHARED_LIBRARY_FILES),--shared-lib $(l)) \
+ $(if $(PRIVATE_ALLOW_UNDEFINED_SYMBOLS),--allow-undefined-symbols) \
+ --llvm-readobj=$(LLVM_READOBJ) \
+ $<
+ $(hide) touch $@
+
+ifneq ($(PRODUCT_CHECK_ELF_FILES)$(CHECK_ELF_FILES),)
+ifneq ($(LOCAL_CHECK_ELF_FILES),false)
+$(LOCAL_BUILT_MODULE): $(check_elf_files_stamp)
+check-elf-files: $(check_elf_files_stamp)
+endif # LOCAL_CHECK_ELF_FILES
+endif # PRODUCT_CHECK_ELF_FILES or CHECK_ELF_FILES
+
+endif # SHARED_LIBRARIES, EXECUTABLES, NATIVE_TESTS
+endif # !LOCAL_IS_HOST_MODULE
diff --git a/core/clang/config.mk b/core/clang/config.mk
index ca3a1fa..063fd34 100644
--- a/core/clang/config.mk
+++ b/core/clang/config.mk
@@ -1,5 +1,7 @@
## Clang configurations.
+LLVM_READOBJ := $(LLVM_PREBUILTS_BASE)/$(BUILD_OS)-x86/$(LLVM_PREBUILTS_VERSION)/bin/llvm-readobj
+
LLVM_RTLIB_PATH := $(LLVM_PREBUILTS_BASE)/linux-x86/$(LLVM_PREBUILTS_VERSION)/lib64/clang/$(LLVM_RELEASE_VERSION)/lib/linux/
define convert-to-clang-flags
diff --git a/core/clear_vars.mk b/core/clear_vars.mk
index f9e07f8..e190916 100644
--- a/core/clear_vars.mk
+++ b/core/clear_vars.mk
@@ -304,6 +304,7 @@
LOCAL_WHOLE_STATIC_LIBRARIES:=
LOCAL_XOM:=
LOCAL_YACCFLAGS:=
+LOCAL_CHECK_ELF_FILES:=
# TODO: deprecate, it does nothing
OVERRIDE_BUILT_MODULE_PATH:=
diff --git a/core/config.mk b/core/config.mk
index 89bcdb2..cc8b40b 100644
--- a/core/config.mk
+++ b/core/config.mk
@@ -707,6 +707,7 @@
DATA_BINDING_COMPILER := $(HOST_OUT_JAVA_LIBRARIES)/databinding-compiler.jar
FAT16COPY := build/make/tools/fat16copy.py
CHECK_LINK_TYPE := build/make/tools/check_link_type.py
+CHECK_ELF_FILE := build/make/tools/check_elf_file.py
LPMAKE := $(HOST_OUT_EXECUTABLES)/lpmake$(HOST_EXECUTABLE_SUFFIX)
BUILD_SUPER_IMAGE := build/make/tools/releasetools/build_super_image.py
diff --git a/core/main.mk b/core/main.mk
index c84cbe0..bac613c 100644
--- a/core/main.mk
+++ b/core/main.mk
@@ -786,9 +786,43 @@
$(call resolve-shared-libs-depes,HOST_CROSS_,,true)
endif
+# Pass the shared libraries dependencies to prebuilt ELF file check.
+define add-elf-file-check-shared-lib
+$(1): PRIVATE_SHARED_LIBRARY_FILES += $(2)
+$(1): $(2)
+endef
+
+define resolve-shared-libs-for-elf-file-check
+$(foreach m,$($(if $(2),$($(1)2ND_ARCH_VAR_PREFIX))$(1)DEPENDENCIES_ON_SHARED_LIBRARIES),\
+ $(eval p := $(subst :,$(space),$(m)))\
+ $(eval mod := $(firstword $(p)))\
+ \
+ $(eval deps := $(subst $(comma),$(space),$(lastword $(p))))\
+ $(if $(2),$(eval deps := $(addsuffix $($(1)2ND_ARCH_MODULE_SUFFIX),$(deps))))\
+ $(eval root := $(1)OUT$(if $(call streq,$(1),TARGET_),_ROOT))\
+ $(eval deps := $(filter $($(root))/%$($(1)SHLIB_SUFFIX),$(call module-built-files,$(deps))))\
+ \
+ $(eval r := $(firstword $(filter \
+ $($(if $(2),$($(1)2ND_ARCH_VAR_PREFIX))TARGET_OUT_INTERMEDIATES)/EXECUTABLES/%\
+ $($(if $(2),$($(1)2ND_ARCH_VAR_PREFIX))TARGET_OUT_INTERMEDIATES)/NATIVE_TESTS/%\
+ $($(if $(2),$($(1)2ND_ARCH_VAR_PREFIX))TARGET_OUT_INTERMEDIATES)/SHARED_LIBRARIES/%,\
+ $(call module-built-files,$(mod)))))\
+ \
+ $(if $(r),\
+ $(eval stamp := $(dir $(r))check_elf_files.timestamp)\
+ $(eval $(call add-elf-file-check-shared-lib,$(stamp),$(deps)))\
+ ))
+endef
+
+$(call resolve-shared-libs-for-elf-file-check,TARGET_)
+ifdef TARGET_2ND_ARCH
+$(call resolve-shared-libs-for-elf-file-check,TARGET_,true)
+endif
+
m :=
r :=
p :=
+stamp :=
deps :=
add-required-deps :=
@@ -1529,6 +1563,9 @@
.PHONY: findlsdumps
findlsdumps: $(FIND_LSDUMPS_FILE)
+.PHONY: check-elf-files
+check-elf-files:
+
#xxx scrape this from ALL_MODULE_NAME_TAGS
.PHONY: modules
modules:
diff --git a/core/prebuilt_internal.mk b/core/prebuilt_internal.mk
index 9b995e2..0b48a77 100644
--- a/core/prebuilt_internal.mk
+++ b/core/prebuilt_internal.mk
@@ -168,6 +168,9 @@
include $(BUILD_SYSTEM)/link_type.mk
endif # prebuilt_module_is_a_library
+# Check prebuilt ELF binaries.
+include $(BUILD_SYSTEM)/check_elf_file.mk
+
# The real dependency will be added after all Android.mks are loaded and the install paths
# of the shared libraries are determined.
ifdef LOCAL_INSTALLED_MODULE
diff --git a/core/product.mk b/core/product.mk
index 0219ce4..c3cbb0c 100644
--- a/core/product.mk
+++ b/core/product.mk
@@ -227,6 +227,7 @@
PRODUCT_BUILD_USERDATA_IMAGE \
PRODUCT_UPDATABLE_BOOT_MODULES \
PRODUCT_UPDATABLE_BOOT_LOCATIONS \
+ PRODUCT_CHECK_ELF_FILES \
define dump-product
$(info ==== $(1) ====)\
diff --git a/core/product_config.mk b/core/product_config.mk
index 27e8ecf..1f44692 100644
--- a/core/product_config.mk
+++ b/core/product_config.mk
@@ -477,6 +477,11 @@
PRODUCT_ENFORCE_RRO_TARGETS := \
$(strip $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_ENFORCE_RRO_TARGETS))
+# Whether the product would like to check prebuilt ELF files.
+PRODUCT_CHECK_ELF_FILES := \
+ $(strip $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_CHECK_ELF_FILES))
+.KATI_READONLY := PRODUCT_CHECK_ELF_FILES
+
# Add reserved headroom to a system image.
PRODUCT_SYSTEM_HEADROOM := \
$(strip $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SYSTEM_HEADROOM))
diff --git a/core/soong_cc_prebuilt.mk b/core/soong_cc_prebuilt.mk
index 088b076..39cc0f0 100644
--- a/core/soong_cc_prebuilt.mk
+++ b/core/soong_cc_prebuilt.mk
@@ -93,6 +93,12 @@
name_without_suffix :=
endif
+# Check prebuilt ELF binaries.
+ifneq ($(LOCAL_CHECK_ELF_FILES),)
+my_prebuilt_src_file := $(LOCAL_PREBUILT_MODULE_FILE)
+include $(BUILD_SYSTEM)/check_elf_file.mk
+endif
+
# The real dependency will be added after all Android.mks are loaded and the install paths
# of the shared libraries are determined.
ifdef LOCAL_INSTALLED_MODULE
diff --git a/core/soong_config.mk b/core/soong_config.mk
index 1b6b9d3..f8088b7 100644
--- a/core/soong_config.mk
+++ b/core/soong_config.mk
@@ -123,6 +123,8 @@
$(call add_json_bool, Treble_linker_namespaces, $(filter true,$(PRODUCT_TREBLE_LINKER_NAMESPACES)))
$(call add_json_bool, Enforce_vintf_manifest, $(filter true,$(PRODUCT_ENFORCE_VINTF_MANIFEST)))
+$(call add_json_bool, Check_elf_files, $(filter true,$(PRODUCT_CHECK_ELF_FILES)))
+
$(call add_json_bool, Uml, $(filter true,$(TARGET_USER_MODE_LINUX)))
$(call add_json_bool, Use_lmkd_stats_log, $(filter true,$(TARGET_LMKD_STATS_LOG)))
$(call add_json_str, VendorPath, $(TARGET_COPY_OUT_VENDOR))