From 301114328a3e736efadf87c117ef9b74b9e05a15 Mon Sep 17 00:00:00 2001 From: Antonis Lilis Date: Fri, 3 Jul 2026 10:40:42 +0200 Subject: [PATCH 1/5] fix(android): Force 16 KB ELF alignment for libsentry-tm-perf-logger.so MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The native library added in 8.17.0 (#6307) for Turbo Module performance tracking is compiled from source with the app's NDK. On NDK r27 and earlier the linker defaults to 4 KB segment alignment, so this library was the lone misaligned `.so` in the APK (all prebuilt libs are already 16 KB aligned). This tripped Android 15+'s 16 KB page size compatibility check and Google Play's 16 KB requirement for New Architecture apps — even when `enableTurboModuleTracking` was left disabled, since the library is packaged whenever the New Architecture is enabled. Pass `-Wl,-z,max-page-size=16384` to the CMake target. Verified with NDK 27 against the RN 0.86 source tree: LOAD segment alignment goes from 0x1000 (4 KB) to 0x4000 (16 KB), with no file-size change. Backward compatible with 4 KB-page devices (16 KB is a multiple of 4 KB) and a no-op on NDK r28+ where 16 KB is already the default. Fixes #6394 Co-Authored-By: Claude Opus 4.8 --- CHANGELOG.md | 8 ++++++++ .../core/android/src/main/jni/CMakeLists.txt | 16 ++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ec3591fa7..33ce54daa2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,14 @@ > 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 no longer breaks 16 KB page size compatibility on Android 15+ ([#6394](https://github.com/getsentry/sentry-react-native/issues/6394)) + + The native library added in 8.17.0 for Turbo Module performance tracking is built from source with the app's NDK. On NDK r27 and earlier the linker defaults to 4 KB segment alignment, leaving this library misaligned while every other bundled `.so` is already 16 KB aligned. This tripped Android's 16 KB page size compatibility check (and Google Play's 16 KB requirement) for New Architecture apps, even when `enableTurboModuleTracking` was not enabled. The CMake target now passes `-Wl,-z,max-page-size=16384` (a no-op on NDK r28+). + ## 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 From fc452f175d322d4970976c8e05da415f2c64ebfc Mon Sep 17 00:00:00 2001 From: Antonis Lilis Date: Fri, 3 Jul 2026 10:43:35 +0200 Subject: [PATCH 2/5] Update changelog --- CHANGELOG.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 33ce54daa2..2a11077aec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,9 +10,7 @@ ### Fixes -- Force 16 KB ELF alignment for `libsentry-tm-perf-logger.so` so it no longer breaks 16 KB page size compatibility on Android 15+ ([#6394](https://github.com/getsentry/sentry-react-native/issues/6394)) - - The native library added in 8.17.0 for Turbo Module performance tracking is built from source with the app's NDK. On NDK r27 and earlier the linker defaults to 4 KB segment alignment, leaving this library misaligned while every other bundled `.so` is already 16 KB aligned. This tripped Android's 16 KB page size compatibility check (and Google Play's 16 KB requirement) for New Architecture apps, even when `enableTurboModuleTracking` was not enabled. The CMake target now passes `-Wl,-z,max-page-size=16384` (a no-op on NDK r28+). +- Force 16 KB ELF alignment for `libsentry-tm-perf-logger.so` so it does not breaks 16 KB page size compatibility on Android 15+ ([#6396](https://github.com/getsentry/sentry-react-native/pull/6396)) ## 8.17.0 From c36ee2b2c91c7f83a062cd13127cdc382439c426 Mon Sep 17 00:00:00 2001 From: Antonis Lilis Date: Fri, 3 Jul 2026 10:50:42 +0200 Subject: [PATCH 3/5] test(android): Add CI check for 16 KB native library alignment Guards against regressing #6394. Adds scripts/check-android-16kb-alignment.sh which unzips a built APK/AAB and asserts every bundled .so has ELF LOAD segments aligned to at least 16 KB (p_align >= 0x4000), failing otherwise. Wired into the sample-application workflow right after the New Architecture Android app build, where a real APK is produced. The script is portable across the macOS (bash 3.2) and Ubuntu CI (mawk) shells and resolves readelf from $READELF, then llvm-readelf, then readelf. Co-Authored-By: Claude Opus 4.8 --- .github/workflows/sample-application.yml | 3 + scripts/check-android-16kb-alignment.sh | 88 ++++++++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100755 scripts/check-android-16kb-alignment.sh diff --git a/.github/workflows/sample-application.yml b/.github/workflows/sample-application.yml index 5d5a687904..4d372b9fa0 100644 --- a/.github/workflows/sample-application.yml +++ b/.github/workflows/sample-application.yml @@ -187,6 +187,9 @@ jobs: ./scripts/set-dsn-aos.mjs ./scripts/build-android.sh -PreactNativeArchitectures=x86 + - name: Check 16 KB native library alignment + run: ./scripts/check-android-16kb-alignment.sh ${{ env.REACT_NATIVE_SAMPLE_PATH }}/app.apk + - name: Archive Android App if: ${{ matrix.rn-architecture == 'new' && matrix.build-type == 'production' }} run: | diff --git a/scripts/check-android-16kb-alignment.sh b/scripts/check-android-16kb-alignment.sh new file mode 100755 index 0000000000..aeacb072e1 --- /dev/null +++ b/scripts/check-android-16kb-alignment.sh @@ -0,0 +1,88 @@ +#!/bin/bash + +# Verifies every native library (`.so`) bundled inside an Android APK/AAB has +# its 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. +# +# Usage: scripts/check-android-16kb-alignment.sh +# +# `readelf` is resolved from (in order): $READELF, `llvm-readelf`, `readelf`. + +set -euo pipefail + +REQUIRED_ALIGN=16384 # 16 KB, expressed in bytes + +apk="${1:-}" +if [[ -z "$apk" || ! -f "$apk" ]]; then + echo "usage: $0 " >&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 libs+=("$lib"); done < <(find "$workdir" -type f -name '*.so' | sort) +if [[ ${#libs[@]} -eq 0 ]]; then + echo "error: no .so libraries found inside $apk" >&2 + 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" From fd22cdbfe250c1e4a37f2abee9c4e3e716d5391c Mon Sep 17 00:00:00 2001 From: Antonis Lilis Date: Fri, 3 Jul 2026 10:52:45 +0200 Subject: [PATCH 4/5] docs: Fix grammar in changelog entry Co-Authored-By: Claude Opus 4.8 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a11077aec..48f69e1e8d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ ### Fixes -- Force 16 KB ELF alignment for `libsentry-tm-perf-logger.so` so it does not breaks 16 KB page size compatibility on Android 15+ ([#6396](https://github.com/getsentry/sentry-react-native/pull/6396)) +- 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 From d95105903ae691460b4966178b66ca23d35dc227 Mon Sep 17 00:00:00 2001 From: Antonis Lilis Date: Fri, 3 Jul 2026 11:11:00 +0200 Subject: [PATCH 5/5] test(android): Scope 16 KB alignment check to Sentry-owned libraries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The initial CI run failed because the check scanned every .so in the APK, including React Native and third-party libraries (libreactnative, libhermesvm, libreanimated, librnscreens, ...) which are 4 KB aligned in the x86 build CI produces. RN aligns arm64 to 16 KB but not x86, and those libraries are outside this repo's control. Add an optional name-filter argument to the check script and pass 'libsentry' in CI so the guard verifies only the libraries this repo ships (libsentry-tm-perf-logger.so et al.). A regression that drops the max-page-size flag from our CMake target is still caught — verified locally: with the filter, our lib at 4 KB still fails the check. Co-Authored-By: Claude Opus 4.8 --- .github/workflows/sample-application.yml | 6 ++++- scripts/check-android-16kb-alignment.sh | 29 +++++++++++++++++++----- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/.github/workflows/sample-application.yml b/.github/workflows/sample-application.yml index 4d372b9fa0..1fc586ea20 100644 --- a/.github/workflows/sample-application.yml +++ b/.github/workflows/sample-application.yml @@ -188,7 +188,11 @@ jobs: ./scripts/build-android.sh -PreactNativeArchitectures=x86 - name: Check 16 KB native library alignment - run: ./scripts/check-android-16kb-alignment.sh ${{ env.REACT_NATIVE_SAMPLE_PATH }}/app.apk + # 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' }} diff --git a/scripts/check-android-16kb-alignment.sh b/scripts/check-android-16kb-alignment.sh index aeacb072e1..5f4702b798 100755 --- a/scripts/check-android-16kb-alignment.sh +++ b/scripts/check-android-16kb-alignment.sh @@ -1,7 +1,7 @@ #!/bin/bash -# Verifies every native library (`.so`) bundled inside an Android APK/AAB has -# its ELF LOAD segments aligned to at least 16 KB (`p_align >= 0x4000`). +# 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 @@ -9,7 +9,14 @@ # guards against a regression like https://github.com/getsentry/sentry-react-native/issues/6394 # where `libsentry-tm-perf-logger.so` shipped misaligned. # -# Usage: scripts/check-android-16kb-alignment.sh +# 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`. @@ -18,8 +25,9 @@ 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 " >&2 + echo "usage: $0 [name-filter-regex]" >&2 exit 2 fi @@ -43,9 +51,18 @@ trap 'rm -rf "$workdir"' EXIT unzip -qq -o "$apk" 'lib/*' 'base/lib/*' -d "$workdir" 2>/dev/null || true libs=() -while IFS= read -r lib; do libs+=("$lib"); done < <(find "$workdir" -type f -name '*.so' | sort) +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 - echo "error: no .so libraries found inside $apk" >&2 + 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