GCC的学习(六)__attribute__控制动态库可见性

《GCC的学习(五)动态库接口可见性》一文我翻译了一篇gcc官方文档,那么在实际中,这个可见性是如何体现的呢?

设置__attribute__的优点

  • 减少了动态库装载时间
  • 方便编译器优化代码
  • 更加轻便的动态库

导出所有符号的方法

默认情况下,所有头文件的符号都将会导出。

//fun.cpp
#include <iostream>
#include "fun.h"
void printOne()
{
    
    
    std::cout<<"1"<<std::endl;
}

void printTwo()
{
    
    
    std::cout<<"2"<<std::endl;
}

void printThree()
{
    
    
    std::cout<<"3"<<std::endl;
}

void printAny(double num)
{
    
    
    std::cout<<num<<std::endl;
}

int getValue()
{
    
    
    return 4396;
}

对应的头文件

//fun.h
void printOne();
void printTwo();
void printThree();
void printAny(double num);
int getValue();

两步完成动态库制作:

  • -fPIC选项编译-c源代码,生成与位置无关的目标文件fun.o;gcc -fPIC fun.cpp -c
  • --shared选项生成动态库;gcc fun.o --shared -o libFun.so

使用nm -CD +动态库查看:

0000000000000880 T _init
                 w _ITM_deregisterTMCloneTable
                 w _ITM_registerTMCloneTable
                 w _Jv_RegisterClasses
0000000000000a74 T printThree()
0000000000000ae7 T getValue()
0000000000000aa6 T printAny(double)
0000000000000a10 T printOne()
0000000000000a42 T printTwo()

全部函数都被导出了。

选择性导出符号的方法

gcc在编译源代码时可以指定一个默认的符号导出规则,如果你没有定义这个选项,那么其选项是默认的公有(default).

具体的:

  • default 若全部公开,那没必要传递额外的到导出选项
  • hidden 默认私有
  • internal 暂不了解
  • protect 暂不了解

比如,我在编译fun.cpp时指定了默认导出规则hidden

gcc -fPIC -fvisibility=hidden -c fun.cpp -shared -o libFun.so  # 一定要放在一条语句执行,而且-c不能省略

对于部分需要公开的,我们可以单独指定:

void printOne();
void printTwo();
void printThree(); //没有加__attribute__选项,默认使用编译时的默认属性hidden
__attribute__((visibility ("hidden")))void printAny(double num); //显示使用hidden属性
__attribute__((visibility ("default")))int getValue();//显示指定default属性,即公有
00000000000007d0 T _init
                 w _ITM_deregisterTMCloneTable
                 w _ITM_registerTMCloneTable
                 w _Jv_RegisterClasses
0000000000000a37 T getValue() #只有这个被导出

只有getValue函数被导出。用户使用时确实实现了隐藏:
2
PS:不要用readelf -s libFun.so查看so文件是否输出属性。不知道为啥网上都是这个。事实上,你在生成.o的时候可以用,其他情况不可以用。

用宏来帮助你

前缀分为两种,一种是可见,另一种是不可见。将其设置为宏可以帮助你简单的声明一个动态库可见性。

#define DLL_PUBLIC __attribute__ ((visibility ("default")))
#define DLL_LOCAL  __attribute__ ((visibility ("hidden")))

QT中也用了类似的宏:

#  ifdef Q_OS_WIN
#    define Q_DECL_EXPORT     __declspec(dllexport)
#    define Q_DECL_IMPORT     __declspec(dllimport)
#  elif defined(QT_VISIBILITY_AVAILABLE)
#    define Q_DECL_EXPORT     __attribute__((visibility("default")))
#    define Q_DECL_IMPORT     __attribute__((visibility("default")))
#    define Q_DECL_HIDDEN     __attribute__((visibility("hidden")))
#  endif

这样一来我们需要保证正确的宏在恰当的时候被调用,而无须关心我们是在编译动态库或者是使用动态库了。通常,我们会增加一个特殊的头文件来解决这个问题。我们先假设我们的动态库名字叫做mysharedlib,一个特殊的头文件mysharedlib_global.h将会被创建,其内容大概是这样:

#include <QtCore/QtGlobal>

#if defined(MYSHAREDLIB_LIBRARY)
#  define MYSHAREDLIB_EXPORT Q_DECL_EXPORT
#else
#  define MYSHAREDLIB_EXPORT Q_DECL_IMPORT
#endif

[1] https://blog.csdn.net/mutourenzhang/article/details/47803803?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.baidujs&dist_request_id=1328680.52720.16163984131051535&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.baidujs

猜你喜欢

转载自blog.csdn.net/weixin_39258979/article/details/115081159