dlsym returns NULL where GetProcAddress returns va

2019-08-22 17:23发布

问题:

While making a cross-platform game I found the need for a cross-platform DLL linker, my DLL linker class seems to be "up to standards" but it only works on Windows machines, while it is designed to work on both Window and Linux machines.

This is due to dlsym( IntPtr handle, string symbol ) returning IntPtr.Zero whereas GetProcAddress( IntPtr hModule, string lpProcName ) returns a valid IntPtr pointing to the desired symbol.

Code

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
}

I used this custom class to (partially) load LZ4 as such:

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
}

As said, every dlsym call returns IntPtr.Zero (NULL), whereas the Windows "equivalent" works just fine.

回答1:

Seems like name mangling/release differences were the issue. lz4.dll exported LZ4_decompress_default, whereas liblz4.so exported LZ4_decompress.