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