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

2023年 10月 7日 40.0k 0

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

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

使用 DependenciesGui 查看编译好的 libass DLL 文件

这个时候该怎么办呢?网上的文章大多会让你使用 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,内容如下:

{
    global:
        ass_library_init;
    
    local:
        *;
};

上述代码表示的意思是:

  • 我们将 ass_library_init 指定为 global,所以最终导出的 dll 文件里就只会出现 ass_library_init 这一个符号;
  • 所有其他符号都被指定为 local,也就是不会被导出。
  • 然后,我们在编译时,加上一个参数 -Wl,--version-script=version-script.txt,这样就可以使用 version script 来控制符号的导出了。

    检查一下输出的结果:

    使用 DependenciesGui 查看输出的 libass.dll 文件,发现只有 ass_library_init 函数被导出

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

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

    相关文章

    JavaScript2024新功能:Object.groupBy、正则表达式v标志
    PHP trim 函数对多字节字符的使用和限制
    新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
    使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
    为React 19做准备:WordPress 6.6用户指南
    如何删除WordPress中的所有评论

    发布评论