Board logo

标题: 控制共享库的符号可见性-符号可见性简介(3) [打印本页]

作者: look_w    时间: 2018-1-8 15:21     标题: 控制共享库的符号可见性-符号可见性简介(3)

3. 使用导出列表上面两种解决方案可以在源代码级别发挥作用,并且只需要编译器就可以实现目的。然而,这要求用户能够告诉链接器去执行类似的工作,因为主要是在动态链接中涉及到符号可见性。针对链接器的解决方案是导出列表。
导出列表由编译器(或相关工具,如 CreateExportlist)在创建共享库的时候自动生成。也可以由开发人员手工编写。导出列表由链接器选项传入并当作链接器的输入。然而,由于编译器驱动程序会完成所有琐碎的工作,所以程序员很少关注那些非常详细的选项。
导出列表的原理是,显式地告诉链接器可以通过外部文件从对象文件导出的符号是哪些。GNU 用户将此类外部文件称作为“导出映射”。我们可以为本文的示例编写一个导出映射:
1
2
3
4
{
global: func1;
local: *;
};




上面的描述告诉链接器,只有 func1 符号将被导出,其他符号(由 * 匹配)是局部的。程序员也可以显式地列出 func0 或 myintvar 为局部符号 (local:func0;myintvar;)。但是很明显,全部匹配 (*) 更为方便。一般来说,高度推荐使用全部匹配 (*) 来将所有符号都标记为局部并只挑出需要导出的符号,因为这样更安全。这样可以避免用户忘记保持一些符号为局部的,也可以避免两个列表中出现重复,重复可能会导致非预期的行为。
要用这一方法生成 DSO,程序员必须利用 --version-script 链接器选项传递导出映射文件:
1
$ 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 上,导出列表被称作导出文件
编写导出文件很简单。程序员只需将需要导出的符号放入导出文件中即可。在本示例中,就像如下所示这么简单:
1
func1__Fi  // symbol name




因此,我们用一个链接器选项指定导出文件时,只有我们想要导出的符号被添加到 XCOFF 的“加载器符号表”中,其他符号都保持为非导出的。
对于 AIX 6.1 及以上版本,程序员可能还会附加一个 visibility 属性来描述导出文件中符号的可见性。AIX 链接器现在接受 4 个这样的 visibility 属性类型:
export 和 hidden 之间的区别很明显。然而,exported 和 protected 之间的区别则很微妙。下一节我们将更加详细地讨论符号抢占。
总之,上面 4 个关键字可用于导出文件中。通过将它们附加到符号的末尾(带有一个空格),将会提供不同粒度的符号可见性控制。在本例中,我们也可以像如下所示一样指定符号可见性(在 AIX 6.1 及更高版本上):
1
2
3
func1__Fi export
func0__Fv hidden
myintvar hidden




这通知链接器只有 func1__Fi(即  func1)将会导出,其他符号不会导出。
您可能注意到了,与 GNU 导出映射不同,导出文件中列出的符号都是重整后的名称。重整后的名称看起来不是那么友好,因为程序员可能会不了解重整规则。但是这确实有助于链接器快速地进行名称解析。为了弥补这一缺陷,AIX OS 选择利用一个工具来帮助程序员。
简而言之,如果程序员在调用 XL C/C++ 编译器时指定 -qmkshrobj 选项,那么在编译器成功生成对象文件之后,编译器驱动程序将调用 CreateExportList 工具来自动生成导出文件,其中包含已重整符号的名称。编译器驱动程序然后将此导出文件传递给链接器来处理符号可见性设置。考虑下面这个例子,如果我们调用:
1
$ xlC -qpic a.C -qmkshrobj -o libtest.a




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




在本例中,您会发现所有符号如下所示:
1
2
3
func0__Fv
func1__Fi
myintvar




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




这将完成所有的步骤。现在,生成的 DSO 将不让 func1__Fi(即  func1)导出:
1
2
3
4
5
6
$ 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 实用工具来显式地生成导出文件,如下所示:
1
$ CreateExportList exportfile a.o




在本文示例中,效果跟上面的方法相同。
对于 AIX 6.1 及更高版本上的新格式,逐个地为符号可见性附加关键字可能需要较多的精力。然而,XL C/C++ 编译器计划进行一些更改,以便让程序员的工作更轻松(本系列下一部分中将介绍相关的信息)。
在导出列表解决方案中,所有的信息都保留在导出列表中,程序员不需要更改源文件。这将代码开发和库开发的工作分离开来。然而,我们可能会面临此过程的一个问题。因为我们保持源文件不修改,所以编译器生成的二进制代码可能会不是最优的。编译器失去了优化那些由于缺少信息而不被导出的符号的机会。这会增加所生成二进制文件的大小,或者减慢符号解析的处理速度。然而,对于大多数应用程序来说,这并不是一个主要问题。
下表比较了以上所有解决方案,并且让视图更为集中。
表 1. 每种解决方案的比较解决方案优点缺点static 关键字导出列表指定 visibility 属性




欢迎光临 电子技术论坛_中国专业的电子工程师学习交流社区-中电网技术论坛 (http://bbs.eccn.com/) Powered by Discuz! 7.0.0