【问题标题】:MSVC linker seems to link wrong symbol without `__declspec(dllimport)`MSVC 链接器似乎在没有 `__declspec(dllimport)` 的情况下链接了错误的符号
【发布时间】:2021-11-04 00:20:29
【问题描述】:

我有两个 C++ 源代码(a.ccb.cc),一个将编译成共享库(a.dll),另一个将编译成可执行文件(b.exe)并链接a.dll,但 MSVC 链接器在没有 __declspec(dllimport) 的情况下无法正确链接 a.dll 中的符号。


a.cc

extern "C" int mysym;
// extern "C" __declspec(dllexport) int mysym;

int mysym = 123;
cl -std:c++17 -O2 -fp:fast -EHsc  -I"C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30133\include" -I"C:\Program Files (x86)\Windows Kits\10\Include\10.0.19041.0\ucrt" -I"C:\Program Files (x86)\Windows Kits\10\Include\10.0.19041.0\shared" -I"C:\Program Files (x86)\Windows Kits\10\Include\10.0.19041.0\um" -DNOMINMAX  -LD .\a.cc -Fe:a.dll  -link -LIBPATH:"C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30133\lib\x64" -LIBPATH:"C:\Program Files (x86)\Windows Kits\10\Lib\10.0.19041.0\um\x64" -LIBPATH:"C:\Program Files (x86)\Windows Kits\10\Lib\10.0.19041.0\ucrt\x64" -EXPORT:mysym

b.cc

#include <iostream>

using namespace std;

// this is OK
// extern "C" __declspec(dllimport) int mysym;

// this is wrong
extern "C" int mysym;

int main() {
    // expect output 123
    std::cerr << mysym << std::endl;
    std::cerr << &mysym << std::endl;
    return 0;
}
cl -std:c++17 -O2 -fp:fast -EHsc .\b.cc a.lib -I"C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30133\include" -I"C:\Program Files (x86)\Windows Kits\10\Include\10.0.19041.0\ucrt" -I"C:\Program Files (x86)\Windows Kits\10\Include\10.0.19041.0\shared" -I"C:\Program Files (x86)\Windows Kits\10\Include\10.0.19041.0\um" -DNOMINMAX -Fe:b.exe  -link -LIBPATH:"C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30133\lib\x64" -LIBPATH:"C:\Program Files (x86)\Windows Kits\10\Lib\10.0.19041.0\um\x64" -LIBPATH:"C:\Program Files (x86)\Windows Kits\10\Lib\10.0.19041.0\ucrt\x64"

当我执行b.exe时,命令输出一个随机数,而不是123。当我添加__declspec(dllimport) 时,该命令按预期工作。

为了弄清楚原因,我在b的构建命令中转储了-VERBOSE的日志,发现了一些线索:

// log without __declspec(dllimport)
Searching libraries
    searching a.lib:
      Found mysym
        ref in b.obj
        loaded a.lib(a.dll)

// log with __declspec(dllimport)
Searching libraries
    searching a.lib:
      Found mysym
        ref in a.exp
        loaded a.lib(a.dll)

似乎符号查找错误,我的问题是:

  1. 如果链接器未发现符号或符号查找错误,是否会产生链接错误,而不是将其链接到随机未初始化的地址?
  2. 我可以告诉编译器或链接器mysym 需要__declspec(dllimport) 而不更改源代码吗?因为我的项目比较大,改源码是不可能的。

【问题讨论】:

    标签: c++ windows visual-studio visual-c++ linker


    【解决方案1】:

    Extern "C" int mysym(不带__declspec(dllimport))符合C++语法。所以不会报错。您可以使用 DLL 中的命名空间来区分源代码中的变量。并且链接出错时会报错。

    在你的DLL中使用下面的方式可以避免在源代码中添加__declspec(dllimport)。

    #ifdef _DLLEXPORT
    #define _DLL  __declspec(dllexport)
    #else
    #define _DLL __declspec(dllimport)
    #endif 
    
    #define _DLLEXPORT
    

    【讨论】:

      【解决方案2】:

      需要导出函数,不像 Windows 上的大多数 *nix 平台,符号默认为模块私有。

      您不必使用 declspec(dllexport),还有 .def 文件选项甚至是命令行参数(传递给 DLL 项目的链接器),但其中任何一个通常都比较麻烦比 __declspec(dllexport),特别是对于 C++ 符号,因为其中任何一个都需要修饰符号名称。

      找到其他符号时不可能出错,因为链接器无法知道找到的不是所需的。

      【讨论】:

      • 感谢您的回复,但我认为可以检测到此类错误。先转储链接日志,然后使用dumpbin -SYMBOLS b.obj,dumpbin 会告诉我mysym 符号在b.obj 中不存在。但我认为会有更好的方法。
      猜你喜欢
      • 1970-01-01
      • 2011-11-23
      • 2013-11-07
      • 1970-01-01
      • 2016-09-13
      • 2020-08-09
      • 1970-01-01
      • 1970-01-01
      • 2018-11-06
      相关资源
      最近更新 更多