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

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>

Related

How do I link libraries with rpath in 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

Problems compiling Ruby 2.4.2

I am trying to build a minimal easily transportable Ruby without relying on external projects, just for my own learning process.
I am using otool -L ruby to determine what libraries the Ruby binary is dynamically linking against as I am trying to minimize it.
Yesterday when i built Ruby it only dynamically linked against 3 libraries, however today when i build Ruby it is linking against 13. I have no idea what has changed between yesterday and today but I am thoroughly confused -- especially as many of the libraries it is linking against existed prior to yesterday's compilation.
On Yesterday's Ruby executable the result of otool -L was:
otool -L ruby
ruby:
/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 1349.8.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1238.60.2)
/usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 228.0.0)
/usr/lib/libgcc_s.1.dylib (compatibility version 1.0.0, current version 489.0.0)
But today it is:
otool -L ./ruby
./ruby:
/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 1349.8.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1238.60.2)
/usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 228.0.0)
/usr/local/opt/openssl/lib/libcrypto.1.0.0.dylib (compatibility version 1.0.0, current version 1.0.0)
/usr/local/opt/openssl/lib/libssl.1.0.0.dylib (compatibility version 1.0.0, current version 1.0.0)
/usr/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.2.8)
/usr/lib/libffi.dylib (compatibility version 1.0.0, current version 1.0.0)
/usr/local/opt/gdbm/lib/libgdbm.4.dylib (compatibility version 5.0.0, current version 5.0.0)
/usr/lib/libutil.dylib (compatibility version 1.0.0, current version 1.0.0)
/usr/lib/libedit.3.dylib (compatibility version 2.0.0, current version 3.0.0)
/usr/lib/libncurses.5.4.dylib (compatibility version 5.4.0, current version 5.4.0)
/usr/lib/libgcc_s.1.dylib (compatibility version 1.0.0, current version 489.0.0)
Does anyone know what is going on?
I am compiling Ruby 2.4.2 like so:
X="-arch i386 -mmacosx-version-min=10.5"
CFLAGS="$X" CXXFLAGS="$X" LDFLAGS="$X" ./configure --prefix=/tmp/ruby-deploy --with-openssl-dir=$(brew --prefix openssl) --disable-install-doc --without-gmp
The answer to this was that I had also run:
CFLAGS="$X" CXXFLAGS="$X" LDFLAGS="$X" ./configure --prefix=/tmp/ruby-deploy --with-openssl-dir=$(brew --prefix openssl) --with-static-linked-ext --disable-install-doc --without-gmp
at one point -- note the --with-static-linked-ext. This resulted in the other dynamically linked libs.
However because i had subsequently run ./configure again WITHOUT that option i assumed it would have reset the configuration, but it didn't.
I discovered the problem by wiping out the Ruby source folder and re-fetching from github. Running the initial ./configure (excluding the --with-static-linked-ext resolved the issue!

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.

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