动态库的unresolved external symbols问题


跨平台的程序(在Linux下测试通过)如果是用CMake的CMakeLists.txt组织起来的,有时候会遇到这样的问题:里边有若干个工程是add_library,作为库,若干个工程是add_executable,作为可执行程序,其中,可执行程序调用了另一个工程生成的库,该库为动态库
在编译、运行该可执行程序的时候,链接过程中报经典链接错误——unresolved external symbols这个问题其实大家都十分熟悉,常常是由于库没有添加造成的。然而此时,明明dll就老实地呆在exe的边上,为什么,会无法解析外部符号呢?

分析

我们日常使用 dll 的方式,都是 dll + lib,即动态库+动态库的导入库的形式,我们像使用静态库一样将动态库的导入库 lib 链接到程序中,然后和静态库一样的方式调用动态库中的函数。

那个导入库(后缀也是.lib),不同于静态库,我们可以做以下实验来看看导入库究竟是什么:

实验工具(以64位的dll为例):

mingw-w64或者VS x64 Win64 Command Prompt

模块定义文件的小知识:模块定义 (.def) 文件为链接器提供有关被链接程序的导出、属性及其他方面的信息。模块定义文件的msdn资料链接

其基本格式为:

ps:序号可以省略

特别需要注意的是,C语言的函数名和C++的函数名有所不同,C++中的函数名有改编,不同编译器改编不同。

从dll中可以自动生成导出函数的信息(.def文件的形式),由 def 文件可以生成dll的导入库(对该库源码为导出,对使用该库源码方为导入)

实验准备:

包含导出信息的add.dll与
add.def

实验:从def生成导入库lib

cd进入 add.def 所在路径,输入 lib /machine:X86 /def:add.def, 则生成 add.lib ——这就是一个导入库,add.lib导入库中包含的信息正是生成add.dll的源码中有函数add,该函数从源码中导出,可以通过导入库add.lib导入到使用add函数的源码中

实验:

从dll生成def:

VS的dll生成def工具:

官方主页链接: http://purefractalsolutions.com/show.php?a=utils/expdef

下载src源码,用cl expdef.cpp编译生成exe,用where cl命令查看cl.exe的位置,exe拷贝到同一位置,这样可以针对不同位数生成不同exe

mingw的dll生成def工具:

  • mingw-w64:gendef
  • mingw(32位):pexports

用例:

无法解析外部符号+dll存在且位置正确,于是我们应该思考一个问题,dll真的导出了exe程序源码中需要使用的函数么?

原因

使用gendef或者expdef命令可以对gcc或者VS编译器生成的dll导出def文件查看导出了哪些函数、变量,查看出错的dll可以发现,dll中没有导出生成dll的源码中的任何函数,难怪会无法解析外部符号!实验可知 GCC (and mingw family) exports all symbols by default, VS doesn’t export anything by default. And if your dll doesn’t export anything, VS simply doesn’t generate the import library gcc以及其他mingw的编译器默认导出所有符号,而VS默认什么也不导出,不在源码做任何导出标记(比如__declspec(dllexport)导出标记在C++中非常常用)且不借助实现写好的def文件写好哪些需要导出,直接通过VS生成的动态库dll中不包含任何导出信息,其导入库lib也是空的

解决

要么在生成dll的源码中添加__declspec(dllexport)导出标记或者自己手工写def文件(对于C++改编名很麻烦,应该没人想这么干)并添加到该生成dll的工程中,再重新编译该工程生成dll,要么直接将库加个STATIC标记,生成静态库。

《动态库的unresolved external symbols问题》有4个想法

  1. 一秃顶患者走进一诊所。“听说您这儿,可以诊断秃顶病因?”“是的,当然! ”“大夫,能帮我瞧瞧吗?”“哦!我明白你的病因了。”“您的病因是因为,缺氧所致。”“?”“您的头在高处对吗?”“朱穆朗玛锋它顶上长毛吗?没有。那是因为高山缺氧,所以你的病情与它类似此类病情即使华佗在世也回天无术,恕我无能为力。”
    http://shcuifeng.com http://shcuifeng.com

发表评论

电子邮件地址不会被公开。 必填项已用*标注