问题描述
我正在尝试链接到使用 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 接口通常跨编译器兼容的说法是不正确的? 或者这是它不起作用的特殊情况?如果是后者,什么可能导致链接失败?如果我重新编译为共享库 (dll
s) 而不是静态库(即使我仍然使用 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.dll
、libopenal.dll.a
和 libopenal.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.lib
、libavformat
、libavcodec
、libavutil
、{{1} } 和 libavdevice
,到目前为止所有这些都有效。
有点复杂,但它似乎对我很有效,所以我希望这能帮助其他有同样问题的人。