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>