AppImage

Is it possible with "-s deploy" to include libraries loaded at runtime

Hi,

With the recent successes in a “self contained” build I wonder if there is a way to include a file my program uses at runtime?

The library is libsqlite3.so, my program allows databases in text based format and/or sqlite3 DB’s. The problem is there are no references to the library in the binary because that library needs to be set at runtime, and so -s deploy can not find and analyze it.

While libsqlite3.so is “easy” to find on most distros, it becomes a more involved process on, for example, NixOS (and most probably guix also). It would, of course, be wonderful if I could simply look in a path relative to the payload.

So is there any way currently to have appimagetool consider that library (and its dependencies) for inclusion in the AppImage, perhaps by placing it in the appdir in a specific path?

I do know that linuxdeploy allows library inclusion, but I’m not sure if it would be analyzed properly. Also I’ve become addicted to appimagetool because its so easy and works so well. My project is getting to the release stage soon and I’m super stoked that it runs so well as an AppImage.

Have you tried copying that library to one of the library directories inside the AppDir before you run -s deploy on it?

It would be easier to help you and work collaboratively if you could set up a build of your software e.g., on Travis CI.

(I had started doing it here: https://github.com/probonopd/commandoo/blob/patch-1/.travis.yml but ran into some issues I have no clue about since I don’t know any Lazarus)

No I didn’t place the library in any appdir folder. After I saw how appimagetool generated a variety of “lib” paths I was unsure which would be correct. I’ll do some experimentation here.

I’ll try and look into that link and travis CI in a while. I have a lot of learning curves I’m going thru right now and my git / travis knowledge is minimal…

All of them should work.

Wow. That is cool work you’ve done. I’ve finally finished this iteration of commandoo (V2.0.1) and committed Release version in github (BTW, your commandoo fork is 11 commits behind).

This means that there is now more room in my brain and I want to take a look at this stuff in more detail. A lot of this is new to me so it could a take a while but I must say you’ve really piqued my interest to learn more about it.

RE the “issues” link you provided…it is trying to build the Lazarus package itself! Not commandoo. I need to look at this and puzzle out what you are trying to do. I did look at the Travis CI website and pascal is not a supported language but what you were doing intrigues me. As I said I need to wrap myself around all this, so I will report back later.

Hi, I will be going back to this libsqlite thing, and I should be able to provide more information in the next few days.

Well, it may not be supported by the Travis CI templates but since you can be root on Travis CI every programming language is “supported” (by Ubuntu)…

So we should probably work with the Lazarus people to get an AppImage of Lazarus made? Ideally one that could build FreePascal source code into AppImages…

https://forum.lazarus.freepascal.org/index.php?topic=50991.0

Here is what I’ve found so far regarding including libsqlite3 in an -s deploy build.

It does work! But only under specific circumstances, and with one new issue (which is probably not applicable to your code).

In my AppDir I placed the libsqlite3 libraries (libsqlite3.so.0.8.6, libsqlite3.so.0) in /lib/x86_64-linux-gnu. Otherwise this folder was empty, just those two files, then did a -s deploy build.

Since I wasn’t sure how my application sees the “world”, I assume that a “/lib/…” path to commandoo looks relative to the /tmp/… mount folder. So I tried forcing my program to use:
/lib/x86_64-linux-gnu/libsqlite3.so.0.8.6.

I got:
Run experimental self-contained bundle
/tmp/.mount_commanxAZCZY/usr/bin/commandoo: error while loading shared libraries: libdl.so.2: cannot open shared object file: No such file or directory

But libdl.so.2 is in the AppDir, placed there by -s deploy. Similar result if I used the libsqlite3.so.0 file. So a direct call to /lib/… fails.

So next I had my program use the APPDIR environment variable and then pathed to /lib/… from there and it worked in my dev Kubuntu 20.04. I also tried in in OpenSuse Leap VM (which will not work with a regular non-self contained AppImage) and it also worked. So that is all great.

I’m still confused as to exactly where commandoo looks when it is given paths and so while the above looks quite good and seems to be solved, I wanted a test that would be confirmation without a doubt, so I tried it in NixOS where the paths are completely different.

Earlier, with non-self contained AppImage, I could get the AppImage to work in NixOS but first one must download, from the nix Store, a program called appimage-run.

Then when invoked so: appimage-run /path/to/my.AppImage I would get:

QStandardPaths: XDG_RUNTIME_DIR not set, defaulting to ‘/tmp/runtime-juus’

But, with the -s version, I now get:

Run experimental self-contained bundle
/home/juus/.cache/appimage-run/99d70eea0f6127f63b550ccd181829302f31691c4465b096d16a6de3f34196ca/squashfs-root/AppRun: line 84: /home/juus/.cache/appimage-run/99d70eea0f6127f63b550ccd181829302f31691c4465b096d16a6de3f34196ca/squashfs-root/usr/lib64/ld-linux-x86-64.so.2: Permission denied
/home/juus/.cache/appimage-run/99d70eea0f6127f63b550ccd181829302f31691c4465b096d16a6de3f34196ca/squashfs-root/AppRun: line 84: exec: /home/juus/.cache/appimage-run/99d70eea0f6127f63b550ccd181829302f31691c4465b096d16a6de3f34196ca/squashfs-root/usr/lib64/ld-linux-x86-64.so.2: cannot execute: Permission denied

I tried everything I could think of to get it to run, but no go. So while this probably has something to do with the appimage-run program, I found it interesting that the specified file is …//usr/lib64/ld-linux-x86-64.so.2

So, I’m quite interested in all this. If I can be of further help in testing, please let me know. But it does “appear” that including libraries that are called at runtime works just fine in the general case.

As regards this issue. Yes I want to look into this. One other thing I noticed with that the code you called, it was trying to use Lazarus 1.6…, that is very, very old. The latest release is 2.0.10, with 2.1 in the works. My program build in a 1.6 Laz would in all likelyhood fail anyway.

Not only that but due to a horrific bug in 2.0.10 that affected how tooltips show (SIG fault!), I was forced to use the trunk (where the bug had been fixed) to build my app. So there is no current version of (a released) Lazarus that would build my app properly, and my program lies in a limbo between Laz versions.

But what you’ve shown me with the yaml and issues output encourages me to look into all of that and see if I can get something going. Also, an AppImage of Lazarus would be really huge I think. Better to, I think, do something like what you were trying to do.

And finally, a question: When does the -s option move out of experimental??

Thanks @Juuliuus.

Your application needs to

  1. Find out the path to itself. On Linux, one can use something like readlink("/proc/self/exe", buf, bufsize) - but be aware that this is not portable to, e.g., FreeBSD. The proper solution is to use something like https://android.googlesource.com/platform/bionic/+/f077784/libc/upstream-freebsd/lib/libc/stdlib/realpath.c
  2. Using this information, construct a path to the library you want to dlopen()

Does this make sense?

Once it is known to work in most cases :slight_smile:

:smiley:
Yes, thanks.

I found the contents of the APPDIR environment variable to be perfect to set up pathing to specific embedded files.

I want to create and test a version of commandoo that has the embedded libsqlite file, I’ll let you know how that all goes.

Hi @probono,

My tests here with -s deploy and embedding the libsqlite libs and calling said libsqlite libs at runtime at their embedded paths works great. So my vote for the -s option is an emphatic yes, and that the time is coming where it does not need to be experimental any longer, at least as regards my situation.

I did see https://github.com/AppImageCrafters/AppRun/issues/7 discussing the new -l option in your 556 release. I tried various combinations of -s and -l in the appimage build process, but saw no obvious differences other than a “-s -l deploy” build failed the final appimage build with a “no AppRun” message. (BTW: the appimage link I had sent you, I deleted, hence his 404 error, because it was an intermediate, completely not ready commandoo build. If you need another, released and ready, appimage, let me know).

I’m more than happy to help you debug this -l option, to help get -s deploy off the experimental level, or whatever else you need. So don’t hesitate to let me know if I can.

Hi @Juuliuus thanks for your offer to help.

As for -s, I’ve had some success with it, it just needs more testing… which is where your help is more than appreciated.

I could not get -l to work properly yet and didn’t find time to work on it further, so currently it is known broken. If you are really determined and want to help, you might want to get in contact with azubieta on GitHub or IRC and discuss what needs to be implemented in go-appimage to make it work.

Hi @probono, I’ll certainly give it a go, I need to wrap my brain around the process in overview and your code. If you have any links to code or discussion that will help educate me please post them here.

Hi again @probono, never mind about the links, I’ve been able to get them all through all these posts and the issues pages. If I need anything else I’ll let you know. But I do have a question regarding -l option: what exactly is it going to do? If you have a link to the -l code, I’d just like to see it. I’m assuming it will setup the environment as described on the appimagecrafters site.

Also, I could / will run the permutations, but maybe you could provide clarity for me:

I’m assuming the correct usage would be with the -s option, like:
appimagetool -s -l …?

Or two steps:
appimagetool -s …
appimagetool -l …

Thanks.

-l is currently undocumented because it is work in progress and is not working correctly yet at all. It is supposed to one day use libapprun_hooks.so from https://github.com/AppImageCrafters/AppRun.

Do you have some time to help implement it fully?

Yes. I’ve already read all the dialog between you and azubieta, and, hence, saw that the answer is “-s -l deploy”. After reading his/her instructions I also had the same questions you did regarding how the .env file is used, for instance, the UUID entry vs. how your appimage is mounted, and etc. Unfortunately s/he has not answered those questions yet.

I’m still curious as to what the -l option intends? I’m assuming it was to set up to export the vars required by azubieta’s AppRun? And then to copy in that AppRun? etc.

My plan as of right now is simply to work with all the example appimages (as well as my own appimage) and .env files and see what I can get results-wise. That is, to reproduce your error and/or perhaps find some answers by trial and error.

As you know I come from a pascal background and am only noddingly familiar with C and C++, so I don’t have the knowledge that comes with those languages as regards linux and its workings. But I can read and try things out, so that’s where I was going to go for now. If you have suggestions about how to proceed I’m more that willing to study and learn. And let me know if you want to take this conversation to another location…

Hi, I’ve been working on this for a bit now…first from the example you had posted, then from my appimage…with no success. But.

I have some ideas, I’m thinking that A’s AppRun and its .env are expecting a solid path for the mounted appimage. This is suggested by his UUID var and the pathing he uses then (from his example .env).

Then, all the "self contained"export’s you had in your AppRun need to be moved to the .env file, hence we would need a solid mount path. This, then, would have to be a special build that generates a UUID and then uses that to build the path, rather than md5.

I also suspect this since she explictly states in the readme that an $INTERPRETER and $APPDIR must be exported, and the only we can do this (?) is through the .env file.

This is, of course, only conjecture from what I’m seeing here so far, but maybe worth attempting. What do you think?

Yes. The objective is to use libapprun_hooks.so to get the functionality it provides. So we need to create that .env file as per @azubieta’s instructions and put in his AppRun. Maybe we can get away with a shell script as AppRun instead of these two things.

It just needs to be implemented. I failed because I didnt’t fully understand how it has to look.

@azubieta said the need for GUID will go away iirc.

As you know I come from a pascal background and am only noddingly familiar with C and C++

Good thing that this is written in Golang :slight_smile: actually as my first project in this language. So I am still learning it, too. But I can say it’s not hard. Much less so than C/C++.

@azubieta can you please provide a bit of guidance, maybe a sample .env after all the latest changes (e.g., no more GUID)?

Hello! The AppRun/libapprun_hooks workflow is as folows:

AppRun is the AppImage entry point, it read it’s configuration from an .env file. This file looks like this:

APPIMAGE_UUID=d98464dd-d400-4b68-a489-10bfe84de668
SYSTEM_INTERP=/lib64/ld-linux-x86-64.so.2
XDG_DATA_DIRS=${APPDIR}/usr/local/share:${APPDIR}/usr/share:${XDG_DATA_DIRS}
XDG_CONFIG_DIRS=$APPDIR/etc/xdg:$XDG_CONFIG_DIRS
PATH=$APPDIR/bin:$APPDIR/usr/bin:$PATH
EXEC_PATH=$APPDIR/usr/games/openttd
EXEC_ARGS=$@
APPDIR_LIBRARY_PATH=$APPDIR/usr/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/2.10.0/loaders:$APPDIR/usr/lib/x86_64-linux-gnu:$APPDIR/lib/x86_64-linux-gnu:$APPDIR/usr/local/lib/x86_64-linux-gnu:$APPDIR/usr/lib/x86_64-linux-gnu/pulseaudio/
LIBC_LIBRARY_PATH=$APPDIR/opt/libc/lib/x86_64-linux-gnu:$APPDIR/opt/libc/usr/lib/x86_64-linux-gnu:$APPDIR/opt/libc/usr/local/lib/x86_64-linux-gnu
APPDIR_LIBC_VERSION=2.27
GDK_PIXBUF_MODULEDIR=$APPDIR/usr/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/2.10.0/loaders
GDK_PIXBUF_MODULE_FILE=$APPDIR/usr/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/2.10.0/loaders.cache
APPRUN_PATH_MAPPINGS=/usr/share/games/openttd:$APPDIR/usr/share/games/openttd;
LD_PRELOAD=libapprun_hooks.so

The first two entries and the last are the more important. They provide information about the bundle, the required glibc interpreter and the hooks library name.

The APPIMAGE_UUID is set at build time and every PT_INTERP entry is patched using patchelf. The new PT_INTERP value will be /tmp/appimage-<uuid>-<interp file name>. You can read more about this at https://azubieta.net/appimage/appimagebuilder/ld_so/glibc/2020/03/09/how-to-swicth-ld.so-at-runtime.html

In short it allows to switch the interpreter to be used at runtime. AppRun expects the original interpreter to be deployed using APPDIR/opt/libc as install prefix along with glibc and other libs that are built togeter. By example the original interpreter file will be found at AppDir/opt/libc/lib64/ld-linux-x86-64.so.2.

To decide which glibc use at runtime it reads the version string from the system binary, something like GLIBC_2.30. Then it’s compared with the one specified at APPDIR_LIBC_VERSION (this value is read at build time).

The selected interpreter is copied (not linked) to /tmp/appimage--` so the binaries can found it when executed.

Next, all the listed variables are exported along with their value before starting the app APPRUN_ORIGINAL and the value at the moment of starting the app APPRUN_STARTUP. Those will be used later to detect wheter they value was modified by the app and to run binaries that are not part of the bundle.

Finally execv is called using the EXEC_PATH and EXEC_ARGS.

At runtime libapprun_hooks will intercept all the libc calls that receive a file path as argument and will rewrite if required according to the path mapping rules. execve calls will also be intercepted and the original environment variables will be restored if the binaries lives outside the bundle.

All of that can be done with a bash script, but notice that you will found serious issues due the many execve calls done by bash. Also notice that libapprun_hooks will remove the AppImage specific environment, with the side efect that external apps needs to reset the runtimen environment (.env) when executing apps inside the bundle. So you will have a hard time when using grep, cut, sort and other utilities inside your bundle.

1 Like

Hi @azubieta. Thanks for the details. I’m digesting everything you wrote and educating myself now with things I didn’t know before. So, most likely, I will have more questions soon. I have some ideas I’m going to try first.