Merge "Reland "check_elf_file: check prebuilts are aligned"" into main am: 69e8c9be5f

Original change: https://android-review.googlesource.com/c/platform/build/+/3110301

Change-Id: I53a7274547eb1731191a5ea4690d4627926f9e13
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Treehugger Robot 2024-06-04 21:47:36 +00:00 committed by Automerger Merge Worker
commit 9830e817d6
5 changed files with 102 additions and 3 deletions

View file

@ -7,9 +7,12 @@
#
# Inputs:
# - LOCAL_ALLOW_UNDEFINED_SYMBOLS
# - LOCAL_IGNORE_MAX_PAGE_SIZE
# - LOCAL_BUILT_MODULE
# - LOCAL_IS_HOST_MODULE
# - LOCAL_MODULE_CLASS
# - TARGET_CHECK_PREBUILT_MAX_PAGE_SIZE
# - TARGET_MAX_PAGE_SIZE_SUPPORTED
# - intermediates
# - my_installed_module_stem
# - my_prebuilt_src_file
@ -26,6 +29,21 @@ $(check_elf_files_stamp): PRIVATE_SYSTEM_SHARED_LIBRARIES := $(my_system_shared_
# In addition to $(my_check_elf_file_shared_lib_files), some file paths are
# added by `resolve-shared-libs-for-elf-file-check` from `core/main.mk`.
$(check_elf_files_stamp): PRIVATE_SHARED_LIBRARY_FILES := $(my_check_elf_file_shared_lib_files)
# For different page sizes to work, we must support a larger max page size
# as well as properly reflect page size at runtime. Limit this check, since many
# devices set the max page size (for future proof) than actually use the
# larger page size.
ifeq ($(strip $(TARGET_CHECK_PREBUILT_MAX_PAGE_SIZE)),true)
ifeq ($(strip $(LOCAL_IGNORE_MAX_PAGE_SIZE)),true)
$(check_elf_files_stamp): PRIVATE_MAX_PAGE_SIZE :=
else
$(check_elf_files_stamp): PRIVATE_MAX_PAGE_SIZE := $(TARGET_MAX_PAGE_SIZE_SUPPORTED)
endif
else
$(check_elf_files_stamp): PRIVATE_MAX_PAGE_SIZE :=
endif
$(check_elf_files_stamp): $(my_prebuilt_src_file) $(my_check_elf_file_shared_lib_files) $(CHECK_ELF_FILE) $(LLVM_READOBJ)
@echo Check prebuilt ELF binary: $<
$(hide) mkdir -p $(dir $@)
@ -33,6 +51,7 @@ $(check_elf_files_stamp): $(my_prebuilt_src_file) $(my_check_elf_file_shared_lib
$(hide) $(CHECK_ELF_FILE) \
--skip-bad-elf-magic \
--skip-unknown-elf-machine \
$(if $(PRIVATE_MAX_PAGE_SIZE),--max-page-size=$(PRIVATE_MAX_PAGE_SIZE)) \
$(if $(PRIVATE_SONAME),--soname $(PRIVATE_SONAME)) \
$(foreach l,$(PRIVATE_SHARED_LIBRARY_FILES),--shared-lib $(l)) \
$(foreach l,$(PRIVATE_SYSTEM_SHARED_LIBRARIES),--system-shared-lib $(l)) \

View file

@ -106,6 +106,7 @@ LOCAL_GTEST:=true
LOCAL_HEADER_LIBRARIES:=
LOCAL_HOST_PREFIX:=
LOCAL_HOST_REQUIRED_MODULES:=
LOCAL_IGNORE_MAX_PAGE_SIZE:=
LOCAL_INIT_RC:=
LOCAL_INJECT_BSSL_HASH:=
LOCAL_INSTALLED_MODULE:=

View file

@ -419,6 +419,13 @@ else
endif
.KATI_READONLY := TARGET_MAX_PAGE_SIZE_SUPPORTED
ifdef PRODUCT_CHECK_PREBUILT_MAX_PAGE_SIZE
TARGET_CHECK_PREBUILT_MAX_PAGE_SIZE := $(PRODUCT_CHECK_PREBUILT_MAX_PAGE_SIZE)
else
TARGET_CHECK_PREBUILT_MAX_PAGE_SIZE := false
endif
.KATI_READONLY := TARGET_CHECK_PREBUILT_MAX_PAGE_SIZE
# Boolean variable determining if AOSP relies on bionic's PAGE_SIZE macro.
ifdef PRODUCT_NO_BIONIC_PAGE_SIZE_MACRO
TARGET_NO_BIONIC_PAGE_SIZE_MACRO := $(PRODUCT_NO_BIONIC_PAGE_SIZE_MACRO)

View file

@ -32,6 +32,7 @@ _product_single_value_vars += PRODUCT_MODEL_FOR_ATTESTATION
# PRODUCT_MAX_PAGE_SIZE_SUPPORTED=65536, the possible values for PAGE_SIZE could be
# 4096, 16384 and 65536.
_product_single_value_vars += PRODUCT_MAX_PAGE_SIZE_SUPPORTED
_product_single_value_vars += PRODUCT_CHECK_PREBUILT_MAX_PAGE_SIZE
# Boolean variable determining if AOSP relies on bionic's PAGE_SIZE macro.
_product_single_value_vars += PRODUCT_NO_BIONIC_PAGE_SIZE_MACRO

View file

@ -67,7 +67,7 @@ ELFHeader = collections.namedtuple(
ELF = collections.namedtuple(
'ELF',
('dt_soname', 'dt_needed', 'imported', 'exported', 'header'))
('alignments', 'dt_soname', 'dt_needed', 'imported', 'exported', 'header'))
def _get_os_name():
@ -195,7 +195,8 @@ class ELFParser(object):
@classmethod
def _read_llvm_readobj(cls, elf_file_path, header, llvm_readobj):
"""Run llvm-readobj and parse the output."""
cmd = [llvm_readobj, '--dynamic-table', '--dyn-symbols', elf_file_path]
cmd = [llvm_readobj, '--program-headers', '--dynamic-table',
'--dyn-symbols', elf_file_path]
out = subprocess.check_output(cmd, text=True)
lines = out.splitlines()
return cls._parse_llvm_readobj(elf_file_path, header, lines)
@ -205,9 +206,56 @@ class ELFParser(object):
def _parse_llvm_readobj(cls, elf_file_path, header, lines):
"""Parse the output of llvm-readobj."""
lines_it = iter(lines)
alignments = cls._parse_program_headers(lines_it)
dt_soname, dt_needed = cls._parse_dynamic_table(elf_file_path, lines_it)
imported, exported = cls._parse_dynamic_symbols(lines_it)
return ELF(dt_soname, dt_needed, imported, exported, header)
return ELF(alignments, dt_soname, dt_needed, imported, exported, header)
_PROGRAM_HEADERS_START_PATTERN = 'ProgramHeaders ['
_PROGRAM_HEADERS_END_PATTERN = ']'
_PROGRAM_HEADER_START_PATTERN = 'ProgramHeader {'
_PROGRAM_HEADER_TYPE_PATTERN = re.compile('^\\s+Type:\\s+(.*)$')
_PROGRAM_HEADER_ALIGN_PATTERN = re.compile('^\\s+Alignment:\\s+(.*)$')
_PROGRAM_HEADER_END_PATTERN = '}'
@classmethod
def _parse_program_headers(cls, lines_it):
"""Parse the dynamic table section."""
alignments = []
if not cls._find_prefix(cls._PROGRAM_HEADERS_START_PATTERN, lines_it):
raise ELFError()
for line in lines_it:
# Parse each program header
if line.strip() == cls._PROGRAM_HEADER_START_PATTERN:
p_align = None
p_type = None
for line in lines_it:
if line.strip() == cls._PROGRAM_HEADER_END_PATTERN:
if not p_align:
raise ELFError("Could not parse alignment from program header!")
if not p_type:
raise ELFError("Could not parse type from program header!")
if p_type.startswith("PT_LOAD "):
alignments.append(int(p_align))
break
match = cls._PROGRAM_HEADER_TYPE_PATTERN.match(line)
if match:
p_type = match.group(1)
match = cls._PROGRAM_HEADER_ALIGN_PATTERN.match(line)
if match:
p_align = match.group(1)
if line == cls._PROGRAM_HEADERS_END_PATTERN:
break
return alignments
_DYNAMIC_SECTION_START_PATTERN = 'DynamicSection ['
@ -434,6 +482,24 @@ class Checker(object):
sys.exit(2)
def check_max_page_size(self, max_page_size):
for alignment in self._file_under_test.alignments:
if alignment % max_page_size != 0:
self._error(f'Load segment has alignment {alignment} but '
f'{max_page_size} required.')
self._note()
self._note('Fix suggestions:')
self._note(f' use linker flag "-Wl,-z,max-page-size={max_page_size}" '
f'when compiling this lib')
self._note()
self._note('If the fix above doesn\'t work, bypass this check with:')
self._note(' Android.bp: ignore_max_page_size: true,')
self._note(' Android.mk: LOCAL_IGNORE_MAX_PAGE_SIZE := true')
self._note(' Device mk: PRODUCT_CHECK_PREBUILT_MAX_PAGE_SIZE := false')
# TODO: instead of exiting immediately, we may want to collect the
# errors from all checks and emit them at once
sys.exit(2)
@staticmethod
def _find_symbol(lib, name, version):
@ -514,6 +580,8 @@ def _parse_args():
help='Ignore the input file with unknown machine ID')
parser.add_argument('--allow-undefined-symbols', action='store_true',
help='Ignore unresolved undefined symbols')
parser.add_argument('--max-page-size', action='store', type=int,
help='Required page size alignment support')
# Other options
parser.add_argument('--llvm-readobj',
@ -542,6 +610,9 @@ def main():
checker.check_dt_needed(args.system_shared_lib)
if args.max_page_size:
checker.check_max_page_size(args.max_page_size)
if not args.allow_undefined_symbols:
checker.check_symbols()