macos_dylib_image

in Android, MacOS, Tutorials, UE4

MacOS dynamic library linking

This is only a guide to fix some problems related with the dynamic library linking that we can found in MacOS platform and some issues that can show up using Unreal Engine 4 with this platform too.

With the current version of Unreal Engine 4 (4.25) we can package and use the editor with dynamic libraries with no problem. We can also use plugins that load and use dynamic thirdparty libraries.

But sometimes we want to use a local copy of a plugin for our project, if we want to modify a plugin and use it instead of the plugin downloaded from the marketplace we can do it.

To do that we need to convert our project in a C++ Project and add a Plugin folder to include a copy of the Plugin that can be found in the Engine path.

project_plugin

Now our local plugin copy will be compiled along with the project.

This technique can solve some problems related with code-based plugin and packaging process of UE4, for example, packaging for Android and arm64 architecture with code-based plugins result in this error when the packaged game is launched.

arm64
Android error capture
Plugin ____ failed to load because module ____ could not be found. 
Please ensure the plugin is properly installed, otherwise consider disabling the plugin for this project.

Using a copy of the plugin inside the project folder solves that problem.

MacOS platform

But, what happens with MacOS platform. Well, ironically, the situation is the opposite. Using the packaging with the plugin from the Engine path go fine, but using a copy of the plugin inside the project results in a library linkage error.

If we view the log of a failed execution we can find something like that:

Application Specific Information:
dyld: launch, loading dependent libraries

Dyld Error Message:
Library not loaded: @rpath/libSDL2.dylib
Referenced from: /Users/USER/Downloads/*/AA_MacLibrary.app/Contents/MacOS/AA_MacLibrary
Reason: image not found

The reason of this problem is that the library can not be found in any the search paths defined in the .app.

An .app file is just a packaged folder , so we can browse their content with the file explorer and see the structure of the packaged game. Here we have an example:

content_browser

We can see that the local plugin has been copied inside the packaged game, so the problem must be in the libraries search paths.

To see the search paths defined for our package we can use the otool command in a console window with the path to the executable included in the .app, with the “-l” option this command will display the load commands. For the previous example:

otool -l ./pathToApp/AA_MacLibrary.app/Contents/MacOS/AA_MacLibrary

It will display a lot of information, but we want to see only some sections:

Load command 13
          cmd LC_LOAD_DYLIB
      cmdsize 48
         name @rpath/libSDL2.dylib (offset 24)
   time stamp 2 Thu Jan  1 01:00:02 1970
      current version 1.0.0
compatibility version 1.0.0

The first one (LC_LOAD_DYLIB) is the section that define the path used to load the library.

Unreal Engine tool uses the @rpath method to specify the paths to the dynamic libraries. With rpath the executable will search the library inside all the paths included in the LC_RPATH section. So what can find in that section:

Load command 51
          cmd LC_RPATH
      cmdsize 32
         path @loader_path/ (offset 12)
Load command 52
          cmd LC_RPATH
      cmdsize 32
         path @executable_path/ (offset 12)
Load command 53
          cmd LC_RPATH
      cmdsize 40
         path @executable_path/../../../ (offset 12)
Load command 54
          cmd LC_RPATH
      cmdsize 112
         path @loader_path/../../../../../Plugins/Runtime/MacLibraryLoader/Source/Thirdparty/SDL2/Libraries/Mac (offset 12)
Load command 55
          cmd LC_RPATH
      cmdsize 112
         path @loader_path/../../../../../Plugins/Runtime/MacLibraryLoader/Source/Thirdparty/SDL2/Libraries/Mac (offset 12)

The @loader_path is resolved as the path to the executable inside the .app so for example it is like /AA_MacLibrary.app/Contents/MacOS/

Now we can see that there is two search paths that can be a candidate for our library, and both are identical, this is already a clue that something goes wrong during the packaging process.

If we look more closely to the path, we realize that this path is outside the packaged game so, it will no work unless the machine that runs the game has the plugin folder in the correct place.

This path is ok while we are on a development machine using the Unreal Editor, but no in a packaged game.

For our example a correct path will be:

@loader_path/../UE4/AA_MacLibrary/Plugins/Marketplace/AudioAnalyzer/Source/Thirdparty/SDL2/Libraries/Mac

This path points to the Plugin folder included with the game during the packaged process, we can see that using the file explorer.

Fortunately, we can add more paths on a .app file without repackage the game again.

To do that we can use the command install_name_tool with -add_rpath option

install_name_tool -add_rpath (SEARCH_PATH) (APP_PATH)

For our example:

install_name_tool -add_rpath @loader_path/../UE4/AA_MacLibrary/Plugins/Marketplace/AudioAnalyzer/Source/Thirdparty/SDL2/Libraries/Mac ./AA_MacLibrary.app/Contents/MacOS/AA_MacLibrary

We can also remove a search path for the list using the option -delete_rpath instead.

Now our game can find the library inside the .app and runs correctly.

This workaround can be useful until they fix the MacToolChain.cs file to cover this case.

Support this blog!

For the past year I've been dedicating more of my time to the creation of tutorials, mainly about game development. If you think these posts have either helped or inspired you, please consider supporting this blog. Thank you so much for your contribution!

Write a Comment

Comment