Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Link statically with two versions of the same library

183 views
Skip to first unread message

Frederick Virchanza Gotham

unread,
Mar 15, 2023, 7:22:29 PM3/15/23
to

I use wxWidgets when I'm writing desktop GUI programs. It allows me to write a program once and have it run well on Apple macOS, MS-Windows, Linux.

I link my program statically with the wxWidgets library, which exports symbols which all begin with 'wx', for example:
wxShowTip, wxYield, wxRadioBox::Enable (mangled to _ZN10wxRadioBox6EnableEb)

Currently on my Linux PC, I have two versions of the wxWidgets static libraries installed:
(1) Based on GTK3
(2) Based on X11

The GTK3 is smoother and nicer, but the X11 will work on anything running an X server.

I've been able to build an executable file that links dynamically with the GUI library -- I mean I can build an executable for GTK3 that loads 'libgtk-3.0.so' at runtime. Similarly I can build an executable for X11 that loads 'libX11.so' at runtime. I already have that tested and working. If neither GTK3 nor X11 are present then my executable can still be used as a simple console app (it only needs 'glibc' installed).

I was thinking it would be cool though, if I were to link my executable statically with both the GTK3 and X11 versions of the wxWidgets libraries, but of course though, I would get symbol clashes. So here's what I would need to do:

(Step 1) Use objcopy on the wxWidgets-GTK3 static libraries to prefix "gtk3_" to every exported symbol.
(Step 2) Use objcopy on the wxWidgets-X11 static libraries to prefix "x11_" to every exported symbol.

So then my executable would have symbols linked into it such as "gtk3_wxYield" and "x11_wxYield".

Then at runtime, my executable would check for the existence of "libgtk-3.0.so", and if it finds it, it would load it and use it. However if it can't find it, it would load 'libX11.so' instead.

So how am I going to do this? Well a few ideas are swirling in my head right now, and it's 11:15pm here so I'll probably go to bed and wake up with a solution, but anyway here's what I could do:

(Step 3) Compile all of my C++ source files to object files, for example (main.cpp -> main.o)
(Step 4) Make a copy of all the object files, so that one is called main.o.x11, and the other is called main.o.gtk3.
(Step 5) Use objcopy on all the ".o.x11" files in order to prefix "x11_" to every symbol that begins with "wx".
(Step 6) Use objcopy on all the ".o.gtk3" files in order to prefix "gtk3_" to every symbol that begins with "wx".
(Step 7) At runtime, determine if "libgtk-3.0.so" exists and can be loaded. If it can be loaded, then call the function "main_gtk3". Otherwise call the function "main_x11".

I bet I can get this working. The final executable file will only have one dependency -- it will link dynamically with 'glibc' (i.e. libc.so and libm.so) -- so you'll still be able to use it as a simple command line program. But if you have GTK3 installed, then you can use the GTK3 gui. If you don't have GTK3 installed then you can use the X11 gui.

Anyone got any ideas about this?

Frederick Virchanza Gotham

unread,
Mar 17, 2023, 1:21:06 PM3/17/23
to
On Wednesday, March 15, 2023 at 11:22:29 PM UTC, Frederick Virchanza Gotham wrote:
>
> I bet I can get this working.


I've prefixed all the symbols exported from the wxGTK3 library with '[b]wxGTK3_[/b]', and I've prefixed all the symbols exported from the wxX11 library with '[b]wxX11_[/b]', as follows:

[code]
#!/bin/sh
echo - - - - Deleting all files
rm -rf *.o *.a
echo - - - - Copying intact .a files
/usr/local/lib/wx/config/gtk3-unicode-static-3.2 --libs all | tr ' ' '\n' | sort | uniq | grep "\.a$" | xargs -i -r -n1 cp "{}" ./
echo - - - - Extracting .o files from .a files
find -name "*\.a" | xargs -i -r -n1 ar -x "{}"
echo - - - - Deleting all .a files
rm -rf *.a
echo - - - - Compiling an exhaustive list of exported symbols
nm *.o | grep -E "^[0-9a-f]{16} T " | cut -d ' ' -f 3 | sort | uniq > exported_symbols.txt
echo - - - - Adding a prefix to all the exported symbols
find -name "*\.o" | while read objfile; do cat exported_symbols.txt | xargs -i -r -n1 objcopy --redefine-sym {}=wxGTK3_{} ${objfile}; done
[/code]

Next in the [b]Makefile[/b] for my program, I compile all the GUI-related files twice, once for wxGTK3 and once for wxX11:
[code]
wxwidgets/%.cpp.x11.o: wxwidgets/%.cpp $(wildcard wxwidgets/*.h wxwidgets/*.hpp)
$(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $< -o $@ $(wxwidgets_cxxflags_x11)
cat wxwidgets/x11/exported_symbols.txt | while read symbol; do objcopy --redefine-sym $${symbol}=wxX11_$${symbol} $@; done

wxwidgets/%.cpp.gtk3.o: wxwidgets/%.cpp $(wildcard wxwidgets/*.h wxwidgets/*.hpp)
$(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $< -o $@ $(wxwidgets_cxxflags_gtk3)
cat wxwidgets/gtk3/exported_symbols.txt | while read symbol; do objcopy --redefine-sym $${symbol}=wxGTK3_$${symbol} $@; done
[/code]

When I link my program together, the object files seeking symbols from wxX11 should find them all prefixed with "wxX11_" in the altered wxX11 static library files, and the object files seeking symbols from wxGTK3 should find them all prefixed with "wxGTK3_" in the altered wxGTK3 static library files.

I might have this finished today. It's taking ages to rename all the symbols in static library files -- it's still not finished doing that.

So the finished product will be an executable file which links dynamically with glibc, but has no other dependencies. You'll be able to run it in 3 modes:
(1) Simple console program
(2) GUI program using GTK3 (on top of either X11 or Wayland)
(3) GUI program using X11 directly

Frederick Virchanza Gotham

unread,
Mar 18, 2023, 11:26:25 AM3/18/23
to
Yesterday I wrote:
> I might have this finished today. It's taking ages to rename all the
> symbols in static library files -- it's still not finished doing that.

Holy crap I got this working just now. Here's how my executable file looks:

$ readelf -a ./ssh | grep NEEDED
0x0000000000000001 (NEEDED) Shared library: [libm.so.6]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
0x0000000000000001 (NEEDED) Shared library: [ld-linux-x86-64.so.2]
$ ldd ./ssh
linux-vdso.so.1 (0x00007ffd28d58000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f632db77000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f632d94f000)
/lib64/ld-linux-x86-64.so.2 (0x00007f632efd7000)

At runtime, it uses 'dlopen' to try to dynamically load libgtk-3.so', and if it succeeds, the GTK3 gui is shown. If it fails, then instead it loads libX11.so' and displays the X11 gui.

What I have here is an executable file which is linked statically with two different versions of wxWidgets, specifically:
* gtk3-unicode-static-3.2
* x11univ-ansi-debug-static-2.8

If neither GTK3 nor X11 is installed, the executable can still be used as a simple console program.

gst

unread,
Mar 26, 2023, 8:54:09 AM3/26/23
to
really nice recipe, would be worth having in a github/gitlab/.. repo or gitst and sharing.

0 new messages