android / android/ndk

[Bug]: Non-deterministic .gnu.hash generation when building from Windows symlinked directory in WSL

Open
#2,221 0 comments 0 reactions 0 assignees View on GitHub
bug
Dominant language
No language data
Stars
2.3k
Forks
310
PR merge metrics
No merged PRs in 30d

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)
#01 pc 0000000000061a5c /apex/com.android.runtime/bin/linker64 (plain_relocate_impl+328)
#02 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:
1. Build native library from symlinked directory (~/d/delaycam)
2. Note APK/library checksum
3. Clean and rebuild from same directory
4. 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

- [x] 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

Contributor guide

Open the contributing guide

Research direction

The report names build.gradle.kts, the symlinked ~/d/delaycam directory, the ~/delaycam-test control copy, and the affected .so files. First reproduce clean rebuilds in both locations and compare checksums plus the reported .gnu.hash and .dynstr sections. Done means identifying the r28/r29 source of the nondeterminism and linker crash, with a reproducible verification.

Written by the indexing model from the issue text.

Assessment

Tech stack
cmake, linux
Domain
build-system, operating-systems
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.