X Tutup
Skip to content

DLLImport fails on Godot on Linux #768

@sandsalamand

Description

@sandsalamand

I'm using Steamworks.NET Standalone version 2025.163.0 on Linux with Godot. The project builds, but at runtime, I get errors:

  ERROR: [Steamworks.NET] Could not load [lib]steam_api.dll/so/dylib. It's likely not in the correct location. Refer to the README for more details.

  ERROR: System.DllNotFoundException: Unable to load shared library 'steam_api' or one of its dependencies. In order to help diagnose loading problems, consider using a tool like strace. If you're using glibc, consider setting the LD_DEBUG environment variable:

  ERROR: /usr/lib/dotnet/shared/Microsoft.NETCore.App/9.0.11/steam_api.so: cannot open shared object file: No such file or directory
  ERROR: /usr/lib/dotnet/shared/Microsoft.NETCore.App/9.0.11/libsteam_api.so: cannot open shared object file: No such file or directory
  ERROR: /usr/lib/dotnet/shared/Microsoft.NETCore.App/9.0.11/steam_api: cannot open shared object file: No such file or directory
  ERROR: /usr/lib/dotnet/shared/Microsoft.NETCore.App/9.0.11/libsteam_api: cannot open shared object file: No such file or directory 

I created a Post-Build copy script for libsteam_api.so and steam_appid.txt, and I verified that they are being copied into the output directory.

The problem:
.NET Core uses the native function dlopen(NULL, RTLD_LAZY) to search for assemblies specified by DllImportAttribute. The NULL argument tells dlopen to include the main program, but since Steamworks.NET.Dll is loaded in-memory by Godot rather than directly from the file, Assembly.Location is null. Therefore, dlopen does not search the directory that Steamworks.NET.Dll is in. It falls back to searching system directories, and it does not find libsteam_api.so.

Temp workaround:
The LD_LIBRARY_PATH environment variable can be set to the project root, which tells dlopen where to search.

Real solutions:

  1. Add [DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory)] above the DLLImport lines in Steamworks.NET.

  2. SteamManager.cs can be modified (tested and working on Godot Linux, untested on other platforms):

    private void HookSteamDllResolver()
	{
		NativeLibrary.SetDllImportResolver(typeof(SteamAPI).Assembly, (libraryName, assembly, searchPath) =>
		{
			// Ignore assemblies that are not the Steam API library (might be unnecessary)
			if (libraryName != "steam_api") return IntPtr.Zero;

			// Determine the correct filename based on the platform
			string nativeFile = "steam_api64.dll"; // Default Windows
			
			if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
			{
				nativeFile = "libsteam_api.so";
			}
			else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
			{
				nativeFile = "libsteam_api.dylib";
			}

			// Check next to the executing assembly
			string localPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, nativeFile);
			if (File.Exists(localPath))
			{
				GD.Print("Found it in executing assembly directory");
				return NativeLibrary.Load(localPath);
			}

			// Failed to find it
			return IntPtr.Zero;
		});
	}

	public override void _EnterTree()
	{
		if (Instance == null)
			Instance = this;
		else
		{
			QueueFree();
			return;
		}

		HookSteamDllResolver();

		...
	}
  1. Add a LinkerArg in the Steamworks.NET .csproj to tell Steamworks.NET.dll to search its own directory for other shared libraries. This might work, but I've heard that the Steamworks.NET build system is extremely complicated, so I'm uncertain of how this would fit in.
<ItemGroup>
  <LinkerArg Include="-Wl,-rpath,'$ORIGIN'" />
</ItemGroup>

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      X Tutup