Open probonopd opened 5 years ago
Wdyt @TheAssassin @azubieta?
I vaguely remember that MuseScore also is doing some run-time dependency checking, correct @shoogle?
That's right. MuseScore's AppRun has an accompanying portable-utils script that does runtime dependency checks.
Download MuseScore's AppImage and run it with the check-depends
option.
./MuseScore*.AppImage check-depends
It then prints out a dependency report like this:
Distro: Ubuntu 18.04
Architecture: x86_64
Kernel: X.Y.Z
In package only: 2
libfoo.so
libbar.so
System only: 1
libc.so
Provided by both: 2
libdeb.so
libX.so
Provided by neither: 1
libbaz.so
Extra: (in package but unlinked. Possibly needed by plugins) 2
libQtPluginA.so
libQtPluginB.so
What the headings mean:
ld
, but were not found anywhere.ld
mechanism.dlopen
(often true for Qt plugins) or else they have been bundled unnecessarily and can be removed from the AppImage.The idea is that the developer runs the check-depends
on all target systems and then bundles libraries that appeared under the "Provided by neither" heading on any system. At this point the application should run, but may crash when you try to run a plugin. You can work out which extra libraries are required by making an educated guess based on:
strace
and examining the crash reportAlso, if the AppImage crashes on a user's machine we can ask them to create a report like this:
./MuseScore*.AppImage check-depends > dependency-report.txt
They can attach this report to their issue or bug report.
FYI, the dependency lists are populated as follows.
find
) for all ELF executables and libraries.
/tmp
.ldd-recursive
(a bundled script).
ldd-recursive
checks for dependencies, and dependencies of dependencies, etc./tmp
, ldd-recursive
will only find system dependencies.
/tmp
and the next one copied and checked.ldd-recursive
as either found or not found in (2), is "Extra".The process takes longer than it should because find
reports every file in the AppDir as an executable. This is because all files are given execute permission when runtime.c
mounts the AppImage. (I suppose we could do something a bit cleverer to detect executables, such as look for magic bits or ELF headers.)
Thanks for the detailed explanation @shoogle.
all files are given execute permission when
runtime.c
mounts the AppImage
Sounds like a bug?
At AppImage build time we can run e.g., appimagelint. I wonder whether this can be used to save some data which we can check against at runtime, to me faster and more efficient at runtime.
I think the ideal solution is to detect when the AppImage's payload application crashes (as opposed to a normal exit) and then intercept the error message and try to replace it with something human-readable. I know this is something @TheAssassin was contemplating for AppImageLauncher, but I don't know how feasible it is. It might involve source changes for downstream projects.
An easier solution might be to embed linuxdeploy's dependency checking logic into the AppImages it creates. Users would have to trigger the check manually by providing a command line option, such as --appimage-check-depends
.
I think the ideal solution is to detect when the AppImage's payload application crashes (as opposed to a normal exit) and then intercept the error message and try to replace it with something human-readable. I know this is something @TheAssassin was contemplating for AppImageLauncher, but I don't know how feasible it is. It might involve source changes for downstream projects.
It's doable to some extent, of course you can only provide somewhat generic error messages to the users (without having the apps in the AppImages collaborate with AIL), but it's doable.
Sounds like a bug?
Maybe not actually. I think ISO 9660 (which MuseScore is still using until this PR gets merged) doesn't store file permissions, so runtime.c
has no choice but to give everything execute permission.
I wonder whether this can be used to save some data which we can check against at runtime, to [be?] faster and more efficient at runtime.
If these are one-off checks that are called manually then slowness is not necessarily a problem. You could just do what MuseScore does and ship ldd-recursive
with a few lines of Bash. It's arguably a good thing if the checking is done by different code to the bundling, as this acts as a form of CI test.
However, if you wanted to do something in advance, you could store a dependency tree for all bundled binaries. The main executable is the root of the tree, and each branch is a bundled library. The leaf nodes are libraries with no dependencies, or the word "system" to indicate that the libary is on the exclude list. Performing the run-time check basically completes the tree by looking to see whether those system libraries are present, and whether their dependencies (which may be different on each system) are present, etc. Other executables would have their own tree.
Possibly the best thing to do would be to try launching the payload application, and only if this fails, start a (time-consuming) "investigation" of what went wrong.
@Conan-Kudo suggested in https://twitter.com/Det_Conan_Kudo/status/1137997295490875393:
Sounds like a good idea. Are we talking about runtime or AppImage creation build time checks?
I like it very much. Do you still have your old code around and would you be willing to contribute it to the project?
Do you see a way to do the same that does not involve querying the package manager, but checking the actual files on disk? Because not every distribution is using rpm...