using System;
using System.Runtime.InteropServices;
///
/// This class uses and resolves libdl, which dependning on the OS comes in a libdl.so or libdl.so.2 flavor.
///
static class LibDl
{
static IntPtr load(string name) => libDl.dlopen(name, rtld_now);
static void close(IntPtr ptr) => libDl.dlclose(ptr);
/// Gets most recent load error.
public static string GetError()
{
var error = libDl.dlerror();
if (IntPtr.Zero == error) return null;
return Marshal.PtrToStringAuto(error);
}
static void clearError() => libDl.dlerror();
const int rtld_now = 2;
interface ILibDL
{
IntPtr dlopen(string filename, int flags);
int dlclose(IntPtr handle);
IntPtr dlerror();
IntPtr dlsym(IntPtr handle, string symbol);
}
///
/// libc5 (shipped with Ubuntu 20.04 and older) and older contains this libdl version, but it is not shipped with libc6
///
class libdl1 : ILibDL
{
private const string libName = "libdl.so";
[DllImport(libName)]
static extern IntPtr dlopen(string fileName, int flags);
[DllImport(libName)]
static extern int dlclose(IntPtr handle);
[DllImport(libName)]
static extern IntPtr dlerror();
[DllImport(libName)]
static extern IntPtr dlsym(IntPtr handle, string symbol);
IntPtr ILibDL.dlopen(string fileName, int flags) => dlopen(fileName, flags);
int ILibDL.dlclose(IntPtr handle) => dlclose(handle);
IntPtr ILibDL.dlerror() => dlerror();
IntPtr ILibDL.dlsym(IntPtr handle, string symbol) => dlsym(handle, symbol);
}
///
/// libc6 (shipped with Ubuntu 22.04) only ships libdl.so.2. This seems to be the only way to resolve it.
/// Trying "libdl" or just "dl" also does not seem to resolve to libdl.so.2.
///
class libdl2 : ILibDL
{
private const string libName = "libdl.so.2";
[DllImport(libName)]
static extern IntPtr dlopen(string fileName, int flags);
[DllImport(libName)]
static extern int dlclose(IntPtr handle);
[DllImport(libName)]
static extern IntPtr dlerror();
[DllImport(libName)]
static extern IntPtr dlsym(IntPtr handle, string symbol);
IntPtr ILibDL.dlopen(string fileName, int flags) => dlopen(fileName, flags);
int ILibDL.dlclose(IntPtr handle) => dlclose(handle);
IntPtr ILibDL.dlerror() => dlerror();
IntPtr ILibDL.dlsym(IntPtr handle, string symbol) => dlsym(handle, symbol);
}
///
/// MacOS calls it libdl
///
class libdlMac : ILibDL
{
private const string libName = "libdl";
[DllImport(libName)]
static extern IntPtr dlopen(string fileName, int flags);
[DllImport(libName)]
static extern int dlclose(IntPtr handle);
[DllImport(libName)]
static extern IntPtr dlerror();
[DllImport(libName)]
static extern IntPtr dlsym(IntPtr handle, string symbol);
IntPtr ILibDL.dlopen(string fileName, int flags) => dlopen(fileName, flags);
int ILibDL.dlclose(IntPtr handle) => dlclose(handle);
IntPtr ILibDL.dlerror() => dlerror();
IntPtr ILibDL.dlsym(IntPtr handle, string symbol) => dlsym(handle, symbol);
}
static readonly ILibDL libDl;
static LibDl()
{
if (OpenTap.OperatingSystem.Current == OpenTap.OperatingSystem.MacOS)
{
libDl = new libdlMac();
}
else
{
try
{
libDl = new libdl2();
// call dlerror to ensure library is resolved
libDl.dlerror();
}
catch (DllNotFoundException)
{
libDl = new libdl1();
}
}
}
public static IntPtr Sym(IntPtr lib, string name) => libDl.dlsym(lib, name);
public static IntPtr Load(string name)
{
clearError();
return load(name);
}
public static void Unload(IntPtr lib)
{
if (lib == IntPtr.Zero)
throw new NullReferenceException();
close(lib);
}
}