在迁移Linux/Mac的动态库到 Windows 时遇到一个问题:静态库生成了 lib/a.lib,且可以打印函数的符号表;但动态库 bin/a.dll 中无法查到函数的符号表,且在连接 a.dll 的时候报错,提示找不到对应的 lib/a.lib(没有生成)。
我们来问问ChatGPT:win cmake ninja 编译为什么dll编译了,但是没有lib呢?
您好,这是Bing。根据网络搜索结果,您的问题可能有以下几种原因和解决办法:
* 您的代码中没有导出任何方法、类等,所以生成的DLL不需要lib文件来记载导出符号1。您可以在头文件中增加一行 __declspec(dllexport) void nothing(void); 或者使用模块定义文件(.def)来导出DLL的函数1。
* 您使用的是mingw(gcc)编译器,它默认是没有MSVC连接动态库所需要的lib文件的2。您可以使用 dlltool 命令来从DLL中生成lib文件2。
* 您使用的是cmake+ninja构建系统,它可能有一些特殊的设置或选项来控制DLL和lib文件的生成3。您可以查看cmake的文档或变量来了解更多细节3。
希望这些信息对您有所帮助。
其实先google了一通,基本有思路了才问的ChatGPT.
从上面的回答中,发现很可能是没有加 __declspec(dllexport)
导致的。之前有遇到过类似的问题,有兴趣的参考公众号中的:Windows下查看dll/lib包含函数名
Case
Ninja总是链接不导出任何符号的库(在Windows上)
先看一个case:
CMakeLists.txt
cmake_minimum_required(VERSION 2.8)
project(foo CXX)
add_library(foo SHARED test.cpp)
test.cpp
void foo()
{}
有了这样的设置,当我运行cmake (cmake -G Ninja)
和执行几个 连续的build,每次 Ninja 链接“foo”库。
如果test.cpp
包含导出的符号,例如:
__declspec(dllexport) void foo(){}
Ninja 的工作与预期一致,二次build后:"ninja: no work to do"。
问题的原因是,cmake生成的链接规则“foo.dll”
在这样的方式,它依赖于“foo.Lib"
文件存在:
build foo.dll foo.lib: CXX_SHARED_LIBRARY_LINKER__foo
CMakeFiles\foo.dir\test.cpp.obj
如果一个库没有导出符号,MSVC不会产生“.Lib "文件。因为Ninja假设它需要重新链接来生成“foo.dll”的import库 。
复制步骤:
- extract attached archive with test files (CMakeLists.txt and test.cpp).
- run
cmake -G Ninja
ninja
(builds and links library)ninja
(should report "no work to do" but relinks the library again)
附加信息:
这是一种特定于Windows
的情况(更具体地说是Windows + MSVC
,我假设,至少没有用MinGW工具链测试),不会发生在linux
上,因为没有“import library”
这样的东西用于共享库。
总结下,没有用
import library
的情况下,win的.dll
没有符号导出,所以不生成.lib
。但是Ninja在连接时总是依赖.lib
。
解决方法
我发现CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS
,它允许在Windows上轻松构建一个共享库,即DLL
。
if(WIN32)
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
endif()
add_library(mylib SHARED ${LIB_SRC})
这在所有类unix操作系统上都没有任何问题。然而,在Windows与MSVC我发现,构建共享库时会创建mylib.dll
和mylib.lib
。而构建静态库也会创建mylib.lib
。
$ ls build/bin/
k2_log.dll* k2_log.dll.manifest k2context.dll* k2context.dll.manifest k2fsa.dll* k2fsa.dll.manifest
$ ls build/lib/
k2_log.exp k2_log.lib k2context.exp k2context.lib k2fsa.exp k2fsa.lib
# k2fsa.dll.manifest
<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
<assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel level='asInvoker' uiAccess='false' />
</requestedPrivileges>
</security>
</trustInfo>
</assembly>
D:k2\build> dumpbin -exports bin/k2_log.dll
Microsoft (R) COFF/PE Dumper Version 14.29.30138.0
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file bin\k2_log.dll
File Type: DLL
Section contains the following exports for k2_log.dll
00000000 characteristics
FFFFFFFF time date stamp
0.00 version
1 ordinal base
132 number of functions
132 number of names
ordinal hint RVA name
1 0 00001000 ??$?0$$V@?$\_Compressed\_pair@V?$allocator@D@std@@V?$\_String\_val@U?$\_Simple\_types@D@std@@@2@$00@std@@QEAA@U_Zero_then_variadic_args_t@1@@Z
2 1 00001000 ??$?0AEBV?$allocator@D@std@@$$V@?$\_Compressed\_pair@V?$allocator@D@std@@V?$\_String\_val@U?$\_Simple\_types@D@std@@@2@$00@std@@QEAA@U_One_then_variadic_args_t@1@AEBV?$allocator@D@1@@Z
...
Summary
1000 .data
1000 .pdata
5000 .rdata
1000 .reloc
1000 .rsrc
3000 .text
D:\k2\build>dumpbin /LINKERMEMBER lib/k2_log.lib
Microsoft (R) COFF/PE Dumper Version 14.29.30138.0
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file lib\k2_log.lib
File Type: LIBRARY
Archive member name at 8: /
FFFFFFFF time/date
uid
gid
0 mode
50CE size
correct header end
257 public symbols
A23A __IMPORT_DESCRIPTOR_k2_log
A460 __NULL_IMPORT_DESCRIPTOR
A596 k2_log_NULL_THUNK_DATA
D138 __imp_??_R0?AV?$\_Iosb@H@std@@@8
...
257 public symbols
E ??$?0$$V@?$\_Compressed\_pair@V?$allocator@D@std@@V?$\_String\_val@U?$\_Simple\_types@D@std@@@2@$00@std@@QEAA@U_Zero_then_variadic_args_t@1@@Z
F ??$?0AEBV?$allocator@D@std@@$$V@?$\_Compressed\_pair@V?$allocator@D@std@@V?$\_String\_val@U?$\_Simple\_types@D@std@@@2@$00@std@@QEAA@U_One_then_variadic_args_t@1@AEBV?$allocator@D@1@@Z
...
Summary
C0 .debug$S
14 .idata$2
14 .idata$3
8 .idata$4
8 .idata$5
C .idata$6
了解更多,往下看。
Create dlls
on Windows without declspec()
using new CMake export all
feature
CMake 3.4将有一个新特性,可以 简化使用共享库将C和c++软件从Linux/UNIX移植到Windows 。Linux/UNIX开发人员经常会惊讶地发现,在Windows上创建一个称为DLL
(动态链接库)的共享库需要修改源代码或显式列出DLL将导出的所有符号
。
Linux/UNIX上的编译器能够自动导出共享库中的所有符号 。
在Windows上,你必须使用编译器指令__declspec(import)
和__declspec(export)
来声明从共享库导出/导入哪些符号,或者你必须创建一个包含所有你想导出的符号列表的模块定义文本文件(.def)
,并将该文件传递给链接器 。
对于C库,手动或自动创建.def
文件并不困难,因为您只需要列出库中所有函数的名称。然而,对于c++代码,名称篡改
和the sheer number of functions
使得手工制作一个.def
文件几乎是不可能的。标准的解决方案使用预处理器有条件地将__declspec(import)
和__declspec(export)
插入到代码中。然而,对于现有的大量c++代码库,编辑所有的源代码可能会很困难,也很耗时。 CMake现在有一个特性,允许它查询.obj
文件,这些文件将组成一个DLL,并自动创建一个.def文件,在大多数情况下不需要修改原始源代码 。
该特性通过一个新的目标属性WINDOWS_EXPORT_ALL_SYMBOLS
在CMake中实现 。当启用该属性时,CMake会自动为Windows上的共享库创建一个包含所有在输入.obj
文件中找到的符号的.def
文件。.def
文件将被传递给链接器,从而从DLL导出所有符号。对于全局数据符号
,在针对DLL中的代码进行编译时仍然必须使用__declspec(dllimport)
。该符号从DLL中正确地自动导出,但编译器需要知道它是在编译时从DLL中导入的。所有其他函数符号将由调用者自动导出和导入。这通过减少对显式dlexport
标记的需求(甚至在c++类中)简化了将项目移植到Windows的过程。当创建目标时,这个属性通过CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS
变量的值初始化。
要在现有的项目上尝试此功能,请运行:
cmake -DCMAKE_WINDOWS_EXPORT_ALL_SYMBOLS=TRUE -DBUILD_SHARED_LIBS=TRUE
在CMake项目上。这应该将所有未显式指定构建类型的add_library调用转换为共享构建。 如果项目中没有全局数据变量,则将所有库构建为dll,不会出现错误 。 如果遇到未定义的符号,请检查全局数据,如类的静态数据成员 。 处理全局数据最简单的方法是像这样使用CMake GenerateExportHeader
模块 :
add_library(mylibrary ${mylibrary_SOURCES})
# add these lines
include(GenerateExportHeader)
generate_export_header(mylibrary)
//编辑源代码如下:
#include <mylibary\_export.h>
class myclass
{
static mylibrary_EXPORT int GlobalCounter;
…
注意,如果您使用GenerateExportHeader
并且仍然希望静态构建工作,您将需要在静态构建期间添加一个-Dmylibrary_STATIC
。有关详细信息,请参阅生成的mylibary_export.h
。
Real World Use Cases
详情请看:https://www.kitware.com/create-dlls-on-windows-without-declspec-using-new-cmake-export-all-feature/
参考文献
- https://cmake.org/pipermail/cmake/2018-September/068312.html
- https://cmake.org/cmake/help/git-master/prop\_tgt/WINDOWS\_EXPORT\_ALL\_SYMBOLS.html
- https://cmake.org/pipermail/cmake-developers/2015-December/027291.html
- https://www.kitware.com/create-dlls-on-windows-without-declspec-using-new-cmake-export-all-feature/