Stop the DNS shim and LSFG layer from loading libandroid.so
Merged
UT
18d ago+43+43+01234567890123456789-25-25-01234567890123456789
Description
libandroid.so pulls in ~200 system libraries, and inside a game they look up their own dependencies through our LD_LIBRARY_PATH, so they bind to imagefs libraries with the same name. On Samsung (libsqlite needs BoringSSL's libcrypto) and Xiaomi HyperOS (libjpeg-hyper needs the system libjpeg) loading libandroid.so fails. Gamescope is covered by #2227. This PR fixes the two other libraries we ship that load it into game processes.
DNS shim (app/src/main/cpp/gamenative_dns_v4mapped.c, preloaded into every game)
- When
dlopen("libandroid.so")failed,res_query()returned ENOSYS for every query. EA App's DirtySDK networking and the Rockstar launcher go through this path, which may explain the EA/Rockstar reports from these phones. - Wine's Android
dnsapi(Proton 9 and 11) also dlopenslibandroid.so. It then callsandroid_res_nquerywithout a NULL check, so on these phones the firstDnsQueryjumps to address 0. - Fix: call
resNetworkQuery/resNetworkResultfromlibnetd_client.sodirectly. That is all libandroid's wrappers do (checked in the disassembly), and libnetd_client's dependencies share no names with imagefs. The shim also exportsandroid_res_nquery/android_res_nresultwith libandroid's exact behaviour. Bionic'sdlsym(NULL, ...)isRTLD_DEFAULT, so Proton's dnsapi finds ours when its own dlopen fails, with no Proton rebuild.android_getaddrinfofornetwork(0, ...)is the same call bionic'sgetaddrinfomakes, so that now goes straight togetaddrinfo.
LSFG layer (liblsfg-vk-layer.so)
- It had a
NEEDEDonlibandroid.sobut imports nothing from it (its only non-libc import is__android_log_print, from liblog). On these phones the Vulkan loader could not load the layer, so LSFG silently did nothing. - Fix: rebuilt from source with GameNative/lsfg-vk-android#9, which links
nativewindowinstead ofandroid(releaseneeds neither;android-gen8-fixesneedsAHardwareBuffer_*, which libnativewindow exports). Same dynamic exports as the old binary, 16 KB alignment.RUNTIME_VERSIONis bumped so existing containers reinstall the layer.
Testing
On a POCO F6 (HyperOS 1, Android 14), with a test binary and the Samsung failure forced by putting a stub libsqlite.so first on LD_LIBRARY_PATH:
| libandroid loads | libandroid fails | |
|---|---|---|
old DNS shim, res_query A | 40 bytes | -1 |
old DNS shim, dnsapi-style dlsym(NULL, "android_res_nquery") | found | NULL |
new DNS shim, res_query A | 40 bytes | 40 bytes |
| new DNS shim, dnsapi-style lookup, SRV / A | 94 / 40 bytes | 94 / 40 bytes |
old LSFG layer dlopen | loads | fails |
new LSFG layer dlopen | loads | loads |
Not yet tested in a real game session.
Recording
N/A, no UI change.
Type of Change
- Bug fix
- Performance / stability improvement
- Compatibility improvements
- Other (requires prior approval)
Checklist
- If I have access to
#code-changes, I have discussed this change there and it has been green-lighted. If I do not have access, I have still provided clear context in this PR. If I skip both, I accept that this change may face delays in review, may not be reviewed at all, or may be closed. - This change aligns with the current project scope (core functionality, stability, or performance). If not, it has been explicitly approved beforehand.
- I have attached a recording of the change.
- I have read and agree to the contribution guidelines in
CONTRIBUTING.md.