如何包装要在 Wine 中使用的 Linux 库?我不断收到 BAD_IMAGE_FORMAT
How to wrap a Linux library to be used in Wine? I keep getting BAD_IMAGE_FORMAT
我有一个 Windows-only 游戏,可以通过插件和一个 Linux 库扩展,我想使用。该应用程序(Unity 游戏)在 Wine/Proton 下运行良好。我发现 Winelib 可以做到这一点,我一直在关注 User's Guide,特别是第 5 部分提到了我的确切用例。
经过一些尝试(比如从 winemaker 中删除 --single-target arg),我发现 Proton 调试日志一直显示 c000007b,这似乎是 STATUS_BAD_IMAGE_FORMAT。
请注意,我重命名了 Linux 库(并链接到它),以避免它与我的包装器之间的潜在冲突。
我只有一个 64 位版本的库,它是使用 cdecl 调用约定从 C# 代码调用的。这是我到目前为止尝试过的(以及这些的组合):
- 添加了一个包含内容的 .spec 文件(首先没有 -arch 并且具有不同的序号):
1 cdecl -arch=x86_64 LibraryFunc (long ptr ptr) LibraryFuncWine
- 添加了库的头文件和 .c 文件:
#include "library_header.h"
#include <windef.h> /* Part of the Wine header files */
enum ELibResult WINAPI LibraryFuncWine(Version version, struct CreateParams* params, struct ICore** result)
{
enum ELibResult ret = LibraryFunc(version, params, result);
return ret;
}
- 使用酿酒师
winemaker --nosource-fix --dll --nomfc -I. -L. -llinux_library .
然后 make
- 复制了整个游戏的 .dll.so 文件
- 将 .dll.so 文件复制到 steamapps/common/Proton 5.0/dist/lib64/wine/ 并将本机库复制到更高一级 (lib64/) 并删除其他文件
- 还尝试复制到 lib/ 而不是 lib64/
- 为文件添加了 dll 覆盖并将其设置为内置
- 从生成文件中删除了引用的 DLL(odbc32、ole32、oleaut32、winspool、odbccp32)和库 (uuid)
- 将
--mno-cygwin
添加到编译器标志
- 将
-m64
和 -fPIC
添加到 compiler/linker 标志(基于 Wine spec files)
- 我也试过制作一个 .def 文件并使用它
- 再次使用 winemaker 添加了
--nomsvcrt
参数和 运行 make
(头文件包括 string.h)
来自质子日志(+模块,其他人没有为我提供更多信息):
00dc:trace:module:load_dll looking for L"Z:\D\a\library\path\library_name" in L"Z:\D\gamepath;C:\windows\system32;C:\windows\system;C:\windows;C:\Program Files (x86)\Steam;.;C:\windows\system32;C:\windows;C:\windows\system32\wbem;C:\windows\system32\WindowsPowershell\v1.0"
00dc:trace:module:get_load_order looking for L"Z:\D\a\library\path\library_name.dll"
00dc:trace:module:get_load_order_value got standard key b for L"library_name"
00dc:trace:module:load_builtin_dll Trying built-in L"library_name.dll"
00dc:trace:module:load_so_dll loading L"\??\Z:\D\a\library\path\library_name.dll" from so lib "/D/Games/SteamLibrary/steamapps/common/Proton 5.0/dist/bin/../lib64/wine/library_name.dll.so"
00dc:warn:module:load_dll Failed to load module L"Z:\D\a\library\path\library_name"; status=c000007b
使用和不使用 .dll 并从 lib(library_name.dll、liblibrary_name 和 liblibrary_name.dll)开始重复同样的操作。
除了那个错误代码之外,我无法获得更多关于到底出了什么问题的日志。
在 C# 端(Proton 中的 Unity 运行)它导致 DllNotFoundException。
Wine 版本(winebuild、winegcc):5.9(暂存)- gcc 9.0.1
Proton 版本:5.0 - wine-5.0-603-g068dee4
可能只有 .so
使用 RPC 的二进制文件解决方案。
但是,由于 many/most linux 库有源代码,[没有] 的具体库是什么?是否有类似的替代库? 可能 是您的二进制库的源代码,如果您仔细寻找的话。
但是,我假设您确实必须使用二进制linux库。
但是,这可能涉及比您愿意做的更多的工作。
那么,你有多想要这个? ;-)
这是用于生产还是仅供个人使用?我假设仅供个人使用,因为您正试图让 WinX 游戏在 wine 下运行。
就 [您的 wine 程序需要调用] 的 public API 函数的数量而言,.so
有多大?涉及多少种不同的结构?
您可能需要为每个 API 调用和结构创建 glue/interface 例程。
基本方法是使用[某种]RPC[远程过程调用]机制(例如)https://en.wikipedia.org/wiki/Remote_procedure_call
您创建了一个 linux 服务器,其中包含所需的 [linux only] 库。您编写包装器例程,将 RPC 调用转换为库的 API 调用。
在游戏中,您的插件会发出发送到服务器的 RPC 调用,服务器进行处理,然后发回结果。
有一些工具可以帮助您(例如 rpcgen
)
通常,RPC 是通过 network/socket 连接完成的。出于您的目的,这可以通过本地主机或使用 AF_UNIX
连接。也许这对你的目的来说已经足够快了。
否则,您可能必须建立共享内存缓冲池并将其添加为 RPC 调用的附件。
更新:
Thanks, but can you provide more information on why it's not doable using Winelib? In the guide it says I can link a Linux library to mine I think.
或许可以写一些胶水函数。这样做可能会有问题。但是,根据我在 Discord SDK 中发现的内容,您也许可以绕过[所有]。见下文。
ABI 差异:在 WinX 中,前四个参数在 regs 中传递,而在 linux 中,前六个参数在 regs 中传递。寄存器 不同 。这个 可以 用一个 "thunk" 例程来处理,该例程建立一个新的堆栈框架并将值移动到 linux 的正确寄存器。参见:https://en.wikipedia.org/wiki/X86_calling_conventions
但是,AFAICT,至少在我的机器上,wine
最终会转到 /usr/bin/wine32
,这是一个 32 位可执行文件。但是,您的 linux 库是 64 位的。那个问题不容易解决。参见: 就我个人而言,如果必须这样做,我会选择 RPC 路线。
The library exports 2 functions, I'm only interested in 1, the rest of the functionality is via function pointers (I'm not sure these would work over wine but not there yet). The lib is actually Discord's Game SDK so there's no replacement for it, however this is just a hobby thing.
我刚刚从网站上下载 discord_game_sdk.zip
并解压。它有源代码示例程序。并且,必要的 .h
个文件。
有一个 lib/x86
子目录,其中包含:discord_game_sdk.dll
和 discord_game_sdk.dll.lib
,它们是 32 位 PE 格式文件。而且,还有一个 lib/x86_64
子目录 [它有一个 .so
]
所以,您可以直接 link 代替 linux .so
那些 PE 格式文件吗? (例如)它们可能可以用 LoadLibrary
加载
I could use sockets if there is no direct way (however wouldn't creating a unix socket have the same issues? I can definitely use localhost though).
然后,将此视为后备位置。
PF_INET
对 localhost 的套接字调用非常快,并且 [有一些 "trickery"] 你可以在你的插件 [under wine
] 和 space 之间建立共享内存 space [您创建的] 服务器程序,其中包含 linux .so
link。
我有一个 Windows-only 游戏,可以通过插件和一个 Linux 库扩展,我想使用。该应用程序(Unity 游戏)在 Wine/Proton 下运行良好。我发现 Winelib 可以做到这一点,我一直在关注 User's Guide,特别是第 5 部分提到了我的确切用例。
经过一些尝试(比如从 winemaker 中删除 --single-target arg),我发现 Proton 调试日志一直显示 c000007b,这似乎是 STATUS_BAD_IMAGE_FORMAT。
请注意,我重命名了 Linux 库(并链接到它),以避免它与我的包装器之间的潜在冲突。
我只有一个 64 位版本的库,它是使用 cdecl 调用约定从 C# 代码调用的。这是我到目前为止尝试过的(以及这些的组合):
- 添加了一个包含内容的 .spec 文件(首先没有 -arch 并且具有不同的序号):
1 cdecl -arch=x86_64 LibraryFunc (long ptr ptr) LibraryFuncWine
- 添加了库的头文件和 .c 文件:
#include "library_header.h"
#include <windef.h> /* Part of the Wine header files */
enum ELibResult WINAPI LibraryFuncWine(Version version, struct CreateParams* params, struct ICore** result)
{
enum ELibResult ret = LibraryFunc(version, params, result);
return ret;
}
- 使用酿酒师
winemaker --nosource-fix --dll --nomfc -I. -L. -llinux_library .
然后make
- 复制了整个游戏的 .dll.so 文件
- 将 .dll.so 文件复制到 steamapps/common/Proton 5.0/dist/lib64/wine/ 并将本机库复制到更高一级 (lib64/) 并删除其他文件
- 还尝试复制到 lib/ 而不是 lib64/
- 为文件添加了 dll 覆盖并将其设置为内置
- 从生成文件中删除了引用的 DLL(odbc32、ole32、oleaut32、winspool、odbccp32)和库 (uuid)
- 将
--mno-cygwin
添加到编译器标志 - 将
-m64
和-fPIC
添加到 compiler/linker 标志(基于 Wine spec files) - 我也试过制作一个 .def 文件并使用它
- 再次使用 winemaker 添加了
--nomsvcrt
参数和 运行make
(头文件包括 string.h)
来自质子日志(+模块,其他人没有为我提供更多信息):
00dc:trace:module:load_dll looking for L"Z:\D\a\library\path\library_name" in L"Z:\D\gamepath;C:\windows\system32;C:\windows\system;C:\windows;C:\Program Files (x86)\Steam;.;C:\windows\system32;C:\windows;C:\windows\system32\wbem;C:\windows\system32\WindowsPowershell\v1.0"
00dc:trace:module:get_load_order looking for L"Z:\D\a\library\path\library_name.dll"
00dc:trace:module:get_load_order_value got standard key b for L"library_name"
00dc:trace:module:load_builtin_dll Trying built-in L"library_name.dll"
00dc:trace:module:load_so_dll loading L"\??\Z:\D\a\library\path\library_name.dll" from so lib "/D/Games/SteamLibrary/steamapps/common/Proton 5.0/dist/bin/../lib64/wine/library_name.dll.so"
00dc:warn:module:load_dll Failed to load module L"Z:\D\a\library\path\library_name"; status=c000007b
使用和不使用 .dll 并从 lib(library_name.dll、liblibrary_name 和 liblibrary_name.dll)开始重复同样的操作。
除了那个错误代码之外,我无法获得更多关于到底出了什么问题的日志。
在 C# 端(Proton 中的 Unity 运行)它导致 DllNotFoundException。
Wine 版本(winebuild、winegcc):5.9(暂存)- gcc 9.0.1
Proton 版本:5.0 - wine-5.0-603-g068dee4
可能只有 .so
使用 RPC 的二进制文件解决方案。
但是,由于 many/most linux 库有源代码,[没有] 的具体库是什么?是否有类似的替代库? 可能 是您的二进制库的源代码,如果您仔细寻找的话。
但是,我假设您确实必须使用二进制linux库。
但是,这可能涉及比您愿意做的更多的工作。 那么,你有多想要这个? ;-)
这是用于生产还是仅供个人使用?我假设仅供个人使用,因为您正试图让 WinX 游戏在 wine 下运行。
就 [您的 wine 程序需要调用] 的 public API 函数的数量而言,.so
有多大?涉及多少种不同的结构?
您可能需要为每个 API 调用和结构创建 glue/interface 例程。
基本方法是使用[某种]RPC[远程过程调用]机制(例如)https://en.wikipedia.org/wiki/Remote_procedure_call
您创建了一个 linux 服务器,其中包含所需的 [linux only] 库。您编写包装器例程,将 RPC 调用转换为库的 API 调用。
在游戏中,您的插件会发出发送到服务器的 RPC 调用,服务器进行处理,然后发回结果。
有一些工具可以帮助您(例如 rpcgen
)
通常,RPC 是通过 network/socket 连接完成的。出于您的目的,这可以通过本地主机或使用 AF_UNIX
连接。也许这对你的目的来说已经足够快了。
否则,您可能必须建立共享内存缓冲池并将其添加为 RPC 调用的附件。
更新:
Thanks, but can you provide more information on why it's not doable using Winelib? In the guide it says I can link a Linux library to mine I think.
或许可以写一些胶水函数。这样做可能会有问题。但是,根据我在 Discord SDK 中发现的内容,您也许可以绕过[所有]。见下文。
ABI 差异:在 WinX 中,前四个参数在 regs 中传递,而在 linux 中,前六个参数在 regs 中传递。寄存器 不同 。这个 可以 用一个 "thunk" 例程来处理,该例程建立一个新的堆栈框架并将值移动到 linux 的正确寄存器。参见:https://en.wikipedia.org/wiki/X86_calling_conventions
但是,AFAICT,至少在我的机器上,wine
最终会转到 /usr/bin/wine32
,这是一个 32 位可执行文件。但是,您的 linux 库是 64 位的。那个问题不容易解决。参见:
The library exports 2 functions, I'm only interested in 1, the rest of the functionality is via function pointers (I'm not sure these would work over wine but not there yet). The lib is actually Discord's Game SDK so there's no replacement for it, however this is just a hobby thing.
我刚刚从网站上下载 discord_game_sdk.zip
并解压。它有源代码示例程序。并且,必要的 .h
个文件。
有一个 lib/x86
子目录,其中包含:discord_game_sdk.dll
和 discord_game_sdk.dll.lib
,它们是 32 位 PE 格式文件。而且,还有一个 lib/x86_64
子目录 [它有一个 .so
]
所以,您可以直接 link 代替 linux .so
那些 PE 格式文件吗? (例如)它们可能可以用 LoadLibrary
I could use sockets if there is no direct way (however wouldn't creating a unix socket have the same issues? I can definitely use localhost though).
然后,将此视为后备位置。
PF_INET
对 localhost 的套接字调用非常快,并且 [有一些 "trickery"] 你可以在你的插件 [under wine
] 和 space 之间建立共享内存 space [您创建的] 服务器程序,其中包含 linux .so
link。