首页 > 解决方案 > 如何包装要在 Wine 中使用的 Linux 库?我不断收到 BAD_IMAGE_FORMAT

问题描述

我有一个仅限 Windows 的游戏,可以通过插件和我想使用的 Linux 库进行扩展。该应用程序(Unity 游戏)在 Wine/Proton 下运行良好。我发现 Winelib 可以做到这一点,并且我遵循了用户指南,特别是第 5 节,其中提到了我的确切用例。

经过一些尝试(比如从酿酒师中删除 --single-target arg),我到了 Proton 调试日志一直显示 c000007b 似乎是 STATUS_BAD_IMAGE_FORMAT 的地步。

请注意,我重命名了 Linux 库(并与之链接),以避免它与我的包装器之间的潜在冲突。

我只有一个 64 位版本的库,它是使用 cdecl 调用约定从 C# 代码调用的。这是我迄今为止尝试过的(以及这些的组合):

1 cdecl -arch=x86_64 LibraryFunc (long ptr ptr) LibraryFuncWine
#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;
}

从质子日志(+模块,其他人没有为我提供更多信息):

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# 端(由 Unity 在 Proton 中运行)它会导致 DllNotFoundException。

Wine 版本 (winebuild, winegcc): 5.9 (staging) - gcc 9.0.1

质子版本:5.0 - wine-5.0-603-g068dee4

标签: cwinewinelib

解决方案


可能只有.so使用 RPC 才能为您的二进制文件提供解决方案。

但是,由于许多/大多数 linux 库都有源代码,那么具体的库是什么[没有]?是否有类似的替代库?如果您足够努力,可能会有二进制库的源代码。

但是,我假设您必须使用二进制 linux 库。

但是,这可能涉及比您愿意做的更多的工作。 那么,你有多想要这个?;-)

这是用于生产还是仅供个人使用?我假设只是供个人使用,因为您正试图让 WinX 游戏在 wine 下工作。

.so就公共 API 函数的数量而言 [您的 wine 程序需要调用的]有多大?涉及多少种不同的结构?

您可能需要为每个 API 调用和结构创建粘合/接口例程。

基本方法是使用 [某种] RPC [远程过程调用] 机制(例如)https://en.wikipedia.org/wiki/Remote_procedure_call

您创建一个包含所需 [仅限 linux] 库的 linux 服务器。您编写将 RPC 调用转换为库的 API 调用的包装例程。

在游戏中,您的插件会发出 RPC 调用,然后发送到服务器,服务器进行处理,然后发回结果。

有一些工具可以帮助您(例如rpcgen

通常,RPC 是通过网络/套接字连接完成的。出于您的目的,这可以通过 localhost 或使用AF_UNIX连接。也许这对您的目的来说已经足够快了。

否则,您可能必须建立一个共享内存缓冲池并将其添加为 RPC 调用的附件。


更新:

谢谢,但你能提供更多关于为什么使用 Winelib 不可行的信息吗?在指南中,它说我可以将 Linux 库链接到我的库。

可以编写一些胶水函数。这样做可能会有问题。但是,根据我在 Discord SDK 中找到的内容,您也许可以绕过 [all] 这一点。见下文。


ABI 的区别:在 WinX 中,前四个参数在 regs 中传递,而在 linux 中,前六个参数在 regs 中传递。寄存器不同。这可以通过“thunk”例程来处理,该例程建立一个新的堆栈帧并将值移动到 linux 的正确寄存器。见:https ://en.wikipedia.org/wiki/X86_calling_conventions

但是,至少在我的机器上,AFAICTwine最终会/usr/bin/wine32使用 32 位可执行文件。但是,你的 linux lib 是 64 位的。这个问题不容易解决。请参阅:是否可以在 64 位 Linux 中的同一可执行文件中同时使用 64 位和 32 位指令?就个人而言,如果我必须这样做,我会走 RPC 路线。


该库导出 2 个函数,我只对 1 个感兴趣,其余的功能是通过函数指针(我不确定这些是否可以在 wine 上工作,但还没有)。该库实际上是 Discord 的 Game SDK,因此无法替代它,但这只是一种爱好。

我刚从discord_game_sdk.zip官网下载并解压。它有源代码示例程序。并且,必要的.h文件。

有一个lib/x86子目录有:discord_game_sdk.dll并且discord_game_sdk.dll.lib是 32 位 PE 格式文件。而且,还有一个lib/x86_64子目录 [其中有一个.so]

那么,代替 linux .so,您可以直接链接到那些 PE 格式文件吗?(例如)它们可能可以加载LoadLibrary


如果没有直接的方法,我可以使用套接字(但是创建 unix 套接字不会有同样的问题吗?我当然可以使用 localhost)。

然后,将其视为后备位置。

PF_INET对 localhost 的套接字调用非常快,并且 [使用一些“技巧”] 您可以在您的插件 [在] 和一个将 linux链接到wine其中的服务器程序 [您创建的]之间建立共享内存空间。.so


推荐阅读