为了账号安全,请及时绑定邮箱和手机立即绑定

控制目标文件符号可见性

标签:
Python

控制符号可见性

实验环境

系统:  16.04.1-Ubuntu
编译器:gnu 5.4.0

参考

第 1 部分 - 符号可见性简介

nm工具使用

使用NM查看目标文件的符号列表

// file : symtest.hppclass SymTest{
    SymTest();
    SymTest(int x);
    ~SymTest();    void foo();
};// file : symtest.cc#include "symtest.hpp"SymTest::SymTest()       {}
SymTest::SymTest( int x) {}
SymTest::~SymTest()      {}void SymTest::foo()      {}

> g++ -g -shared -o libsymtest.so symtest.cc // 编译动态库> g++ -g -c symtest.cc -o libsymtest.o; ar rvs libsymtest.a libsymtest.o // 编译静态库
  • so 编译时,加上 -g 编译信息

  • -g 仅显示外部符号 -C 显示用户级名字

$ nm -g libsymtest.a 

libsymtest.o:0000000000000026 T _ZN7SymTest3fooEv000000000000000c T _ZN7SymTestC1Ei0000000000000000 T _ZN7SymTestC1Ev000000000000000c T _ZN7SymTestC2Ei0000000000000000 T _ZN7SymTestC2Ev000000000000001a T _ZN7SymTestD1Ev000000000000001a T _ZN7SymTestD2Ev
$ nm -C libsymtest.a 

libsymtest.o:0000000000000026 T SymTest::foo()000000000000000c T SymTest::SymTest(int)0000000000000000 T SymTest::SymTest()000000000000000c T SymTest::SymTest(int)0000000000000000 T SymTest::SymTest()000000000000001a T SymTest::~SymTest()000000000000001a T SymTest::~SymTest()

ps. 构造函数和系够函数会出现两次,见使用NM查看目标文件的符号列表

  • -A 符号前显示二进制文件名称

$ nm -C -A libsymtest.a

libsymtest.a:libsymtest.o:0000000000000026 T SymTest::foo()
libsymtest.a:libsymtest.o:000000000000000c T SymTest::SymTest(int)
libsymtest.a:libsymtest.o:0000000000000000 T SymTest::SymTest()
libsymtest.a:libsymtest.o:000000000000000c T SymTest::SymTest(int)
libsymtest.a:libsymtest.o:0000000000000000 T SymTest::SymTest()
libsymtest.a:libsymtest.o:000000000000001a T SymTest::~SymTest()
libsymtest.a:libsymtest.o:000000000000001a T SymTest::~SymTest()
  • -l 显示行号,需要配合 -g 调试选项使用

$ nm -C -A -l libsymtest.a
 
libsymtest.a:libsymtest.o:0000000000000026 T SymTest::foo()
libsymtest.a:libsymtest.o:000000000000000c T SymTest::SymTest(int)  /home/zhanghl/001_cpp/005_snipts/0004_gnu_symbol_visiable/with_cpp/symtest.cc:4libsymtest.a:libsymtest.o:0000000000000000 T SymTest::SymTest() /home/zhanghl/001_cpp/005_snipts/0004_gnu_symbol_visiable/with_cpp/symtest.cc:3libsymtest.a:libsymtest.o:000000000000000c T SymTest::SymTest(int)
libsymtest.a:libsymtest.o:0000000000000000 T SymTest::SymTest()
libsymtest.a:libsymtest.o:000000000000001a T SymTest::~SymTest()    /home/zhanghl/001_cpp/005_snipts/0004_gnu_symbol_visiable/with_cpp/symtest.cc:5libsymtest.a:libsymtest.o:000000000000001a T SymTest::~SymTest()

符号类型

符号类型一列有两个字母时,小写字母代表局部符号,大写则为全局/外部符号。

‘A’   - 符号的值为连接期间不能改变
‘B b’ - BSS 段, 存放未初始化的数据
‘C’   - comm symbols ?
'D d' - 初始化的数据段
‘G g’ - 存放小对象的初始化数据段,某些目标文件格式支持小对象的快速访问,比如全局的 int 类型可以存放在此处,而大型全局数组不能存放在这里
'i'   - 看不懂 ?
"N"   - 调试相关的符号
"p"   - 该符号在堆栈展开部分
"R r" - 常量,只读数据段
"S s" - 存放小对象的未初始化数据段
"T t" - text段,代码段
"U"   - 未定义符号
"u"   - 独占的全局符号,GNU对ELF标准的拓展,链接过程需要保证该符号是独占的
"V v" - 看不懂?
"W w" - 看不懂?
"-"   - a.out 文件中的调试信息相关
"?"   - 看不懂?

符号及符号可见性是什么?

符号概念与对象文件(.o)、链接等概念相关,对于 C/C++ 语言,用户定义的变量、函数名称、及命名空间、类/结构/名称等,都会在对象文件中生成符号,这些符号对于链接器(linker)确定不同模块(对象文件、动态共享库、可执行文件)是否会共享相同的数据或代码很有用。 ps. 水平有限,此处只关注函数的符号可见性

举例

nm命令中符号类型详解

// file : demo.cc// description : demo symbol of elements in Cint a1;int a2 = 1;const int a3 = 1;static int sa = 1;static int funA() {return 1;}int funB() {return 2;}


$ g++ -g -c demo.cc 
$ nm -C -A -l demo.o 
demo.o:0000000000000000 B global BBS段-未初始化变量   a1   /home/zhanghl/001_cpp/005_snipts/0004_gnu_symbol_visiable/with_cpp/demo.cc:1demo.o:0000000000000000 D global 数据段-初始化变量    a2    /home/zhanghl/001_cpp/005_snipts/0004_gnu_symbol_visiable/with_cpp/demo.cc:5demo.o:000000000000000b T global 代码段             funB() /home/zhanghl/001_cpp/005_snipts/0004_gnu_symbol_visiable/with_cpp/demo.cc:6demo.o:0000000000000000 r local  常量               a3    /home/zhanghl/001_cpp/005_snipts/0004_gnu_symbol_visiable/with_cpp/demo.cc:5demo.o:0000000000000004 d local  数据段-初始化变量    sa    /home/zhanghl/001_cpp/005_snipts/0004_gnu_symbol_visiable/with_cpp/demo.cc:5demo.o:0000000000000000 t local  代码段             funA() /home/zhanghl/001_cpp/005_snipts/0004_gnu_symbol_visiable/with_cpp/demo.cc:5> readelf -s demo.o
Symbol table '.symtab' contains 20 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name     6: 0000000000000000     4 OBJECT  LOCAL  DEFAULT    4 _ZL2a3     7: 0000000000000004     4 OBJECT  LOCAL  DEFAULT    2 _ZL2sa     8: 0000000000000000    11 FUNC    LOCAL  DEFAULT    1 _ZL4funAv    17: 0000000000000000     4 OBJECT  GLOBAL DEFAULT    3 a1    18: 0000000000000000     4 OBJECT  GLOBAL DEFAULT    2 a2    19: 000000000000000b    11 FUNC    GLOBAL DEFAULT    1 _Z4funBv

为什么控制符号可见性?

编译器默认导出所有的符号,存在很高的风险,链接时可能会导致符号冲突,只要有人链接一个跟您的库具有相同符号名称的库,当进行链接器解析时,该库就可能会意外地覆盖您自己的符号。

导出所有的符号,会增加动态库的加载和链接时间。

举例

> g++ -c demo.cc                          // 生成目标文件 demo.o> cp demo.cc demo1.cc                     // 此时 demo1.cc 同样具有符号 a、sa、funA 和 funB> g++ -c demo1.cc                         // 生成目标文件 demo1.o> g++ -shared -o demo.so demo.o demo1.o   // 链接生成动态库 demo.so, 产生符号冲突demo1.o:(.data+0x0): `a'被多次定义
demo.o:(.data+0x0):第一次在此定义
demo1.o:在函数‘funB()’中:
demo1.cc:(.text+0xb): `funB()'被多次定义
demo.o:demo.cc:(.text+0xb):第一次在此定义
collect2: error: ld returned 1 exit status

题外话 - 头文件中放置函数的定义引起符号冲突

// file: demo1.hpp#ifndef __demo1_hpp_#define __demo1_hpp_int test1() { return 1; }#endif//#ifndef __demo1_hpp_// file: demo2.cc#include "demo1.hpp"int test2(){ return test1(); }// file: demo3.cc#include "demo1.hpp"int test3(){ return test1(); }

> g++ -fPIC -shared -o demo.so demo2.cc demo3.cc
/tmp/cchKMCYz.o:在函数‘test1()’中:
demo3.cc:(.text+0x0): `test1()'被多次定义
/tmp/ccLp8ynr.o:demo2.cc:(.text+0x0):第一次在此定义
collect2: error: ld returned 1 exit status

// 1, demo1.hpp 中的 `#ifndef ... #define ...#endif` 不能阻止此类符号冲突
// 2, 将 demo1.hpp 中的函数 test1 改为 inline  可以修复该编译错误
// 3, 也可以将 demo1.hpp 中的函数 test1 改为 static [inline], 会生成两个版本的 test1

控制符号可见性的方式

1, static 关键字

如上所示 demo.cc 中, static 改变可见性

2, (仅针对gnu)visibility属性

// file : demo.cc// description : demo visibility change the visiable of symbol#if defined(__GNUC__) && ((__GNUC__ >= 4) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3))#define NP_VISIBILITY_DEFAULT __attribute__((visibility("default")))#else#define NP_VISIBILITY_DEFAULT#endif#define NP_EXPORT(__type) NP_VISIBILITY_DEFAULT __typeint a1; 
int a2 = 1;const int a3 = 1;int sa = 1;int funA() {return 1;} 
NP_EXPORT(int) funB() {return 2;} 

$ g++ -g -shared -o libdemo.so -fvisibility=hidden demo.cc 
$ nm -C -a -l libdemo.so 

000000000020102c b a1   /home/zhanghl/001_cpp/005_snipts/0004_gnu_symbol_visiable/with_cpp/demo.cc:90000000000201020 d a2   /home/zhanghl/001_cpp/005_snipts/0004_gnu_symbol_visiable/with_cpp/demo.cc:100000000000201024 d sa   /home/zhanghl/001_cpp/005_snipts/0004_gnu_symbol_visiable/with_cpp/demo.cc:120000000000000600 t funA()   /home/zhanghl/001_cpp/005_snipts/0004_gnu_symbol_visiable/with_cpp/demo.cc:13000000000000060b T funB()   /home/zhanghl/001_cpp/005_snipts/0004_gnu_symbol_visiable/with_cpp/demo.cc:140000000000000624 r a3

3, 使用导出列表

// file : demo.cc// description : demo version-script change the visiable of symbolint a = 1;int sa = 1;int funA() {return 1;}int funB() {return 2;}/*
// file : exportmap
// description : define which is global or local
{
global:
a;         // var a is global, D
_Z4funAv;  // funA is global, T
local: *;  // default is local
};
*/> g++ -shared -o demo.so  demo.cc -fPIC -Wl,--version-script=exportmap 
> nm demo.so0000000000201020 D a0000000000201024 d sa0000000000000570 T _Z4funAv000000000000057b t _Z4funBv

cpp 文件

// file : symtest.hpp#if defined(__GNUC__) && ((__GNUC__ >= 4) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3))#define NP_EXPORT __attribute__((visibility("default")))#else#define NP_EXPORT#endifclass SymTest{
    SymTest() ;    NP_EXPORT SymTest(int x); 
    ~SymTest();    void foo();
};// file : symtest.cc#include "symtest.hpp"SymTest::SymTest()       {}
SymTest::SymTest( int x) {}
SymTest::~SymTest()      {}void SymTest::foo()      {}

$ g++ -g -fvisibility=hidden -shared -o libsymtest.so symtest.cc
$ nm -C -A -l libsymtest.so 

libsymtest.so:0000000000000656 t SymTest::foo() /home/zhanghl/001_cpp/005_snipts/0004_gnu_symbol_visiable/with_cpp/symtest.cc:6libsymtest.so:000000000000063c T SymTest::SymTest(int)  /home/zhanghl/001_cpp/005_snipts/0004_gnu_symbol_visiable/with_cpp/symtest.cc:4libsymtest.so:0000000000000630 t SymTest::SymTest() /home/zhanghl/001_cpp/005_snipts/0004_gnu_symbol_visiable/with_cpp/symtest.cc:3libsymtest.so:000000000000063c T SymTest::SymTest(int)
libsymtest.so:0000000000000630 t SymTest::SymTest()
libsymtest.so:000000000000064a t SymTest::~SymTest()    /home/zhanghl/001_cpp/005_snipts/0004_gnu_symbol_visiable/with_cpp/symtest.cc:5libsymtest.so:000000000000064a t SymTest::~SymTest()



作者:呆呆的张先生
链接:https://www.jianshu.com/p/82e1ef7a23f5


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消