probonopd / linuxdeployqt

Makes Linux applications self-contained by copying in the libraries and plugins that the application uses, and optionally generates an AppImage. Can be used for Qt and other applications
Other
2.22k stars 414 forks source link

linuxdeployqt overrides RPATH while running on Linux #556

Open GilfoyleProger opened 2 years ago

GilfoyleProger commented 2 years ago

Hi, We use linuxdeployqt to automatically deploy our Qt project in Ubuntu 20.04. Here is an example of qmake config: deploy.commands = $$PWD/../installer/linuxdeployqt-continuous-x86_64.AppImage \"PROD_NAME\" deploy.commands += -qmldir=$$QML_DIR deploy.commands += -qmake=$$QMAKE_QMAKE deploy.commands += -always-overwrite ... deploy.commands += -appimage

I have shared libraries (.so) of Boost, "some private lib", which are dynamically loaded by another dynamic library. It seems to me that after running linuxdeployqt, the RPATH is overwritten and as a result:

  1. The generated .AppImage while running gives a message that the .so files of this libraries cannot be found.
  2. Even when launching the project directly from Qt Creator the libraries cannot be found (linuxdeployqt also runs when I start the project in release mode)

When linuxdeployqt calling is commented out in qmake, the libraries are successfully found when I run the program from Qt Creator. Can you tell me please, is there any way to avoid overwriting the RPATH variable? I used the "QMAKE_LFLAGS += "-Wl,-rpath,'$$ORIGIN/libs'"" flag, but it is also overwritten after running linuxdeployqt. Is there any way to handle this in qmake or it is a linuxdeployqt bug? Let me know if you need more information.

probonopd commented 2 years ago

Rewriting the rpath is not optional, it is key to how linuxdeployqt works. If all goes well, linuxdeployqt should find all required libraries using ldd and deploy them into the AppDir. If that is not happening, it is most likely due to one of two things, or both:

  1. Libraries on the build system are not in the "usual" path (e.g., in some subdirectory of /usr/lib/x86_64/). If this is the case, try moving or copying such libraries to the standard library location (e.g., /usr/lib/x86_64/ before running linuxdeployqt
  2. Libraries are opened using dlopen() rather than being defined as a hard dependency in the ELF executable or library. If this is the case, try adding a hard dependency on the library using patchelf --add-needed ... before running linuxdeployqt.

If the above still does not help: Can you post a link to your build script which is calling linuxdeployqt?

GilfoyleProger commented 2 years ago

Thank you for your reply.

  1. There is no problem with the standard system libraries. The problem is only with external libraries. After deploying "somePrivateLib" (which is successfully opened by dlopen) can't find: libboost_system-mt, libboost_thread-mt and "someChildPrivateLib". These child libraries are dynamically loaded internally by "somePrivateLib".
  2. It seems to me that using patchelf --add-needed ... is not quite appropriate because it is not a default tool. Deployment of the project should be universal, we also build it on Jenkins. Please correct me if I'm wrong and the patchelf tool can be used another way.

Unfortunately, I can't send you the build script due to privacy, but I will try to describe some output information:

  1. Before deploying, all the required libraries are copied to the main folder, where linuxdeployqt is then started.
  2. If I first open the Boost, "someChildPrivateLib" libraries via dlopen, and then open "somePrivateLib" via dlopen, all libraries are successfully found by "somePrivateLib".
  3. When I unpacked the .AppImage after deployment, all the necessary libraries are there.

So, the problem is only related to linking.

probonopd commented 2 years ago

Thing is, linuxdeployqt has no way of knowing about the existence of libraries that are loaded via dlopen, and hence does not deploy its dependencies. Does someChildPrivateLib depend on libraries that are not part of the AppImage? Is the rpath in someChildPrivateLib set in such a way that it can find its dependencies? Try passing linuxdeployqt -executable=/path/in/AppDir/to/someChildPrivateLib.so ... which should result in linuxdeployqt try to deploy the dependencies of someChildPrivateLib as well.

Also, where are you dlopening the library from? You'd need to construct an absolute path to someChildPrivateLib based on readlink /proc/self/exe or something like that. Otherwise dlopen has no chance to know where to find that library.

GilfoyleProger commented 2 years ago

All someChildPrivateLib dependencies exist in AppImage. Using the command "linuxdeployqt -executable" is unnecessary because all dependencies are fully collected in AppImage (I already checked it). I checked the RPATH of these external libraries with "patchelf" - the RPATH was empty. For test, I used the "dlopen" command with relative path and "QMAKE_LFLAGS += "-Wl,-rpath,'$$ORIGIN/libs'"" to open Boost, "someChildPrivateLib" libraries. But this is not just an AppImage problem. If the AppImage linking problem is solved, it won't fix the problem of running the program from Qt Creator as described in the first comment.

And please can you tell me how linuxdeployqt overwrites the RPATH (which path is set)? Because I can't find information about it anywhere.

probonopd commented 2 years ago

You can use patchelf --print-rpath to see what rpath gets set by linuxdeployqt in the executables and libraries deployed by linuxdeployqt in the AppDir.

I think that in its present form linuxdeployqt is unable to do what you need; so you need to do some manual fixes before/after AppDir creation. Such as setting rpaths manually.

GilfoyleProger commented 2 years ago

Thanks for advice. And I have one more question. I tried the patchelf --print-rpath command with binaries after deployment and got this result: $ORIGIN/lib Why did linuxdeployqt add the "lib" folder to RPATH if the "lib" folder doesn't exist in our project? Please could you provide more information on how linuxdeployqt generates RPATH for the binaries?

probonopd commented 2 years ago

Looks like we hardcoded lib:

https://github.com/probonopd/linuxdeployqt/blob/d6ac06cab496b5066af13bdfe14f56a6e9c11732/tools/linuxdeployqt/shared.cpp#L1173-L1176