How do I link libraries with rpath in Xcode? - xcode

I can't find this information anywhere. I have some libraries installed in my system, so it works on my computer, but I want to distribute the libraries with my application. The problem that I see is that otool shows absolute paths to system libraries, so when I copy my binaries to other machine it can't find them even tho I have them in a directory that is added in "runpath search paths". I have one local library (located in project dir) and it is linked with #rpath prefix. Other system libraries are linked the same way but they have absolute paths as otool shows. How do I force them to be linked with #rpath prefix?

Well this partially answers my question: How to set dyld_library_path in Xcode
I tried to automate that, so when any new library is added then it would set things up properly, but I gave up (gosh, why does it have to be so complicated!?)

TL;DR
Using install_name_tool -change will certainly set the #rpath for the referenced frameworks/libraries. However, it doesn't change the names of the libraries themselves.
To do that you need to use install_name_tool -id.
Using a rough example of a project I'm working on currently, where QtGui.framework (amongst others, i.e. QtCore, etc.) is a bundled framework within an application bundle called serialplot.app.
Having first run install_name_tool -change to fix the relative paths for QtGui (and QtCore etc.) being called by serialplot application binary
$ install_name_tool -change /usr/local/opt/qt#5/lib/QtGui.framework/Versions/5/QtGui #rpath/QtGui.framework/Versions/5/QtGui serialplot.app/Contents/MacOS/serialplot
... (omitted commands for clarity)
$ install_name_tool -change /usr/local/opt/qt#5/lib/QtCore.framework/Versions/5/QtCore #rpath/QtCore.framework/Versions/5/QtCore serialplot.app/Contents/MacOS/serialplot
as well as install_name_tool -change to fix the relative path for QtCore which being called by QtGui itself
$ install_name_tool -change /usr/local/Cellar/qt#5/5.15.2/lib/QtCore.framework/Versions/5/QtCore #rpath/QtCore.framework/Versions/5/QtCore serialplot.app/Contents/Frameworks/QtGui.framework/Versions/5/QtGui
Now running otool -L on the application binary gives
serialplot.app/Contents/MacOS/serialplot:
#rpath/qwt.framework/Versions/6/qwt (compatibility version 6.2.0, current version 6.2.0)
#rpath/QtSvg.framework/Versions/5/QtSvg (compatibility version 5.15.0, current version 5.15.2)
#rpath/QtWidgets.framework/Versions/5/QtWidgets (compatibility version 5.15.0, current version 5.15.2)
#rpath/QtGui.framework/Versions/5/QtGui (compatibility version 5.15.0, current version 5.15.2)
/System/Library/Frameworks/AppKit.framework/Versions/C/AppKit (compatibility version 45.0.0, current version 1671.10.106)
/System/Library/Frameworks/Metal.framework/Versions/A/Metal (compatibility version 1.0.0, current version 1.0.0)
#rpath/QtSerialPort.framework/Versions/5/QtSerialPort (compatibility version 5.15.0, current version 5.15.2)
#rpath/QtNetwork.framework/Versions/5/QtNetwork (compatibility version 5.15.0, current version 5.15.2)
#rpath/QtCore.framework/Versions/5/QtCore (compatibility version 5.15.0, current version 5.15.2)
So everything looks fine, with #rpath being used for QtGui (as well as the other Qt frameworks).
However, running otool -L on the bundled QtGui, within the application bundle, shows
serialplot.app/Contents/Frameworks/QtGui.framework/QtGui:
/usr/local/opt/qt#5/lib/QtGui.framework/Versions/5/QtGui (compatibility version 5.15.0, current version 5.15.2)
/System/Library/Frameworks/AppKit.framework/Versions/C/AppKit (compatibility version 45.0.0, current version 1671.10.106)
/System/Library/Frameworks/Metal.framework/Versions/A/Metal (compatibility version 1.0.0, current version 1.0.0)
#rpath/QtCore.framework/Versions/5/QtCore (compatibility version 5.15.0, current version 5.15.2)
/System/Library/Frameworks/DiskArbitration.framework/Versions/A/DiskArbitration (compatibility version 1.0.0, current version 1.0.0)
/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit (compatibility version 1.0.0, current version 275.0.0)
/System/Library/Frameworks/CoreGraphics.framework/Versions/A/CoreGraphics (compatibility version 64.0.0, current version 1247.4.1)
/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation (compatibility version 300.0.0, current version 1560.12.0)
/System/Library/Frameworks/OpenGL.framework/Versions/A/OpenGL (compatibility version 1.0.0, current version 1.0.0)
/System/Library/Frameworks/AGL.framework/Versions/A/AGL (compatibility version 1.0.0, current version 1.0.0)
/System/Library/Frameworks/ApplicationServices.framework/Versions/A/ApplicationServices (compatibility version 1.0.0, current version 50.1.0)
/usr/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.2.11)
/usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 400.9.4)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1252.200.5)
/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 1560.12.0)
/System/Library/Frameworks/CoreText.framework/Versions/A/CoreText (compatibility version 1.0.0, current version 1.0.0)
/usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 228.0.0)
Note that (as you state) the absolute path (/usr/local/opt/qt#5/lib/), and not #rpath is used
$ otool -L serialplot.app/Contents/Frameworks/QtGui.framework/QtGui
serialplot.app/Contents/Frameworks/QtGui.framework/QtGui:
/usr/local/opt/qt#5/lib/QtGui.framework/Versions/5/QtGui (compatibility version 5.15.0, current version 5.15.2)
...
Now run install_name_tool -id
$ install_name_tool -id #rpath/QtGui.framework/Versions/5/QtGui serialplot.app/Contents/Frameworks/QtGui.framework/Versions/5/QtGui
and the output of otool -L now gives you what you want
$ otool -L serialplot.app/Contents/Frameworks/QtGui.framework/QtGui
serialplot.app/Contents/Frameworks/QtGui.framework/QtGui:
#rpath/QtGui.framework/Versions/5/QtGui (compatibility version 5.15.0, current version 5.15.2)
...
Yes, it is complex, and annoying, but perfectly scriptable with a little patience.
For more details on this particular example, see my blog on this project: Porting serialplot to OS X

Related

Why are shared libraries linked with absolute paths on OS X?

So I'm porting a game from Linux to OS X and having successfully compiled and linked it, I'm now running up against problems starting it – the dynamic linker can't find the libs.
Here's the otool -L output:
./foo:
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1197.1.1)
#rpath/SDL2.framework/Versions/A/SDL2 (compatibility version 1.0.0, current version 3.1.0)
/System/Library/Frameworks/OpenGL.framework/Versions/A/OpenGL (compatibility version 1.0.0, current version 1.0.0)
/System/Library/Frameworks/CoreServices.framework/Versions/A/CoreServices (compatibility version 1.0.0, current version 59.0.0)
/usr/lib/libGLEW.1.10.0.dylib (compatibility version 1.10.0, current version 1.10.0)
/usr/lib/libtheoradec.1.dylib (compatibility version 3.0.0, current version 3.4.0)
/usr/lib/libtheora.0.dylib (compatibility version 4.0.0, current version 4.10.0)
/usr/local/lib/libvorbis.0.dylib (compatibility version 5.0.0, current version 5.7.0)
/usr/local/lib/libogg.0.dylib (compatibility version 9.0.0, current version 9.2.0)
#loader_path/libsteam_api.dylib (compatibility version 1.0.0, current version 1.0.0)
/Users/macosx/some/path/to/code/openal-soft-1.16.0/build/libopenal.1.dylib (compatibility version 1.0.0, current version 1.16.0)
/usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 120.0.0)
The thing is, some of those paths are plain wrong. For instance, neither GLEW nor Ogg/Vorbis/Theora exist in /usr/…, and for whatever reason the path to OpenAL Soft is burnt in as an absolute one to where it was built.
Still, by setting DYLD_LIBRARY_PATH correctly, I can get the game to start. However, for multiple reasons I'd prefer not to embed my system's absolute paths within the binary.
What's going on here? Is there a way to force relative paths (that's the #loader_path thing, I presume?) on libraries of my choice? Is there documentation on the matter available?
Just as #trojanfoe suggested – this works:
install_name_tool -change <from> <to> <executable>

Mac OS X how to change where an app looks for libraries

I'm building an application using Qt5.1.1 on a Mac. I'm using packages to create an installer. I have it set up so that the installer installs the .app file and all of the libraries into a folder in /Applications/. The problem is when I install the application it is not looking where I want it to look for the libraries.
An otool -L shows this:
esu:
libboost_system.dylib (compatibility version 0.0.0, current version 0.0.0)
/Users/ken/Qt5.1.1/5.1.1/clang_64/lib/QtWidgets.framework/Versions/5/QtWidgets (compatibility version 5.1.0, current version 5.1.1)
/Users/ken/Qt5.1.1/5.1.1/clang_64/lib/QtGui.framework/Versions/5/QtGui (compatibility version 5.1.0, current version 5.1.1)
/Users/ken/Qt5.1.1/5.1.1/clang_64/lib/QtCore.framework/Versions/5/QtCore (compatibility version 5.1.0, current version 5.1.1)
/Users/ken/Qt5.1.1/5.1.1/clang_64/lib/QtSerialPort.framework/Versions/5/QtSerialPort (compatibility version 5.1.0, current version 5.1.1)
/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit (compatibility version 1.0.0, current version 275.0.0)
/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 744.1.0)
/Users/ken/Qt5.1.1/5.1.1/clang_64/lib/QtNetwork.framework/Versions/5/QtNetwork (compatibility version 5.1.0, current version 5.1.1)
/System/Library/Frameworks/OpenGL.framework/Versions/A/OpenGL (compatibility version 1.0.0, current version 1.0.0)
/System/Library/Frameworks/AGL.framework/Versions/A/AGL (compatibility version 1.0.0, current version 1.0.0)
/usr/lib/libstdc++.6.dylib (compatibility version 7.0.0, current version 56.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 169.3.0)
How can I change where the application is looking for these libraries?
If you run macdeployqt it will change some of them automaticaly (thoses used by Qt) for the other ones, you can do it with install_name_tool
install_name_tool -change oldpath newpath target
I ended up going to where my release esu.app file was and I just used the macdeployqt. Trying to manually set up lib dependencies with install_name_tool was too much of a pain.
This is the command I used:
macdeployqt esu.app -verbose=2 -dmg

Multiple libcurl.4.dylib with different compatibility versions

I'm trying to run a binary executable built for 10.7 (ISCAgent in the below example) on Mac OS X 10.6.8.
The problem with the binary is it depends on /usr/lib/libcurl.4.dylib
with compatibility version of 7.0.0, while I only have version 6.0.0 installed:
$ otool -L ISCAgent
ISCAgent:
#executable_path/libsslserver.dylib (compatibility version 0.0.0, current version 0.0.0)
#executable_path/libssl.dylib (compatibility version 1.0.0, current version 1.0.0)
#executable_path/libcrypto.dylib (compatibility version 1.0.0, current version 1.0.0)
#executable_path/libcachecom.dylib (compatibility version 0.0.0, current version 0.0.0)
#executable_path/libxerces-c-3.1.dylib (compatibility version 0.0.0, current version 0.0.0)
#executable_path/libicuuc.40.dylib (compatibility version 40.0.0, current version 40.0.0)
#executable_path/libicudata.40.dylib (compatibility version 40.0.0, current version 40.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 159.1.0)
/usr/lib/libstdc++.6.dylib (compatibility version 7.0.0, current version 52.0.0)
$ otool -L libxerces-c-3.1.dylib
libxerces-c-3.1.dylib:
#executable_path/libxerces-c-3.1.dylib (compatibility version 0.0.0, current version 0.0.0)
/usr/lib/libcurl.4.dylib (compatibility version 7.0.0, current version 7.0.0)
#executable_path/libicuuc.40.dylib (compatibility version 40.0.0, current version 40.0.0)
#executable_path/libicudata.40.dylib (compatibility version 40.0.0, current version 40.0.0)
/usr/lib/libstdc++.6.dylib (compatibility version 7.0.0, current version 52.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 159.1.0)
$ otool -L /usr/lib/libcurl.4.dylib
/usr/lib/libcurl.4.dylib:
/usr/lib/libcurl.4.dylib (compatibility version 6.0.0, current version 6.1.0)
/usr/lib/libssl.0.9.8.dylib (compatibility version 0.9.8, current version 0.9.8)
/usr/lib/libcrypto.0.9.8.dylib (compatibility version 0.9.8, current version 0.9.8)
/usr/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.2.3)
/System/Library/Frameworks/LDAP.framework/Versions/A/LDAP (compatibility version 1.0.0, current version 2.2.0)
/System/Library/Frameworks/Kerberos.framework/Versions/A/Kerberos (compatibility version 5.0.0, current version 5.0.0)
/usr/lib/libresolv.9.dylib (compatibility version 1.0.0, current version 41.1.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 125.2.11)
Currently, I've just replaced libxerces-c-3.1.dylib shipped with the executable with one I built myself on my Snow Leopard, and everything seems to work.
I would like to know what my other options are, however.
Particularly:
How can I (I'm pretty sure I can't, but still) have different versions of libcurl.4.dylib with different compatibility version values in the same /usr/lib directory? I'm confused a bit here -- if code contract (API) and/or ABI changes, the version number in the library file name should also be increased, shouldn't it? If, on the other hand, we have a compatibility version mechanism, what the original version number present in the file name is good for?
How can I affect the hard-coded library paths in a 3rd-party shared object? I have a libcurl.4.dylib (from MacPorts, compatibility version 8.0.0) residing under /opt/local/lib, but exporting a proper DYLD_LIBRARY_PATH doesn't help -- libxerces-c-3.1.dylib always picks libcurl.4.dylib from /usr/lib.

dyld: Library not loaded: libboost_system.dylib

Yet another dyld: Library not loaded dylib problem..
I've got a dylib (libboost_system.dylib) compiled from the Boost sources,
then used install-name-tool to prepare it for inclusion in the Frameworks folder of my app bundle like so:
install_name_tool -id #executable_path
/../Frameworks/
libboost_system.dylib libboost_system.dylib
Verified with otool:
$> otool -L libboost_system.dylib
/Users/TesterCodeSandbox/OgreSDK
/BOOST_DYLIBS/libboost_system.dylib:
#executable_path/../Frameworks
/libboost_system.dylib (compatibility version 0.0.0, current version 0.0.0)
/usr/lib/libstdc++.6.dylib (compatibility version 7.0.0, current version 7.9.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 125.2.11)
Now added to the Xcode project, linked against the app and copied to the Frameworks folder.
But when checking the built app with otool -L it's still referring to the original (i.e. 'non-relocated') dylib:
OgreTest-ekeeqmnyciddaxbzyrehtcoijcki/Build
/Products/Debug/OgreTest.app/Contents/MacOS/OgreTest:
#executable_path/../Plugins
/RenderSystem_GL.dylib (compatibility version 0.0.0, current version 1.8.0)
/System/Library/Frameworks/Cocoa.framework/
Versions/A/Cocoa (compatibility version 1.0.0, current version 15.0.0)
#executable_path/../Frameworks/Ogre.framework/
Versions/1.8.0/Ogre (compatibility version 0.0.0, current version 1.8.0)
#executable_path/../Frameworks/Cg.framework
/Cg (compatibility version 0.0.0, current version 0.0.0)
/System/Library/Frameworks/IOKit.framework/Versions/A
/IOKit (compatibility version 1.0.0, current version 275.0.0)
/System/Library/Frameworks/Carbon.framework/Versions/A
/Carbon (compatibility version 2.0.0, current version 152.0.0)
libboost_system.dylib (compatibility version 0.0.0, current version 0.0.0)
/usr/lib/libstdc++.6.dylib
(compatibility version 7.0.0, current version 7.9.0)
/usr/lib/libSystem.B.dylib
(compatibility version 1.0.0, current version 125.2.11)
/usr/lib/libobjc.A.dylib
(compatibility version 1.0.0, current version 227.0.0)
/System/Library/Frameworks/CoreFoundation.framework
/Versions/A/CoreFoundation
(compatibility version 150.0.0, current version 550.43.0)
/System/Library/Frameworks/AppKit.framework
/Versions/C/AppKit
(compatibility version 45.0.0, current version 1038.36.0)
And the app fails to start with the 'dyld: Library not loaded: libboost_system.dylib' error. Any ideas?
Note 1
Now it does work if I also post-process my app's binary with install_name_tool, i.e.:
install_name_tool -change libboost_system.dylib #executable_path/../Frameworks/libboost_system.dylib TestApp
Am I missing some Xcode option to force the compiler to change the dylib location in my app automatically as part of the build?
Surely invoking install_name_tool manually for the application consuming the dylib (i.e. one of potentially many) can just be a work-around for something I've screwed up when configuring the (application) project....

MacOS shared library path

I have a C++ application that depends on several .dylib; Amongst them, libproj and PythonQt.
I pre-built these libs in separate directories.
2 (related) questions :
The executable finds the full path of the libproj.dylib, although it's not in the path, not in DYLD_LIBRARY_PATH, not in QtCreator's configuration. How ?
The executable does NOT find PythonQt, although it's linked exactly the same way. I have to manually add the right path in DYLD_LIBRARY_PATH. Why ?
It's probably related to the fact that there is a .la and a .dylib for libproj, but only a .dylib for PythonQt; but I'm not sure and I don't understand the relation.
Sample output of otool :
/Users/arnaud/Projects/myprojectname/external/proj-4.8.0/bin_mac64_gcc421_release/lib/libproj.0.dylib (compatibility version 8.0.0, current version 8.0.0)
( so it finds libproj correctly, even in another directory )
libPythonQt.1.dylib (compatibility version 1.0.0, current version 1.0.0)
(... but not libPythonQt, which is in a similar directory )
/Library/Frameworks/Python.framework/Versions/2.7/Python (compatibility version 2.7.0, current version 2.7.0)
QtOpenGL.framework/Versions/4/QtOpenGL (compatibility version 4.8.0, current version 4.8.3)
QtGui.framework/Versions/4/QtGui (compatibility version 4.8.0, current version 4.8.3)
QtCore.framework/Versions/4/QtCore (compatibility version 4.8.0, current version 4.8.3)
QtNetwork.framework/Versions/4/QtNetwork (compatibility version 4.8.0, current version 4.8.3)
/System/Library/Frameworks/OpenGL.framework/Versions/A/OpenGL (compatibility version 1.0.0, current version 1.0.0)
/System/Library/Frameworks/AGL.framework/Versions/A/AGL (compatibility version 1.0.0, current version 1.0.0)
/usr/lib/libstdc++.6.dylib (compatibility version 7.0.0, current version 7.9.0)
/usr/lib/libgcc_s.1.dylib (compatibility version 1.0.0, current version 697.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 125.2.11)

Resources