Skip to content

Stop the DNS shim and LSFG layer from loading libandroid.so - #2228

Merged
utkarshdalal merged 2 commits into
masterfrom
dns-lsfg-no-libandroid
Oct 23, 2026
Merged

utkarshdalal merged 2 commits into
masterfrom
dns-lsfg-no-libandroid

Conversation

@utkarshdalal

@utkarshdalal utkarshdalal commented Oct 23, 2026 •

Copy link
Copy Markdown
Owner

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 dlopens libandroid.so. It then calls android_res_nquery without a NULL check, so on these phones the first DnsQuery jumps to address 0.
  • Fix: call resNetworkQuery / resNetworkResult from libnetd_client.so directly. That is all libandroid's wrappers do (checked in the disassembly), and libnetd_client's dependencies share no names with imagefs. The shim also exports android_res_nquery / android_res_nresult with libandroid's exact behaviour. Bionic's dlsym(NULL, ...) is RTLD_DEFAULT, so Proton's dnsapi finds ours when its own dlopen fails, with no Proton rebuild. android_getaddrinfofornetwork(0, ...) is the same call bionic's getaddrinfo makes, so that now goes straight to getaddrinfo.

LSFG layer (liblsfg-vk-layer.so)

  • It had a NEEDED on libandroid.so but 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 Link nativewindow instead of android on Android GameNative/lsfg-vk-android#9, which links nativewindow instead of android (release needs neither; android-gen8-fixes needs AHardwareBuffer_*, which libnativewindow exports). Same dynamic exports as the old binary, 16 KB alignment. RUNTIME_VERSION is 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.

libandroid.so pulls in ~200 system libraries that look up their own
dependencies through the guest's LD_LIBRARY_PATH, so they bind to imagefs
libraries of the same name and fail to link on Samsung (libsqlite needs
BoringSSL's libcrypto) and Xiaomi HyperOS (libjpeg-hyper needs the system
libjpeg).

DNS shim: res_query() returned ENOSYS for every query when that dlopen
failed, and Wine's Android dnsapi, which dlsyms android_res_nquery from its
own failed libandroid handle, called a NULL pointer. Call resNetworkQuery and
resNetworkResult from libnetd_client.so (what libandroid's wrappers do) and
export android_res_nquery/android_res_nresult with libandroid's behaviour so
dnsapi finds them through RTLD_DEFAULT. android_getaddrinfofornetwork(0, ...)
is the same call as getaddrinfo(), so use that.

LSFG layer: drop its unused DT_NEEDED on libandroid.so (it only imports
__android_log_print from liblog) and bump the runtime version so containers
reinstall it.
@coderabbitai

coderabbitai Bot commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 91045679-6b84-4b7d-8e96-51a0a49325a1
📥 Commits

Reviewing files that changed from the base of the PR and between 56e8981 and 3af7b4a.

⛔ Files ignored due to path filters: 2
  • app/src/main/jniLibs/arm64-v8a/libgamenative_dns_v4mapped.so is excluded by !**/*.so
  • app/src/main/jniLibs/arm64-v8a/liblsfg-vk-layer.so is excluded by !**/*.so
📒 Files selected for processing: 2
  • app/src/main/cpp/gamenative_dns_v4mapped.c
  • app/src/main/java/app/gamenative/utils/LsfgVkManager.kt

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 7 remain after this review.

📝 Walkthrough

The DNS shim now loads resolver functions from libnetd_client.so, routes DNS queries and results through them, and sends getaddrinfo calls to RTLD_NEXT. The LSFG runtime version identifier also changes.

Changes: DNS resolver routing

Layer / File(s) Summary
Netd resolver calls
app/src/main/cpp/gamenative_dns_v4mapped.c
The DNS shim resolves query and result functions from libnetd_client.so. res_query uses the new wrappers, and network_getaddrinfo calls RTLD_NEXT’s getaddrinfo.

LSFG runtime version

Layer / File(s) Summary
Runtime version identifier
app/src/main/java/app/gamenative/utils/LsfgVkManager.kt
RUNTIME_VERSION changes from v1.3.3-android-arm64-v8a to v1.3.3-android-arm64-v8a-2.

Priority: ➖ Normal

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix

Sequence Diagram(s)

sequenceDiagram
  participant res_query
  participant android_res_nquery
  participant libnetd_client
  participant android_res_nresult
  res_query->>android_res_nquery: DNS query parameters
  android_res_nquery->>libnetd_client: resNetworkQuery
  libnetd_client-->>android_res_nquery: Query file descriptor
  res_query->>android_res_nresult: File descriptor and answer buffer
  android_res_nresult->>libnetd_client: resNetworkResult
Loading

Possibly related PRs

Suggested reviewers: joshuatam

Merge Risk: ⚪ Minimal · up to 3af7b

The bundled LSFG update is delivered to existing installations, and the inspected DNS routing does not show a network-selection regression. The change is mergeable after normal checks.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 6 functions across 2 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Title check Passed The title clearly and concisely describes the primary change: preventing the DNS shim and LSFG layer from loading libandroid.so.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Description check Passed The pull request includes all required sections, explains the technical changes and rationale, documents testing and limitations, identifies the change type, and addresses the checklist. The recording…
Full details: Description check

Explanation

The pull request includes all required sections, explains the technical changes and rationale, documents testing and limitations, identifies the change type, and addresses the checklist. The recording is marked N/A because the change has no UI component, and the unchecked recording item is justified.

✨ Finishing Touches: 💡 1
📝 Generate docstrings: 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 4 files

View guided diff | Turn on auto-fix | Re-trigger cubic

Built from GameNative/lsfg-vk-android#9 (release + link nativewindow instead
of android) instead of patching the old binary.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 4 files

View guided diff | Turn on auto-fix | Re-trigger cubic

@utkarshdalal
utkarshdalal merged commit 5a1d22b into master Oct 23, 2026
4 checks passed
utkarshdalal added a commit that referenced this pull request Oct 23, 2026
The gamescope_system_libs symlinks to /system/lib64/libcrypto.so and
libjpeg.so only existed because libgndrm_shim.so linked libandroid.so,
whose OEM deps resolved against imagefs's same-named libs. The shim no
longer links libandroid (gamenative-drm#9, app #2228), so nothing in a
gamescope session loads those system libs. The gamescope LD_LIBRARY_PATH
now starts at gamescope/lib.
utkarshdalal added a commit to joshuatam/GameNative that referenced this pull request Oct 23, 2026
The gamescope_system_libs symlinks to /system/lib64/libcrypto.so and
libjpeg.so only existed because libgndrm_shim.so linked libandroid.so,
whose OEM deps resolved against imagefs's same-named libs. The shim no
longer links libandroid (gamenative-drm#9, app utkarshdalal#2228), so nothing in a
gamescope session loads those system libs. The gamescope LD_LIBRARY_PATH
now starts at gamescope/lib.
utkarshdalal added a commit that referenced this pull request Oct 23, 2026
…es (payload 20261023.3) (#2230)

* gamescope: hide the boot splash on the first mapped window, as X11 does

Under the X11 display server the app hides the "Launching game" splash on
the first mapped window and the user sees black until the game's first
frame, because the renderers never draw explorer. Under gamescope the
splash instead waited for a game-window note from the shim's present path
or from gamescope's commit-rate scan, because hiding it earlier showed
Wine's blue desktop wallpaper and taskbar, which gamescope composites.
That gate kept the splash up for tens of seconds after there was
something to show (L.A. Noire loads for 25 s behind it).

Both modes now use the same rule. Game sessions get a black desktop
background with no wallpaper and explorer's shell (taskbar, start menu,
standalone systray) turned off for the "shell" desktop
(WineThemeManager.gameSessionTheme and the enableShell flag of apply,
applied in setupWineSystemFiles for launches that are not
bootToContainer). That is a no-op under X11 and removes the blue and the
taskbar under gamescope; "Open container" keeps the user's theme and shell.

Payload 20261023.3 ships gamescope A43 reworked to send the game-window
note on the first top-level X map, like the X11 rule, with a
once-a-second resend (GameNative/gamescope#6), and the shim without its
own game-window report (gamenative-drm#12) on top of the device-gated
proc lookup (gamenative-drm#11). The bundled libgamenative_drm.so is the
daemon from gamenative-drm#12: it clears the splash as soon as the note
arrives instead of on the next published frame.

* gamescope: drop the system libcrypto/libjpeg links

The gamescope_system_libs symlinks to /system/lib64/libcrypto.so and
libjpeg.so only existed because libgndrm_shim.so linked libandroid.so,
whose OEM deps resolved against imagefs's same-named libs. The shim no
longer links libandroid (gamenative-drm#9, app #2228), so nothing in a
gamescope session loads those system libs. The gamescope LD_LIBRARY_PATH
now starts at gamescope/lib.

* Default new containers to gamescope only on Adreno

Gamescope is verified on Adreno 735, 750, 830 and 840. Mali-G710 works,
but Mali-G68 cannot create gamescope's compute pipelines, and Xclipse
and PowerVR are untested, so every non-Adreno GPU now defaults to X11.

setContainerDefaults resolves the default from the cached GL renderer
string at startup and stores it in DefaultVersion.DISPLAY_SERVER, which
the unset display server preference and new ContainerData use. The
per-container setting, known and community configs, and an explicitly
set global preference still win; existing containers keep their value.

---------

Co-authored-by: Utkarsh Dalal <utkarsh.dalal@toptal.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant