库不断演变,有时原始功能会被证明为不符合需求。添加新功能,废弃旧功能,这已成为常态。如果要考虑向后兼容性,则必须维护库中的此类旧功能,以便于处理现有目标文件。但您可能不希望再次用到这些功能。桩目标文件可用于实施这一策略。可使用 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