控制共享库的符号可见性: 第 1 部分 - 符号可见性简介

本系列介绍符号可见性,分为两个部分,本文是第 1 部分。在这一部分中,将了解关于符号和符号可见性的基本概念,以及程序员在 IBM POWER® 平台上运行的 IBM® AIX® 中控制符号可见性的方式。此外,还将详细介绍符号抢占(symbol preemption),以展示导出的符号与导出且受保护的符号之间的细微差别。

Liu Zhipeng, 软件开发人员, IBM

http://www.ibm.com/developerworks/i/authors/zhipliu_60X84.jpgLiu, Zhipeng 是 IBM 中国 XL Compilers 小组的一名软件开发人员,负责 IBM XL C/C++ 编译器前端开发,主要关注调试、编译器兼容性等。



Ban Huaiyun, 软件测试人员, IBM

http://www.ibm.com/developerworks/i/authors/banhysh_60X84.jpgBan, Huaiyun 是 IBM 中国 XL Compilers 小组的一名软件测试人员,已经做了 4 年的测试,在编译器兼容性方面有着丰富的经验。



Guan Xiaofeng, 软件开发人员, IBM

http://www.ibm.com/developerworks/i/authors/xfguan_60X84.jpgGuan, Xiaofeng (Garfee) 是一名 IBM XL C/C++/Fortran 编译器后端开发人员,主要关注编译器优化、代码生成、工具链、编译器兼容性等。他在 developerWorks 发表过一篇文章。



Zhang Qingshan, 软件开发人员, IBM

http://www.ibm.com/developerworks/i/authors/qshanz_60x84.jpgZhang, Qingshan 于 2010 年加入 IBM,一直专注于 XL C/C++ 编译器前端开发,他对 C++ 语言标准和编译器技术非常感兴趣。



2013 年 9 月 09 日

什么是符号和符号可见性

符号是谈及对象文件、链接等内容时的基本术语之一。实际上,在 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,以及两个函数 func0func1。默认情况下,在 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”表示符号是导出的。函数名称 func0func1 被 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

现在,如信息所示,只有 func1 被导出。然而,尽管 static 关键字可以隐藏符号,但是它还定义了一条额外的规则,即变量或函数只可以在定义它们的文件范围内使用。因此,如果我们定义:

extern int myintvar;

后面,在文件 b.C 中,您可能想要从 a.o 和 b.o 构建 libtest.a。您这样做时,链接器将显示一条错误消息,指出定义在 b.C 中的 myintvar 无法被链接,因为链接器没有在其他地方找到定义。这中断了相同模块内的数据/代码共享,而这种共享通常是程序员所需要的。因此,这种方法更多地用作文件内变量/函数的可见性控制,而不用于低级别符号的可见性控制。实际上,大多数变量/函数不会依赖于 static 关键字来控制符号可见性。因此,我们可以考虑第二种方法:

2. 定义 visibility 属性(仅针对 GNU)

下一个控制符号可见性的备选方法是使用 visibility 属性。ELF 应用程序二进制接口 (ABI) 定义符号的可见性。一般来说,它定义 4 个类,但是大多数情况下,最常用的只有其中两个:

  • STV_DEFAULT - 用它定义的符号将被导出。换句话说,它声明符号是到处可见的。
  • STV_HIDDEN - 用它定义的符号将不被导出,并且不能从其他对象进行使用。

注意,这只是 GNU C/C++ 的一个扩展。因此,目前 PowerLinux 客户可以将它用作符号的 GNU 属性。下面是针对本文示例情况的一个例子:

int myintvar __attribute__ ((visibility ("hidden")));
int __attribute__ ((visibility ("hidden"))) func0 () {
  return ++myintvar;
}
...

要定义 GNU 属性,需要包含 __attribute__ 和用括号括住的内容。您可以将符号的可见性指定为 visibility(“hidden”)。在上面的示例中,我们可以将 myintvarfunc0 标记为 hidden 可见性。这将不允许它们在库中被导出,但是可以在源文件之间共享。实际上,隐藏的符号将不会出现在动态符号表中,但是还被留在符号表中用于静态链接。这是一种良好定义的行为,完全可以达到我们的目的。它显然优于 static 关键字方法。

注意,对于用 visibility 属性指定的变量,将它声明为 static 可能会让编译器感到混淆。因此,编译器会显示一条警告消息。

ELF ABI 也定义其他可见性模式:

  • STV_PROTECTED:符号在当前可执行文件或共享对象之外可见,但是不会被覆盖。换句话说,如果共享库中的一个受保护符号被该共享库中的另一个代码引用,那么此代码将总是引用共享库中的此符号,即便可执行文件定义了相同名称的符号。
  • STV_INTERNAL:符号在当前可执行文件或共享库之外不可访问。

注意,此方法目前不受 XL C/C++ 编译器支持,即便在 PowerLinux 平台上亦是如此。但是,我们还有别的方法。

3. 使用导出列表

上面两种解决方案可以在源代码级别发挥作用,并且只需要编译器就可以实现目的。然而,这要求用户能够告诉链接器去执行类似的工作,因为主要是在动态链接中涉及到符号可见性。针对链接器的解决方案是导出列表。

导出列表由编译器(或相关工具,如 CreateExportlist)在创建共享库的时候自动生成。也可以由开发人员手工编写。导出列表由链接器选项传入并当作链接器的输入。然而,由于编译器驱动程序会完成所有琐碎的工作,所以程序员很少关注那些非常详细的选项。

导出列表的原理是,显式地告诉链接器可以通过外部文件从对象文件导出的符号是哪些。GNU 用户将此类外部文件称作为“导出映射”。我们可以为本文的示例编写一个导出映射:

{
global: func1;
local: *;
};

上面的描述告诉链接器,只有 func1 符号将被导出,其他符号(由 * 匹配)是局部的。程序员也可以显式地列出 func0myintvar 为局部符号 (local:func0;myintvar;)。但是很明显,全部匹配 (*) 更为方便。一般来说,高度推荐使用全部匹配 (*) 来将所有符号都标记为局部并只挑出需要导出的符号,因为这样更安全。这样可以避免用户忘记保持一些符号为局部的,也可以避免两个列表中出现重复,重复可能会导致非预期的行为。

要用这一方法生成 DSO,程序员必须利用 --version-script 链接器选项传递导出映射文件:

$ gcc -shared -o libtest.so a.C -fPIC -Wl,--version-script=exportmap

利用 readelf 二进制实用工具加上 -s 选项读取 ELF 对象文件:readelf -s mylib.so

它将显示只有 func1 对该模块是全局可见的(.dynsym 部分中的信息项),其他符号被隐藏为局部的。

对于 IBM AIX OS 链接器,提供了一个类似的导出列表。确切说,在 AIX 上,导出列表被称作导出文件

编写导出文件很简单。程序员只需将需要导出的符号放入导出文件中即可。在本示例中,就像如下所示这么简单:

func1__Fi  // symbol name

因此,我们用一个链接器选项指定导出文件时,只有我们想要导出的符号被添加到 XCOFF 的“加载器符号表”中,其他符号都保持为非导出的。

对于 AIX 6.1 及以上版本,程序员可能还会附加一个 visibility 属性来描述导出文件中符号的可见性。AIX 链接器现在接受 4 个这样的 visibility 属性类型:

  • export:符号用全局导出属性进行导出。
  • hidden:符号不导出。
  • protected:符号被导出,但是不能被重新绑定(被抢占),即便使用的是运行时链接。
  • internal:符号不导出。符号的地址不得提供给其他程序或共享对象,但是链接器不对此进行验证。

exporthidden 之间的区别很明显。然而,exportedprotected 之间的区别则很微妙。下一节我们将更加详细地讨论符号抢占。

总之,上面 4 个关键字可用于导出文件中。通过将它们附加到符号的末尾(带有一个空格),将会提供不同粒度的符号可见性控制。在本例中,我们也可以像如下所示一样指定符号可见性(在 AIX 6.1 及更高版本上):

func1__Fi export
func0__Fv hidden
myintvar hidden

这通知链接器只有 func1__Fi(即 func1)将会导出,其他符号不会导出。

您可能注意到了,与 GNU 导出映射不同,导出文件中列出的符号都是重整后的名称。重整后的名称看起来不是那么友好,因为程序员可能会不了解重整规则。但是这确实有助于链接器快速地进行名称解析。为了弥补这一缺陷,AIX OS 选择利用一个工具来帮助程序员。

简而言之,如果程序员在调用 XL C/C++ 编译器时指定 -qmkshrobj 选项,那么在编译器成功生成对象文件之后,编译器驱动程序将调用 CreateExportList 工具来自动生成导出文件,其中包含已重整符号的名称。编译器驱动程序然后将此导出文件传递给链接器来处理符号可见性设置。考虑下面这个例子,如果我们调用:

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

这将生成 libtest.a 库,并且所有符号都被导出(这是默认情况)。尽管这没有达到我们的目的,但是至少整个过程对程序员看起来是透明的。程序员也可以选择使用 CreateExportList 实用工具来生成导出文件。如果选择这种方式,您现在能够手工修改导出文件。例如,假设您想要的导出文件名称是 exportfile,那么 qexpfile=exportfile 就是您需要传递给 XL C/C++ 编译器驱动程序的选项。

$ xlC -qmkshrobj -o libtest.a a.o -qexpfile=exportfile

在本例中,您会发现所有符号如下所示:

func0__Fv
func1__Fi
myintvar

根据需要,我们可以简单地删除带有 myintvarfunc0 的行,或者在它们后面附加 hidden 可见性关键字,然后保存导出文件,并使用链接器选项 -bE:exportfile 来传回修改后的导出文件。

$ xlC -qmkshrobj -o libtest.a a.o -bE:exportfile

这将完成所有的步骤。现在,生成的 DSO 将不让 func1__Fi(即 func1)导出:

$ 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

另外,程序员也可以使用 CreateExportList 实用工具来显式地生成导出文件,如下所示:

$ CreateExportList exportfile a.o

在本文示例中,效果跟上面的方法相同。

对于 AIX 6.1 及更高版本上的新格式,逐个地为符号可见性附加关键字可能需要较多的精力。然而,XL C/C++ 编译器计划进行一些更改,以便让程序员的工作更轻松(本系列下一部分中将介绍相关的信息)。

在导出列表解决方案中,所有的信息都保留在导出列表中,程序员不需要更改源文件。这将代码开发和库开发的工作分离开来。然而,我们可能会面临此过程的一个问题。因为我们保持源文件不修改,所以编译器生成的二进制代码可能会不是最优的。编译器失去了优化那些由于缺少信息而不被导出的符号的机会。这会增加所生成二进制文件的大小,或者减慢符号解析的处理速度。然而,对于大多数应用程序来说,这并不是一个主要问题。

下表比较了以上所有解决方案,并且让视图更为集中。

表 1. 每种解决方案的比较
解决方案优点缺点
static 关键字
  • 简单
  • 语言级别支持
  • static 关键字限定变量或函数只可以在定义它的文件范围内使用
导出列表
  • 消除了 static 关键字上的限制
  • 不需要更多的代码
  • 可以关联版本信息
  • 具有不同粒度的符号可见性控制 (AIX)
  • 需要额外的精力去修改导出文件
  • 需要了解符号重整的基本知识
  • 缺少优化信息
指定 visibility 属性
  • 消除了 static 关键字上的限制
  • 更多可见性选择(4 种)来控制导出符号
  • 在代码编写阶段编写更多的代码来设置符号的可见性


符号抢占

正如前面所提到的,可见性关键字 exportprotected 之间存在微妙的区别。此微妙区别就在于符号抢占(symbol preemption)上。符号抢占出现在当链接时解析的符号地址被另一个在运行时解析的符号地址取代时(注意,尽管在 AIX 上运行时链接是可选的)。从概念上讲,运行时链接会在程序执行开始之后解析共享模块中未定义和非延迟的符号。符号抢占是一种提供运行时定义(这些函数定义在链接时不可用)和符号重新绑定功能的机制。在 AIX 上,当主程序利用 -brtl 标志进行链接时或者当预加载的库利用 LDR_CNTRL 环境变量进行指定时,程序能够使用运行时链接设施。利用 -brtl 进行编译会向程序添加一个对动态链接器的引用,当程序开始运行时,该引用会被程序的启动代码 (/lib/crt0.o) 调用。共享对象输入文件按其在命令行中指定的相同顺序在程序加载器部分被列出为关联项。当程序开始运行时,系统加载器加载这些共享对象,以便它们的定义对动态链接器可用。

因此,在运行时重新定义共享对象中的条目是一种叫做符号抢占的功能。 符号抢占只有在 AIX 上使用运行时链接时才能发挥作用。在链接时绑定到一个模块的导入会在运行时重新绑定到另一个模块。一个局部定义是否可以被导入的实例抢占,取决于模块的链接方式。然而,非导出符号永远不会在运行时被抢占。运行时加载器加载组件时,该组件中所有具有默认可见性的符号都会被已经加载的组件中相同名称的符号抢占。注意,因为主程序映像总是最先加载的,所以其定义的任何符号都不会被抢占(重新定义)。

受保护符号会被导出,但是不可以被抢占。相反,导出的符号可被导出并抢占(如果使用运行时链接的话)。

对于默认符号,Linux® 和 AIX 之间存在差别。GNU 编译器和 ELF 文件格式定义一种默认可见性,用于可被导出和抢占的符号。这类似于 AIX 上定义的 exported 可见性。

下面的代码以 AIX 平台为例:

清单 3. func.C
#include <stdio.h>
void func_DEFAULT(){
        printf("func_DEFAULT in the shared library, Not preempted\n");
}

void func_PROC(){
        printf("func_PROC in the shared library, Not preempted\n");
}
清单 4. invoke.C
extern void func_DEFAULT();
extern void func_PROC();

void invoke(){
        func_DEFAULT();
        func_PROC();
}
清单 5. main.C
#include <stdio.h>

extern void func_DEFAULT();
extern void func_PROC();
extern void invoke();

int main(){
        invoke();
        return 0;
}

void func_DEFAULT(){
        printf("func_DEFAULT redefined in main program, Preempted ==> EXP\n");
}

void func_PROC(){
        printf("func_PROC redefined in main program, Preempted ==> EXP\n");
}

在上面的描述中,我们在 func.C 和 main.C 中都定义了 func_DEFAULTfunc_PROC。它们名称相同,但是行为不同。来自 invoke.C 的函数 invoke 将依次调用 func_DEFAULTfunc_PROC。我们将使用下面的 exportlist 代码来看符号是否被导出,以及是如何导出的。

清单 6. exportlist
func_DEFAULT__Fv export
func_PROC__Fv protected
invoke__Fv

如果使用的是 AIX 6.1 之前的链接器版本,可以使用空格代替 exportsymbolic 关键字代替 protected 关键字。下面代码中列出了构建 libtest.so 库和 main 可执行文件的命令:

/* generate position-independent code suitable for use in shared libraries. */
$ xlC -c func.C invoke.C -qpic

/* generate shared library, exportlist is used to control symbol visibility */
$ xlC -G -o libtest.so func.o invoke.o -bE:exportlist

$ xlC -c main.C

/* -brtl enable runtime linkage. */
$ xlC main.o -L. -ltest -brtl -bexpall -o main

本质上,我们是从 func.o 和 invoke.o 构建 libtest.so。我们使用 exportlist 来将 func.C 中的 func_DEFAULT 和 func.C 中的 func_PROC 设置为导出符号,但是仍然是受保护的。这样,libtest.so 就有两个导出符号和一个受保护符号。对于主程序,我们从 main.C 导出所有符号,但是将它链接到 libtest.so。注意,我们使用 -brtl 标志来为 libtest.so 启用动态链接。

下一步是调用主程序。

$ ./main
func_DEFAULT redefined in main program, Preempted ==> EXP
func_PROC in the shared library, Not preempted

在这里我们看到一些有趣的东西:func_DEFAULT是来自 main.C 的版本,而 func_PROC 是来自 libtest.so (func.C) 的版本。func_DEFAULT 符号被抢占,因为来自 libtest.so 的局部版本(我们说它是局部的,是因为调用函数 invoke 来自于 invoke.C,后者本质上与来自 func.C 的 func_DEFAULT 位于同一模块)被来自另一个模块的 func_DEFAULT 符号所取代。然而,func_PROC 上确实出现了相同的条件,它在导出文件中被指定为 protected 可见性。

注意,可以抢占其他符号的符号应该总是导出符号。假设我们在构建可执行文件 main 时删除了 -bexpall 选项,那么输出如下所示:

$ xlC main.o -L. -ltest -brtl -o main; //-brtl enable runtime linkage.
$ ./main
func_DEFAULT in the shared library, Not preempted
func_PROC in the shared library, Not preempted

这里没有发生抢占。所有符号都保持模块中的相同版本。

实际上,要在运行时检查符号是否是导出符号或者受保护符号,我们可以使用 dump 实用工具:

$ dump -TRv libtest.so
libtest.so:

                        ***Loader Section***

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

[0]     0x00000000    undef      IMP     DS EXTref   libc.a(shr.o) printf
[1]     0x2000040c    .data      EXP     DS SECdef        [noIMid] func_DEFAULT__Fv
[2]     0x20000418    .data      EXP     DS SECdef        [noIMid] func_PROC__Fv
[3]     0x20000424    .data      EXP     DS SECdef        [noIMid] invoke__Fv

                        ***Relocation Information***
             Vaddr      Symndx      Type      Relsect    Name
        0x2000040c  0x00000000   Pos_Rel      0x0002     .text
        0x20000410  0x00000001   Pos_Rel      0x0002     .data
        0x20000418  0x00000000   Pos_Rel      0x0002     .text
        0x2000041c  0x00000001   Pos_Rel      0x0002     .data
        0x20000424  0x00000000   Pos_Rel      0x0002     .text
        0x20000428  0x00000001   Pos_Rel      0x0002     .data
        0x20000430  0x00000000   Pos_Rel      0x0002     .text
        0x20000434  0x00000003   Pos_Rel      0x0002     printf
        0x20000438  0x00000004   Pos_Rel      0x0002     func_DEFAULT__Fv
        0x2000043c  0x00000006   Pos_Rel      0x0002     invoke__Fv

这是来自 libtest.so 的输出。我们可以发现,func_DEFAULT__Fvfunc_PROC__Fv 都是导出符号。然而,func_PROC__Fv 不具有任何重新定位。这意味着,加载器可能找不到方法来替换 TOC 表中 func_PROC 的地址。TOC 表中 func_PROC 的地址是函数调用要将控制转移到的地方。因此,func_PROC 似乎不会被抢占。我们然后认识到,它是受保护的

实际上,符号抢占使用得很少。然而,它让我们可以在运行时动态地替换符号,但是也会留下一些安全漏洞。如果不想让库中的关键符号被抢占(但是仍然需要导出),为安全起见,需要将它设置为受保护的。


致谢

非常感谢 Jinsong Ji 博士对本文进行审阅并提出有价值的建议。

参考资料

学习

  • GCC wiki 了解基本的可见性概念。这里还解释了为什么可见性很有用,以及如何使用 visibility 属性。
  • 了解如何使用 GNU 导出映射,它让 GNU 链接器可以在链接时正确地设置符号可见性。
  • wiki 上了解库的基本概念,以及涉及到的一些相关概念。
  • IBM AIX 文档中关于符号的正式介绍。
  • IBM AIX 6.1 文档中关于共享库和共享内存的正式介绍。
  • IBM AIX 6.1 文档中关于 ld 命令的详细信息。
  • IBM AIX 6.1 文档中关于 -brtl 选项的单独介绍
  • IBM AIX 6.1 文档中关于符号可见性的精确定义。
  • AIX and UNIX 专区:developerWorks 的“AIX and UNIX 专区”提供了大量与 AIX 系统管理的所有方面相关的信息,您可以利用它们来扩展自己的 UNIX 技能。
  • AIX and UNIX 新手入门:访问“AIX and UNIX 新手入门”页面可了解更多关于 AIX 和 UNIX 的内容。
  • AIX and UNIX 专题汇总:AIX and UNIX 专区已经为您推出了很多的技术专题,为您总结了很多热门的知识点。我们在后面还会继续推出很多相关的热门专题给您,为了方便您的访问,我们在这里为您把本专区的所有专题进行汇总,让您更方便的找到您需要的内容。
  • AIX and UNIX 下载中心:在这里你可以下载到可以运行在 AIX 或者是 UNIX 系统上的 IBM 服务器软件以及工具,让您可以提前免费试用他们的强大功能。
  • IBM Systems Magazine for AIX 中文版:本杂志的内容更加关注于趋势和企业级架构应用方面的内容,同时对于新兴的技术、产品、应用方式等也有很深入的探讨。IBM Systems Magazine 的内容都是由十分资深的业内人士撰写的,包括 IBM 的合作伙伴、IBM 的主机工程师以及高级管理人员。所以,从这些内容中,您可以了解到更高层次的应用理念,让您在选择和应用 IBM 系统时有一个更好的认识。

讨论

  • 加入 developerWorks 中文社区。查看开发人员推动的博客、论坛、组和维基,并与其他 developerWorks 用户交流。

条评论

developerWorks: 登录

标有星(*)号的字段是必填字段。


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件

 


在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。

所有提交的信息确保安全。

选择您的昵称



当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

标有星(*)号的字段是必填字段。

(昵称长度在 3 至 31 个字符之间)

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

 


所有提交的信息确保安全。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=AIX and UNIX, Rational
ArticleID=943730
ArticleTitle=控制共享库的符号可见性: 第 1 部分 - 符号可见性简介
publish-date=09092013