Wrap a .dll as a .so using libwine. (partially working?)

Questions about Wine on Linux
Locked
slembcke
Newbie
Newbie
Posts: 2
Joined: Mon Jul 22, 2019 9:50 am

Wrap a .dll as a .so using libwine. (partially working?)

Post by slembcke »

Background: I'm on a project where we are experimenting to see if VR is an effective way to make certain mental excercises more effective. Basically we are making a handful of small but polished games in Unity3D, hooking people up to a bunch of biosensors, and putting them in VR. It's been a pretty neat project to work on. I've been doing most of my development in Linux because I prefer it, and more importantly because I can! Unity3D and SteamVR work pretty well under Linux, and I have much easier access to a ton of high quality tools like Jupyter for tinkering with the captured bio data to figure out what to do with it. For practicality reasons, a lot of my development has been in Windows though too.

<rant>
Well... a couple weeks ago Windows 10 decided that I just had to install an update in the middle of the workday, rebooted my computer, installed the update, and rebooted again to a blue screen. It corrupted my hard drive. -_- After downloading farting around with the recovery tools for half a day, it became clear that I was going to have to backup, format, and reinstall. The Windows disk was listed first in the list for the other tools, so I reformatted disk 0 and started the installer... without checking which disk that actually was. So now I had two non-working OSes on my work computer. -_- I'm still so unreasonably mad about the whole experience, that I don't care about "practicality" so much anymore.
</rant>

So anyway. My only link to Windows right now is that the expensive bio sensor interface only has a Windows .dll for it's interface. It connects over UDP, and the whole API is 20 functions, with the most exotic data type being double[]. So I started reading and found out about libwine and winegcc. I'm hoping I can create a .so that wraps the .dll using libwine, but I've run into some snags.

The first thing I tried was to grab SDL2.dll and used winegcc to LoadLibray() and GetProcAddress() from it to do some logging and open a window. Good so far. Next I tried building the wrapped code into a .so using winegcc -shared. From regular Linux code I can use dlopen() to load the wrapper library, but it crashes with a segmentation fault when the wrapper library tries to call LoadLibrary() to load the wrapped Windows dll. Drat! Stepping into the call with GDB, LoadLibrary() calls LoadLibraryA(), which I found in the Wine source code to be a simple tail call:

Code: Select all

HMODULE WINAPI DECLSPEC_HOTPATCH LoadLibraryA(LPCSTR libname){
    return LoadLibraryExA(libname, 0, 0);
}
GDB's disassembly seemed to agree, but when stepping the jump instruction, it seems to jump to nowhere and promptly crashes.

Code: Select all

0x00007ffff7e20148 in LoadLibraryA () from ./wrap-win.dll.so
(gdb) disassemble
Dump of assembler code for function LoadLibraryA:
=> 0x00007ffff7e20148 <+0>:     jmpq   *0x304a(%rip)        # 0x7ffff7e23198
End of assembler dump.
(gdb) si
0x000000000000019e in ?? ()
(gdb) 
I'm not at all an expert at x86 assembly, and so I guessed at what that opcode syntax meant and tried this, which seems to agree. It certainly doesn't point to LoadLibraryExA().

Code: Select all

(gdb) p/x *(void**)($rip + 0x304a)
$4 = 0x19e000000000000
(gdb) p LoadLibraryExA
$5 = {<text variable, no debug info>} 0x7ffff759dda0 <LoadLibraryExA>
So? What is going on here? LoadLibrary(), LoadLibraryA(), and LoadLibraryExA() are all defined in the same file. So I don't really understand why it makes an indirect jump at all. Admittedly I'm in *way* over my head here, and maybe there is something obvious going on here? Is it some winegcc magic, and the jump table hasn't been initialized yet because I didn't launch the process using wine?
slembcke
Newbie
Newbie
Posts: 2
Joined: Mon Jul 22, 2019 9:50 am

Re: Wrap a .dll as a .so using libwine. (partially working?)

Post by slembcke »

I poked at this a bit more today.

I created a new host executable and I can successfully load .so files created with winegcc or the regular gcc. The executable created with regular gcc can only load the .so created with regular gcc. Hmm.

Diving in with GDB, I found something interesting.

Code: Select all

0x00007ffff6978148 in LoadLibraryA () from ./wrap-win.dll.so
(gdb) disassemble
Dump of assembler code for function LoadLibraryA:
=> 0x00007ffff6978148 <+0>:     jmpq   *0x304a(%rip)        # 0x7ffff697b198
End of assembler dump.
(gdb) p/x *(void**)($rip + 0x304a)
$1 = 0x6ea0000000007b47
(gdb) si
0x000000007b476ea0 in LoadLibraryA () from /usr/lib/wine/../x86_64-linux-gnu/wine/kernel32.dll.so
(gdb) disassemble
Dump of assembler code for function LoadLibraryA:
=> 0x000000007b476ea0 <+0>:     lea    0x0(%rsp),%rsp
   0x000000007b476ea8 <+8>:     push   %rbp
   0x000000007b476ea9 <+9>:     xor    %r8d,%r8d
   0x000000007b476eac <+12>:    xor    %edx,%edx
   0x000000007b476eae <+14>:    mov    %rsp,%rbp
   0x000000007b476eb1 <+17>:    and    $0xfffffffffffffff0,%rsp
   0x000000007b476eb5 <+21>:    sub    $0x20,%rsp
   0x000000007b476eb9 <+25>:    callq  0x7b476da0 <LoadLibraryExA>
   0x000000007b476ebe <+30>:    leaveq 
   0x000000007b476ebf <+31>:    retq
I had figured LoadLibraryA () from ./wrap-win.dll.so was... wrong before. So the LoadLibrary() defined in my executable is just a wrapper for a jumptable location that gets filled in with the real LoadLibrary() address in kernel32.dll.so. So what causes that table to be initialized?
Locked