TL;DR; Is it possible, under wine, to call a function in a Windows exe, by function pointer, from the Linux proxy library?
I have a Windows x64 DLL called morag.dll
containing a function foo
. I also have a Linux SO called morag.so
containing the Linux implementation of foo
(same parameters on each platform). I have a Windows application that loads morag.dll
that I want to run under wine. I have created the mapping between Windows foo
and Linux foo
by creating a morag.dll.spec
file, and a proxy wrapper and building this using winegcc
.
morag.dll.spec
contains:-
1 cdecl foo (ptr ptr) Proxyfoo
morag.c
contains:-
#include <windef.h>
#include <FOO.h> /* def of FOOSTRUCT */
void WINAPI Proxyfoo (long * parm1, FOOSTRUCT * parm2)
{
foo(parm1, parm2);
}
Build command:-
winegcc -m64 -fPIC -d_WIN64=1 -I various-includes -z muldefs morag.c morag.dll.spec -o morag.dll.so -shared -L various-libraries -lfoo
This works well. I can successfully call foo
from my Windows application and invoke foo
in the Linux implementation.
The issue I am facing is when foo
is a function that is given a callback function pointer to run later. Clearly the Windows application that calls foo
provides it with a function pointer using Windows x64 conventions, but the Linux implementation of foo
is invoking the function pointer under the assumption it is using Linux x64 conventions. So unsurprisingly, it fails.
To try to fix this I have attempted, in the proxy layer coded in morag.c
to switch out the function pointer with one that meets Linux x64 conventions, and then when it is called back, drive the originally provided function pointer from the proxy layer with Windows x64 conventions.
So now, morag.c
contains:-
#include <windef.h>
#include <FOO.h> /* def of FOOSTRUCT */
typedef void (WINAPI * CBFN)(long * cbparm);
static CBFN SavedCallbackFn;
void ProxyCallbackFn(long * cbparm)
{
SavedCallbackFn(cbparm);
}
void WINAPI Proxyfoo (long * parm1, FOOSTRUCT * parm2)
{
SavedCallbackFn = parm2->CallbackFn;
parm2->CallbackFn = ProxyCallbackFn;
foo(parm1, parm2);
}
However, this does not work. The ProxyCallbackFn
is successfully called by the Linux implementation of foo
, but my call to SavedCallbackFn
causes SIGSEGV
. I had thought that the "trick" needed was simply to get WINAPI
onto the function definition.
In other words is it possible to call a function in the Windows exe, by function pointer, from the Linux proxy library?
What am I missing?
As you found yeah, Windows application that calls foo provides it with a function pointer using Windows x64 conventions, but the Linux implementation of foo is invoking the function pointer under the assumption it is using Linux x64 conventions.
Detail | Windows x64 ABI | System V (Linux) x64 ABI |
---|---|---|
First integer arg regs | RCX, RDX, R8, R9 | RDI, RSI, RDX, RCX |
Shadow space | Yes (32 bytes) | No |
Stack alignment | 16-byte before call | 16-byte before call |
One way would be to convert windows abi to linux by manual math,other way would be to use libffi to call the Windows callback function using the correct ABI with helper wine_call_from_64_to_64
or similar internal helpers (in dlls/ntdll/unix/signal_x86_64.c
) but there are no well know example/documentation, and that's the hard rough way.
The other way will be to tell linux to use windows abi using __attribute__((ms_abi))
// CBFN is the Windows-side callback type
typedef void (WINAPI * CBFN)(long * cbparm);
static CBFN SavedCallbackFn;
// This is a Linux ABI function
void ProxyCallbackFn(long * cbparm)
{
// Now you want to call a Windows ABI function pointer from here
// Solution: Call into the Windows address using wine's `__attribute__((ms_abi))`
typedef void msabi_cbfn_t(long * cbparm) __attribute__((ms_abi));
((msabi_cbfn_t *)SavedCallbackFn)(cbparm);
}
the above code seems to miss imports and its probable the code above need to have #include "FOO.h",#include <windows.h> ,#include <windef.h>