diff --git a/.github/workflows/sample-application.yml b/.github/workflows/sample-application.yml index 5d5a687904..1fc586ea20 100644 --- a/.github/workflows/sample-application.yml +++ b/.github/workflows/sample-application.yml @@ -187,6 +187,13 @@ jobs: ./scripts/set-dsn-aos.mjs ./scripts/build-android.sh -PreactNativeArchitectures=x86 + - name: Check 16 KB native library alignment + # Only Sentry-owned libraries are checked: third-party/React Native + # libraries are aligned by their own build (RN ships arm64 at 16 KB but + # x86 at 4 KB, and CI builds x86), so a whole-APK check is not + # meaningful here. Guards against regressing #6394. + run: ./scripts/check-android-16kb-alignment.sh ${{ env.REACT_NATIVE_SAMPLE_PATH }}/app.apk 'libsentry' + - name: Archive Android App if: ${{ matrix.rn-architecture == 'new' && matrix.build-type == 'production' }} run: | diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ec3591fa7..48f69e1e8d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,12 @@ > make sure you follow our [migration guide](https://docs.sentry.io/platforms/react-native/migration/) first. +## Unreleased + +### Fixes + +- Force 16 KB ELF alignment for `libsentry-tm-perf-logger.so` so it does not break 16 KB page size compatibility on Android 15+ ([#6396](https://github.com/getsentry/sentry-react-native/pull/6396)) + ## 8.17.0 ### Features diff --git a/packages/core/android/src/main/jni/CMakeLists.txt b/packages/core/android/src/main/jni/CMakeLists.txt index 4f5e5403b2..19da93eeea 100644 --- a/packages/core/android/src/main/jni/CMakeLists.txt +++ b/packages/core/android/src/main/jni/CMakeLists.txt @@ -62,6 +62,22 @@ target_link_libraries( ReactAndroid::reactnative ) +# Force 16 KB ELF LOAD-segment alignment. The NDK linker only defaults to a +# 16 KB `max-page-size` from r28 onwards; RN 0.86 / Expo SDK 57 still build +# with NDK 27, whose default is 4 KB. Every other `.so` bundled by RN/AGP is +# already 16 KB aligned, so without this flag `libsentry-tm-perf-logger.so` +# is the lone misaligned library and trips Android 15+'s 16 KB page-size +# compatibility check (and Google Play's 16 KB requirement) for the whole +# app — even for hosts that never opt in to `enableTurboModuleTracking`, +# since the library is packaged whenever the New Architecture is enabled. +# On NDK r28+ this option is a redundant no-op. +# https://github.com/getsentry/sentry-react-native/issues/6394 +target_link_options( + sentry-tm-perf-logger + PRIVATE + "-Wl,-z,max-page-size=16384" +) + # Note: we deliberately do NOT pass `-Wl,--strip-all` (or similar) here. # Android Gradle Plugin's `StripDebugSymbolsTask` already strips the .so for # the packaged APK while preserving the unstripped artefact under diff --git a/scripts/check-android-16kb-alignment.sh b/scripts/check-android-16kb-alignment.sh new file mode 100755 index 0000000000..5f4702b798 --- /dev/null +++ b/scripts/check-android-16kb-alignment.sh @@ -0,0 +1,105 @@ +#!/bin/bash + +# Verifies native libraries (`.so`) bundled inside an Android APK/AAB have +# their ELF LOAD segments aligned to at least 16 KB (`p_align >= 0x4000`). +# +# Android 15+ devices with a 16 KB page size (and Google Play's 16 KB +# requirement) reject apps that ship a `.so` aligned to only 4 KB. Libraries +# built from source with NDK r27 and earlier default to 4 KB, so this check +# guards against a regression like https://github.com/getsentry/sentry-react-native/issues/6394 +# where `libsentry-tm-perf-logger.so` shipped misaligned. +# +# By default every `.so` is checked. Pass a name filter (a regex matched +# against each library's path) to restrict the check to the libraries this +# repo actually controls — third-party/React Native libraries are aligned by +# their own build (RN ships arm64 at 16 KB but x86 at 4 KB, so a whole-APK +# check is not meaningful on the x86 builds CI produces). +# +# Usage: scripts/check-android-16kb-alignment.sh [name-filter-regex] +# e.g. scripts/check-android-16kb-alignment.sh app.apk 'libsentry' +# +# `readelf` is resolved from (in order): $READELF, `llvm-readelf`, `readelf`. + +set -euo pipefail + +REQUIRED_ALIGN=16384 # 16 KB, expressed in bytes + +apk="${1:-}" +name_filter="${2:-}" +if [[ -z "$apk" || ! -f "$apk" ]]; then + echo "usage: $0 [name-filter-regex]" >&2 + exit 2 +fi + +readelf_bin="${READELF:-}" +if [[ -z "$readelf_bin" ]]; then + if command -v llvm-readelf >/dev/null 2>&1; then + readelf_bin="llvm-readelf" + elif command -v readelf >/dev/null 2>&1; then + readelf_bin="readelf" + else + echo "error: neither 'llvm-readelf' nor 'readelf' found on PATH (set \$READELF to override)" >&2 + exit 2 + fi +fi + +workdir="$(mktemp -d)" +trap 'rm -rf "$workdir"' EXIT + +# Extract only the native libraries. APK and AAB both store them under a +# top-level `lib/` (APK) or `base/lib/` (AAB) directory. +unzip -qq -o "$apk" 'lib/*' 'base/lib/*' -d "$workdir" 2>/dev/null || true + +libs=() +while IFS= read -r lib; do + if [[ -n "$name_filter" && ! "$lib" =~ $name_filter ]]; then + continue + fi + libs+=("$lib") +done < <(find "$workdir" -type f -name '*.so' | sort) +if [[ ${#libs[@]} -eq 0 ]]; then + if [[ -n "$name_filter" ]]; then + echo "error: no .so libraries matching /$name_filter/ found inside $apk" >&2 + else + echo "error: no .so libraries found inside $apk" >&2 + fi + exit 2 +fi + +misaligned=() +for lib in "${libs[@]}"; do + rel="${lib#"$workdir"/}" + # Smallest LOAD-segment alignment in this library, in bytes. `readelf` + # prints the align column as a hex literal (e.g. `0x4000`); bash arithmetic + # parses the `0x` prefix directly, so we avoid gawk-only `strtonum`. + min_align=0 + while IFS= read -r align_hex; do + [[ -z "$align_hex" ]] && continue + align=$((align_hex)) + if [[ "$min_align" -eq 0 || "$align" -lt "$min_align" ]]; then + min_align="$align" + fi + done < <("$readelf_bin" -lW "$lib" | awk '$1 == "LOAD" { print $NF }') + + if [[ "$min_align" -eq 0 ]]; then + echo "warn: $rel — no LOAD segments found, skipping" >&2 + continue + fi + + if [[ "$min_align" -lt "$REQUIRED_ALIGN" ]]; then + misaligned+=("$rel (align=$min_align)") + printf 'FAIL: %-48s align=%d (need >= %d)\n' "$rel" "$min_align" "$REQUIRED_ALIGN" + else + printf 'OK: %-48s align=%d\n' "$rel" "$min_align" + fi +done + +echo +if [[ ${#misaligned[@]} -gt 0 ]]; then + echo "✖ ${#misaligned[@]} library/libraries are not 16 KB aligned:" >&2 + for m in "${misaligned[@]}"; do echo " - $m" >&2; done + echo " Pass -Wl,-z,max-page-size=16384 when linking, or build with NDK r28+." >&2 + exit 1 +fi + +echo "✔ all ${#libs[@]} libraries are >= 16 KB aligned"