动态库导出问题

系统之间差异

在ubuntu或mac编译好的动态库,放到windows上编译出现了大量“无法解析的外部符号”错误,对Windows编译不太了解的同学一定感觉到特别困惑,这主要是不同系统关于动态库定义之间的差异。

Windows系统
使用msvc生成的dll动态库,必须使用__declspec(dllexport)导出,否则使用方不能正常访问。与动态库配套出现的是导入库,导入库里包含了动态库导出类、函数相关信息。其他项目引用动态库时,直接引用导入库即可,导入库相关的信息会被编译到引用方产物中,但最终的打包产物中只有动态库,而没有导入库。导入库并不是必须的,在没有导入库的情况下,可以使用动态加载的方式来加载动态库,通过指定的函数名来引用函数。

hInstance = LoadLibrary("library.dll");
fun = (FUN)GetProcAddress(hInstance, "add")
k = fun(1, 2);

Unix系统
mac、linux、android等都是unix。
动态库里定义的类、类中的public方法默认都是可以被引入动态库的项目访问,如果想像Windows系统一样只暴露部分API给使用方的话,可以使用visibility("default")方法来标记类或方法,在编译时添加-fvisibility=hidden,修改默认访问权限为隐藏,只有使用visibility(“default”)的相关类和方法可以被其他使用动态库的项目访问到。

MinGW编译

mingw可以使用gcc或llvm对源码进行编译,这两种编译器在编译dll动态库时,可以像Unix系统一样,使用或不使用visibility("default")导出函数,当然也可以使用dllexport来导出函数。

  • visibility(“default”) (类Unix导出方法)
  • __declspec(dllexport)(Windows导出方式)

导出方法命名规则

使用__declspec(dllexport)编译生成的dll,.c文件生成的函数名保持不变

_Z7testOnev
_ZN3One3addEii
_ZN3One4multEii
_ZN3One8toStringEv
sayHello

使用__attribute__((visibility("default")))编译.cpp文件生成的dll。.c文件生成的函数名保持不变

_Z7testOnev
_ZN3One3addEii
_ZN3One4multEii
_ZN3One8toStringEv
_ZNSt3__111char_traitsIcE6lengthEPKc
_ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEC2B7v160006IDnEEPKc
_ZNSt3__116__non_trivial_ifILb1ENS_9allocatorIcEEEC2B7v160006Ev
_ZNSt3__117__compressed_pairINS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE5__repES5_EC2B7v160006INS_18__default_init_tagESA_EEOT_OT0_
_ZNSt3__118__constexpr_strlenB7v160006EPKc
_ZNSt3__119__debug_db_insert_cB7v160006INS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEEEvPT_
_ZNSt3__122__compressed_pair_elemINS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE5__repELi0ELb0EEC2B7v160006ENS_18__default_init_tagE
_ZNSt3__122__compressed_pair_elemINS_9allocatorIcEELi1ELb1EEC2B7v160006ENS_18__default_init_tagE
_ZNSt3__19allocatorIcEC2B7v160006Ev
sayHello

编译出来的.c文件,函数名保持原样

glfwCreateCursor
glfwCreateStandardCursor
glfwCreateWindow
glfwCreateWindowSurface
glfwDefaultWindowHints
glfwDestroyCursor

导出方式混乱带来的问题

MinGW编译dll时,可以使用dllexport或visibility(“default”)两种方式进行方法导出,因依赖的库内置的导出方式不同,就会产生混乱使用的情况。动态库引用动态库,无论两个动态如何配置,没有问题;但动态库引入静态库场景,可能会产生问题

  • 静态库使用dllexport导出,动态库使用visibility(“default”)导出
    编译出的动态库只有静态库中使用dllexport导出的相关函数和类。在引用动态库编译可执行文件时,会提示动态库中被引用的方法未实现
  • 静态库使用visibility(“default”)导出,动态库使用dllexport导出
    动态库中使用dllexport的函数及类全部导出,静态库中的方法全部不能导出
  • 同一动态库中即使用dllexport又使用visibility(“default”),则只有dllexport标记的函数和类被导出

总结:两者不要混合使用。在混合使用时,dllexport的优先级更高,visibility("default")相关标记会被忽略

llvm-mingw-20230614-msvcrt-x86_64\bin/clang++ -target x86_64-pc-windows-gnu -rtlib=compiler-rt -stdlib=libc++ -lunwind -lpthread -Qunused-arguments -fuse-ld=lld -o ./test.exe @./test.exe.rsp
ld.lld: error: undefined symbol: Two::toString()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值