找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 4644|回复: 0
打印 上一主题 下一主题
收起左侧

GCC的符号可见性——解决多个库同名符号冲突问题

[复制链接]
跳转到指定楼层
楼主
ID:90014 发表于 2015-9-14 16:06 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

问题

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

主程序依赖了两个库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.sog++ -fPIC libB.cpp -shared -o libB.sog++ main.cpp libA.so libB.so -o mainexport LD_LIBRARY_PATH=../main

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

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

原因

我们通过readelf来查看符号:

$ readelf -a libA.so | grep subfunc000000200a60  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 popint funcA(int a, int b) {    return subfunc(a, b);}

这种方式更方便灵活。

如果是dlopen载入so可以通过参数控制。


参考文档

GCC Visibility

source:https://github.com/wwbmmm/blog/wiki/gcc_visibility






分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享淘帖 顶 踩
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表