使用 C 接口将 MinGW 库链接到 MSVC 应用程序

问题描述

我正在尝试链接到使用 OpenAL soft 编译的 Media Autobuild Suite 库,但我从 Visual Studio 收到以下错误

libopenal.a(source.cpp.o) : Fatal error LNK1143: invalid or corrupt file: no symbol for COMDAT section 0xA

我的应用程序使用 C++ 并直接在 Visual Studio 2019 中编译(但是,使用 VS2017 工具集)。 OpenAL soft 是用 C++ 编写的,但暴露了一个 C 接口,MAB Suite 使用 MinGW/gcc 编译并生成一个 libopenal.a 静态库文件

我从多个其他问题(例如 From MinGW static library (.a) to Visual Studio static library (.lib)How to use libraries compiled with MingW in MSVC?)中了解到,由于名称修改,使用不同编译器编译的目标文件通常与 C++ 兼容,但是通常与 C 链接兼容。因为 C 不使用名称修饰,并且因为 ABI(通常)依赖于操作系统,所以在同一平台上编译的具有 C 接口的库通常是兼容的。

尽管如此,我还是遇到了链接错误,即上面的 LNK1143。我已经确认包含的标头使用 extern "C" {提示 C 链接,并且两个构建的目标平台 (x64) 是相同的。我还按照 this answer 的建议链接libgcc.a,但没有收到任何链接错误

这是否意味着 C 接口通常跨编译器兼容的说法是不正确的? 或者这是它不起作用的特殊情况?如果是后者,什么可能导致链接失败?如果我重新编译为共享库 (dlls) 而不是静态库(即使我仍然使用 MinGW 的 .a 文件而不是 .lib),我的运气会更好吗?

我无法从 MSVC 更改我的主应用程序的编译器。我打算在未来使用 MAB 套件中的更多库,因此如果可能的话,我更愿意使用 MinGW 来处理这些依赖项,因为我不想手动重新编译所有 70 多个。

感谢任何帮助。提前致谢。

解决方法

混合编译器很棘手并且容易出现问题。

在一些非常简单的情况下它可能会起作用,但肯定有很多情况您会遇到问题,例如:

  • 如果不同的组件使用不同的运行时库
  • 如果内存管理是混合的(例如,忘记使用 MinGW 中的 malloc() 在 MSVC 中释放用 free() 分配的内存)
  • 在 C++ 中使用异常处理时

我的建议是使用相同的编译器(甚至是该编译器的相同版本)来完成所有工作。

特别是在您的情况下,OpenAL 可以使用 MinGW-w64 构建。所以也许你应该研究一下,而不是从网上下载一些预先构建的版本。

或者 - 稍微简单一点 - 使用 MSYS2 并使用其 pacman 包管理器来获得自己的 MinGW-w64 版本的 OpenAL。

,

我想出了什么对我有用,所以我会分享。

我无法像最初尝试的那样在编译器之间链接静态库。我的理解是,保存在 lib 中以允许链接时代码生成的额外信息是特定于编译器的。 Brecht Sanders's answer 概述了代码不兼容的一些可能原因。

不过,我可以通过一些额外的步骤链接到共享库。

使用相同的套件(请参阅问题),我编译为共享并得到 libopenal.dlllibopenal.dll.alibopenal.def。就我而言,.def 文件是由套件生成的。根据 this answer,您可以使用 gcc 生成 .def 文件:

gcc -shared -o your_dll.dll your_dll_src.c -Wl,--output-def,your_dll.def

尝试链接到 libopenal.dll.a 仍然给我错误(我不知道确切的原因,我已经丢弃了日志。)我所做的是从 {{} 生成一个 .lib 文件1}} 文件。在 Visual Studio 的内置终端中:

.def

这在工作目录中生成了一个 lib /machine:x64 /def:libopenal.def 文件。链接到这个文件效果很好,我能够在运行时成功加载 dll。

我已经用套件中的许多其他 MinGW 编译库测试了同样的方法,包括 libopenal.liblibavformatlibavcodeclibavutil、{{1} } 和 libavdevice,到目前为止所有这些都有效。

有点复杂,但它似乎对我很有效,所以我希望这能帮助其他有同样问题的人。