【问题标题】:Linker error when C++ executable links in Fortran library and main in C++ libraryFortran 库中的 C++ 可执行链接和 C++ 库中的主链接时出现链接器错误
【发布时间】:2020-02-02 21:44:30
【问题描述】:

我有一个基于 CMake 的项目,包含三个目标:

  • 用 Fortran 编写的静态库 FortLib
  • 一个静态库LibWithMain,用C++编写,包含int main()的定义。
  • 一个可执行文件App 链接到上述两个库中。

这是 CMakeList 的内容:

cmake_minimum_required(VERSION 3.7.2)
project(Mcve LANGUAGES C CXX Fortran)

add_library(FortLib STATIC fort.f90)
target_compile_options(FortLib PRIVATE /names:lowercase /assume:underscore /iface:cref)

add_library(LibWithMain STATIC main.cpp)

add_executable(App app.cpp)
target_link_libraries(App PRIVATE FortLib LibWithMain)

(可用于重现问题的源文件内容见底部)

我的问题是链接App 会导致以下链接器错误:

libifcoremdd.lib(for_main.obj) : error LNK2019: unresolved external symbol MAIN__ referenced in function main

请注意,此引用来自 libifcoremdd.lib,这是一个显然隐式链接的 Intel Fortran 库。

如果函数main 直接在App 中定义,则不会发生这种情况。这可以通过在上面的 CMakeList 中交换文件 main.cppapp.cpp 来显示(以便在应用程序内部定义 main)。然后一切都建立并成功链接。事实上,main 的定义来自 LibWithMain,这让链接器感到困惑。

在我的真实代码中,LibWithMain 是 Boost.Test,因此将 main 移出它对我来说并不是一个真正的选择。

静态库的顺序无关紧要:无论链接行上哪个库跟随哪个库,都会出现错误。

我的工具链是 Visual Studio 2017 和 Intel Fortran 18,我的平台是 Win64(Visual Studio 术语中的“x64”)。此时不需要支持其他编译器/平台。

我是一名 C++ 开发人员,对 Fotran 或英特尔 Fortran 生态系统几乎一无所知,所以我不知道是什么原因造成的,也不知道如何解决。因此,这是我的问题:

是什么导致了链接器错误,我该如何解决?


上面 CMakeList 中使用的这些简单文件足以重现问题:

fort.f90

integer function fortfunc
  implicit none
  fortfunc = 42
end function

main.cpp

#include <iostream>

int work();

int main()
{
  std::cout << work() << std::endl;
  return 0;
}

app.cpp

extern "C" int fortfunc_();

int work()
{
  return fortfunc_();
}

【问题讨论】:

  • 你有没有试过改变两个静态库的链接顺序,即将LibWithMain移到FortLib前面?
  • @vre 在您发表评论之前没有,但现在我有。它没有帮助。我将编辑问题以包含这一点。

标签: c++ cmake linker fortran intel


【解决方案1】:

我不知道 cmake 是如何工作的,但我猜你需要反过来做。 main 可能是您工具链中的特殊功能,不应该存在于库中。

  1. 构建 fortran 库
  2. 在没有 main (app.cpp) 的情况下构建 c 库
  3. 同时使用 c lib 和 fortran lib 构建 main。

在构建 c lib 时,它可能不会抱怨缺少外部组件,因为它是一个库,并不是所有事情都需要解决。

此外,在 app.cpp 中,您需要记住的是,在 Fortran 中,被调用者将参数解栈,但在 C 中,调用者会解栈。

当被调用者取消堆栈时,您需要 __stdcall 作为声明的一部分。这曾经是旧 MS 编译器上的 extern PASCAL。当调用者出栈时,您可以选择添加 __cdecl。

【讨论】:

  • Windows x64 使用更现代的调用约定,不涉及 caller-pops。如果您正在构建过时的 32 位代码,则 stdcall 与 cdecl 只是一个可能的问题。 (唯一可能的区别是 vectorcall 与 fastcall,但 IIRC 仅在 XMM/YMM 寄存器中按值传递/返回 __m128__m256 SIMD 向量内在类型的方式不同)
  • 猜猜这是 op 的另一个问题 - 它是 32 位还是 64 位。
  • 调用约定等由我传入的 Fortran 选项正确处理。请注意,如果文件 main.cppapp.cpp 被交换,则代码链接很好,所以问题完全出在让main 来自图书馆(问题是这样说的,但我现在对其进行了编辑以使其更明确)。我正在构建 64 位;也将其添加到 Q 中。
猜你喜欢
  • 2018-12-11
  • 1970-01-01
  • 2015-12-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多