Wine环境下Linux代理库通过函数指针调用Windows程序函数的问题
在Wine环境中从Linux代理库调用Windows程序函数指针的解决方案
结论:可以实现,但必须处理Windows x64与Linux x64的调用约定差异、栈对齐要求,以及参数寄存器的映射
你的问题核心在于跨调用约定的回调调用:Windows x64和Linux x64(System V)的调用约定在参数传递、栈清理、栈对齐规则上完全不同,直接转换指针调用必然导致崩溃。以下是你遗漏的关键细节和解决方法:
1. 明确标记调用约定,避免隐式转换
必须为Windows回调和Linux回调分别定义明确的类型,并用编译器属性强制指定调用约定:
// Windows x64 调用约定的回调类型 typedef void (__attribute__((ms_abi)) *WindowsCallbackFn)(int arg); // Linux x64 (System V) 调用约定的回调类型 typedef void (__attribute__((sysv_abi)) *LinuxCallbackFn)(int arg); // 保存Windows侧传入的回调指针 static WindowsCallbackFn saved_win_callback;
2. 手动处理参数寄存器映射与栈对齐
Windows x64用RCX/RDX/R8/R9传递前4个整数参数,要求调用前栈16字节对齐;而Linux x64用RDI/RSI/RDX/RCX/R8/R9传递前6个参数,调用前栈8字节对齐。直接调用会导致参数传递错误和栈对齐违规,这是你触发SIGSEGV的主要原因。
需要用汇编封装调用逻辑,完成寄存器转换和栈对齐:
// 给Linux侧提供的代理回调,遵循System V约定 void __attribute__((sysv_abi)) proxy_linux_callback(int arg) { __asm__ __volatile__ ( "sub $8, %%rsp\n" // 将栈从8字节对齐调整为16字节对齐 "mov %%rdi, %%rcx\n" // 把System V的第一个参数(RDI)转到Windows的RCX寄存器 "call *%1\n" // 调用Windows回调指针 "add $8, %%rsp\n" // 恢复栈指针 : : "D"(arg), "r"(saved_win_callback) : "rcx", "rax", "rdx", "r8", "r9", "r10", "r11", "memory" ); }
3. 代理层的正确映射逻辑
在你的foo代理函数中,将Windows传入的回调指针保存,同时把代理回调指针传给Linux的foo实现:
// Windows DLL导出的foo函数,遵循Windows调用约定 void __attribute__((ms_abi)) foo(WindowsCallbackFn callback) { saved_win_callback = callback; // 调用Linux SO中的foo,传入代理回调 linux_foo(proxy_linux_callback); }
4. 编译与链接的正确参数
用winegcc编译代理DLL时,确保链接Wine的系统库,并指定编译为Windows DLL:
winegcc -shared -o morag.dll morag.c morag.spec -lkernel32
spec文件中要正确导出foo函数:
@ foo
内容的提问来源于stack exchange,提问作者Morag Hughson
相关产品推荐
相关产品推荐

