Linux/Mac 动态库迁移 Windows

向量数据库大模型云通信

在迁移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库

复制步骤:

  1. extract attached archive with test files (CMakeLists.txt and test.cpp).
  2. run cmake -G Ninja
  3. ninja (builds and links library)
  4. 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.dllmylib.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/

参考文献

picture.image

0
0
0
0
关于作者

文章

0

获赞

0

收藏

0

相关资源
高性能存储虚拟化方案 NVMe over Fabric 在火山引擎的演进
在云计算中,虚拟化存储扮演着重要角色,其中 iSCSI 协议在业界开放、流行多年。近年来,拥有更优性能的 NVMe over Fabrics 协议也得到了发展。本次分享介绍了 NVMe over Fabrics 在云原生和虚拟化方向的演进工作和成果。
相关产品
评论
未登录
看完啦,登录分享一下感受吧~
暂无评论