DLL built with Mingw G++ , crash when called

Viewed 409

Trying to build a DLL with C++ code with mingw does not work like it should...

I am trying to wrap a larger C++ class in a dll for use in other .net environments. But it keeps crashing when calling.

It has nothing to do with the .net stuff because first I tried to call the DLL from a little testprogram made with C and Visual Studio.

The small testprogram I made opens the lib, gets functions pointers, and calls a function adding 2 integers. Up to there it works. The next call passes in a string, and it it jumps into invliad memoryspace.

I am getting the feeling that the Mingw built lib contains parts in its libstdc++, which needs to be initialized? (stuff normally called by framework code before calling your main() in a program?)

Using Mingw g++ 10.2.0 with this commandline to build the little DLL:

g++ -o gehtnichdll.dll -s -Wl,--subsystem,windows src/dllmain.cpp

A strange thing I nodted is that I need to add a function called 'WinMain', otherwise G++ complains about it missing.

I picked up the info on building DLL from https://www.transmissionzero.co.uk/computing/building-dlls-with-mingw/ .

Noting that mingw has an option -mdll , I tried that and got a dll which no longer loads (LoadLibrary fail 193)

I found a link to a page http://mingw.org/wiki/MSVC_and_MinGW_DLLs , but that page is no longer available.

Here is the source of the DLL. Stripped down to the almost minimum to show the problem. The class I tried to wrap is also completely removed as even the simple calls in here already fail.

#include <windows.h>
#include <stdint.h>
#include <stdio.h>
#include <string>

extern "C" __declspec(dllexport)
BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}


#define IID_OFFSET 0x07C007EC

typedef float A;    // removed class A, not related to problem.

extern "C" __declspec(dllexport)
uint64_t AA__create(int var)
{
    A *a;
    a = new A(var); // There was the class constructor here.
                // Removed as it is not related to the problem.
    printf("called create (%d)\n",var);
    return (uint64_t)a;
}

extern "C" __declspec(dllexport)
void AA__destroy(uint64_t iid)
{
    A *a = (A*)iid;
    if(!a)return;
    delete a;
}

extern "C" __declspec(dllexport)
void AA__reset(uint64_t iid)
{
    A *a = (A*)iid;
    if(!a)return;
}

extern "C" __declspec(dllexport)
void AA__step(uint64_t iid)
{
    A *a = (A*)iid;
    if(!a)return;
    (*a)++;
}

extern "C" __declspec(dllexport)
uint16_t strlen2(const char *str)
{
    uint16_t res=0;
    while( str && str[res] )res++;
    return res;
}

extern "C" __declspec(dllexport)
void printstring(const char *str)
{
    printf("string: '%s'\n",str);
}

// Without this WinMain, G++ exits with   undefined reference to `WinMain'
// So providing a dummy function. Never called when loading as DLL.
extern "C" __declspec(dllexport)
int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                     PSTR pCmdLine, int nCmdShow)
{
    printf("dll as main.\n");
    uint64_t iid = AA__create(100);
    AA__destroy(iid);
    return 0;
}

extern "C" __declspec(dllexport)
int dummy_add(int a,int b)
{
    return a+b;
}

The C program calling this up, buit with MSVC++, is this (is C, not C++):

#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>
#include <Windows.h>

typedef void __declspec(dllimport) (*c_step)(uint64_t);
typedef uint64_t __declspec(dllimport) (*c_create)(int);
typedef int __declspec(dllimport) (*c_add)(int,int);
typedef void __declspec(dllimport) (*c_print)(const char *);
typedef uint16_t __declspec(dllimport) (*c_strlen)(const char *);

int main()
{
    printf("Hello World!\n");

    HMODULE h;
    h = LoadLibrary("C:\\muell\\gehtnichdll\\gehtnichdll.dll");

    if(!h)
    {
        int er = GetLastError();
        fprintf(stderr,"loadlib failed, err = %d\n",er);
        return 1;
    }

    c_create func_create = (c_create)GetProcAddress(h,"AA__create");
    c_step func_step = (c_step)GetProcAddress(h,"AA__step");
    c_add func_add = (c_add)GetProcAddress(h,"dummy_add");
    c_print func_prn = (c_print)GetProcAddress(h,"strlen2");
    c_strlen func_strl = (c_strlen)GetProcAddress(h,"printstring");
    if(!(func_create&&func_step&&func_add&&func_prn&&func_strl))
        {fprintf(stderr,"could not get function pointers.\n");return 1;}

    int x = func_add(4,7);
    printf("x = %d\n",x);

    // this function call fails with an invalid jump.
    uint16_t l = func_strl("lenth of this string");
    printf("l = %u\n",(unsigned int)l);

    // this call passes, but does not do output.
    func_prn("hello world");

    // These calls also fail (if commenting out the ones before)
    uint64_t iid = func_create(0xBB1BE);
    printf("Created, iid = 0x%" PRIXPTR "\n",(void*)iid);
    func_step(iid);

    return 0;
}
1 Answers

I have solved my problem for now. Not very satisfying as I don't really know what was wrong.

Changing the mingw G++ call to static linking and with -mdll fixes it. Then I can call the functions. I assume that the linker now generates the proper initialization code.

g++ -o gehtnichdll.dll -mdll -static-libstdc++ -static src/dllmain.cpp

Building just with -mdll and not the statics made a dll depending on libstdc++-6.dll , which is not accepted even when placed in the proper folder. Using ProcessMonitor, I could observe it being opened and closed again - followed by more searches for it elsewhere. Without the -mdll option, the resulting dll accepted that version of the libstdc++-6.dll at the same location.

Strange thing though that I can remove DllMain and WinMain now, but why did it not conflict with a DllMain brought in by MinGW?

Anyway, changing the build commandline fixes it.

Also , the link to transmissionzero.co.ok , which I find referenced a few times on Stackoverflow, just seems not to work (with MinGW 10)

Leaves many open question. Works for now.....

Related