What can cause a .NET Core binary to load a native library, then throw a DllNotFoundException?

1.5k views Asked by At

I'm trying to run an SFML.NET game on MacOS having ported the game to .NET Core.

I have been unable to get SFML to load its native dependencies correctly at runtime, so I set up a minimal test example to try to isolate the issue, and still get the following error output (I have set DYLD_PRINT_LIBRARIES):

dyld: loaded: /System/Library/Frameworks/CoreData.framework/Versions/A/CoreData
dyld: loaded: /System/Library/Frameworks/ServiceManagement.framework/Versions/A/ServiceManagement
dyld: loaded: /System/Library/PrivateFrameworks/BackgroundTaskManagement.framework/Versions/A/BackgroundTaskManagement
dyld: loaded: /usr/lib/libxslt.1.dylib
dyld: loaded: /usr/local/share/dotnet/shared/Microsoft.NETCore.App/2.2.0/libclrjit.dylib
dyld: loaded: /usr/local/share/dotnet/shared/Microsoft.NETCore.App/2.2.0/System.Globalization.Native.dylib
dyld: loaded: /usr/local/share/dotnet/shared/Microsoft.NETCore.App/2.2.0/System.Native.dylib
Hello World!
dyld: loaded: /Users/ashley/RiderProjects/SFML.NET Test/SFML.NET Test/bin/Debug/netcoreapp2.2/libcsfml-window.dylib
dyld: unloaded: /Users/ashley/RiderProjects/SFML.NET Test/SFML.NET Test/bin/Debug/netcoreapp2.2/libcsfml-window.dylib
dyld: loaded: libcsfml-window.dylib
dyld: unloaded: libcsfml-window.dylib

Unhandled Exception: System.DllNotFoundException: Unable to load shared library 'libcsfml-window.dylib' or one of its dependencies. In order to help diagnose loading problems, consider setting the DYLD_PRINT_LIBRARIES environment variable: dlopen(liblibcsfml-window.dylib.dylib, 1): image not found
   at SFML.Window.VideoMode.sfVideoMode_getDesktopMode()
   at SFML.Window.VideoMode.get_DesktopMode() in /Users/ashley/SFML.Net/src/Window/VideoMode.cs:line 86
   at SFML.NET_Test.Program.Main(String[] args) in /Users/ashley/RiderProjects/SFML.NET Test/SFML.NET Test/Program.cs:line 12
dyld: unloaded: /usr/local/share/dotnet/shared/Microsoft.NETCore.App/2.2.0/libhostpolicy.dylib

The code I am trying to run is as follows:

using System;
using SFML.Graphics;
using SFML.Window;

namespace SFML.NET_Test
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");
            RenderWindow window = new RenderWindow(VideoMode.DesktopMode, "Test");
            while (true)
            {
            }
        }
    }
}

I have copies of the relevant native libraries in the running directory and the executable bin (this isn't a permanent set-up -- just while experimenting) and it seems to try to load both in sequence, before giving up and unloading them.

Given this, I'm basically wondering what can lead to this occurring?

I suspect it might indicate my library is the wrong version? (I've tried CSFML 2.2, 2.3, 2.5, so for my sanity's sake I hope this isn't the case) Or maybe there are missing dependencies that it's trying and failing to load? Is there any way to get further debug information out of dyld?

I've tried searching around about this, but it's quite difficult to find any useful resources so I'd appreciate pointers from people who understand the runtime linking system better.

I'm not entirely sure what extra information will be useful for helping answer this. My hope is that there's a specific, well-defined set of scenarios in which this behaviour can surface, which I can apply to fix my specific problem. Otherwise, I'm very happy to provide any additional information that helps.

My .NET Core version is 2.2.101

1

There are 1 answers

0
l'L'l On BEST ANSWER

After the discussion in the comments it appears the pre-built binaries for SMFL have incorrect paths and filenames set within them. These type of issues can generally be solved by otool and install name tool utilities to inspect the binaries and then change the paths/name of the libraries that need to be loaded.

An example with otool -l might look like this:

$ otool -l /path/to/binary
Load command 2 <-- OLD
          cmd LC_ID_DYLIB
      cmdsize 40
         name libtest.dylib (offset 24)
   time stamp 1 Wed Dec 31 18:00:01 1969

To change the path you could do:

$ install_name_tool -id "@loader_path/../libtest.dylib" libtest.dylib 

which would give you a new path such as:

Load command 2 <-- NEW
          cmd LC_ID_DYLIB
      cmdsize 64
         name @loader_path/../libtest.dylib (offset 24)

If you wanted to use rpath instead you could do:

$ install_name_tool -id "@rpath/libtest.dylib" libtest.dylib

I answered a previous question regarding rpath and loader_path which goes into more detail on the subject. Every scenario is potentially different in the case of loading dynamic libraries, so it really comes down to what you find out by investigating via otool -l.