My goal is dynamically linked to libraries from brew. How to bind for distribution?

I built an audio plugin. I am targeting .app and .component .

I am dynamically linking to the brew -installed library, libfluidsynth .

I copied libfluidsynth to .app / .component .
I used install_name_tool to re-link the binary to point to the associated libfluidsynth .

libfluidsynth depends on glib , gthread , intl .
I copied these libraries into a package, re-linked libfluidsynth to prefer a bundled copy.
I also did the same for these libraries and their dependencies.

Here's a quick look at what it looks like:

 ls /Users/me/git/juicysfplugin/Builds/MacOSX/build/Release/juicysfplugin.app/Contents/Frameworks libfluidsynth.1.7.1.dylib libglib-2.0.0.dylib libgthread-2.0.0.dylib libintl.8.dylib libpcre.1.dylib otool -L \ /Users/me/git/juicysfplugin/Builds/MacOSX/build/Release/juicysfplugin.app/Contents/MacOS/juicysfplugin \ /Users/me/git/juicysfplugin/Builds/MacOSX/build/Release/juicysfplugin.app/Contents/Frameworks/* \ | grep -vE '\t(/System/Library|/usr/lib)' /Users/me/git/juicysfplugin/Builds/MacOSX/build/Release/juicysfplugin.app/Contents/MacOS/juicysfplugin: @executable_path/../Frameworks/libfluidsynth.1.7.1.dylib (compatibility version 1.0.0, current version 1.7.1) /Users/me/git/juicysfplugin/Builds/MacOSX/build/Release/juicysfplugin.app/Contents/Frameworks/libfluidsynth.1.7.1.dylib: @loader_path/../Frameworks/libfluidsynth.1.dylib (compatibility version 1.0.0, current version 1.7.1) @loader_path/../Frameworks/libgthread-2.0.0.dylib (compatibility version 5401.0.0, current version 5401.3.0) @loader_path/../Frameworks/libglib-2.0.0.dylib (compatibility version 5401.0.0, current version 5401.3.0) @loader_path/../Frameworks/libintl.8.dylib (compatibility version 10.0.0, current version 10.5.0) /Users/me/git/juicysfplugin/Builds/MacOSX/build/Release/juicysfplugin.app/Contents/Frameworks/libglib-2.0.0.dylib: @loader_path/../Frameworks/libglib-2.0.0.dylib (compatibility version 5401.0.0, current version 5401.3.0) @loader_path/../Frameworks/libpcre.1.dylib (compatibility version 4.0.0, current version 4.9.0) @loader_path/../Frameworks/libintl.8.dylib (compatibility version 10.0.0, current version 10.5.0) /Users/me/git/juicysfplugin/Builds/MacOSX/build/Release/juicysfplugin.app/Contents/Frameworks/libgthread-2.0.0.dylib: @loader_path/../Frameworks/libgthread-2.0.0.dylib (compatibility version 5401.0.0, current version 5401.3.0) @loader_path/../Frameworks/libglib-2.0.0.dylib (compatibility version 5401.0.0, current version 5401.3.0) @loader_path/../Frameworks/libpcre.1.dylib (compatibility version 4.0.0, current version 4.9.0) @loader_path/../Frameworks/libintl.8.dylib (compatibility version 10.0.0, current version 10.5.0) /Users/me/git/juicysfplugin/Builds/MacOSX/build/Release/juicysfplugin.app/Contents/Frameworks/libintl.8.dylib: @loader_path/../Frameworks/libintl.8.dylib (compatibility version 10.0.0, current version 10.5.0) /Users/me/git/juicysfplugin/Builds/MacOSX/build/Release/juicysfplugin.app/Contents/Frameworks/libpcre.1.dylib: @loader_path/../Frameworks/libpcre.1.dylib (compatibility version 4.0.0, current version 4.9.0) 

This worked great for .app . Here's a look at the file ( opensnoop | grep 'dylib' ):

 EXECNAME PATH juicysfplugin juicysfplugin.app/Contents/MacOS/../Frameworks/libfluidsynth.1.7.1.dylib juicysfplugin juicysfplugin.app/Contents/MacOS/../Frameworks/../Frameworks/libgthread-2.0.0.dylib juicysfplugin juicysfplugin.app/Contents/MacOS/../Frameworks/../Frameworks/libglib-2.0.0.dylib juicysfplugin juicysfplugin.app/Contents/MacOS/../Frameworks/../Frameworks/libintl.8.dylib juicysfplugin juicysfplugin.app/Contents/MacOS/../Frameworks/../Frameworks/../Frameworks/libpcre.1.dylib 

.app only searches for dylib inside its packed Frameworks folder. Perfect.

Then I made the same copies and duplicate links to my .component target. This did not work.

I load .component into the host of the audio plugin and check if the file opens:

 EXECNAME PATH Plugin Host /Users/me/Library/Audio/Plug-Ins/Components/juicysfplugin.component/Contents/MacOS/juicysfplugin Plugin Host /usr/local/lib/libfluidsynth.1.7.1.dylib Plugin Host /usr/local/opt/glib/lib/libgthread-2.0.0.dylib Plugin Host /usr/local/opt/glib/lib/libglib-2.0.0.dylib Plugin Host /usr/local/opt/gettext/lib/libintl.8.dylib Plugin Host /usr/local/opt/pcre/lib/libpcre.1.dylib 

He is looking for libraries under /usr/local . What for? As a health check, I used otool to confirm that I really connected correctly:

 otool -L \ /Users/me/Library/Audio/Plug-Ins/Components/juicysfplugin.component/Contents/MacOS/juicysfplugin \ /Users/me/Library/Audio/Plug-Ins/Components/juicysfplugin.component/Contents/Frameworks/* \ | grep -vE '\t(/System/Library|/usr/lib)' /Users/me/Library/Audio/Plug-Ins/Components/juicysfplugin.component/Contents/MacOS/juicysfplugin: @executable_path/../Frameworks/libfluidsynth.1.7.1.dylib (compatibility version 1.0.0, current version 1.7.1) /Users/me/Library/Audio/Plug-Ins/Components/juicysfplugin.component/Contents/Frameworks/libfluidsynth.1.7.1.dylib: @loader_path/../Frameworks/libfluidsynth.1.dylib (compatibility version 1.0.0, current version 1.7.1) @loader_path/../Frameworks/libgthread-2.0.0.dylib (compatibility version 5401.0.0, current version 5401.3.0) @loader_path/../Frameworks/libglib-2.0.0.dylib (compatibility version 5401.0.0, current version 5401.3.0) @loader_path/../Frameworks/libintl.8.dylib (compatibility version 10.0.0, current version 10.5.0) /Users/me/Library/Audio/Plug-Ins/Components/juicysfplugin.component/Contents/Frameworks/libglib-2.0.0.dylib: @loader_path/../Frameworks/libglib-2.0.0.dylib (compatibility version 5401.0.0, current version 5401.3.0) @loader_path/../Frameworks/libpcre.1.dylib (compatibility version 4.0.0, current version 4.9.0) @loader_path/../Frameworks/libintl.8.dylib (compatibility version 10.0.0, current version 10.5.0) /Users/me/Library/Audio/Plug-Ins/Components/juicysfplugin.component/Contents/Frameworks/libgthread-2.0.0.dylib: @loader_path/../Frameworks/libgthread-2.0.0.dylib (compatibility version 5401.0.0, current version 5401.3.0) @loader_path/../Frameworks/libglib-2.0.0.dylib (compatibility version 5401.0.0, current version 5401.3.0) @loader_path/../Frameworks/libpcre.1.dylib (compatibility version 4.0.0, current version 4.9.0) @loader_path/../Frameworks/libintl.8.dylib (compatibility version 10.0.0, current version 10.5.0) /Users/me/Library/Audio/Plug-Ins/Components/juicysfplugin.component/Contents/Frameworks/libintl.8.dylib: @loader_path/../Frameworks/libintl.8.dylib (compatibility version 10.0.0, current version 10.5.0) /Users/me/Library/Audio/Plug-Ins/Components/juicysfplugin.component/Contents/Frameworks/libpcre.1.dylib: @loader_path/../Frameworks/libpcre.1.dylib (compatibility version 4.0.0, current version 4.9.0) 

I thought I connected this correctly. I used the exact same script (it is automated and parameterized). What am I doing wrong? How is the host of the audio plugin for finding dependencies in /usr/local known? Why were my library links ignored?

I released binaries for: https://github.com/Birch-san/juicysfplugin/releases/tag/1.0.1

My relinking script is here: https://github.com/Birch-san/juicysfplugin/blob/master/Builds/MacOSX/relink-build-for-distribution.sh

The main difference between using .app and .component :

  • .app is standalone
  • .component is an Audio Unit plugin that you load into the DAW / audio host module.

So, is it possible the resolution of the runtime dependency is different when the parent process is responsible for loading our executable?

+5
source share
1 answer

Ok, I got his job .

macOS boot time binding is documented on man dyld .

The problem is that I said that my binary is looking for libraries regarding @executable_path .

This works great for .app, since .app binary is an executable.

But for my .vst and .component plugins, the binary is loaded into another executable: the host of the audio plugin.

So, if we want to find a library relative to our binary ( juicysfplugin.component/Contents/MacOS/juicysfplugin ) , we need to use @loader_path , not @executable_path .


Now, to other secrets ... why the download compiler ignored my installation path ( @executable_path/../Frameworks/libfluidsynth.1.7.1.dylib ) and instead look for find fluidsynth under /usr/local/lib/libfluidsynth.1.7.1.dylib ?

This is because of DYLD_FALLBACK_LIBRARY_PATH !

It is used as the default location for libraries not found on the installation path. The default value is $(HOME)/lib:/usr/local/lib:/lib:/usr/lib .

I believe that the library could not be found on the installation path. He then looked up the sheet file name - libfluidsynth.1.7.1.dylib - in several directories, including /usr/local/lib (which was successfully executed).

Why didn’t I see unsuccessful attempts to find files in opensnoop? It probably uses a different syscall than open / open_nocancel / open_extended . For example, running stat in a file does not appear in opensnoop.

It is also possible that load time binding is done using the dyld process. SIP will prevent DTrace from joining this process.

+2
source

Source: https://habr.com/ru/post/1275834/


All Articles