Proper usage of install_name_tool - xcode

I am building an application on OSX with Xcode.
I am using an external library that I build myself (wxWidgets). I didn't run make install for it, and so it is still located in my home directory where I build it.
Now in order to run the AppBundle independently I'm copying the dylib's inside the Bundle and running install_name_tool in order for the linker to pick up the proper directory. This is the code I'm running at the end of the build:
cp -f ~/wxWidgets/buildC11/lib/libwx_baseu-3.2.dylib "$TARGET_BUILD_DIR/$TARGET_NAME.app/Contents/Frameworks/libwx_baseu-3.2.dylib"
install_name_tool -change /usr/local/lib/libwx_baseu-3.2.dylib #executable_path/../Frameworks/libwx_baseu-3.2.dylib "$TARGET_BUILD_DIR/$TARGET_NAME.app/Contents/MacOS/$PRODUCT_NAME"
My question would be:
Is my install_name_tool command good? It is executing successfully, but gives problems during the run-time.

Related

Why can this dynamic library file include all dependencies? [duplicate]

Program is part of the Xenomai test suite, cross-compiled from Linux PC into Linux+Xenomai ARM toolchain.
# echo $LD_LIBRARY_PATH
/lib
# ls /lib
ld-2.3.3.so libdl-2.3.3.so libpthread-0.10.so
ld-linux.so.2 libdl.so.2 libpthread.so.0
libc-2.3.3.so libgcc_s.so libpthread_rt.so
libc.so.6 libgcc_s.so.1 libstdc++.so.6
libcrypt-2.3.3.so libm-2.3.3.so libstdc++.so.6.0.9
libcrypt.so.1 libm.so.6
# ./clocktest
./clocktest: error while loading shared libraries: libpthread_rt.so.1: cannot open shared object file: No such file or directory
Is the .1 at the end part of the filename? What does that mean anyway?
Your library is a dynamic library.
You need to tell the operating system where it can locate it at runtime.
To do so,
we will need to do those easy steps:
Find where the library is placed if you don't know it.
sudo find / -name the_name_of_the_file.so
Check for the existence of the dynamic library path environment variable(LD_LIBRARY_PATH)
echo $LD_LIBRARY_PATH
If there is nothing to be displayed, add a default path value (or not if you wish to)
LD_LIBRARY_PATH=/usr/local/lib
We add the desired path, export it and try the application.
Note that the path should be the directory where the path.so.something is. So if path.so.something is in /my_library/path.so.something, it should be:
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/my_library/
export LD_LIBRARY_PATH
./my_app
Reference to source
Here are a few solutions you can try:
ldconfig
As AbiusX pointed out: If you have just now installed the library, you may simply need to run ldconfig.
sudo ldconfig
ldconfig creates the necessary links and cache to the most recent
shared libraries found in the directories specified on the command
line, in the file /etc/ld.so.conf, and in the trusted directories
(/lib and /usr/lib).
Usually your package manager will take care of this when you install a new library, but not always, and it won't hurt to run ldconfig even if that is not your issue.
Dev package or wrong version
If that doesn't work, I would also check out Paul's suggestion and look for a "-dev" version of the library. Many libraries are split into dev and non-dev packages. You can use this command to look for it:
apt-cache search <libraryname>
This can also help if you simply have the wrong version of the library installed. Some libraries are published in different versions simultaneously, for example, Python.
Library location
If you are sure that the right package is installed, and ldconfig didn't find it, it may just be in a nonstandard directory. By default, ldconfig looks in /lib, /usr/lib, and directories listed in /etc/ld.so.conf and $LD_LIBRARY_PATH. If your library is somewhere else, you can either add the directory on its own line in /etc/ld.so.conf, append the library's path to $LD_LIBRARY_PATH, or move the library into /usr/lib. Then run ldconfig.
To find out where the library is, try this:
sudo find / -iname *libraryname*.so*
(Replace libraryname with the name of your library)
If you go the $LD_LIBRARY_PATH route, you'll want to put that into your ~/.bashrc file so it will run every time you log in:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/path/to/library
Update
While what I write below is true as a general answer about shared libraries, I think the most frequent cause of these sorts of message is because you've installed a package, but not installed the -dev version of that package.
Well, it's not lying - there is no libpthread_rt.so.1 in that listing. You probably need to re-configure and re-build it so that it depends on the library you have, or install whatever provides libpthread_rt.so.1.
Generally, the numbers after the .so are version numbers, and you'll often find that they are symlinks to each other, so if you have version 1.1 of libfoo.so, you'll have a real file libfoo.so.1.0, and symlinks foo.so and foo.so.1 pointing to the libfoo.so.1.0. And if you install version 1.1 without removing the other one, you'll have a libfoo.so.1.1, and libfoo.so.1 and libfoo.so will now point to the new one, but any code that requires that exact version can use the libfoo.so.1.0 file. Code that just relies on the version 1 API, but doesn't care if it's 1.0 or 1.1 will specify libfoo.so.1. As orip pointed out in the comments, this is explained well at here.
In your case, you might get away with symlinking libpthread_rt.so.1 to libpthread_rt.so. No guarantees that it won't break your code and eat your TV dinners, though.
You need to ensure that you specify the library path during
linking when you compile your .c file:
gcc -I/usr/local/include xxx.c -o xxx -L/usr/local/lib -Wl,-R/usr/local/lib
The -Wl,-R part tells the resulting binary to also look for the library
in /usr/local/lib at runtime before trying to use the one in /usr/lib/.
Try adding LD_LIBRARY_PATH, which indicates search paths, to your ~/.bashrc file
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/path_to_your_library
It works!
The linux.org reference page explains the mechanics, but doesn't explain any of the motivation behind it :-(
For that, see Sun Linker and Libraries Guide
In addition, note that "external versioning" is largely obsolete on Linux, because symbol versioning (a GNU extension) allows you to have multiple incompatible versions of the same function to be present in a single library. This extension allowed glibc to have the same external version: libc.so.6 for the last 10 years.
cd /home/<user_name>/
sudo vi .bash_profile
add these lines at the end
LD_LIBRARY_PATH=/usr/local/lib:<any other paths you want>
export LD_LIBRARY_PATH
Another possible solution depending on your situation.
If you know that libpthread_rt.so.1 is the same as libpthread_rt.so then you can create a symlink by:
ln -s /lib/libpthread_rt.so /lib/libpthread_rt.so.1
Then ls -l /lib should now show the symlink and what it points to.
I had a similar error and it didn't fix with giving LD_LIBRARY_PATH in ~/.bashrc .
What solved my issue is by adding .conf file and loading it.
Go to terminal an be in su.
gedit /etc/ld.so.conf.d/myapp.conf
Add your library path in this file and save.(eg: /usr/local/lib).
You must run the following command to activate path:
ldconfig
Verify Your New Library Path:
ldconfig -v | less
If this shows your library files, then you are good to go.
running:
sudo ldconfig
was enough to fix my issue.
I had this error when running my application with Eclipse CDT on Linux x86.
To fix this:
In Eclipse:
Run as -> Run configurations -> Environment
Set the path
LD_LIBRARY_PATH=/my_lib_directory_path
Wanted to add, if your libraries are in a non standard path, run ldconfig followed by the path.
For instance I had to run:
sudo ldconfig /opt/intel/oneapi/mkl/2021.2.0/lib/intel64
to make R compile against Intel MKL
All I had to do was run:
sudo apt-get install libfontconfig1
I was in the folder located at /usr/lib/x86_64-linux-gnu and it worked perfectly.
Try to install lib32z1:
sudo apt-get install lib32z1
If you are running your application on Microsoft Windows, the path to dynamic libraries (.dll) need to be defined in the PATH environment variable.
If you are running your application on UNIX, the path to your dynamic libraries (.so) need to be defined in the LD_LIBRARY_PATH environment variable.
The error occurs as the system cannot refer to the library file mentioned. Take the following steps:
Running locate libpthread_rt.so.1 will list the path of all the files with that name. Let's suppose a path is /home/user/loc.
Copy the path and run cd home/USERNAME. Replace USERNAME with the name of the current active user with which you want to run the file.
Run vi .bash_profile and at the end of the LD_LIBRARY_PATH parameter, just before ., add the line /lib://home/usr/loc:.. Save the file.
Close terminal and restart the application. It should run.
I got this error and I think its the same reason of yours
error while loading shared libraries: libnw.so: cannot open shared
object file: No such file or directory
Try this. Fix permissions on files:
cd /opt/Popcorn (or wherever it is)
chmod -R 555 * (755 if not ok)
I use Ubuntu 18.04
Installing the corresponding -dev package worked for me,
sudo apt install libgconf2-dev
Before installing the above package, I was getting the below error:
turtl: error while loading shared libraries: libgconf-2.so.4: cannot open shared object file: No such file or directory
I got this error and I think its the same reason of yours
error while loading shared libraries: libnw.so: cannot open shared object
file: No such file or directory
Try this. Fix permissions on files:
sudo su
cd /opt/Popcorn (or wherever it is)
chmod -R 555 * (755 if not ok)
chown -R root:root *
A similar problem can be found here.
I've tried the mentioned solution and it actually works.
The solutions in the previous questions may work. But the following is an easy way to fix it.
It works by reinstalling the package libwbclient
in fedora:
dnf reinstall libwbclient
You can read about libraries here:
https://domiyanyue.medium.com/c-development-tutorial-4-static-and-dynamic-libraries-7b537656163e

Failing to properly copy the library to Application Bundle

ALL,
I am working with the Xcode. My project contains one executable binary and couple dylibs with the inter-dependencies
In my Xcode project I successfully created an script phase to copy dylib files (all of them) inside the Application Bundle. However when I try to run the application from the Terminal - it fails.
The error I am gettting is as follows:
Dyld Error Message:
Library not loaded: /usr/local/lib/liblibpropertypages.dylib
Referenced from: /Users/igorkorot/dbhandler/dbhandler/Build/Products/Debug/dbhandler.app/Contents/Frameworks/liblibdbwindow.dylib
Reason: image not found
But in the script I have the following code:
cp -f ~/dbhandler/dbhandler/Build/Products/Debug/liblibpropertypages.dylib "$TARGET_BUILD_DIR/$TARGET_NAME.app/Contents/Frameworks/liblibpropertypages.dylib"
install_name_tool -change /usr/local/lib/liblibpropertypages.dylib #executable_path/../Frameworks/liblibpropertypages.dylib "$TARGET_BUILD_DIR/$TARGET_NAME.app/Contents/MacOS/$PRODUCT_NAME"
cp -f ~/dbhandler/dbhandler/Build/Products/Debug/liblibdbwindow.dylib "$TARGET_BUILD_DIR/$TARGET_NAME.app/Contents/Frameworks/liblibdbwindow.dylib"
install_name_tool -change /usr/local/lib/liblibdbwindow.dylib #executable_path/../Frameworks/liblibdbwindow.dylib "$TARGET_BUILD_DIR/$TARGET_NAME.app/Contents/MacOS/$PRODUCT_NAME"
So the place to load the liblibdbwindow.dylib is changed, but the place to load liblibpropertypages.dylib is not. The code is exactly the same.
What am I missing?
TIA!
One of your libraries depends on the other one, so you need to change the reference there too.
install_name_tool -change /usr/local/lib/liblibpropertypages.dylib #executable_path/../Frameworks/liblibpropertypages.dylib "$TARGET_BUILD_DIR/$TARGET_NAME.app/Contents/Frameworks/liblibdbwindow.dylib"
And you'll probably want to change the IDs of those libraries as well:
install_name_tool -id #executable_path/../Frameworks/liblibpropertypages.dylib "$TARGET_BUILD_DIR/$TARGET_NAME.app/Contents/Frameworks/liblibpropertypages.dylib"
install_name_tool -id #executable_path/../Frameworks/liblibdbwindow.dylib "$TARGET_BUILD_DIR/$TARGET_NAME.app/Contents/Frameworks/liblibdbwindow.dylib"

dylib is not able to load, refreneced from other dylibs

I am getting following error when i try to execute my executable:
**Dyld Error Message:
Library not loaded: /Library/Application support/XYZ/a.dylib
Referenced from :/Library/Application support/ABC/b.dylib
Reason: image not found**
I have used "install name tool" in Run script of my Xcode project to change search location for both a.dylib and b.dylib. I have copied the both libraries(a.dylib and b.dylib)from folder XYZ to folder ABC.
(For not linking from old location I have renamed the old folder as _XYZ)
Then also I get above error.
So, Is it any way to change the install name of dylibs which is refrenced from other dylib?
I'm guessing you just upgraded to Yosemite? Had the same issue, this solved it for me:
https://github.com/Homebrew/homebrew/issues/27106#issuecomment-36694023
I have resolved this issue by changing install name like this
install_name_tool -change "old path" "new path" "path of depended dylib"
ex:
install_name_tool -change "/Library/Application support/XYZ/a.dylib" "/Library/Application support/ABC/a.dylib" "/Library/Application support/ABC/b.dylib"

how to make Xcode project with homebrew libraries portable?

I have installed FreeType on my mac using Brew. The code on my mac works fine, but when I try to run the project on other mac I get the below mentioned linking error.
dyld: Library not loaded: /usr/local/opt/freetype/lib/libfreetype.6.dylib
Referenced from: /Users/ashutosh/Library/Developer/Xcode/DerivedData/InstrumentChamp-
etytleaabhxmokadgrjzbgtmwxfl/Build/Products/Debug/instrumentchamp.app/Contents/MacOS/instrumentchamp
Reason: image not found (lldb)
All the library directories and include directories of Freetype are included in project's '$SRCROOT/' directory when I try to run the code on other mac.
The path you see in the linking error for library is where brew had installed freetype in the mac where I created this project.
/usr/local/opt/freetype/lib/libfreetype.6.dylib
I have copied all the lib/ include/ directories that were needed to my project's home folder.
And I have set the library and include paths in Xcode.
What is it that I am missing here? What else do I have to do to make my code portable on any other Mac.
I got the project to run on other mac by installing Brew, but I want to do it without the need to install brew.
PS: I had to install freetype using brew, as I couldn't compile the .dylib for freetype for 32bit processor, a 64bit copy of .dylib was giving me error such as 'wrong architecture!'
The basic idea I was getting at in my comments is that OS X is pretty stupid about where it searches for libraries, it will use the same absolute path used during compilation to resolve them at run-time.
Usually when you want to deploy/distribute your application to a different machine from the one that built it, you will include your libraries with the install package/bundle. But you probably want them to use a path relative to your application at run-time, thus install_name_tool -change allows you to replace the nasty absolute path with something relative.
Hope this makes sense, Apple makes it really easy to use system-wide frameworks on OS X, custom libraries not so much. If you compile using a system-wide framework, /System/Library/Frameworks/... is universally available on all OS X installs (given the same target release version).
To solve your problem, I would do the following:
install_name_tool -change /usr/local/opt/freetype/lib/libfreetype.6.dylib #executable_path/lib/libfreetype.6.dylib <executable_name_here>
Then it will stop looking for libfreetype.6.dylib in the location it was when you compiled the software, and instead search for it relative to your executable's location at run-time (in this case, in the sub-directory lib/).
In the end, this did not end up as easy as I originally thought. It works but there are caveats.
Building on the answers from Andon M. Coleman & Alexander Klimetschek, here is a script function which takes dylib names from Homebrew, copies them into the app bundle's Contents/Frameworks directory, and sets the executable's corresponding search path using install_name_tool. I noticed that sometimes the actual .dylib location is different from what the Xcode linker finds when putting together the executable and this handles that as well by querying the exe with otool -L.
The script below copies the .dylib for the liblo library into the app bundle. Adding new libs should be as simple as adding a new copy lib ###.dlyib line to the bottom of the script.
# exit on error
set -e
# paths
LOCALLIBS_PATH="$SRCROOT/libs"
HOMEBREW_PATH="/usr/local"
FRAMEWORKS_PATH="$BUILT_PRODUCTS_DIR/$FRAMEWORKS_FOLDER_PATH"
EXE_PATH="$BUILT_PRODUCTS_DIR/$EXECUTABLE_FOLDER_PATH/$EXECUTABLE_NAME"
# code signing identity
IDENTITY="$EXPANDED_CODE_SIGN_IDENTITY_NAME"
if [ "$IDENTITY" == "" ] ; then
IDENTITY="$CODE_SIGN_IDENTITY"
fi
# copy lib into Contents/Frameworks and set lib search path
# $1: library .dylib
# $2: optional path to libs folder, otherwise use Homebrew
copylib() {
LIB=$1
if [ "$2" == "" ] ; then
# use homebrew
LIB_PATH=$(find "$HOMEBREW_PATH" -type f -name $LIB)
else
# use given path
LIB_PATH="$2/$LIB"
fi
echo "$LIB:"
echo " $LIB_PATH -> $FRAMEWORKS_FOLDER_PATH/$LIB"
# copy lib, set permissions, and sign
mkdir -p "$FRAMEWORKS_PATH"
cp "$LIB_PATH" "$FRAMEWORKS_PATH/"
chmod 755 "$FRAMEWORKS_PATH/$LIB"
codesign --verify --force --sign "$IDENTITY" "$FRAMEWORKS_PATH/$LIB"
# set new path within executable
# note: grep --max 1 as multi-arch builds will print path once *per arch*
OLD=$(otool -L "$EXE_PATH" | grep --max 1 $LIB | awk '{print $1}')
NEW="#executable_path/../Frameworks/$LIB"
install_name_tool -change "$OLD" "$NEW" "$EXE_PATH"
# print to confirm new path
otool -L "$EXE_PATH" | grep $LIB
}
##### GO
# use lib from homebrew
copy lib liblo.7.dylib
# or use local lib
#copylib liblo.7.dylib "$LOCALLIBS_PATH/liblo/lib"
Add this as a Run Script build phase in the Xcode project. You can also save it as an external file and call it using sh within the Run Script phase with:
sh $PROJECT_DIR/path-to/copy-libs.sh
Example build report output:
liblo.7.dylib:
/usr/local/Cellar/liblo/0.29/lib/liblo.7.dylib ->> YOURAPP.app/Contents/Frameworks/liblo.7.dylib
#executable_path/../Frameworks/liblo.7.dylib (compatibility version 11.0.0, current version 11.0.0)
The $(find $HOMEBREW_PATH -type f -name $LIB) could probably be improved by following the .dylib symlink in /usr/local/lib instead.
UPDATE: You will probably also need to make sure the new dylibs are signed otherwise the code signing step will fail when building, at least it eventually did for me. Following this SO post, adding --deep to the Other Code Signing Flags option in the project Build Settings works.
There might be a better way to sign only the files that the script adds, but I didn't feel like digging into running codesign manually. From what I've read, however, --deep is not suggested by Apple for anything but temporary fixes, so it's probably not a best practice.
UPDATE 2: Running --deep didn't actually solve the problem and the app still would not run on other machines. In the end, I needed to code sign the copied .dylib manually which turned out to be relatively easy.
One note: I ran into an ambiguous "Mac Developer" identities error when running codesign which seemed to be confused by two certificates with the same name in the Keychain: ie. the current developer certificate and the previous expired developer certificate. Opening Keychain Access and deleting the expired certificate solved the problem.
UPDATE 3: Quoted path variable usage to fix paths with spaces.
UPDATE 4: This solution works fine for building apps for recent systems, but I ran into a code signing problem when running the app on macOS 10.10. Judging from this SO post, older macOS versions use a different codesign hash algorithm and the app would run fine on a 10.12 system but fail with a code signing error on 10.10. On both systems, codesign verified that everything was signed correctly.
You basically need to build the libraries with the -mmacosx-version-min set so codesign will be able to tell which algorithms to use to support the min and the recent macOS versions. I tried to pass custom CFLAGS/LDFLAGS to Homebrew while building liblo from source, but it's not possible without editing the brew formula. In the end, I decided to write a build script to build liblo from source myself and I modified the copy script so it can also take a local location. Now everything is finally working.
TLDR: Homebrew libs work great for initial development & testing, but building the libs manually works better for deployment.
Here is an automated script based on Andon's answer that will replace all #executable_path/../Frameworks/ with #executable_path/, so that all frameworks can be in the same folder as the executable. This also adapts the frameworks themselves, if they have dependencies on each other.
Beware, this only makes sense for command-line applications. If it's a regular OSX bundles app, one probably shouldn't change the "../Frameworks" convention.
Instructions
Add this as "Run Script" using Shell = /bin/sh at the end of your Xcode project Build Phases.
Also make sure to have the "Copy Files" step with a "Destination = Products Directory" for all the frameworks.
Script
# change the hardcoded ../Frameworks relative path that Xcode does by rewriting the binaries after the build
# check with:
# otool -L <binary>
# this is the new location
# make sure this is the same Destination the Frameworks are copied to in the "Copy Files" step
# the default "#executable_path/" would be Destination = Products Directory
NEW_FRAMEWORKS_PATH="#executable_path/"
# the one we want to replace
DEFAULT_FRAMEWORKS_PATH="#executable_path/../Frameworks/"
function change_binary {
local libpaths=`otool -L "$1" | grep "$DEFAULT_FRAMEWORKS_PATH" | tr '\t' ' ' | cut -d " " -f2`
local lib
for lib in $libpaths; do
if [ "$2" == "recursive" ]; then
local libbinary=`echo $lib | sed "s,$DEFAULT_FRAMEWORKS_PATH,,"`
change_binary "$libbinary"
fi
local newlib=`echo $lib | sed "s,$DEFAULT_FRAMEWORKS_PATH,$NEW_FRAMEWORKS_PATH,"`;
echo "changing library path in '$1': '$lib' => '$newlib'"
install_name_tool -change "$lib" "$newlib" "$1"
done
}
cd $BUILT_PRODUCTS_DIR
change_binary "$EXECUTABLE_NAME" recursive
Note that changing NEW_FRAMEWORKS_PATH to e.g. a relative subpath does not work, the script would need to become a bit more complex to handle this.
Result Structure
$BUILT_PRODUCTS_DIR/
executable
Some.framework/
Another.framework/
...
And otool -L executable would look like this:
executable:
#executable_path/Some.framework/Versions/A/Some (...)
#executable_path/Another.framework/Versions/A/Another (...)
...

Using install_name_tool what's going wrong?

I'm trying to change the install path of a dylib after it has been built. I use otool -L to check what the current path is. And then I do:
$ install_name_tool -change /my/current/path/libmine.dylib \
/my/new/path/libmine.dylib libmine.dylib
I don't get an error, but nothing changes. If I check the path again the old one is still there. Also the new path is a lot shorter then the old one, so no problem there, and I think the lib is even compiled with extra flag for more filepath space.
Any ideas?
The man page for install_name_tool says that -change is for dependencies. You're trying to change the name of the library itself.
Having just experimented, I found I couldn't change the name of a dylib that appears inside the dylib itself but I could change the names of other dependencies.
Having experimented more: install_name_tool -id newname file will do the trick.

Resources