如果由与符号关联的实现维护自身状态,则在直接绑定环境内多次定义的同名符号可能会出现问题。从这一点来说,数据符号通常是违规者,然而维护自身状态的函数也可能会出现问题。
在直接绑定环境中,可以绑定到同一符号的多个实例。因此,不同的绑定实例可以在一个进程内操纵原本打算成为单个实例的不同状态变量。
例如,假设两个共享目标文件包含相同的数据项 errval。另外,假设两个函数 action() 和 inspect() 存在于不同的共享目标文件中。这些函数预期分别读取和写入 errval 值。
使用缺省搜索模型时,errval 的一个定义将插入到另一个定义上。函数 action() 和 inspect() 都将绑定到 errval 的同一实例。因此,如果 action() 向 errval 写入了一个错误代码,则 inspect() 可以读取和处理该错误状态。
但是,假设包含 action() 和 inspect() 的目标文件分别被绑定到了各自定义了 errval 的不同依赖项。在直接绑定环境内,这些函数将被绑定到 errval 的不同定义。action() 可以向 errval 的一个实例写入错误代码,而 inspect() 读取 errval 的另一个未初始化的定义。结果是,inspect() 未检测到要处理的错误状态。
如果符号是在标头中声明的,则通常会出现数据符号的多个实例。
int bar;
该数据声明会导致包含该标头的每个编译单元生成一个数据项。此暂定结果数据项会导致在不同的动态目标文件中定义符号的多个实例。
但是,通过将数据项显式定义为外部数据项,可以为包含该标头的每个编译单元生成对数据项的引用。
extern int bar;
然后,在运行时可以将这些引用解析为一个数据实例。
有时候,应当保留您要删除的符号实现的接口。同一接口的多个实例可以向量化到一个实现,同时保留任何现有接口。通过使用 FILTER mapfile 关键字创建单个符号过滤器,可以实现此模型。SYMBOL_SCOPE / SYMBOL_VERSION 指令中描述了此关键字。
当依赖项期望在已删除了某个符号实现的目标文件中找到该符号时,创建单个符号过滤器很有用。
例如,假设函数 error() 存在于两个共享目标文件 A.so.1 和 B.so.1 中。为消除符号重复,您希望从 A.so.1 中删除该实现。不过,其他依赖项正依赖于从 A.so.1 提供的 error()。以下示例显示了 A.so.1 中 error() 的定义。然后,使用一个 mapfile 来允许删除 error() 实现,同时为定向到 B.so.1 的该符号保留一个过滤器。
$ cc -o A.so.1 -G -Kpic error.c a.c b.c .... $ elfdump -sN.dynsym A.so.1 | fgrep error [3] 0x300 0x14 FUNC GLOB D 0 .text error $ cat mapfile $mapfile_version 2 SYMBOL_SCOPE { global: error { TYPE=FUNCTION; FILTER=B.so.1 }; }; $ cc -o A.so.2 -G -Kpic -M mapfile a.c b.c .... $ elfdump -sN.dynsym A.so.2 | fgrep error [3] 0 0 FUNC GLOB D 0 ABS error $ elfdump -y A.so.2 | fgrep error [3] F [0] B.so.1 error
error() 函数是全局的,并保留 A.so.2 的一个导出接口。但是,到该符号的任何运行时绑定都将指向 filtee B.so.1。字母 "F" 表示该符号是过滤器。
该模型在向量化到一个实现时保留现有接口,多个 Oracle Solaris 库中均使用该模型。例如,曾经在 libc.so.1 中定义的许多数学接口现已向量化到 libm.so.2 中函数的首选实现。