Linker reads library but can't find symbol within it? Unresolved External Symbol but only for Win32 and not x64

nedshares picture nedshares · Nov 11, 2013 · Viewed 12.7k times · Source

Background

I have a C astronomical library that I want to use in my C++ application.

I've built it in Visual Studio 2012 Express in both Win32 and x64 configurations, and:

  • dynamic debug (.dll)
  • dynamic release (.dll)
  • static debug (.lib)
  • static release (.lib)

...so that is 2 * 4 = 8 total binaries (not counting *.pdb files, etc.)

Then I use Batch Build to build all configurations, as sometimes I need different versions and I find getting this all done at the start and with a process is better than haphazardly when it's easy to mix things up.

Well in my C++ app, I have the same process, and link to the library based on the name. Specifically, in my project properties Linker -> Input field, I have:

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

...and the Additional Library Directories is properly set. Everything seems right. The library files are in the library directory.

Here is the trouble:

Out of the 8 total configurations I have, all are linking and building properly except for two:

  1. Win32 dynamic debug
  2. Win32 dynamic release

For both of these configurations, the same linker error:

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

I have tried some things to diagnose or fix, but none have worked:

  1. rebuilding the library from scratch in a new Solution/Project, ensuring no deviation between Win32 and x64 other than the /MACHINE linker flag
  2. creating a new fresh Solution/Project with nothing but a main() that called this one library function swe_close() in Win32 dynamic debug configuration and linked to that *.lib.

The error is always the same, as shown above. I've turned on verbose linker output, and what really puzzles me is that the linker seems to successfully find and read the SwissEphemeris_Win32_DynamicDebug.lib file, but still cannot find the swe_close() symbol within it. Even when dumpbin.exe shows that symbol, among all the other ones I need, are in it.

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 ==========

scratching head

Does anyone have any idea why the linker is failing to find the symbol, and ONLY for Win32 dynamic debug/release, but working fine for all 6 other configurations?

Just to be sure that the library was indeed built for Win32/X86, here is the linker command-line options from the library project's properties:

(paths are slightly different than the paths above--where the linker attempts to find the library--because the library is first built to this 'bin' directory and then copied to the library directory, which is definitely happening.)

/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 

Output from 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)
         ...

Where the DLL is defined for export in the library header file. Only MAKE_DLL and PASCAL are defined, so the only statements here that are active are the #define PASCAL_CONV PASCAL and the #else /* 32bit DLL */ block.

/* 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 

...Then the actual function swe_close() declaration looks like:

ext_def( void ) swe_close(void);

They use a lot of macro footwork, so this resolves to:

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

I am unfamiliar with the far and the PASCAL. Could these be interfering with anything? And why would the x64 configuration work fine with this, but the Win32 break?

Answer

Hans Passant picture Hans Passant · Nov 12, 2013
 9    8 00001389 _swe_close@0 = @ILT+900(_swe_close@0)

There's a clear mismatch between the function name in the DLL and the one that the linker is looking for. The DLL project has it compiled to an __stdcall function, you can tell from the @0 decoration, the linker is looking for a __cdecl function.

That can be caused by a compiler option, /Gd vs /Gz but that's unlikely and you already checked it. The more likely cause is the .h file where the exported functions are declared so that another project can #include it and use the DLL. That's usually done with macro soup so that the __declspec(dllexport) and __declspec(dllimport) attributes are correct for usage. And the calling convention should always be explicitly declared, typically with another macro. Like the way the WINAPI macro is used in Windows SDK headers. I'll put a buck on that soup being the cause.