Linux下__attribute__((visibility ("default")))的使用

在Linux下动态库(.so)中,通过GCC的C++ visibility属性可以控制共享文件导出符号。在GCC 4.0及以上版本中,有个visibility属性,可见属性可以应用到函数、变量、模板以及C++类。

限制符号可见性的原因:从动态库中尽可能少地输出符号是一个好的实践经验。输出一个受限制的符号会提高程序的模块性,并隐藏实现的细节。动态库装载和识别的符号越少,程序启动和运行的速度就越快。导出所有符号会减慢程序速度,并耗用大量内存。

“default”:用它定义的符号将被导出,动态库中的函数默认是可见的。”hidden”:用它定义的符号将不被导出,并且不能从其它对象进行使用,动态库中的函数是被隐藏的。default意味着该方法对其它模块是可见的。而hidden表示该方法符号不会被放到动态符号表里,所以其它模块(可执行文件或者动态库)不可以通过符号表访问该方法。

要定义GNU属性,需要包含__attribute__和用括号括住的内容。可以将符号的可见性指定为visibility(“hidden”),这将不允许它们在库中被导出,但是可以在源文件之间共享。实际上,隐藏的符号将不会出现在动态符号表中,但是还被留在符号表中用于静态链接。

导出列表由编译器在创建共享库的时候自动生成,也可以由开发人员手工编写。导出列表的原理是显式地告诉编译器可以通过外部文件从对象文件导出的符号是哪些。GNU用户将此类外部文件称作为”导出映射”。

以下是测试代码,各个文件的内容如下:

Dynamic_Library/library.hpp:

    #ifndef FBC_LIBRARY_LIBRARY_HPP_
    #define FBC_LIBRARY_LIBRARY_HPP_
     
    // reference: https://gcc.gnu.org/wiki/Visibility
    //            https://developer.apple.com/library/content/documentation/DeveloperTools/Conceptual/CppRuntimeEnv/Articles/SymbolVisibility.html
     
    #ifdef __GNUC__ >= 4 // it means the compiler is GCC version 4.0 or later
        #ifdef FBC_EXPORT
            #warning "===== dynamic library ====="
            #define FBC_API_PUBLIC __attribute__((visibility ("default")))
            #define FBC_API_LOCAL __attribute__((visibility("hidden")))
        #else
            #warning "===== static library ====="
            #define FBC_API_PUBLIC
            #define FBC_API_LOCAL
        #endif
    #else
        #error "##### requires gcc version >= 4.0 #####"
    #endif
     
    #ifdef __cplusplus
    extern "C" {
    #endif
     
    FBC_API_PUBLIC int library_add(int a, int b);
    FBC_API_LOCAL void print_log();
     
    #ifdef FBC_EXPORT
    FBC_API_PUBLIC int value;
    #endif
     
    #ifdef __cplusplus
    }
    #endif
     
    template<typename T>
    class FBC_API_PUBLIC Simple {
    public:
        Simple() = default;
        void Init(T a, T b);
        T Add() const;
     
    private:
        T a, b;
    };
     
     
    #endif // FBC_LIBRARY_LIBRARY_HPP_

Dynamic_Library/library.cpp:

    #include "library.hpp"
    #include <iostream>
    #include <string>
     
    FBC_API_PUBLIC int library_add(int a, int b)
    {
    #ifdef FBC_EXPORT
        value = 11;
    #endif
     
        fprintf(stdout, "File: %s, Function: %s, Line: %d\n", __FILE__, __FUNCTION__, __LINE__);
        return (a+b);
    }
     
    FBC_API_LOCAL void print_log()
    {
        fprintf(stderr, "print_log function is hidden, in dynamic library: %s, %d\n", __FUNCTION__, __LINE__);
    }
     
    template<typename T>
    void Simple<T>::Init(T a, T b)
    {
        this->a = a;
        this->b = b;
    }
     
    template<typename T>
    T Simple<T>::Add() const
    {
        fprintf(stdout, "File: %s, Function: %s, Line: %d\n", __FILE__, __FUNCTION__, __LINE__);
        return (a + b);
    }
     
    template class Simple<int>;
    template class Simple<std::string>;

Test_Dynamic_Library/test_library.hpp:

    #ifndef FBC_CPPBASE_TEST_TEST_LIBRARY_HPP_
    #define FBC_CPPBASE_TEST_TEST_LIBRARY_HPP_
     
    namespace test_library_ {
     
    #ifdef __cplusplus
        extern "C" {
    #endif
     
    int test_library_1();
    int test_library_2();
    int test_library_3();
     
    #ifdef __cplusplus
        }
    #endif
     
    } // namespace test_library_
     
    #endif // FBC_CPPBASE_TEST_TEST_LIBRARY_HPP_

Test_Dynamic_Library/test_library.cpp:

    #include "test_library.hpp"
    #include <iostream>
    #include <string>
     
    #include <library.hpp>
     
    namespace test_library_ {
     
    int test_library_1()
    {
        int a{ 4 }, b{ 5 }, c{ 0 };
     
        c = library_add(a, b);
        fprintf(stdout, "%d + %d = %d\n", a, b, c);
     
    #ifdef FBC_EXPORT
        fprintf(stdout, "dynamic library: value: %d\n", value);
    #endif
     
        return 0;
    }
     
    int test_library_2()
    {
        Simple<int> simple1;
        int a{ 4 }, b{ 5 }, c{ 0 };
     
        simple1.Init(a, b);
        c = simple1.Add();
        fprintf(stdout, "%d + %d = %d\n", a, b, c);
     
        Simple<std::string> simple2;
        std::string str1{ "csdn blog: " }, str2{ "http://blog.csdn.net/fengbingchun" }, str3;
     
        simple2.Init(str1, str2);
        str3 = simple2.Add();
        fprintf(stdout, "contents: %s\n", str3.c_str());
     
        return 0;
    }
     
    int test_library_3()
    {
    #ifdef FBC_EXPORT
        fprintf(stdout, "dynamic library cann't run print_log function\n");
    #else
        print_log();
    #endif
     
        return 0;
    }
     
    } // namespace test_library_

Test_Dynamic_Library/main.cpp:

    #include <iostream>
    #include "test_library.hpp"
     
    int main()
    {
        test_library_::test_library_1();
        test_library_::test_library_2();
        test_library_::test_library_3();
     
        return 0;
    }

CMakeLists.txt:

# CMake file for Samples_Dynamic_Library

# 设定依赖的CMake版本
CMAKE_MINIMUM_REQUIRED(VERSION 3.2)

# 指定项目名称
PROJECT(Test_Dynamic_Library)

# 打印相关信息, CMAKE_CURRENT_SOURCE_DIR指的是当前处理的CMakeLists.txt所在的路径
MESSAGE(STATUS "current path: ${CMAKE_CURRENT_SOURCE_DIR}")

# 使CMake支持C++11特性
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu++0x")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++0x")

# 定义用户自定义变量  
SET(PATH_DYNAMIC_LIBRARY_CPP_FILES ${CMAKE_CURRENT_SOURCE_DIR}/Dynamic_Library)
SET(PATH_TEST_DYNAMIC_LIBRARY_CPP_FILES ${CMAKE_CURRENT_SOURCE_DIR}/Test_Dynamic_Library)
SET(PATH_DYNAMIC_LIBRARY_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Dynamic_Library)
#MESSAGE(STATUS "Dynamic Library cpp files path: ${PATH_DYNAMIC_LIBRARY_CPP_FILES}")
#MESSAGE(STATUS "Test Dynamic Library cpp files path: ${PATH_TEST_DYNAMIC_LIBRARY_CPP_FILES}")

# 递归查询所有匹配的文件:*.cpp
FILE(GLOB_RECURSE DYNAMIC_LIBRARY_CPP_LIST ${PATH_DYNAMIC_LIBRARY_CPP_FILES}/*.cpp)
FILE(GLOB_RECURSE TEST_DYNAMIC_LIBRARY_CPP_LIST ${PATH_TEST_DYNAMIC_LIBRARY_CPP_FILES}/*.cpp)
#MESSAGE(STATUS "Dynamic Library cpp list: ${DYNAMIC_LIBRARY_CPP_LIST}")
#MESSAGE(STATUS "Test Dynamic Library cpp list: ${TEST_DYNAMIC_LIBRARY_CPP_LIST}")

IF (BUILD_DYNAMIC_LIBRARY)
    # 添加编译参数,添加-D预编译宏定义,可以一次添加多个
    ADD_DEFINITIONS(-DFBC_EXPORT)
    # 生成动态库
    ADD_LIBRARY(Dynamic_Library SHARED ${DYNAMIC_LIBRARY_CPP_LIST})
ELSE()
    ADD_LIBRARY(Static_Library STATIC ${DYNAMIC_LIBRARY_CPP_LIST})
ENDIF()

# 指定需要包含的头文件
INCLUDE_DIRECTORIES(${PATH_DYNAMIC_LIBRARY_INCLUDE_DIR})

# 生成可执行文件Test_Dynamic_Library
ADD_EXECUTABLE(Test_Dynamic_Library ${TEST_DYNAMIC_LIBRARY_CPP_LIST})
IF (BUILD_DYNAMIC_LIBRARY)
    # 用来为target添加需要链接的共享库,指定工程所用的依赖库,包括动态库和静态库
    TARGET_LINK_LIBRARIES(Test_Dynamic_Library Dynamic_Library)
ELSE()
    TARGET_LINK_LIBRARIES(Test_Dynamic_Library Static_Library)
ENDIF()

编译方法(ReadMe.txt):

在Linux下通过CMake编译Samples_Dynamic_Library中的测试代码步骤:
将终端定位到Linux_Code_Test/Samples_Dynamic_Library,依次执行如下命令:
$ mkdir build
$ cd build
// Note:-DBUILD_DYNAMIC_LIBRARY=1,编译生成动态库; -DBUILD_DYNAMIC_LIBRARY=0, 编译生成静态库
$ cmake -DBUILD_DYNAMIC_LIBRARY=1 ..
$ make (生成动态库和执行文件)
$ ./Test_Dynamic_Library


        GitHub:https://github.com/fengbingchun/Linux_Code_Test
————————————————
版权声明:本文为CSDN博主「fengbingchun」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/fengbingchun/article/details/78898623

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值