taviso / loadlibrary

Porting Windows Dynamic Link Libraries to Linux
GNU General Public License v2.0
4.34k stars 378 forks source link

Got "attempted to call an unknown symbol" for an hello world example #58

Open dov opened 5 years ago

dov commented 5 years ago

In order to learn how to use the loadlbrary library with the goal of accessing a commercial win32 library from linux, I started off with a simple hello world example.

Here are my files:

* say-hello.c

include

void say_hello() { printf("Say Hello!\n"); }

* hello-main.c - For testing the dll with wine

include "say-hello.h"

int main(int argc, char*argv[]) { say_hello(); }

* The build command of the dll:

i686-w64-mingw32-gcc -o hello-main.obj -c -O2 -mms-bitfields hello-main.c i686-w64-mingw32-gcc -o say-hello.obj -c -fPIC -O2 -mms-bitfields say-hello.c i686-w64-mingw32-gcc -o say-hello.dll -shared say-hello.obj i686-w64-mingw32-gcc -o hello-main.exe hello-main.obj say-hello.dll

* Testing the dll works:

wine hello-main.exe 000b:fixme:winediag:start_process Wine Staging 4.0-rc2 is a testing version containing experimental patches. 000b:fixme:winediag:start_process Please mention your exact version when filing bug reports on winehq.org. Say Hello!


Now I wanted to load this dll into load library and I started at mpclient.c sources and removed as much as I could and then tried to call my say_hello() function. This is what I got:

ifndef _GNU_SOURCE

define _GNU_SOURCE

endif

include

include

include

include

include

include

include "winnt_types.h"

include "pe_linker.h"

include "ntoskernel.h"

int main(int argc, char argv, char envp) { PIMAGE_DOS_HEADER DosHeader; PIMAGE_NT_HEADERS PeHeader; struct pe_image image = { .entry = NULL, .name = "say-hello.dll", };

// Load the mpengine module.
if (pe_load_library(image.name, &image.image, &image.size) == false) {
    printf("You must add the dll and vdm files to the engine directory");
    return 1;
}

// Handle relocations, imports, etc.
link_pe_images(&image, 1);

// Fetch the headers to get base offsets.
DosHeader   = (PIMAGE_DOS_HEADER) image.image;
PeHeader    = (PIMAGE_NT_HEADERS)(image.image + DosHeader->e_lfanew);

// Try calling into the library.
void (*SayHelloPtr)();
if (get_export("say_hello", &SayHelloPtr) != -1) {
    (*SayHelloPtr)();
}
else
    printf("Failed getting pointer to say_hello()!\n");

return 0;

}



It coompiles, the dll loads, the `say_hello` symbol is found, but it then crashes at `(*SayHelloPtr)()`. What did I miss?

Thanks!
dov commented 5 years ago

After I wrote the above, I realized that perhaps the "attempt to call an unknown symbol" error is the call to printf(). I then added a new function to my dll:

int32_t add_hello(int32_t a, int32_t b);

and called it as follows:

    // Try calling into the library.
    int32_t (*AddHelloPtr)(int32_t a, int32_t b);
    if (get_export("add_hello", &AddHelloPtr) != -1) {
        int a=10,b=32;
        printf("%d+%d=%d\n", a,b,AddHelloPtr(a,b));
    }
    else
        printf("Failed getting pointer to say_hello()!\n");

and this worked!

But I still wonder how would I load the windows CRT library so that I can call printf (and other io functions)? Or is there a way to redirect the DLL printf() call to the gcc libc printf() .