Oracle® Solaris 11.2 链接程序和库指南

退出打印视图

更新时间: 2014 年 7 月
 
 

使用桩目标文件隐藏过时的接口

库不断演变,有时原始功能会被证明为不符合需求。添加新功能,废弃旧功能,这已成为常态。如果要考虑向后兼容性,则必须维护库中的此类旧功能,以便于处理现有目标文件。但您可能不希望再次用到这些功能。桩目标文件可用于实施这一策略。可使用 mapfile STUB_ELIMINATE 标志标记某目标文件中的功能或数据,这些功能或数据会从桩目标文件中删除,但仍保留在实际目标文件中。这样便可防止链接到此桩目标文件的新代码使用这些废弃项,并鼓励要重写的代码使用首选接口。由于实际目标文件仍然包含这些项目,现有目标文件仍可使用它们。

上一节中的 libidx5 示例就阐述了这种情况。该库演示了如何从一个目标文件中导出全局数据。然而,导出的全局数据增加了动态链接的复杂性,因此最好避免出现这种情况。较好的办法是提供一个访问此类数据的函数,例如 libidx5 提供的 idx5_func() 函数。继续这个示例,可以使用 STUB_ELIMINATE 使链接到该桩的新代码不可使用全局数据,同时在实际目标文件中提供这些旧接口,以便于处理现有程序。

重写 mapfile 以将 STUB_ELIMINATE 应用于两个全局数据符号。将 STUB_ELIMINATE 应用于全局数据的一个益处是不再需要提供 ASSERT 指令来提供数据大小。在此例中,ASSERT 被注释掉。实际的 mapfile 可能会完全省略它。

$ cat better_mapfile
$mapfile_version 2
STUB_OBJECT;
SYMBOL_SCOPE {
        _idx5 {
                FLAGS=STUB_ELIMINATE;
                #ASSERT { TYPE=data; SIZE=4[5] };
        };
        idx5 {
                FLAGS=STUB_ELIMINATE;
                #ASSERT { BINDING=weak; ALIAS=_idx5 };
        };
        idx5_func;
    local:
        *;
};

新版测试程序仅使用功能接口。

$ cat better_main.c
#include    <stdio.h>

extern int idx5_func(int);

int
main(int argc, char **argv)
{
        int i;
        for (i = 0; i < 5; i++)
                (void) printf("[%d] %d\n", i, idx5_func(i));
        return (0);
}

保存旧的测试程序,用新的 mapfile 重建桩目标文件,然后重建此测试程序并链接到采用 STUB_ELIMINATE 的新桩目标文件。

$ cp a.out original_a.out
$ cc -Kpic -G -M better_mapfile -h libidx5.so.1 idx5.c -o stublib/libidx5.so.1 -zstub
$ cc better_main.c -o better_a.out -L stublib -R '$ORIGIN/lib' -lidx5 -lc
$ ./better_a.out
[0] 0
[1] 1
[2] 2
[3] 3
[4] 4

将无法再构建原始测试程序,因为桩库缺少必要的全局数据符号。然而,早先存在的使用这些全局数据符号的二进制文件仍可正常运行,因为实际库仍提供这些全局数据符号。

$ cc main.c -L stublib -R '$ORIGIN/lib' -lidx5 -lc
Undefined                       first referenced
 symbol                             in file
idx5                                main.o
_idx5                               main.o
ld: fatal: symbol referencing errors
$ ./original_a.out
[0] 0 0 0
[1] 1 1 1
[2] 2 2 2
[3] 3 3 3
[4] 4 4 4