DLL LNK2019 error (unresolved external error __imp)

江枫思渺然 提交于 2019-12-06 14:13:51
greatwolf

I'm adding an answer here to summarize a bit on the above comments, the probable root cause and finding a solution to the unresolved errors. I'll preface this by saying linkers tend to be dumb low level tools. If the symbols referenced in the object files don't match exactly with the library then the build process bails out with those unresolved errors. Therefore, the author of the dll library and clients using it will need to put in some effort to ensure the symbols agree.

Root Causes

Unresolved errors are usually caused by one of the follow:

  • Neither the project nor the libraries linked against implement the function for that symbol.
  • The libraries actually do implement the function but under a differing reference symbol.
  • The headers for the library aren't preprocessed or decorated correctly thereby causing #2.

The Hunt

Base on what the OP described the dll libraries linked against exposes a C API. It is unclear what calling convention is being followed by the library in question. The exported names in the dll contain plain undecorated function names suggesting a __cdecl convention. The provided corresponding headers, OTOH has a function like:

ILAPI void ILAPIENTRY ilDeleteImage(const ILuint Num);

which expands into this after preprocessing:

__declspec(dllimport) void __stdcall ilDeleteImage(const ILuint Num);

From this you can conclude three possible scenarios:

  1. The unresolved functions follow a __cdecl convention and the provided headers are wrong.
  2. The unresolved functions follow a __stdcall convention and the exported dll names are wrong.
  3. The functions follow __stdcall but are undecorated to look like __cdecl in the dll. This suggest that a .def file might have been used to build the dll in question.

A Sad State of Affair

Unfortunately, the function call convention followed in a win32 dll is in a confusing state of affairs. There's nothing in the language standard, C or C++, that addresses this ABI issue. See my other answer here. The toolchain vendor is free to decorate the names however they like but typcially for __cdecl functions it's plain undecorated or with a prefixed leading _ underscore.

WinAPI functions you find, like in kernel32.dll user32.dll gdi32.dll etc. are also undecorated but yet follow __stdcall. However, MSVC itself decorates __stdcall with a trailing ampersand with total bytes of the parameters(eg. ilDeleteImage@4) thus contributing to the confusion. To override how LINK decorates the functions, you have to provide a .def file that specifies the new name alias. See here for more details.

Finding the Real Convention

Assuming you don't have access to the source used to build the dll, there are two approachs I can think of to identify the real convention used.

Create a minimal test application that calls into the dll functions as if it uses __cdecl and see if it crashes and burns. This is the easier more straightforward method and you don't have to understand assembly to do it.

Second approach, you do the same but insert assembly breakpoints into your test application and run it through the debugger doing single step instructions. Make sure to choose a dll function taking at least one parameter. For example:

// pullin dll headers
// etc..

int main()
{
  __asm int 3;
  ilDeleteImage(0xdecafbad);
}

This will break and give control back to the debugger right before the dll function call. From here single step at the assembly level until you reach ilDeleteImage's function prologue.

 ; function prologue
 push   ebp
 mov    ebp, esp
 ; function implemention
 ; more opcodes here
 ; ...

 ; function epilogue
 mov    esp, ebp
 pop    ebp
 ret    0x8

Check what form of ret is being used on function return. The number argument following the ret mnemonic indicates how much to increment the esp stack pointer. Any number >0 suggestes a __stdcall function. The hypothical disassembly above shows a __stdcall function freeing 8 bytes on the stack which also hints that this function takes 2 arguments.

I had this compile error while building a project using external static libraries, QDBM and PCRE:

_main.obj : error LNK2001: unresolved external symbol __imp__dpversion
_main.obj : error LNK2001: unresolved external symbol __imp__regcomp

I had correctly configured these external projects to build static libraries, but I had forgotten to define the correct preprocessor defines in my own code, which used these libraries, to enable static linking.

So when they imported the headers from QDBM and PCRE, they added a __declspec(dllimport) that shouldn't be there (for static linking), so they were trying to import symbols from DLLs that didn't exist.

I added the missing preprocessor defines to my build system, which fixed the error:

-DQDBM_STATIC -DPCRE_STATIC

Which meant adding the following lines to my CMakeLists.txt file:

# Tell QDBM not to build itself as a DLL, because we want to link statically to it.
target_compile_definitions(qdbm PUBLIC -DQDBM_STATIC)
target_compile_definitions(lib_common PUBLIC -DPCRE_STATIC)
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!