Bug creation and email sending has been disabled, file new bugs at gcc.gnu.org/bugzilla
Bug 101 - DLL sample for GDC
Summary: DLL sample for GDC
Status: NEW
Alias: None
Product: GDC
Classification: Unclassified
Component: gdc (show other bugs)
Version: development
Hardware: x86 Other
: --- major
Assignee: Iain Buclaw
URL:
Depends on:
Blocks:
 
Reported: 2014-02-01 11:19 CET by Mike
Modified: 2014-02-01 11:19 CET (History)
0 users

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Mike 2014-02-01 11:19:56 CET
This was migrated from https://bitbucket.org/goshawk/gdc/issue/288/dll-sample-for-gdc


Andrej08 created an issue 2011-12-16
****************************************
Since this will probably be an often-asked question, I think it would be nice to package a DLL sample in some "Samples" directory or maybe on the wiki page. Here's a preliminary sample that works:

mydll.d:

module mydll;

import std.c.windows.windows;

version (Windows)
{
    extern (C) bool rt_init( void delegate( Exception ) dg = null );
    extern (C) bool rt_term( void delegate( Exception ) dg = null ); 
    
    // Globals
    static HINSTANCE    g_hInst;     
    
    extern (Windows)
    BOOL DllMain(HINSTANCE hInstance, ULONG ulReason, LPVOID pvReserved)
    {
        int argc;
        char** argv;
        
        switch (ulReason)
        {
            case DLL_PROCESS_ATTACH:
                rt_init();
                break;
            case DLL_PROCESS_DETACH:
                rt_term();
                break;
            case DLL_THREAD_ATTACH:
            case DLL_THREAD_DETACH:
                return false;
            default:
                break;
        }
        
        g_hInst = hInstance;
        return true;
    }
}

export extern(D) int sum(int x, int y)
{
    return x + y;
}

main.d:

module main;

import mydll;
import std.stdio;

void main()
{
    int x = 1;
    int y = 2;
    writefln("The sum of %s and %s is %s.", x, y, sum(x, y));
}

build.bat:

@echo off
gdc -fintfc -v2 -fsyntax-only -H mydll.d
gdc -v2 -shared -o mydll.dll mydll.d -Wl,--out-implib,implibmydll.a
gdc -v2 -o main.exe main.d implibmydll.a

Unfortunately this isn't reliable since I'm not doing extra work when threads get involved. I had to prototype rt_init and rt_term because I can't import import core.sys.windows.dll and use dll_process_attach and dll_process_detach due to linking errors.

Example:

module mydll;

import std.c.windows.windows;
import core.sys.windows.dll;

__gshared HINSTANCE g_hInst;

version (Windows)
{
    extern (Windows)
    BOOL DllMain(HINSTANCE hInstance, ULONG ulReason, LPVOID pvReserved)
    {
        switch (ulReason)
        {
            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 );
                return false;
            default:
                break;
        }
        
        g_hInst = hInstance;
        return true;
    }
}

export extern(D) int sum(int x, int y)
{
    return x + y;
}

gdc -v2 -shared -o mydll.dll mydll.d -Wl,--out-implib,implibmydll.a

Creating library file: implibmydll.a
d:/mingw32/bin/../lib/gcc/i686-pc-mingw32/4.6.1/../../../libgphobos2.a(dll.o): In function `D4core3sys7windows3dll18dll_process_attachFPvbZb':
C:\crossdev\gdc\v2\build\i686-pc-mingw32\libphobos/../../../gcc-4.6.1/libphobos/core/sys/windows/dll.d:386: undefined reference to `_tls_callbacks_a'
collect2: ld returned 1 exit status

There is a workaround, I can define a dummy symbol called _tls_callbacks_a:

module mydll;

import std.c.windows.windows;
import core.sys.windows.dll;

__gshared HINSTANCE g_hInst;

extern(C) int _tls_callbacks_a;

version (Windows)
{
    extern (Windows)
    BOOL DllMain(HINSTANCE hInstance, ULONG ulReason, LPVOID pvReserved)
    {
        switch (ulReason)
        {
            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 );
                return false;
            default:
                break;
        }
        
        g_hInst = hInstance;
        return true;
    }
}

export extern(D) int sum(int x, int y)
{
    return x + y;
}

This will successfully compile the DLL. The other problem is an issue with DMD's front-end (I think so anyway, and GDC is based on that front-end AFAIK), where using a .di file will create a dependency on a symbol "ModuleInfoZ". I've discussed this in Issue 6019 (http://d.puremagic.com/issues/show_bug.cgi?id=6019), essentially the linker error after the last call here:

gdc -v2 -shared -o mydll.dll mydll.d -Wl,--out-implib,implibmydll.a
gdc -fintfc -v2 -fsyntax-only -H mydll.d
gdc -v2 -o main.exe main.d implibmydll.a

is:

C:\DOCUME~1\Andrej\LOCALS~1\Temp\ccMiZJOR.o:main.d:(.data+0xc): undefined reference to `_D5mydll12__ModuleInfoZ'

To work around that, another dummy symbol must be added to main.d:

module main;

import mydll;
import std.stdio;

extern(C) int _D5mydll12__ModuleInfoZ;

void main()
{
    int x = 1;
    int y = 2;
    writefln("The sum of %s and %s is %s.", x, y, sum(x, y));
}

Anyway I just thought this info would be useful here.



Andrej08 - 2011-12-16
**************************************
Actually there is one more workaround to the ModuleInfoZ problem, it's to use function-local import to core.sys.windows.dll, e.g.:

module mydll;

import std.c.windows.windows;

__gshared HINSTANCE g_hInst;

extern(C) int _tls_callbacks_a;

version (Windows)
{
    extern (Windows)
    BOOL DllMain(HINSTANCE hInstance, ULONG ulReason, LPVOID pvReserved)
    {
        import core.sys.windows.dll;
        
        switch (ulReason)
        {
            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 );
                return false;
            default:
                break;
        }
        
        g_hInst = hInstance;
        return true;
    }
}

export extern(D) int sum(int x, int y)
{
    return x + y;
}


Daniel Green - 2012-01-26
****************************************************
* assigned issue to Daniel Green 

_tls_callbacks_a is a Digital Mars thing. I believe Visual Studio and MinGW use xl_a. It's a pointer to the start of the TLS callbacks. I'll look into this. It may only require alias __xl_a _tls_callbacks and the approriate extern definition for xl_a.

   
Daniel Green - 2012-01-28
*****************************************
Another workaround for 'ModuleInfoZ' is to use -Wl,--export-all-symbols. It is defined, but it's not exported from the Dll. The only issue here, is you can't generate the library file and must link with the Dll.

gdc -v2 -shared -o mydll.dll mydll.d -Wl,--export-all-symbols
gdc -v2 -fintfc -fsyntax-only -H mydll.d
gdc -v2 -o main.exe main.d mydll.dll

Do you think it would be safe to apply the export attribute to generated 'ModuleInfoZ' symbol?

Edit: It also seems DMD wishes to have all ModuleInfoZ symbols declared weak. So that the inability to import one will not prevent linking.