What is DLSupport and DL Library?
Open up DLSupport.cs in MonoDevelop and you'll see three functions that are imported from libdl:[DllImport("libdl.so.2")]
static extern IntPtr dlopen(string filename, int flags);
[DllImport("libdl.so.2")]
static extern IntPtr dlsym(IntPtr handle, string symbol);
[DllImport("libdl.so.2")]
static extern int dlclose(IntPtr handle);
The dlopen function opens a shared object and load it into memory and return a handle which is a pointer so it could be used in other DL functions. The dlsym function retrieve a symbol pointer directly from the loaded library handle with a symbol name which you can view what are exported symbols in the library with the following command in terminal:
using libdl.so.2 as an example
objdump -T /usr/lib/libdl.so.2
which will display a list of symbols that you can call in the library.
dlclose is a dangerous command, because when you execute dlopen and recieving a handle, other part of the code would get the same exact handle when it already been opened previously in the same process. So when you close that handle, that handle will disrupt other codes using it. This is the case with Mono, because Mono is using DL library when you use DllImport attribute.
With this in mind, this is not the case with older Dotnet Core. an older Dotnet Core does not use DL Library to dynamically load the library and retrieve the symbol for DllImport, so that means when you execute functions imported from the same library with DllImport while using delegate approach for accessing variables in the same library, you'll have two very drastically different result from one another. This again, is why it is called the Delegate Approach, it's verbose and necessary if you intend to make it work across Mono and Dotnet Core.
In the most up to date Dotnet Core, you can use DllImport mixed in with Delegate Approach which means you can avoid definding delegate types and functions and instead use DllImport with ease.
The rest of the functions in DLSupport are straightforward such as GetFunctionDelegate and GetSymbolPointer.
To learn more about DL Functions, you can read it here.
Let's Try It Out Anyway
Let's delete the following lines:public delegate void IncrementVariableInC_dt();
IncrementVariableInC_dt IncrementVariableInC;
IncrementVariableInC = GetFunctionDelegate<IncrementVariableInC_dt>("IncrementVariableInC");
And instead, let's import the same function using DllImport!
[DllImport("libtutorial.so")]
public static extern void IncrementVariableInC();
And finally, let's fix the function call in Main method by simply removing "lib.":
IncrementVariableInC();
Build and run program, the output will be 2 which means Mono is using the same library handle as you do.
Now let's try it on Dotnet Core:
There are few changes that have to be made to be compatible on Dotnet Core:
First thing first, create a new directory and run command in terminal in that directory:
dotnet new console
Then copy and paste the code below into Program.cs:
using System;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
public abstract class DLSupport
{
[DllImport("libdl.so.2")]
static extern IntPtr dlopen(string filename, int flags);
[DllImport("libdl.so.2")]
static extern IntPtr dlsym(IntPtr handle, string symbol);
[DllImport("libdl.so.2")]
static extern int dlclose(IntPtr handle);
protected IntPtr handle;
public DLSupport(string dir, string lib, LoadFlag flag)
{
var path = Path.Combine(dir, lib);
if (!File.Exists(path))
throw new FileNotFoundException("Library could not be found!");
handle = dlopen(path, (int)flag);
if (handle == IntPtr.Zero)
throw new DllNotFoundException("Library cannot be loaded with given flag.");
}
public DLSupport(string lib, LoadFlag flag)
{
handle = dlopen(lib, (int)flag);
if (handle == IntPtr.Zero)
throw new DllNotFoundException("Library cannot be found or loaded with given flag.");
}
protected T GetFunctionDelegate<T>(string symbol, bool SuppressSymbolError = false)
{
var ptr = dlsym(handle, symbol);
if (!SuppressSymbolError && ptr == IntPtr.Zero)
throw new Exception(string.Format("Symbol cannot be found for type: {0}", typeof(T).Name));
return Marshal.GetDelegateForFunctionPointer<T>(ptr);
}
protected IntPtr GetSymbolPointer(string symbol)
{
return dlsym(handle, symbol);
}
/// <summary>
/// Should not be invoked unless none of DllImport uses the same library as this handle.
/// The runtime will fail if you attempt to use DllImported functions that happens to be using the same
/// handle as this.
/// </summary>
protected void DangerousDispose()
{
dlclose(handle);
}
}
public enum LoadFlag : int
{
RTLD_LAZY = 0x00001,
/// <summary>
/// Immediate function call binding.
/// </summary>
RTLD_NOW = 0x00002,
/// <summary>
/// Mask of binding time value.
/// </summary>
RTLD_BINDING_MASK = 0x3,
/// <summary>
/// Do not load the object.
/// </summary>
RTLD_NOLOAD = 0x00004,
/// <summary>
/// Use deep binding.
/// </summary>
RTLD_DEEPBIND = 0x00008,
/// <summary>
/// If the following bit is set in the MODE argument to `dlopen',
/// the symbols of the loaded object and its dependencies are made
/// visible as if the object were linked directly into the program.
/// </summary>
RTLD_GLOBAL = 0x00100,
/// <summary>
/// Unix98 demands the following flag which is the inverse to RTLD_GLOBAL.
/// The implementation does this by default and so we can define the
/// value to zero.
/// </summary>
RTLD_LOCAL = 0,
/// <summary>
/// Do not delete object when closed.
/// </summary>
RTLD_NODELETE = 0x01000
}
namespace Tutorial1
{
public class LibTutorial : DLSupport
{
IntPtr VariableInCPtr { get; set; }
int VariableInC
{
get
{
if (VariableInCPtr == IntPtr.Zero)
throw new Exception("Pointer for VariableInC is not assigned!");
return Marshal.ReadInt32(VariableInCPtr);
}
set
{
if (VariableInCPtr == IntPtr.Zero)
throw new Exception("Pointer for VariableInC is not assigned!");
Marshal.WriteInt32(VariableInCPtr, value);
}
}
public LibTutorial() : base(Path.GetDirectoryName(typeof(LibTutorial).GetTypeInfo().Assembly.Location),
"libtutorial.so", LoadFlag.RTLD_LAZY)
{
VariableInCPtr = GetSymbolPointer("VariableInC");
}
[DllImport("libtutorial.so")]
public static extern void IncrementVariableInC();
static void Main()
{
LibTutorial lib = new LibTutorial();
for (int I = 0; I < 100; I++)
IncrementVariableInC();
Console.WriteLine(lib.VariableInC);
}
}
}
Before you can build and run it, you need to copy libtutorial.so from other Tutorial project folder under bin/Debug folder into bin/Debug/netcoreapp1.1 of Dotnet Core project folder.
Finally run the commands below:
dotnet restore
dotnet build
dotnet run
In older verion before 1.0.1-rc4-004953, Dotnet Core will not be using DL library and therefore the output would be a 1 and appearently in current up to date version of Dotnet Core, it is using DL Library which therefore the output would be a 101.
To view the codes above, check out the github repository here.
Onto Tutorial #3


No comments:
Post a Comment