From 4f11feb7084074918d98c0446bf8b4965bb68bfe Mon Sep 17 00:00:00 2001 From: Matthew Zavislak <1291596+elevenfive@users.noreply.github.com> Date: Thu, 28 Aug 2025 16:31:03 -0700 Subject: [PATCH] Add elf alignment check for Android artifacts (#13818) --- .github/workflows/create-test-plan.py | 4 + .github/workflows/generic.yml | 12 ++- build-scripts/check_elf_alignment.sh | 127 ++++++++++++++++++++++++++ build-scripts/strip_fPIC.sh | 21 ----- 4 files changed, 141 insertions(+), 23 deletions(-) create mode 100755 build-scripts/check_elf_alignment.sh delete mode 100755 build-scripts/strip_fPIC.sh diff --git a/.github/workflows/create-test-plan.py b/.github/workflows/create-test-plan.py index 4037700d5f..991b8d4b31 100755 --- a/.github/workflows/create-test-plan.py +++ b/.github/workflows/create-test-plan.py @@ -561,6 +561,10 @@ def spec_to_job(spec: JobSpec, key: str, trackmem_symbol_names: bool) -> JobDeta "testmultiaudio-apk", "testsprite-apk", ] + + # -fPIC is required after updating NDK from 21 to 28 + job.cflags.append("-fPIC") + job.cxxflags.append("-fPIC") case SdlPlatform.Emscripten: job.clang_tidy = False # clang-tidy does not understand -gsource-map job.shared = False diff --git a/.github/workflows/generic.yml b/.github/workflows/generic.yml index 7902035f7d..8a68447846 100644 --- a/.github/workflows/generic.yml +++ b/.github/workflows/generic.yml @@ -76,7 +76,7 @@ jobs: id: setup-ndk with: local-cache: true - ndk-version: r21e + ndk-version: r28c - name: 'Configure Android NDK variables' if: ${{ matrix.platform.android-ndk }} shell: sh @@ -411,6 +411,14 @@ jobs: build-scripts/test-versioning.sh python build-scripts/check_android_jni.py python build-scripts/check_stdlib_usage.py + - name: 'Verify alignment of Android test apks' + if: ${{ matrix.platform.android-ndk && !matrix.platform.no-cmake }} + run: | + find ./ -iname '*.apk' | xargs -L1 ./build-scripts/check_elf_alignment.sh + - name: 'Verify alignment of Android .so files' + if: ${{ matrix.platform.android-ndk && !matrix.platform.no-cmake }} + run: | + find ./ -iname '*.so' | xargs -L1 ./build-scripts/check_elf_alignment.sh - name: 'Upload binary package' uses: actions/upload-artifact@v4 if: ${{ always() && matrix.platform.artifact != '' && (steps.package.outcome == 'success' || steps.cpactions.outcome == 'success') && (matrix.platform.enable-artifacts || steps.tests.outcome == 'failure') }} @@ -433,4 +441,4 @@ jobs: with: if-no-files-found: error name: '${{ matrix.platform.artifact }}-apks' - path: build/test/*.apk \ No newline at end of file + path: build/test/*.apk diff --git a/build-scripts/check_elf_alignment.sh b/build-scripts/check_elf_alignment.sh new file mode 100755 index 0000000000..d3846bcae9 --- /dev/null +++ b/build-scripts/check_elf_alignment.sh @@ -0,0 +1,127 @@ +#!/bin/bash +progname="${0##*/}" +progname="${progname%.sh}" + +# usage: check_elf_alignment.sh [path to *.so files|path to *.apk] + +cleanup_trap() { + if [ -n "${tmp}" -a -d "${tmp}" ]; then + rm -rf ${tmp} + fi + exit $1 +} + +usage() { + echo "Host side script to check the ELF alignment of shared libraries." + echo "Shared libraries are reported ALIGNED when their ELF regions are" + echo "16 KB or 64 KB aligned. Otherwise they are reported as UNALIGNED." + echo + echo "Usage: ${progname} [input-path|input-APK|input-APEX]" +} + +if [ ${#} -ne 1 ]; then + usage + exit +fi + +case ${1} in + --help | -h | -\?) + usage + exit + ;; + + *) + dir="${1}" + ;; +esac + +if ! [ -f "${dir}" -o -d "${dir}" ]; then + echo "Invalid file: ${dir}" >&2 + exit 1 +fi + +if [[ "${dir}" == *.apk ]]; then + trap 'cleanup_trap' EXIT + + echo + echo "Recursively analyzing $dir" + echo + + if { zipalign --help 2>&1 | grep -q "\-P "; }; then + echo "=== APK zip-alignment ===" + zipalign -v -c -P 16 4 "${dir}" | egrep 'lib/arm64-v8a|lib/x86_64|Verification' + echo "=========================" + else + echo "NOTICE: Zip alignment check requires build-tools version 35.0.0-rc3 or higher." + echo " You can install the latest build-tools by running the below command" + echo " and updating your \$PATH:" + echo + echo " sdkmanager \"build-tools;35.0.0-rc3\"" + fi + + dir_filename=$(basename "${dir}") + tmp=$(mktemp -d -t "${dir_filename%.apk}_out_XXXXX") + unzip "${dir}" lib/* -d "${tmp}" >/dev/null 2>&1 + dir="${tmp}" +fi + +if [[ "${dir}" == *.apex ]]; then + trap 'cleanup_trap' EXIT + + echo + echo "Recursively analyzing $dir" + echo + + dir_filename=$(basename "${dir}") + tmp=$(mktemp -d -t "${dir_filename%.apex}_out_XXXXX") + deapexer extract "${dir}" "${tmp}" || { echo "Failed to deapex." && exit 1; } + dir="${tmp}" +fi + +RED="\e[31m" +GREEN="\e[32m" +ENDCOLOR="\e[0m" + +unaligned_libs=() +unaligned_critical_libs=() + +echo +echo "=== ELF alignment ===" + +matches="$(find "${dir}" -type f)" +IFS=$'\n' +for match in $matches; do + # We could recursively call this script or rewrite it to though. + [[ "${match}" == *".apk" ]] && echo "WARNING: doesn't recursively inspect .apk file: ${match}" + [[ "${match}" == *".apex" ]] && echo "WARNING: doesn't recursively inspect .apex file: ${match}" + + [[ $(file "${match}") == *"ELF"* ]] || continue + + res="$(objdump -p "${match}" | grep LOAD | awk '{ print $NF }' | head -1)" + if [[ $res =~ 2\*\*(1[4-9]|[2-9][0-9]|[1-9][0-9]{2,}) ]]; then + echo -e "${match}: ${GREEN}ALIGNED${ENDCOLOR} ($res)" + else + unaligned_libs+=("${match}") + # Check if this is a critical architecture (arm64-v8a or x86_64) + if [[ "${match}" == *"arm64-v8a"* ]] || [[ "${match}" == *"x86_64"* ]]; then + unaligned_critical_libs+=("${match}") + echo -e "${match}: ${RED}UNALIGNED${ENDCOLOR} ($res)" + else + echo -e "${match}: UNALIGNED ($res)" + fi + fi +done + +if [ ${#unaligned_libs[@]} -gt 0 ]; then + echo -e "Found ${#unaligned_libs[@]} unaligned libs (only arm64-v8a/x86_64 libs need to be aligned).${ENDCOLOR}" +fi +echo "=====================" + +# Exit with appropriate code: 1 if critical unaligned libs found, 0 otherwise +if [ ${#unaligned_critical_libs[@]} -gt 0 ]; then + echo -e "${RED}Found ${#unaligned_critical_libs[@]} critical unaligned libs.${ENDCOLOR}" + exit 1 +else + echo -e "${GREEN}ELF Verification Successful${ENDCOLOR}" + exit 0 +fi diff --git a/build-scripts/strip_fPIC.sh b/build-scripts/strip_fPIC.sh deleted file mode 100755 index 8719b896e5..0000000000 --- a/build-scripts/strip_fPIC.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/sh -# -# libtool assumes that the compiler can handle the -fPIC flag -# This isn't always true (for example, nasm can't handle it) -command="" -while [ $# -gt 0 ]; do - case "$1" in - -?PIC) - # Ignore -fPIC and -DPIC options - ;; - -fno-common) - # Ignore -fPIC and -DPIC options - ;; - *) - command="$command $1" - ;; - esac - shift -done -echo $command -exec $command