How to create a dynamic library in D?

I want to create a dynamic library (cross platform) in D, so I made several google. After a while I found this page. I am absolutely stunned by how difficult writing, compiling and even binding to a DLL are. Isn't there a single way to create a shared library like in C? (just leave the main function and pass a few flags to the linker)

+6
source share
1 answer

Well, I decided to spend some time fiddling with this today, and I kind of have something that works, at least if the main program is also written in D (on Linux, I think it will work with C is also on Windows. The reason is that I did not refer to phobos in .so in D, so it relies on exe for these characters. I think tbh I don’t know exactly what is going on here, maybe it will work better if I used shared phobos lib too)

In any case, drop some code first.

This is testdll.d and it builds our dll

module testdll; import std.stdio; extern(C) export void lol() { import core.stdc.stdio; printf("hello from C\n"); writeln("hello!"); } version(Windows) extern(Windows) bool DllMain(void* hInstance, uint ulReason, void*) { import std.c.windows.windows; import core.sys.windows.dll; switch (ulReason) { default: assert(0); case DLL_PROCESS_ATTACH: dll_process_attach( hInstance, true ); break; case DLL_PROCESS_DETACH: dll_process_detach( hInstance, true ); break; case DLL_THREAD_ATTACH: dll_thread_attach( true, true ); break; case DLL_THREAD_DETACH: dll_thread_detach( true, true ); break; } return true; } 

You will notice that this code is WinMain, which simply calls the druntime functions. I think that the main part should be available, at least in the form of a mix, or maybe even completely automatic, as this is a clean template.

And client code:

 import core.runtime; alias extern(C) void function() functype; version(Posix) { extern(C) void* dlsym(void*, const char*); extern(C) void* dlopen(const char*, int); extern(C) char* dlerror(); pragma(lib, "dl"); } else version(Windows) { extern(Windows) void* LoadLibraryA(const char* filename); extern(Windows) void* GetProcAddress(void*, const char*); } void main() { version(Posix) { auto thing = dlopen("./testdll.so", 2); if(thing is null) { import std.conv; import std.stdio; writeln(to!string(dlerror())); return; } auto proc = cast(functype) dlsym(thing, "lol"); } else version(Windows) { auto thing = LoadLibraryA("testdll.dll"); assert(thing !is null); auto proc = cast(functype) GetProcAddress(thing, "lol"); } assert(proc !is null); //import std.stdio; writeln("calling proc"); proc(); } 

This has different code for Windows and Linux, although it is very similar. Listening material is supposed to begin to take care of this soon, as we mentioned in the comments.

Compilation commands are not so bad, but a bit strange. Linux:

 dmd -fPIC -shared testdll.d -defaultlib= # builds the dll 

PIC and shared report that he built .so. I made an empty defaultlib because without it loading the dll at runtime failed with "already defined characters" errors.

Building a client is simple:

 dmd testdllc.d 

Note that there is a pragma (lib) in the file that is automatically associated with the -ldl option. Run it and get hi! BTW make sure that both are in the same directory as this loads ./ in the bootloader.

Now create on Windows.

 dmd -oftestdll.dll -shared testdll.d testdll.def 

Tell us to output our dll, use -shared so that it knows, and then the other thing is the def file, as described here http://dlang.org/dll.html/dllmain

This is the contents of this file:

 LIBRARY testdll EXETYPE NT CODE SHARED EXECUTE DATA WRITE EXPORTS lol 

If you do not use the .def file, the dll will work successfully, but the procedure will not be found because it is not exported. (I think the export keyword in D should be able to do this automatically, bypassing the hte.def file, and I believe this is discussed there, but now it is necessary, as far as I know.)

And the client is also easy:

 dmd testdllc.d 

Run it and get a greeting if everything goes well.

Now, why did I make the alias functype in the client? Easier than doing other casting, etc., and that makes it a beautiful exterior (C).

Why is the lol extern (C) function in the first place? It just has an easier name to use in GetProcAddress / dlsym. May also have a pragma (mangle) or make .mangleof with import. All sorts of options out there are pretty simple, I just wanted it to make it easier to make the test easier to focus on. "lol" is a simpler name than "_D7testdll3lolFZv", or whatever name you were looking for ... (OMG I chopped it right! Sometimes I think I write too much D lol), and yes, it works too eyeball. Note. On Windows, a .def file may have to leave an underscore if you do it this way.

In any case, yes, this created a working dll / so for me and a program to download and use it successfully. Not as beautiful as it can / should be, but it works. At least for me.

+5
source

All Articles