Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

on Linux, dynamically loading libraries should use the versioned SONAME #92

Open
smcv opened this issue Dec 4, 2024 · 3 comments
Open

Comments

@smcv
Copy link

smcv commented Dec 4, 2024

Issue reports at ValveSoftware/steam-runtime#719 and https://community.gaijin.net/issues/p/warthunder/i/NHkOOFjzaY0c say that some War Thunder players on Linux see the game running full-screen on an unexpected display: they have set one of their displays as primary, and they expected War Thunder to draw a full-screen window on that display, but instead it draws a full-screen window on the display that is closest to the top left, coordinate (0,0).

While investigating this I noticed that this engine calls:

    randrLib = os_dll_load("libXrandr.so");

where os_dll_load() is presumably a wrapper around dlopen() on Linux.

This is an understandable thing to try, but in fact it is not correct for two reasons:

  1. The development symlink libXrandr.so is not guaranteed to exist on end-user systems, even if libXrandr is installed. When a library is installed on an end-user system, the only files that normally exist are the version-dependent name of the "real" file (e.g. libXrandr.so.2.2.0 on my Debian 12 system), and a symlink to the "real" file with a name matching the library's SONAME (e.g. libXrandr.so.2 -> libXrandr.so.2.2.0). The libXrandr.so -> libXrandr.so.2 symlink is only intended to be used as part of the implementation of cc -lXrandr, so it normally only exists if you have installed a separate development package, for example libxrandr-dev on Debian/Ubuntu or libXrandr-devel on Fedora/Red Hat.

  2. The SONAME of the library changes whenever there is a non-backward-compatible change to its binary interface (an "ABI break"). If this game engine is expecting the ABI that corresponds to libXrandr.so.2, but it dynamically loads libXrandr.so.1 or libXrandr.so.3 on some user's system, it will not work correctly: a common form of ABI break is that the signature of a function has changed, which means that the engine would push the wrong arguments onto the stack when calling it, causing a crash.

I believe (1.) is the root cause for the unexpected window placement: recent versions of the Steam client run native Linux games in a Steam Linux Runtime container by default, and those containers are similar to an older Debian system with no developer files installed. As a result, libXrandr.so does not exist, so the dlopen() call fails, and the engine presumably falls back to a less clever way to choose which display is primary.

(2.) is probably not a practical problem for War Thunder right now, but it could become a practical problem at any time.

Please use the SONAME of each library that is passed to dlopen(), which would mean making changes similar to this:

-    randrLib = os_dll_load("libXrandr.so");
+    randrLib = os_dll_load("libXrandr.so.2");

And if there are other places in the codebase that call os_dll_load() or other dlopen() wrappers to load an OS/runtime library, please make a similar change there. Otherwise, this game engine will already not work as intended for some Linux users, and could start crashing in future as a result of a routine OS update.

Thanks,
smcv
Steam Runtime team

@smcv
Copy link
Author

smcv commented Dec 4, 2024

https://gitlab.com/torkel104/libstrangle/-/merge_requests/22 and libsdl-org/SDL#10776 are examples of a similar change in some other projects.

@NicSavichev
Copy link
Contributor

Thanks, code updated.
a742bec

@smcv
Copy link
Author

smcv commented Dec 16, 2024

Thanks. Loading libXrandr.so.2 as the preferred version seems correct.

The fallback to loading libXrandr.so.1 seems risky: it's only OK if you can verify that the subset of the ABI of this library that is used by the Dagor engine was 100% compatible between libXrandr.so.2 and libXrandr.so.1. Normally, the expectation is that if the SONAME has changed, then the library is incompatible (that's the purpose of the SONAME).

In practice, I doubt there are any distros remaining where the fallback would succeed: based on the libxrandr changelog in Debian, it seems to have been libXrandr.so.2 since at least 2007. If you don't have a tested scenario where the fallback is necessary, I'd suggest dropping the fallback to make the code more obviously correct.

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

No branches or pull requests

2 participants