floooh / sokol

minimal cross-platform standalone C headers
https://floooh.github.io/sokol-html5
zlib License
6.53k stars 467 forks source link

Load graphical dynamic libraries on demand with dlopen #1024

Closed edubart closed 2 months ago

edubart commented 2 months ago

I want to compile an app with Sokol that can work on both Linux systems with and without a graphical interface (no X11, GL, etc). The idea is that depending if I pass a -no-graphics flag to this app, then a graphical window using sapp is not created.

But the problem is that to compile an app with sokol I always have to link X11, GL, etc. So these are all libraries linked:

ldd sokol_app
    linux-vdso.so.1 (0x00007ffc70411000)
    libGL.so.1 => /usr/lib/libGL.so.1 (0x0000755a8197a000)
    libX11.so.6 => /usr/lib/libX11.so.6 (0x0000755a8183c000)
    libXi.so.6 => /usr/lib/libXi.so.6 (0x0000755a82f6c000)
    libXcursor.so.1 => /usr/lib/libXcursor.so.1 (0x0000755a82f60000)
    libm.so.6 => /usr/lib/libm.so.6 (0x0000755a81750000)
    libc.so.6 => /usr/lib/libc.so.6 (0x0000755a8156e000)
    /lib64/ld-linux-x86-64.so.2 => /usr/lib64/ld-linux-x86-64.so.2 (0x0000755a82fa4000)
    libGLdispatch.so.0 => /usr/lib/libGLdispatch.so.0 (0x0000755a814b6000)
    libGLX.so.0 => /usr/lib/libGLX.so.0 (0x0000755a81484000)
    libxcb.so.1 => /usr/lib/libxcb.so.1 (0x0000755a81459000)
    libXext.so.6 => /usr/lib/libXext.so.6 (0x0000755a81444000)
    libXrender.so.1 => /usr/lib/libXrender.so.1 (0x0000755a81437000)
    libXfixes.so.3 => /usr/lib/libXfixes.so.3 (0x0000755a8142f000)
    libXau.so.6 => /usr/lib/libXau.so.6 (0x0000755a8142a000)
    libXdmcp.so.6 => /usr/lib/libXdmcp.so.6 (0x0000755a81422000)

Ideally to Sokol applications could be linked to almost nothing, use just dlopen() instead for all graphical libraries, like SDL2:

ldd /usr/lib/libSDL2-2.0.so
    linux-vdso.so.1 (0x00007fff9095b000)
    libm.so.6 => /usr/lib/libm.so.6 (0x000077ffa1a84000)
    libc.so.6 => /usr/lib/libc.so.6 (0x000077ffa18a2000)
    /usr/lib64/ld-linux-x86-64.so.2 (0x000077ffa1d63000)

Is that hard to do? My workaround at the moment for this is to provide two different binaries, but ideally I want to provide just one.

floooh commented 2 months ago

The Windows GL loader in sokol_app.h and sokol_gfx.h works like that (e.g.: https://github.com/floooh/sokol/blob/8fd4da85cde72a73ee660e1ea44baac70b6b0aa9/sokol_gfx.h#L6591-L6743), I would like to avoid doing that for all system libraries though because it involves looking up all DLL functions manually.

Letting the linker take care of DLL loading has at least one upside, that you can look at the ldd output to see what system dependencies are required.

A possible solution would be this: compile all sokol headers you are using into a single DLL (and this DLL regularly links against system DLLs). Then in your application, only load the sokol.so/dll if needed. You'll need to do the above mentioned symbol lookup on the sokol calls you are using though.

edubart commented 2 months ago

A possible solution would be this: compile all sokol headers you are using into a single DLL (and this DLL regularly links against system DLLs)

I would have to provide two files anyway with this solution, I am trying to achieve a single file to run everything.

I got another idea, what if I intentionally link with all symbols unresolved, and explicitly use dlopen() before any function call to load the libraries. I think maybe there is a way to let the dynamic linker resolve the function at runtime on the first function call, will try later, unsure if this is possible yet.

floooh commented 2 months ago

what if I intentionally link with all symbols unresolved

Hmm, not sure how to convince the linker in that case... On Windows with MSVC there's a compiler/linker feature called "delayed DLL loading"... with this enabled, loading a DLL is delayed until the first function of that DLL is called (the compiler will basically insert a small stub for each DLL call which first checks if the DLL is already loaded, and if not, does the loading and symbol resolution) - also see: https://learn.microsoft.com/en-us/cpp/build/reference/linker-support-for-delay-loaded-dlls?view=msvc-170

Maybe GCC has a similar thing?

floooh commented 2 months ago

PS: https://github.com/msys2/MINGW-packages/discussions/11720

(or just google around for things like "GCC delayed dll loading")

edubart commented 2 months ago

Hmm, not sure how to convince the linker in that case...

Indeed, I could not convince on this, after some googling I found this project https://github.com/yugr/Implib.so , it does exactly what I need.

The usage was straightforward:

./implib-gen.py /usr/lib/libGL.so
./implib-gen.py /usr/lib/libX11.so
./implib-gen.py /usr/lib/libXi.so
./implib-gen.py /usr/lib/libXcursor.so

It generates a bunch of C files to link instead, then I replaced -lGL -lX11 -lXi -lXcursor with libGL.so.init.c libGL.so.tramp.S libX11.so.init.c libX11.so.tramp.S libXcursor.so.init.c libXcursor.so.tramp.S libXi.so.init.c libXi.so.tramp.S in the CFLAGS, and it worked!

ldd sokol_app
    linux-vdso.so.1 (0x00007ffd22d2f000)
    libm.so.6 => /usr/lib/libm.so.6 (0x000077c231714000)
    libc.so.6 => /usr/lib/libc.so.6 (0x000077c231532000)
    /lib64/ld-linux-x86-64.so.2 => /usr/lib64/ld-linux-x86-64.so.2 (0x000077c232f0f000)

My application works fine, with graphics, but no graphics library is linked!

floooh commented 2 months ago

Clever! Thanks for the link.