GCC 怎么控制 .so/.dll 中导出的符号?这个方法比 --fvisibility 更好用!

在使用 GCC / G++ / MinGW 编译动态链接库的时候,我们常常会遇到需要控制导出符号的问题。比如,有时候我们想把一大堆依赖库塞进一个 .dll 文件里,这样就不会出现 .dll 依赖 .dll 的套娃现象,但是这样一来,编译器会自动把静态库里的所有函数都导出到 dll 里,然后动态链接库里就有了一大堆我们不需要的符号。

举个实际例子,我前段时间编译了 libass 的 DLL,它依赖 fontconfig,fontconfig 又依赖 expat,然后 libass 又依赖 libpng,libpng 依赖 zlib,导致最终生成的 DLL 里有一大堆 XML、zlib、png 和 FT_* 函数:

这个时候该怎么办呢?网上的文章大多会让你使用 gcc 的 -fvisibility=hidden 来控制符号的可见性。将所有其它符号设为不可见,就可以控制要导出的符号了。但是,实际编程中发现这样做需要大量修改源代码,加上 __declspec(dllexport) 定义。有没有更简单的方法呢?有,那就是今天要介绍的 version script。

version script 是 GNU 编译工具链中的一个非常有用的功能,它允许在编译的时候,使用文本文件来控制动态链接库中需要导出的符号,除此之外还有不少高级功能。version script 完整的规范定义在了 ld 工具的手册里(ftp.gnu.org/old-gnu/Man...),这里我们只是使用它控制导出符号的功能,因此只使用它的简化版。

下面我们就以一个实际例子来说明如何使用 version script 来控制符号导出。

这里我们以 libass 为例,首先,在 libass 的源代码根目录下新建一个文件,我们将它命名为 version-script.txt,内容如下:

cpp 复制代码
{
    global:
        ass_library_init;
    
    local:
        *;
};

上述代码表示的意思是:

  1. 我们将 ass_library_init 指定为 global,所以最终导出的 dll 文件里就只会出现 ass_library_init 这一个符号;
  2. 所有其他符号都被指定为 local,也就是不会被导出。

然后,我们在编译时,加上一个参数 -Wl,--version-script=version-script.txt,这样就可以使用 version script 来控制符号的导出了。

检查一下输出的结果:

简单吧?以这种方法,我们就可以轻松地控制导出的符号,无需对源代码进行大量的修改。同时,version script 还有很多高级功能,包括给库指定不同版本等等。感兴趣的读者可以自行阅读 ld 工具手册以了解更多内容。美团也写过一篇关于优化 .so 动态链接库的文章,认为 version script 方式有一些额外的好处:

  1. version script 方式可以控制编译进 so 的静态库的符号是否导出,visibility 和 attribute 方式都无法做到这一点。
  2. visibility 结合 attribute 方式需要在源码中标明每个需要导出的符号,对于导出符号较多的项目来说是很繁杂的。version script 把需要导出的符号统一地放到了一起,能够直观方便地查看和修改,对导出符号较多的项目也非常友好。
  3. version script 支持通配符,* 代表0个或者多个字符,? 代表单个字符。比如 my*; 就代表所有以 my 开头的符号。有了通配符的支持,配置 version script 会更加方便。
  4. 还有非常特殊的一点,version script 方式可以删除 __bss_start 这样的一些符号(这是链接器默认加上的符号)。

文中所使用的 DependenciesGui 工具可以在这里下载

相关推荐
吃着火锅x唱着歌4 小时前
Effective C++ 学习笔记 条款43 学习处理模板化基类内的名称
c++·笔记·学习
felixking5 小时前
C++20 协程
开发语言·c++·协程
小小龙学IT7 小时前
FlatBuffers 深度解析:Google 开源零拷贝序列化库完全指南
c++·开源
wuminyu8 小时前
JDK21中虚拟线程和FFM协同实现高并发源码剖析
java·linux·c语言·jvm·c++
SWAGGY..8 小时前
【C++进阶】:(1)继承机制详解
java·jvm·c++
听取WA声一片(无恶意)9 小时前
CSP-J/CSP-S 深度优先搜索(DFS)完全讲义
c++·算法·深度优先
码匠许师傅9 小时前
【C++ 面试真题】27. 聊聊 C++ 的内存泄漏与内存布局
java·c++·面试
加油码10 小时前
动静态库的链接底层原理(进阶版)
linux·c++
一木 之林10 小时前
三、内存管理、指针与堆栈
c语言·jvm·c++
初夏睡觉11 小时前
原创题目 - 空壳恋爱
c++·算法·dp·oi·信息学·原创题目·值得挑战的题目