控制符号可见性
实验环境
系统: 16.04.1-Ubuntu
编译器:gnu 5.4.0
参考
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. 水平有限,此处只关注函数的符号可见性
举例
// 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
共同学习,写下你的评论
评论加载中...
作者其他优质文章