引用自:https://github.com/wwbmmm/blog/wiki/gcc_visibility

问题

最近项目遇到一些问题,场景如下

主程序依赖了两个库libA的funcA函数和libB的funcB函数。示意的代码(main.cpp)如下:

#include <cstdio>

int funcA(int, int);
int funcB(int, int); int main() {
printf("%d,", funcA(2, 1));
printf("%d\n", funcB(2, 1));
return 0;
}

libA示意实现(libA.cpp)如下:

int subfunc(int a, int b) {
return a + b;
} int funcA(int a, int b) {
return subfunc(a, b);
}

libB示意实现(libB.cpp)如下:

int subfunc(int a, int b) {
return a - b;
} int funcB(int a, int b) {
return subfunc(a, b);
}

可见funcA调用了libA中的内部函数subfunc,funcB调用了libB中的内部函数subfunc,这两个subfunc实现不同,但不幸的是名字不小心起得一样了

这时我们尝试编译并运行:

g++ -fPIC libA.cpp -shared -o libA.so
g++ -fPIC libB.cpp -shared -o libB.so g++ main.cpp libA.so libB.so -o main export LD_LIBRARY_PATH=.
./main

我们期望的结果是3,1(funcA和funcB各自调用不同的subfunc实现),

实际得到的结果是3,3(funcA和funcB都调用了libA中的subfunc实现)

原因

我们通过readelf来查看符号:

$ readelf -a libA.so | grep subfunc
000000200a60 000200000007 R_X86_64_JUMP_SLO 0000000000000708 _Z7subfuncii + 0
2: 0000000000000708 20 FUNC GLOBAL DEFAULT 10 _Z7subfuncii
45: 0000000000000708 20 FUNC GLOBAL DEFAULT 10 _Z7subfuncii $ readelf -a libB.so | grep subfunc
000000200a60 000200000007 R_X86_64_JUMP_SLO 0000000000000708 _Z7subfuncii + 0
2: 0000000000000708 22 FUNC GLOBAL DEFAULT 10 _Z7subfuncii
45: 0000000000000708 22 FUNC GLOBAL DEFAULT 10 _Z7subfuncii

可见libA和libB里面都有subfunc符号,名字完全一样,而且都是GLOBAL的

GLOBAL的符号即全局的符号,同名的全局符号会被认为是同一个符号,由于main先加载了libA,得到了libA中的subfunc符号,再加载libB时,就把libB中的subfunc忽略了。

解决方案

这其实是符号的可见性(Symbol Visibility)问题,既然有GLOBAL符号,那自然会有LOCAL符号,LOCAL的符号只在当前lib可见,全局不可见。

如何将符号变成LOCAL的呢,最直接的就是加上visibility为hidden的标志,修改后的libA.cpp:

__attribute__ ((visibility ("hidden"))) int subfunc(int a, int b) {
return a + b;
} int funcA(int a, int b) {
return subfunc(a, b);
}

再重新编译执行,可以得到结果为3,1,成功!这里再查看一下libA的符号:

$ readelf -a libA.so | grep subfunc
40: 00000000000006a8 20 FUNC LOCAL DEFAULT 10 _Z7subfuncii

可见subfunc符号已经变成了LOCAL

默认LOCAL

上面的方法可以解决问题,但是,实际情况往往是,libA里面有很多的内部函数,而暴露给外部的只有少数,能不能指定少数符号为GLOBAL,其它的都是LOCAL呢?答案是肯定的,修改libA.cpp如下:

int subfunc(int a, int b) {
return a + b;
} __attribute__ ((visibility ("default"))) int funcA(int a, int b) {
return subfunc(a, b);
}

这时,libA的编译参数需要加上-fvisibility=hidden

g++ -fPIC libA.cpp -shared -fvisibility=hidden -o libA.so

同样可以解决问题。

跨平台兼容性

windows平台对于符号的行为是不一样的,windows默认动态库里符号是LOCAL的,通过__declspec(dllexport)来声明GLOBAL符号,所以可以用下面的方式来兼容:

#if defined _WIN32 || defined __CYGWIN__
#ifdef BUILDING_DLL
#ifdef __GNUC__
#define DLL_PUBLIC __attribute__ ((dllexport))
#else
#define DLL_PUBLIC __declspec(dllexport) // Note: actually gcc seems to also supports this syntax.
#endif
#else
#ifdef __GNUC__
#define DLL_PUBLIC __attribute__ ((dllimport))
#else
#define DLL_PUBLIC __declspec(dllimport) // Note: actually gcc seems to also supports this syntax.
#endif
#endif
#define DLL_LOCAL
#else
#if __GNUC__ >= 4
#define DLL_PUBLIC __attribute__ ((visibility ("default")))
#define DLL_LOCAL __attribute__ ((visibility ("hidden")))
#else
#define DLL_PUBLIC
#define DLL_LOCAL
#endif
#endif

隐藏外部依赖的符号

我遇到的实际情况比上面更复杂一些,subfunc并不是在libA中实现的,而是在另一个外部库libsubfunc.a中实现的。libA通过包含头文件来获取到这个函数:

#include "subfunc.h"

int funcA(int a, int b) {
return subfunc(a, b);
}

上面的-fvisibility仅对实现生效,不能对声明生效。但libsubfunc.a是第三方库,我们不能去改它的代码,也不能改它的头文件,对于这种情况,gcc提供了下面方式来支持:

#pragma GCC visibility push(hidden)
#include "subfunc.h"
#pragma GCC visibility pop int funcA(int a, int b) {
return subfunc(a, b);
}

这种方式更方便灵活。

参考文档

GCC的符号可见性——解决多个库同名符号冲突问题的更多相关文章

  1. GCC制作动态库导出符号表【转】

    转自:https://blog.csdn.net/whb_fei/article/details/76974543 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.cs ...

  2. C++常见gcc编译链接错误解决方法

    除非明确说明,本文内容仅针对x86/x86_64的Linux开发环境,有朋友说baidu不到,开个贴记录一下(加粗字体是关键词): 用“-Wl,-Bstatic”指定链接静态库,使用“-Wl,-Bdy ...

  3. gcc编译选项的循环重复查找依赖库等命令

    link时,若liba.a依赖libb.a,若这样写 -lb -la,则链接通不过,gcc有个选项:-Xlinker ,可以让gcc在链接时反复查找依赖库,用法 : gcc -shared -o li ...

  4. 【转】Linux的nm查看动态库和静态库的符号

    转自https://blog.csdn.net/qq_16683355/article/details/52297884 功能 列出.o..a..so中的符号信息,包括符号的值,符号类型及符号名称等. ...

  5. 折腾gcc/g++链接时.o文件及库的顺序问题(转)

    转自: http://www.cnblogs.com/OCaml/archive/2012/06/18/2554086.html#sec-1-1 折腾gcc/g++链接时.o文件及库的顺序问题 Tab ...

  6. [转载]GCC 编译使用动态链接库和静态链接库--及先后顺序----及环境变量设置总结

    来自http://blog.csdn.net/benpaobagzb/article/details/51364005 GCC 编译使用动态链接库和静态链接库 1 库的分类 根据链接时期的不同,库又有 ...

  7. 解决Linux动态库版本兼容问题

    说道“动态库版本兼容”,很多人头脑中首先蹦出的就是“Dll Hell”.啊,这曾经让人头疼的难题.时至今日,这个难题已经很好地解决了. 在进一步讨论之前来思考一个问题:Linux下为什么没有让人头痛的 ...

  8. 【VS开发】Caffelib中出现的问题:强制链接静态库所有符号(包括未被使用的)

    C++程序在链接一个静态库时,如果该静态库里的某些方法没有任何地方调用到,最终这些没有被调用到的方法或变量将会被丢弃掉,不会被链接到目标程序中.这样做大大减小生成二进制文件的体积.但是,某些时候,即使 ...

  9. 折腾gcc/g++链接时.o文件及库的顺序问题

    gcc/g++链接时.o文件以及库的顺序问题 1 写在前面 最近换了xubuntu12.4,把原来的项目co出来编译的时候报"undefined reference to".猜测是 ...

随机推荐

  1. MT【238】内心轨迹

    已知$F_1,F_2$为椭圆$C:\dfrac{x^2}{4}+\dfrac{y^2}{3}=1$的左右焦点,点$P$在椭圆$C$上移动时,$\Delta{F_1PF_2}$的内心$I$的轨迹方程为_ ...

  2. Centos7安装Zabbix4.0步骤

    点击返回:自学Zabbix之路 点击返回:自学Zabbix4.0之路 点击返回:自学zabbix集锦 Centos7安装Zabbix4.0步骤 官方搭建zabbix4.0的环境要求: 1. 环境搭建L ...

  3. [luogu#2019/03/10模拟赛][LnOI2019]长脖子鹿省选模拟赛赛后总结

    t1-快速多项式变换(FPT) 题解 看到这个\(f(x)=a_0+a_1x+a_2x^2+a_3x^3+ \cdots + a_nx^n\)式子,我们会想到我们学习进制转换中学到的,那么我们就只需要 ...

  4. cf786E ALT (最小割+倍增优化建图)

    如果把“我全都要”看作是我全不要的话,就可以用最小割解决啦 源点S,汇点T 我们试图让每个市民作为一个等待被割断的路径 把狗狗给市民:建边(S,i,1),其中i是市民 把狗狗给守卫:建边(j,T,1) ...

  5. ASP.NET MVC 网站优化之压缩技术

    压缩 html 可以去除代码中无用的空格等,这样可提高网站的加载速度并节省带宽. 实现 ActionFilter 来完成 html 的压缩 public class WhitespaceFilterA ...

  6. win7(旗舰版)下,OleLoadPicture 加载内存中的图片(MagickGetImageBlob),返回值 < 0

    昨天去三哥家,想把拍好的照片缩小一下,我用很久前写的一个软件进行缩小,然后进行一次效果预览,这个时候弹出: Call OleLoadPicture Fail - loadPictureFromMW 奇 ...

  7. shelve模块(二十三)

    shelve模块比pickle模块简单,只有一个open函数,返回类似字典的对象,可读可写; key必须为字符串,而值可以是python所支持的数据类型 用的比较少 目的: 将字典写入文件保存起来 i ...

  8. JasperReport 中踩过的坑

      Mac Book Pro 10.13.6Jaspersoft Studio community version 6.6.9JDK 8 安装 Jaspersoft Studio Jasper Rep ...

  9. vue.js2.0开发中的几个技巧

    最近用Vue.js开发了几个项目,Vue的双向数据绑定和组件化让我耳目一新,减少了很多底层重复的工作,和基于jQuey的前端开发不起来,基于Vue的开发给我一种酣畅淋漓的感觉. 下面给出我基于Vue. ...

  10. Linux 防火墙iptables开放特定端口

    1.查看状态:iptables -L -n2.直接编辑:vi /etc/sysconfig/iptables3.端口开放:-A INPUT -m state --state NEW -m tcp -p ...