控制共享库的符号可见性(一) 符号可见性简介

什么是符号和符号可见性

符号是谈及对象文件、链接等内容时的基本术语之一。实际上,在 C/C++ 语言中,符号是很多用户定义的变量、函数名称以 及一些名称空间、类/结构/名称等的对应实体。例如,当我们定义非静态全局变量或非静态函数时,C/C++ 编译器就会在对象文 件中生成符号,这些符号对于链接器(linker)确定不同模块(对象文件、动态共享库、可执行文件)是否会共享相同的数据或 代码很有用。

尽管变量和函数都可能会在模块之间共享,但是对象文件之间的变量共享更为常见。例如,程序员可能会在 a.c 中声明一个 变量:

extern int shared_var;

却在 b.c 中定义该变量:

int shared_var;

这样,两个 shared_var 符号会出现在已编译的对象 a.o 和 b.o 中,最后在链接器解析之后,a.o 中的符号会共享 b.o 的 地址。但是,人们很少让变量在共享库和可执行文件之间共享。对于此类模块,通常只会让函数对其他模块可见。有时,我们将 此类函数称之为 API,因为我们觉得该模块是为其他模块提供调用的接口。我们也把这种符号称为导出的 (exported),因为它对 其他模块可见。注意,此可见性只在动态链接时有效,因为共享库通常在程序运行时被加载为内存映像的一部分。因此,符号可 见性 (symbol visibility) 是所有全局符号的一个用于动态链接的属性。

为什么需要控制符号可见性

在不同的平台上,XL C/C++ 编译器可能会选择导出或者不导出模块中的所有符号。例如,在 IBM PowerLinux 平台上创建 Executable and Linking Format (ELF) 共享库时,默认情况下,所有的符号都会导出。在 POWER 平台上的 AIX 系统中创建 XCOFF 库时,当前 XL C/C++ 编译器在没有工具的帮助下可能会选择不导出任何符号。还有其他方式允许程序员逐个地决定符号 可见性(这是本系列下一部分要介绍的内容)。但是,一般不建议导出模块中的所有符号。程序员可以根据需要导出符号。这不 仅对库的安全有益,也对动态链接时间有益。

程序员选择导出所有符号时,存在很高的风险,链接时可能会出现符号冲突,尤其是当模块是由不同的开发人员开发的时。因 为符号是低级别的概念,所以它不涉及到作用域。只要有人链接一个跟您的库具有相同符号名称的库,当进行链接器解析时,该 库就可能会意外地覆盖您自己的符号(但愿会给出一些警告或错误信息)。大多数情况下,此类符号从来不会被从库设计者的角 度去使用。因此,为符号创建有限制、有含义(经过深思熟虑)的名称,对于避免此类问题有很大帮助。

对于 C++ 编程,现在越来越注重性能了。然而,由于对其他库的依赖性以及使用特定的 C++ 特性(比如模板),编译器/链 接器趋向于会使用和生成大量的符号。因此,导出所有符号会减慢程序速度,并耗用大量内存。导出有限数量的符号可以缩短动 态共享库的加载和链接时间。此外,也支持编译器角度的优化,这意味着会生成更有效的代码。

以上关于导出所有符号的缺点解释了为什么一定要定义符号可见性。在本文中,我们将提供一些解决方案来控制动态共享对象 (DSO) 中的符号。用户可以使用不同的方式解决相同的问题,我们将提议特定平台应该首选哪种解决方式。

控制符号可见性的方式

在后面的讨论中,我们将用到下面的 C++ 代码片段:
清单 1. a.C

int myintvar = 5;

int func0 () {
 return ++myintvar;
}

int func1 (int i) {
 return func0() * i;
}

在 a.C 中,我们定义了一个变量 myintvar,以及两个函数 func0 和 func1。默认情况下,在 AIX 平台上创建共享库时,编 译器和链接器以及 CreateExportList 工具会让所有三个符号都可见。我们可以利用 dump 二进制工具从 Loader Symbol Table Information 检查这一情况:

$ xlC -qpic a.C -qmkshrobj -o libtest.a
$ dump -Tv libtest.a

               ***Loader Symbol Table Information***
[Index]      Value      Scn     IMEX Sclass   Type           IMPid Name

[0]     0x20000280    .data      EXP     RW SECdef        [noIMid] myintvar
[1]     0x20000284    .data      EXP     DS SECdef        [noIMid] func0__Fv
[2]     0x20000290    .data      EXP     DS SECdef        [noIMid] func1__Fi

这里,“EXP”表示符号是导出的。函数名称 func0 和 func1 被 C++ 重整规则(mangling rule)进行了重整( 但是,不难猜出名称的意思)。dump 工具的 -T 选项显示 Loader Symbol Table Information,动态链接器将用到此信息。在本 例中,a.C 中的所有符号都被导出。但是从库编写者的角度,本例中我们可能只想导出 func1。全局符号 myintvar 和函数 func0 被认为只保持/改变内部状态,或者说只在局部使用。因此,对于库编写者来说,让它们不可见至关重要。

我们至少有三种方式可以达此目的。包括:使用 static 关键字,定义 GNU visibility 属性,以及使用导出列表。每种方式 都有各自不同的功用和缺点。下面就来看看这些方式。
1. 使用 static 关键字

C/C++ 中的 static 可能是一个最常用的关键字,因为它可以为变量指定作用域和存储。对于作用域,可以说成它为文件中的 符号禁用了外部链接。这意味着,带有关键字 static 的符号永远不会是可链接的,因为编译器不为链接器留下关于此符号的任 何信息。这是一种语言级别的控制,是最简单的一种隐藏符号的方式。

我们来给上面的例子添加 static 关键字吧:
清单 2. b.C

static int myintvar = 5;

static int func0 () {
 return ++myintvar;
}

int func1 (int i) {
 return func0() * i;
}

生成共享库并再次查看 Loader Symbol Table Information,可以看到预期的效果:

$ xlC -qpic a.C -qmkshrobj -o libtest.a
$ dump -Tv libtest.a

               ***Loader Symbol Table Information***
[Index]      Value      Scn     IMEX Sclass   Type           IMPid Name

[0]     0x20000284    .data      EXP     DS SECdef        [noIMid] func1__Fi

时间: 2024-08-07 21:49:35

控制共享库的符号可见性(一) 符号可见性简介的相关文章

静态库和共享库制作

1静态库和共享库 *本节就如何创建和使用程序库进行论述.所谓"程序库",简单说,就是包含了数据 和执行码的文件.其不能单独执行,可以作为其它执行程序的一部分来完成某些功能.库的 存在,可以使得程序模块化,可以加快程序的再编译,可以实现代码重用,可以使得程序便 于升级.程序库可分静态库(static library)和共享库(shared object).   A:静态库 是在可执行程序运行前就已经加入到执行码中,成为执行程序的一部分:共享库,是在 执行程序启动时加载到执行程序中,可以被

PowerShell实现在控制台中插入绿色的打勾符号_PowerShell

在前面的PowerShell技巧中,我们看到了PowerShell控制台支持输出所有TrueType字体的字符,你只需将你的字符代码转换成Char类型.今天再来一个更高级的例子,让用户在控制台中插入绿色的打勾符号. $greenCheck = @{ Object = [Char]8730 ForegroundColor = 'Green' NoNewLine = $true } Write-Host "Status check... " -NoNewline Start-Sleep -

Linux静态库和共享库【转】

转自:http://www.cnblogs.com/zlcxbb/p/6806269.html 1.什么是静态库 静态库类似windows中的静态lib 关于windows中的静态lib,可参考 Windows动态链接库DLL  特点:包含函数代码声明和实现,链接后所有代码都嵌入到宿主程序中. 只在编译时使用,执行时不再需要该静态库. 2.静态库编写示例如下:addvec.c void addvec(int* x, int* y, int*z, int n) { int i=0; for(; i

【原创】Linux下共享库嵌套依赖问题

问题场景:  动态库 librabbitmq_r.so 内部依赖动态库 libevent_core.so 和 libevent_pthreads.so : 可执行程序 sa 依赖动态库 librabbitmq_r.so ; 在链接生成 sa 的时候希望只指定 librabbitmq_r.so 而不指定 libevent_core.so 和 libevent_pthreads.so . 错误信息:  ... g++ ../source/authorisecfg.o ../source/bmcins

linux-使用tcl8.6共享库,遇到错误提示undefined reference to

问题描述 使用tcl8.6共享库,遇到错误提示undefined reference to 项目中用到了tcl8.6的共享库,把tcl的源码下载后在ubuntu上将tcl的库编译出来了.但是在编译项目的时候总是报未定义引用的错误,我检查了自己编译出来的libtcl8.6.so,这个共享库中是有tclStringType的定义的,也检查了声明tclStringType的头文件,编译tcl共享库时的头文件与编译项目时的头文件是一致的. 以下是共享库符号检查和编译的结果,跪求武林高手出手相帮,不胜感激

librep 0.92.1b发布 共享库

Librep是一个共享库,用于实施一个轻量级.合理的和高度可扩展的Lisp同语系语言.它包含了一个注释器.字节码编译器和虚拟机.应用程序可以使用一个注释器作为扩展语言,或它可能使用在独立的脚本. 关于Lisp Lisp语言最早是在 20 世纪 50 年代末由http://www.aliyun.com/zixun/aggregation/13513.html">麻省理工学院为研究人工智能而开发的.Lisp 语言的强大使它在其它方面诸如编写编辑命令和集成环境等显示其优势.Lisp 代表 LIS

新手小心:c语言中强符号与弱符号的使用_C 语言

声明:下面的实例全部在linux下尝试,window下未尝试.有兴趣者可以试一下.文章针c初学者.c语言的强符号和弱符号是c初学者经常容易犯错的地方.而且很多时候,特别是多人配合开发的程序,它引起的问题往往非常行为怪异而且难以定位.什么是强符号和弱符号?在c语言中,函数和初始化的全局变量是强符号,未初始化的全局变量时弱符号.强符号和弱符号的定义是连接器用来处理多重定义符号的,它的规则是:不允许多个强符号:如果一个强符号和一个弱符号,这选择强符号:如果多个弱符号,则任意选一个.它的陷阱:上代码:

linux使用gcc编译c语言共享库步骤_C 语言

对任何程序员来说库都是必不可少的.所谓的库是指已经编译好的供你使用的代码.它们常常提供一些通用功能,例如链表和二叉树可以用来保存任何数据,或者是一个特定的功能例如一个数据库服务器的接口,就像MySQL. 大部分大型的软件项目都会包含若干组件,其中一些你发现可以用在其他项目中,又或者你仅仅出于组织目的将不同组件分离出来.当你有一套可复用的并且逻辑清晰的函数时,将其构建为一个库会十分有用,这样你就不将这些源代码拷贝到你的源代码中,而且每次都要再次编译它们.除此之外,你还可以保证你的程序各模块隔离,这

linux 静态库、共享库

一.什么是库   本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行.由于windows和linux的本质不同,因此二者库的二进制是不兼容的.   Linux操作系统支持的函数库分为静态库和动态库,动态库又称共享库.Linux系统有几个重要的目录存放相应的函数库,如/lib    /usr/lib.   二.静态函数库.动态函数库   A.  这类库的名字一般是libxxx.a:利用静态函数库编译成的文件比较大,因为整个函数库的所有数据都被整合进目标代码中,他的优点就显而易见了