This is a follow up post on the one I made earlier this week on Glade application bundles.
What I had hoped to be a simple bundling experience turned out to actually take me all week to complete, but hey, you get what you pay for. And I’m very happy with the outcome so it was well worth the effort.
The new test bundle can be found here, and I’m very proud to announce that we require exactly the 2.7 ABI form glibc.
We still require fontconfig/freetype/Xlib libraries to be present on the target host to run what is inside the bundle, and we still require libz and a moderately old version of libglib to actually unpack the bundle.
For the AppImageKit glib & zlib dependencies, I plan to make those static dependencies so that they will not even be required, but first I must do battle with CMake to make that happen.
How can I use this to bundle my GTK+ application ?
This is a question I’ve been expecting to hear (and have been hearing). As the last iteration was only a spike, it involved a very ‘LFS’ technique of manually preparing the bundle, but that has been fixed this week and now you can use the bundling mechanism with a collection of customized jhbuild scripts.
Here is an outline of what we have in our build/linux/ directory, which can serve as a good template / starting point for others to bundle their application:
- README: A step by step instructions for generating the bundle
- jhbuildrc: A custom jhbuildrc for building the bundle
- AppRun: A script to run your GTK+ app from inside the bundled environment
- PrepareAppDir.sh: A script to post-process the built stack including GTK+, it’s dependencies and your app
- LibcWrapGenerator.vala: A program for generating libcwrap.h
- modulesets/bundle.modules: A self-contained moduleset for building the stack
- modulesets/patches/: A few downstream patches needed to build/run the bundle
- triggers/: Some custom jhbuild post-installation triggers
People are welcome to copy this build/linux subdirectory into their project and try to build their own GTK+ based application bundles.
Hand to hand combat with glibc
Most of my work this week has revolved around doing hand to hand combat with glibc. I do feel a bit resentful that gcc does not provide any -target-abi-version=glibc,2.7 sort of command line option, as this work would be much better suited to the actual compiler and linker, however with libcwrap.h and it’s generator, I’ve emerged victorious from this death match.
The technique employed to get the whole stack to depend on glibc 2.7 ABI involves using the .symver directive like so:
__asm__(".symver memcpy, memcpy@GLIBC_2.2.5");
The symbol memcpy() changed in libc’s 2.14 ABI to behave more like memmove(), however linking to the new symbol causes your application to require libc 2.14 ABI, which of course we don’t want. The above .symver directive ensures that you will link to the version of memcpy() which is provided by the glibc 2.2.5 ABI.
However the problem is more intense than just this, there are many symbols in libc with various versions and what we want is to be able to simply choose a target version. The LibcWrapGenerator.vala program takes care of this.
Running the generator on a given platform will call objdump on your system libc libraries and inspect which symbols exist, which of them can be safely accessed without any .symver directive, which symbols can be redirected to their most recent version before your arbitrary target ABI version and finally, of course, which ones simply are not available, for instance the fallocate symbol:
__asm__(".symver fallocate, fallocate@GLIBC_DONT_USE_THIS_VERSION_2.10");
The fallocate symbol was actually initially introduced in the 2.10 ABI, and simply cannot be used when targeting the glibc 2.7 ABI.
The nice thing about the above directive, is that it will cause an informative linker error when you try to link any program using fallocate() against glibc. Some of my patches work around these problems (for instance cairo uses longjump() in it’s test cases, so my patch simply disables these test cases when building for the bundle), in other cases this is totally transparent. For the case of fallocate above; libglib will try to use it if it’s available, but with the forced include of the generated “libcwrap.h” file, glib simply fails to find fallocate at configure time and happily functions without it.
This technique was borrowed from the now defunct autopackage project which used a bash script with awk to generate the header file in question, however this script stopped working for glibc runtimes >= 2.10. A newer port of that old script to vala was created by Jan Niklas Hasse in 2011 which was a good starting point, but still failed to generate the correct output. Today I put my (non-existent) vala skills to the test and now have a working version of this nifty header generator, salvaged from the autopackage wreckage.
More Testing !
I will of course appreciate any more testing of the new bundle, for any systems with the glibc 2.7 ABI or later. We did prove that the bundle works properly for a CentOS 6 system which is already a good start.
I would be interested however to know if other people can build the bundle following my README file in our build/linux directory, specifically I’m looking for cases where you have a very new glibc ABI, generate a new libcwrap.h, and let’s see if the bundling process still works, and also let’s see if we can run the bundled glade on very old systems, generated from very new systems.
That’s all, I think I’ve covered all the ground I intended to in this post… so please try this at home !
And enjoy bundling your own GTK+ applications