-
Notifications
You must be signed in to change notification settings - Fork 293
Description
Description
Non-deterministic .gnu.hash generation when building native libraries from symlinked directory
Environment:
- NDK: r29 (29.0.14206865)
- Platform: Linux (WSL2) - 6.6.87.2-microsoft-standard-WSL2
- Gradle: 8.13
- AGP: (check build.gradle.kts for com.android.application version)
- CMake: (using SDK default)
Issue:
Native library builds produce non-deterministic output when the project is located in a symlinked directory. Specifically, the .gnu.hash section differs between builds despite identical source code and build configuration.
Runtime Impact:
The non-deterministic builds from the symlinked directory also crash at runtime, while the deterministic non-symlink builds work correctly. The crash occurs during library loading with SIGSEGV (signal 11) in the Android dynamic linker:
#00 pc 00000000000624c8 /apex/com.android.runtime/bin/linker64 (soinfo_do_lookup_impl+228)
#1 pc 0000000000061a5c /apex/com.android.runtime/bin/linker64 (plain_relocate_impl+328)
#2 pc 000000000005ffdc /apex/com.android.runtime/bin/linker64 (soinfo::relocate+608)
The crash happens during symbol resolution when the dynamic linker tries to relocate symbols in libdelaycam_tflite.so. This suggests the corrupted .gnu.hash table is causing the linker to fail during runtime symbol lookup, not just a build reproducibility issue.
Setup:
- Symlink: ~/d/delaycam → /mnt/d/python/delaycam
- Control directory (no symlink): ~/delaycam-test (copy of same code)
Steps to Reproduce:
- Build native library from symlinked directory (~/d/delaycam)
- Note APK/library checksum
- Clean and rebuild from same directory
- Compare checksums
Expected Behavior:
Identical source code + identical build config = identical binary output (deterministic builds)
Actual Behavior:
- Non-symlink builds: Deterministic (same checksum every time)
- Symlink builds: Non-deterministic (different checksum each build)
Evidence (NDK r29 builds):
Working (non-symlink):
- Build 1: e987d815ebe3b75d8f5601c0a9e33326
- Build 2: e987d815ebe3b75d8f5601c0a9e33326 ✓ Identical
Symlinked:
- Build 1: 1ca199410fa990e9513343d5f249f65d
- Build 2: 49034b0ac716b72e56ae0999333d9a99 ✗ Different
Technical Details:
Analysis of differing bytes shows:
- 12,312 bytes differ between symlink builds (out of 9.3MB)
- 8,188 bytes in .gnu.hash section (hash table for dynamic linker)
- 4,099 bytes in .dynstr (dynamic string table)
- Build ID differs (expected, computed from binary)
- Code sections (.text, .rodata, .data) are identical (same size, same content)
The .gnu.hash section contains completely different random-looking data between builds, suggesting non-deterministic hash function or iteration over unordered data structure during linking.
Tried to attach, wouldn't let me, can provide on request: three libdelaycam_tflite.so files (28MB total):
- working-libdelaycam_tflite.so - deterministic non-symlink build
- symlinked1-libdelaycam_tflite.so - first symlink build
- symlinked2-libdelaycam_tflite.so - second symlink build (differs from first)
Upstream bug
No response
Commit to cherry-pick
No response
I am using a supported NDK
- I have checked and the NDK I'm using is currently supported
Affected versions
r29, r28
Host OS
Linux
Host OS version
Ubuntu 24.04.2 LTS (WSL2) - Kernel 6.6.87.2-microsoft-standard-WSL2
Affected ABIs
arm64-v8a