【问题标题】:Linker reads library but can't find symbol within it? Unresolved External Symbol but only for Win32 and not x64链接器读取库但在其中找不到符号?未解析的外部符号,但仅适用于 Win32 而不是 x64
【发布时间】:2013-11-22 18:49:21
【问题描述】:

背景

我有一个 C 天文库,我想在我的 C++ 应用程序中使用它。

我在 Visual Studio 2012 Express 中以 Win32 和 x64 配置构建了它,并且:

  • 动态调试 (.dll)
  • 动态发布 (.dll)
  • 静态调试 (.lib)
  • 静态版本 (.lib)

...所以总共 2 * 4 = 8 个二进制文件(不包括 *.pdb 文件等)

然后我使用 Batch Build 来构建所有配置,因为有时我需要不同的版本,而且我发现一开始就完成所有这些工作,并且在容易混淆的情况下,通过一个过程来完成这一切要好于随意。

在我的 C++ 应用程序中,我有相同的过程,并根据名称链接到库。具体来说,在我的项目属性 Linker -> Input 字段中,我有:

SwissEphemeris_$(Platform)_$(Configuration).lib

...并且 其他库目录 已正确设置。一切似乎都是正确的。库文件在库目录中。

问题来了:

在我共有的 8 个配置中,除了两个之外,所有配置都正确链接和构建:

  1. Win32 动态调试
  2. Win32动态发布

对于这两种配置,相同的链接器错误:

main.obj : error LNK2019: unresolved external symbol _swe_close referenced in function _main

我尝试了一些方法来诊断或修复,但都没有奏效:

  1. 在新的解决方案/项目中从头开始重建库,确保 Win32 和 x64 除了/MACHINE 链接器标志之外没有偏差
  2. 创建一个新的解决方案/项目,只使用一个 main(),在 Win32 动态调试配置中调用这个库函数 swe_close() 并链接到该 *.lib。

错误总是一样的,如上所示。我打开了详细的链接器输出,真正让我困惑的是链接器似乎成功地找到并读取了SwissEphemeris_Win32_DynamicDebug.lib 文件,但仍然无法在其中找到swe_close() 符号。即使dumpbin.exe 显示该符号,我需要的所有其他符号都在其中。

1>  Unused libraries:
1>    E:\Data\Code\lib\SwissEphemeris\SwissEphemeris_Win32_DynamicDebug.lib
1>    C:\Program Files (x86)\Windows Kits\8.0\lib\win8\um\x86\user32.lib
1>    C:\Program Files (x86)\Windows Kits\8.0\lib\win8\um\x86\gdi32.lib
1>    C:\Program Files (x86)\Windows Kits\8.0\lib\win8\um\x86\winspool.lib
1>    C:\Program Files (x86)\Windows Kits\8.0\lib\win8\um\x86\comdlg32.lib
1>    C:\Program Files (x86)\Windows Kits\8.0\lib\win8\um\x86\advapi32.lib
1>    C:\Program Files (x86)\Windows Kits\8.0\lib\win8\um\x86\shell32.lib
1>    C:\Program Files (x86)\Windows Kits\8.0\lib\win8\um\x86\ole32.lib
1>    C:\Program Files (x86)\Windows Kits\8.0\lib\win8\um\x86\oleaut32.lib
1>    C:\Program Files (x86)\Windows Kits\8.0\lib\win8\um\x86\uuid.lib
1>    C:\Program Files (x86)\Windows Kits\8.0\lib\win8\um\x86\odbc32.lib
1>    C:\Program Files (x86)\Windows Kits\8.0\lib\win8\um\x86\odbccp32.lib
1>    E:\Programs\VS2012\VC\lib\OLDNAMES.lib
1>  
1>main.obj : error LNK2019: unresolved external symbol _swe_close referenced in function _main
1>E:\Data\Code\test\Debug\test.exe : fatal error LNK1120: 1 unresolved externals
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

挠头

有没有人知道为什么链接器找不到符号,并且 仅 用于 Win32 动态调试/发布,但对于所有其他 6 种配置都可以正常工作?

为了确保库确实是为 Win32/X86 构建的,这里是库项目属性中的链接器命令行选项:

(路径与上面的路径略有不同——链接器尝试查找库的位置——因为库首先构建到这个 'bin' 目录,然后复制到库目录,这绝对是正在发生。)

/OUT:"E:\Data\Code\libBuilders\SwissEphemeris\bin\SwissEphemeris_Win32_DynamicDebug.dll" 
/MANIFEST 
/NXCOMPAT 
/PDB:"E:\Data\Code\libBuilders\SwissEphemeris\bin\SwissEphemeris_Win32_DynamicDebug.pdb" 
/DYNAMICBASE 
/IMPLIB:"E:\Data\Code\libBuilders\SwissEphemeris\bin\SwissEphemeris_Win32_DynamicDebug.lib" 
/DEBUG 
/DLL 
/MACHINE:X86 
/SAFESEH 
/PGD:"E:\Data\Code\libBuilders\SwissEphemeris\bin\SwissEphemeris_Win32_DynamicDebug.pgd" 
/SUBSYSTEM:CONSOLE 
/MANIFESTUAC:"level='asInvoker' uiAccess='false'" 
/ManifestFile:"E:\Data\Code\libBuilders\SwissEphemeris\obj\SwissEphemeris_Win32_DynamicDebug\SwissEphemeris_Win32_DynamicDebug.dll.intermediate.manifest" 
/ERRORREPORT:PROMPT 
/NOLOGO 

来自dumpbin.exe /exports的输出

E:\Data\Code\lib\SwissEphemeris>dumpbin /exports SwissEphemeris_Win32_DynamicDebug.dll
Microsoft (R) COFF/PE Dumper Version 11.00.50727.1
Copyright (C) Microsoft Corporation.  All rights reserved.


Dump of file SwissEphemeris_Win32_DynamicDebug.dll

File Type: DLL

  Section contains the following exports for SwissEphemeris_Win32_DynamicDebug.dll

    00000000 characteristics
    528041A6 time date stamp Sun Nov 10 19:32:06 2013
        0.00 version
           1 ordinal base
         131 number of functions
         131 number of names

    ordinal hint RVA      name

          1    0 00001195 _swe_azalt@40 = @ILT+400(_swe_azalt@40)
          2    1 000011FE _swe_azalt_d@28 = @ILT+505(_swe_azalt_d@28)
          3    2 000012AD _swe_azalt_rev@24 = @ILT+680(_swe_azalt_rev@24)
          4    3 00001357 _swe_azalt_rev_d@20 = @ILT+850(_swe_azalt_rev_d@20)
          5    4 0000126C _swe_calc@24 = @ILT+615(_swe_calc@24)
          6    5 000011BD _swe_calc_d@20 = @ILT+440(_swe_calc_d@20)
          7    6 0000105F _swe_calc_ut@24 = @ILT+90(_swe_calc_ut@24)
          8    7 00001235 _swe_calc_ut_d@20 = @ILT+560(_swe_calc_ut_d@20)
          9    8 00001389 _swe_close@0 = @ILT+900(_swe_close@0)
         10    9 00001212 _swe_close_d@4 = @ILT+525(_swe_close_d@4)
         ...

在库头文件中为导出定义 DLL 的位置。仅定义了 MAKE_DLL 和 PASCAL,因此此处唯一有效的语句是 #define PASCAL_CONV PASCAL 和 #else /* 32bit DLL */ 块。

/* DLL defines */
#ifdef MAKE_DLL
  #if defined (PASCAL)
    #define PASCAL_CONV PASCAL 
  #else
    #define PASCAL_CONV
  #endif
  #ifdef MAKE_DLL16 /* 16bit DLL */
    /* We compiled the 16bit DLL for Windows 3.x using Borland C/C++ Ver:3.x
       and the -WD or -WDE compiler switch. */
    #define EXP16 __export 
    #define EXP32 
  #else /* 32bit DLL */
    /* To export symbols in the new DLL model of Win32, Microsoft 
       recommends the following approach */ 
    #define EXP16 
    #define EXP32  __declspec( dllexport )
  #endif
#else 
  #define PASCAL_CONV 
  #define EXP16 
  #define EXP32 
#endif 

...那么实际的函数swe_close() 声明如下:

ext_def( void ) swe_close(void);

他们使用了大量的宏步法,所以这可以解决:

extern __declspec(dllexport) void far PASCAL swe_close();

我不熟悉far 和PASCAL。这些会干扰什么吗?为什么 x64 配置可以正常工作,但 Win32 会中断?

【问题讨论】:

  • 您对 dumpbin.exe 使用了哪些选项?当您使用 /SYMBOLS 选项时,列表是否实际上显示 _swe_close 在文件中?
  • 好问题。我没有使用 /SYMBOLS 选项,而是使用 /EXPORTS 选项,它确实在其中显示了符号。 /SYMBOLS 没有在其中显示符号 swe_close() (也没有其他许多符号),但是当我在 确实 工作的其他库配置上尝试 /SYMBOLS 时,它显示同样缺少许多符号. /EXPORTS 似乎显示了所有内容。
  • 很可能是您的调用约定不匹配。链接器正在尝试使用__cdecl 调用约定导入swe_close()(请参阅Name Decoration)。这与导出签名匹配吗?
  • 感谢您提出这个问题。我所有的 C 库配置和我的所有 C++ 项目配置都设置为 __cdecl 约定。我找不到任何调用约定的混合。这真的很奇怪。是否有其他我们没有想到的设置或标志可能是罪魁祸首?需要注意的是,C 库自然设置为 /TC(编译为 C 代码)。也许这很明显,但以防万一。
  • @nedshares,我看到您的问题已得到解答。这是更多信息。 far 表示生成的函数地址应该是 16 位段和 16 位偏移的远或 32 位地址,PASCAL 表示使用 Pascal 调用约定而不是 C 调用约定。这些的使用表明这个包已经很旧了。 far 不再使用,并且 PASCAL 倾向于与某些 Windows API 调用一起使用。看到这个en.wikipedia.org/wiki/X86_calling_conventions

标签: c++ visual-studio-2012 dll unresolved-external


【解决方案1】:
 9    8 00001389 _swe_close@0 = @ILT+900(_swe_close@0)

DLL 中的函数名与链接器正在查找的函数名明显不匹配。 DLL工程已经编译成__stdcall函数,从@0装饰可以看出,链接器正在寻找__cdecl函数。

这可能是由编译器选项 /Gd 与 /Gz 引起的,但这不太可能,您已经检查过了。更可能的原因是声明导出函数的 .h 文件,以便另一个项目可以#include 它并使用 DLL。这通常使用宏汤来完成,以便 __declspec(dllexport) 和 __declspec(dllimport) 属性正确使用。并且调用约定应始终显式声明,通常使用另一个宏。就像在 Windows SDK 标头中使用 WINAPI 宏的方式一样。我会为那汤付钱的原因。

【讨论】:

  • 感谢@HansPassant。我会进一步研究这个。我一直在想这可能是在库中定义和未定义的这些宏。这是我仍在学习理解的东西。我在整个库中(在主标题中)找到了一个 __declspec(dllexport) 位置,如果有帮助,我将其周围的语句粘贴到上面的帖子中。我正在尝试自己了解这些约定。
  • 我正在考虑将FAR 和PASCAL 从整个库中删除——或者至少从宏中删除。看这里what is FAR PASCAL?,它说它们来自可能是 16 位的老式代码。也许我可以对这个库进行现代化改造,使其更适合我的现代使用。
  • 正如我猜想的那样,PASCAL 与 __stdcall 相同,并且您的标头仅在定义 MAKE_DLL 时才错误地定义它。心理调试得一分:)
  • 刚刚确认!最后,当我将 PASCAL 和 FAR 定义为无时,我的 C++ 应用程序与 Win32 动态调试/发布中的库正确链接。其中一些库和代码可能非常棘手且难以维护。
猜你喜欢
  • 2012-06-22
  • 1970-01-01
  • 1970-01-01
  • 2016-08-26
  • 1970-01-01
  • 1970-01-01
  • 2012-03-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多