www.digitalmars.com         C & C++   DMDScript  

D.gnu - [Bug 101] New: DLL sample for GDC

http://bugzilla.gdcproject.org/show_bug.cgi?id=101

             Bug #: 101
           Summary: DLL sample for GDC
    Classification: Unclassified
           Product: GDC
           Version: development
          Platform: x86
        OS/Version: Other
            Status: NEW
          Severity: major
          Priority: Normal
         Component: gdc
        AssignedTo: ibuclaw gdcproject.org
        ReportedBy: slavo5150 yahoo.com


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.

-- 
Configure bugmail: http://bugzilla.gdcproject.org/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
You are watching all bug changes.
Feb 01 2014