How to debug "Failed to load the .dll"?

I have a CPlusPlusTOP which is fairly sophisticated in terms of dependencies – a number of dynamic and static lib dependencies.
The code doesn’t load in TouchDesigner with the “Failed to load the .dll” error.
I’m on macOS. How one can debug this error? I think I knew how but it’s been a while since the last time I did it.
I guess, I need at least the error code from the dlopen call that touch uses to load the plugin.
I can’t seem to find any useful output – tried Xcode debugger, Consol App with “TouchDesigner” filter, /var/log/system.log… :frowning:

Though I still couldn’t find the way to get more debug info on the reasons why the library couldn’t be loaded, I solved the problem.

First, I bundled all the dependencies (recursively) into <yourOP>.plugin/Contents/libs using dylibbundler tool (github.com/auriamg/macdylibbundler). For convenience, you can set up “Run Script” Build Phase in your Xcode project which runs it automatically on every Release build:

binary=${BUILT_PRODUCTS_DIR}/${EXECUTABLE_PATH}
curDir=`pwd`
frameworksDir=${BUILT_PRODUCTS_DIR}/${CONTENTS_FOLDER_PATH}/libs
depsDir=`realpath --relative-to=$curDir $frameworksDir`
bundler=dylibbundler

# run dylibbundler
if [ "$CONFIGURATION" = "Release" ]; then
    command -v $bundler >/dev/null 2>&1 || { echo >&2 "$bundler is required for bundling dependencies with the plugin."; exit 1; }
    printf '/usr/local/lib' | $bundler -od -b -x $binary -d $depsDir 2>&1 > /tmp/bundler.log
else
    echo "[bundler] Not bundling in Debug configuration"
fi

Few caveats here:
1/ dylibbundler may ask for paths for the dependencies it can not locate. You need to figure out where those dependencies are and pass their paths like in the script above (notice “printf” part)
2/ Make clean of the project before building, otherwise if dylibbundler will try to bundle already processed binary it’ll stuck in a loop. I do it automatically in pre-build action for my build configuration using this script:

binary=${BUILT_PRODUCTS_DIR}/${EXECUTABLE_PATH}

# clean old binary
if [ "$CONFIGURATION" = "Release" ]; then
rm $binary 2> /dev/null
fi

Second, all those copied libraries must be code signed. To do that, you can use codesign tool or add another “Run Script” Build Phase after the previous one, which looks like this:

if [ "$CONFIGURATION" = "Release" ]; then

    libsDir=${BUILT_PRODUCTS_DIR}/${CONTENTS_FOLDER_PATH}/libs
    fwDir=${BUILT_PRODUCTS_DIR}/${CONTENTS_FOLDER_PATH}/Frameworks
    libs=`ls $libsDir/* $fwDir/*`

    echo "This will sign all libs in $libsDir and $fwDir"

    for l in $libs; do
        codesign -f -v -s $EXPANDED_CODE_SIGN_IDENTITY $l
    done;
fi

Once this all done, you can start TouchDesigner and load you library successfully.

Hi,
I had the same failure and wanted to share my findings here as well.
I needed to implement a library (dylib) which was dependend on a second library (dylib).
First of all i suggest to use otool to to figure out how they are linked to each other, and to see how they think they are thought to be placed.

otool -L PathAndNameOfYourLibraryOrComilledSource

a typical result could be following:
@executable_path/lib/librcdev.1.dylib (compatibility version 1.6.0, current version 1.6.0)
@executable_path/lib/libusb-1.0.0.dylib (compatibility version 2.0.0, current version 2.0.0)
/usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 307.5.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1238.50.2)

While the last two lines refere to libraries in the system, and we generally don’t need to care about them, the first one is the library itself - and could not be changed, The second one is the library it is depending on. @executable_path is a reference on MacOS to the position of the main programs executable in our case TouchDesigner. So the line means that our libusb library is looked for and therefore should be placed in the TouchDesigner app (TouchDesigner.app/Contents/MacOS/lib).

However, there is another tool install_name_tool which can be used to change the path. Also ther is an other relative path reference in MacOS allowing to reference to the location of an executable plugin what is the case here @loader_path (more info here: https://wincent.com/wiki/@executable_path,@load_path_and@rpath.
To change the path you can use something like the following line:

install_name_tool -change @executable_path/lib/libusb-1.0.0.dylib @loader_path/…/Frameworks/libusb-1.0.0.dylib “$TARGET_BUILD_DIR/$TARGET_NAME.plugin/Contents/Frameworks/librcdev.1.dylib”;

the command: install_name_tool -change
the first path @executable_path/lib/libusb-1.0.0.dylib is the original resp. the one we want to change, secondly you enter the path you want to point to instead @loader_path/…/Frameworks/libusb-1.0.0.dylib
and thirdly you need to point to the file you actually want to change. In my case it uses references from Xcode as i use the tools in a RunScript when compiling.

!!!
The dylib I included had another dylib it refered to. However, changing the path in the first dylib to the second dylib with install_name_tool worked basically. But not if the second was placed at

@loader_path/lib/

But then it worked when changing the path to @loader_path/…/Frameworks/

Of course the files were placed correspondingly.

I don’t know why it makes a difference. But somehow it does.

The second lib I use is libusb.1.0.0.dylib which is also a standard lib in the Frameworks folder in the TouchDesigner.app. Referring to this file directly (@executable_path/…/Frameworks/) did work as well, so I would not need to include it again in my plugin.

To sum up…

Placing the second library (libusb.1.0.0.dylib)

It did not work with

myPlugin.plugin/Contents/MacOS/lib @loader_path/lib/

but worked fine with the file placed in

myPlugin.plugin/Contents/Frameworks @loader_path/…/Frameworks

Or referring to the file included in TD

TouchDesigner.app/Contents/Frameworks @executable_path/…/Frameworks

The main library is placed.

myPlugin.plugin/Contents/Frameworks @loader_path/…/Frameworks

It also worked when placing all libraries in (but that does not really make sense for a deployable plugin)

TouchDesigner.app/Contents/MacOS/lib

I conclude that the relative paths referred to with @loader_pat h need to be in a folder parallel to the compiled file in MacOS but not in a folder a step deeper.

I’ll make an entry in the forum in the upcoming days, in the hope to prevent others from the same issues.

Here the hole script i use inside of Xcode under Build Phases as Run Script:

copy the libraries into the wished folder.

rsync -aved api-lib/osx_x86_64/librcdev.dylib “$TARGET_BUILD_DIR/$PRODUCT_NAME.plugin/Contents/Frameworks/”;
rsync -aved api-lib/osx_x86_64/librcdev.1.dylib “$TARGET_BUILD_DIR/$PRODUCT_NAME.plugin/Contents/Frameworks/”;
rsync -aved api-lib/osx_x86_64/librcdev.1.6.dylib “$TARGET_BUILD_DIR/$PRODUCT_NAME.plugin/Contents/Frameworks/”;
rsync -aved api-lib/osx_x86_64/librcdev.1.6.0.dylib “$TARGET_BUILD_DIR/$PRODUCT_NAME.plugin/Contents/Frameworks/”;
rsync -aved api-lib/osx_x86_64/libusb-1.0.0.dylib “$TARGET_BUILD_DIR/$PRODUCT_NAME.plugin/Contents/Frameworks/”;

redirect the libraries internal path

install_name_tool -change @executable_path/lib/librcdev.1.dylib @loader_path/…/Frameworks/librcdev.1.dylib “$TARGET_BUILD_DIR/$PRODUCT_NAME.plugin/Contents/MacOS/$PRODUCT_NAME”;

install_name_tool -change @executable_path/lib/libusb-1.0.0.dylib @loader_path/…/Frameworks/libusb-1.0.0.dylib “$TARGET_BUILD_DIR/$TARGET_NAME.plugin/Contents/Frameworks/librcdev.1.dylib”;

signing the libraries

frmDir=${BUILT_PRODUCTS_DIR}/${CONTENTS_FOLDER_PATH}/Frameworks
libDir=${BUILT_PRODUCTS_DIR}/${CONTENTS_FOLDER_PATH}/MacOS/lib
libs=ls $libDir/* $frmDir/*

for l in $libs; do
codesign -f -v -s $EXPANDED_CODE_SIGN_IDENTITY $l
done;

I hope this helps others to find a solution faster than me. Thanks to Eric and Malcom from Derviative Support for helping me.

Are there issues with developing on Mac 12.1 that make it too strict security-wise? I’m skipping the steps related to code-signing, and I’m using a CMake procedure:

cmake .. -G "Xcode"
xcodebuild -configuration Release -project ChucKDesignerCHOP.xcodeproj

This creates the Plugins folder and items:

  • ChucKDesignerCHOP.plugin
  • libChucKDesignerShared.dylib

I’ll spare you the details, but it’s clear from otool -L that ChucKDesignerCHOP depends on libChucKDesignerShared.dylib and libChucKDesignerShared.dylib depends on only built-in things. In TouchDesigner I get a warning about loading a plugin for the first time. With the CPlusPlus CHOP set to Plugins/ChucKDesignerCHOP.plugin I get “Error: Failed to load the .dll”. However, this same CMake procedure works well on Windows which produces Plugins/ChucKDesignerCHOP.dll. I tried various renaming tricks with install_name_tool -change with no luck.

Update:
Still no luck after code-signing:
codesign --force --deep --sign "Apple Development: example@example.com (ABCDE12345)" ChucKDesignerCHOP.plugin/Contents/MacOS/ChucKDesignerCHOP

You can find your own apple certificate by opening Keychain Access and looking at the login panel.

Then I verify

codesign -vvvv ChucKDesignerCHOP.plugin/Contents/MacOS/ChucKDesignerCHOP
--prepared:/Users/davidbraun/Github/ChucKDesigner/Plugins/ChucKDesignerCHOP.plugin/Contents/Frameworks/libChucKDesignerShared.dylib
--validated:/Users/davidbraun/Github/ChucKDesigner/Plugins/ChucKDesignerCHOP.plugin/Contents/Frameworks/libChucKDesignerShared.dylib
ChucKDesignerCHOP.plugin/Contents/MacOS/ChucKDesignerCHOP: valid on disk
ChucKDesignerCHOP.plugin/Contents/MacOS/ChucKDesignerCHOP: satisfies its Designated Requirement

I tried this with several install_name_tool -change adjustments:

  • @loader_path/libChucKDesignerShared.dylib
  • @loader_path/../libChucKDesignerShared.dylib
  • @loader_path/../../libChucKDesignerShared.dylib
  • @loader_path/../../../libChucKDesignerShared.dylib
  • @loader_path/../Frameworks/libChucKDesignerShared.dylib
  • @loader_path/../../Frameworks/libChucKDesignerShared.dylib
  • /abs/path/to/libChucKDesignerShared.dylib

And I copied libChucKDesignerShared.dylib to a new Frameworks folder at ChucKDesignerCHOP.plugin/Contents/Frameworks.

I get the warning about the plugin loading for the first time but then the same error message. Using TouchDesigner macOS 2021.15800.

Above I forgot to say that I’m on an M1 mac, and that probably explains the solution. Instead of
cmake .. -G "Xcode"
I needed to do
cmake .. -G "Xcode" -DCMAKE_OSX_ARCHITECTURES=x86_64

I didn’t even have to use install_name_tool -change after building.

I think it was defaulting to arm64. Maybe this will change in future builds of TouchDesigner and I can let it be the default.