dlsym returns NULL 其中 GetProcAddress returns 有效 IntPtr

dlsym returns NULL where GetProcAddress returns valid IntPtr

在制作跨平台游戏时,我发现需要一个跨平台 DLL 链接器,我的 DLL 链接器 class 似乎“符合标准”,但它只适用于 Windows机器,而它被设计为在 Window 和 Linux 机器上工作。

这是由于 dlsym( IntPtr handle, string symbol ) 返回 IntPtr.ZeroGetProcAddress( IntPtr hModule, string lpProcName ) returns 指向所需符号的有效 IntPtr

代码

public static class DLL
{
#region DllImport
[DllImport( "kernel32.dll" )]
private static extern IntPtr LoadLibrary( string filename );

[DllImport( "kernel32.dll" )]
private static extern IntPtr GetProcAddress( IntPtr hModule, string procname );

[DllImport( "libdl.so" )]
private static extern IntPtr dlopen( string filename, int flags );

[DllImport( "libdl.so" )]
private static extern IntPtr dlsym( IntPtr handle, string symbol );

const int RTLD_NOW = 2;
#endregion

#region Abstracted
public static bool __linux__
{
    get
    {
        int p = (int)Environment.OSVersion.Platform;
        return ( p == 4 ) || ( p == 6 ) || ( p == 128 );
    }
}
#endregion

#region Fields
private static Type _delegateType = typeof( MulticastDelegate );
#endregion

#region Methods
public static IntPtr Load( string filename )
{
    IntPtr mHnd;

    if ( __linux__ )
        mHnd = dlopen( filename, RTLD_NOW );
    else
        mHnd = LoadLibrary( filename );

    return mHnd;
}

public static IntPtr Symbol( IntPtr mHnd, string symbol )
{
    IntPtr symPtr;

    if ( __linux__ )
        symPtr = dlsym( mHnd, symbol );
    else
        symPtr = GetProcAddress( mHnd, symbol );

    return symPtr;
}

public static Delegate Delegate( Type delegateType, IntPtr mHnd, string symbol )
{
    IntPtr ptrSym = Symbol( mHnd, symbol );
    return Marshal.GetDelegateForFunctionPointer( ptrSym, delegateType );
}

public static void LinkAllDelegates( Type ofType, IntPtr mHnd )
{
    FieldInfo[] fields = ofType.GetFields( BindingFlags.Public | BindingFlags.Static );

    foreach ( FieldInfo fi in fields )
    {
        if ( fi.FieldType.BaseType == _delegateType )
        {
            fi.SetValue( null, Marshal.GetDelegateForFunctionPointer( Symbol( mHnd, fi.Name ), fi.FieldType ) );
        }
    }
}
#endregion
}

我使用这个自定义 class 来(部分)加载 LZ4:

public static class LZ4
{
    private static string _lib = "lz4.dll";
    private static IntPtr _dllHnd;

    #region Delegates
    public delegate int PFNLZ4_COMPRESS_DEFAULTPROC( IntPtr source, IntPtr dest, int sourceSize, int maxDestSize );
    public delegate int PFNLZ4_COMPRESS_FASTPROC( IntPtr source, IntPtr dest, int sourceSize, int maxDestSize, int acceleration );
    public delegate int PFNLZ4_COMPRESS_HCPROC( IntPtr src, IntPtr dst, int srcSize, int dstCapacity, int compressionLevel );
    public delegate int PFNLZ4_DECOMPRESS_FASTPROC( IntPtr source, IntPtr dest, int originalSize );
    public delegate int PFNLZ4_COMPRESSBOUNDPROC( int inputSize );
    #endregion

    #region Methods
    public static void LZ4_Link()
    {
        if ( DLL.__linux__ )
            _lib = "./liblz4.so";

        _dllHnd = DLL.Load( _lib );

        Console.WriteLine( "LZ4_Link: OS is {0}", DLL.__linux__ ? "Linux" : "Windows" );
        Console.WriteLine( "LZ4_Link: Linked {0}", _lib );
        Console.WriteLine( "LZ4_Link: _dllHnd -> 0x{0}", _dllHnd.ToString( "X" ) );

        DLL.LinkAllDelegates( typeof( LZ4 ), _dllHnd );
    }

    public static PFNLZ4_COMPRESS_DEFAULTPROC LZ4_compress_default;
    public static PFNLZ4_COMPRESS_FASTPROC LZ4_compress_fast;
    public static PFNLZ4_COMPRESS_HCPROC LZ4_compress_HC;
    public static PFNLZ4_DECOMPRESS_FASTPROC LZ4_decompress_fast;
    public static PFNLZ4_COMPRESSBOUNDPROC LZ4_compressBound;
    #endregion
}

如前所述,每个 dlsym 调用 returns IntPtr.Zero (NULL),而 Windows “等效”工作正常。

似乎名称 mangling/release 差异是问题所在。 lz4.dll 导出 LZ4_decompress_default,而 liblz4.so 导出 LZ4_decompress