跳过导航链接 | |
退出打印视图 | |
Oracle Solaris 11.1 链接程序和库指南 Oracle Solaris 11.1 Information Library (简体中文) |
可以定义共享目标文件以将其用作过滤器。此技术涉及将过滤器提供的接口与备用共享目标文件进行关联。在运行时,此备用共享目标文件可提供过滤器所提供的一个或多个接口。此备用共享目标文件称为 filtee。filtee 的生成方式与任意共享目标文件的生成方式相同。
过滤提供了一种从运行时环境中提取编译环境的机制。在链接编辑时,将绑定到过滤器接口的符号引用解析为过滤器符号定义。在运行时,可以将绑定到过滤器接口的符号引用重定向到备用共享目标文件。
通过使用 mapfile 关键字 FILTER 或 AUXILIARY,可以将在共享目标文件中定义的单个接口定义为过滤器。或者,共享目标文件可以使用链接编辑器的 -F 或 -f 选项将共享目标文件提供的所有接口定义为过滤器。通常单独使用这些技术,但也可在同一共享目标文件中组合使用这些技术。
存在两种过滤形式。
此过滤只需要一个用于要过滤的接口的符号表项。在运行时,必须通过 filtee 实现过滤器符号定义。
使用链接编辑器的 mapfile 关键字 FILTER 或链接编辑器的 -F 选项,可以定义接口以将其用作标准过滤器。此 mapfile 关键字或选项使用必须在运行时提供符号定义的一个或多个 filtee 的名称进行限定。
在运行时无法处理的 filtee 会被跳过。如果在 filtee 中找不到标准过滤器符号,则也将导致跳过此 filtee。在这两种情况下,无法使用过滤器提供的符号定义来实现此符号查找。
此过滤提供类似于标准过滤的机制,而且该过滤器还提供一种对应于辅助过滤器接口的回退实现。在运行时,可以通过 filtee 实现符号定义。
使用链接编辑器的 mapfile 关键字 AUXILIARY 或链接编辑器的 -f 选项,可以定义接口以将其用作辅助过滤器。此 mapfile 关键字或选项使用在运行时提供符号定义的一个或多个 filtee 的名称进行限定。
在运行时无法处理的 filtee 会被跳过。如果在 filtee 中找不到辅助过滤器符号,则也将导致跳过 filtee。在这两种情况下,将使用过滤器提供的符号定义来实现此符号查找。
要生成标准过滤器,应先定义要对其应用过滤的 filtee。以下示例将生成 filtee filtee.so.1,并提供符号 foo 和 bar。
$ cat filtee.c char *bar = "defined in filtee"; char *foo() { return("defined in filtee"); } $ cc -o filtee.so.1 -G -K pic filtee.c
可以通过以下两种方法之一提供标准过滤。要将共享目标文件提供的所有接口都声明为过滤器,可使用链接编辑器的 -F 选项。要将共享目标文件的单个接口声明为过滤器,可使用链接编辑器 mapfile 和 FILTER 关键字。
在以下示例中,会将共享目标文件 filter.so.1 定义为过滤器。filter.so.1 提供符号 foo 和 bar ,是针对 filtee filtee.so.1 的过滤器。在此示例中,使用环境变量 LD_OPTIONS 阻止编译器动程序解释 -F 选项。
$ cat filter.c char *bar = NULL; char *foo() { return (NULL); } $ LD_OPTIONS='-F filtee.so.1' \ cc -o filter.so.1 -G -K pic -h filter.so.1 -R. filter.c $ elfdump -d filter.so.1 | egrep "SONAME|FILTER" [2] SONAME 0xee filter.so.1 [3] FILTER 0xfb filtee.so.1
创建动态可执行文件或共享目标文件时,链接编辑器可将标准过滤器 filter.so.1 作为依赖项引用。链接编辑器使用此过滤器符号表中的信息来实现任何符号解析。但是,在运行时,对该过滤器符号的任何引用都会导致增加对该 filtee filtee.so.1 的装入。运行时链接程序使用该 filtee 来解析 filter.so.1 定义的所有符号。如果未找到此 filtee,或者在此 filtee 中未找到过滤器符号,则查找该符号时会跳过此过滤器。
例如,以下动态可执行文件 prog 引用符号 foo 和 bar;这两个符号在链接编辑过程中通过过滤器 filter.so.1 进行解析。执行 prog 会导致从 filtee filtee.so.1 中获取 foo 和 bar,而不是从过滤器 filter.so.1 中获取。
$ cat main.c extern char *bar, *foo(); void main() { (void) printf("foo is %s: bar is %s\n", foo(), bar); } $ cc -o prog main.c -R. filter.so.1 $ prog foo is defined in filtee: bar is defined in filtee
在以下示例中,共享目标文件 filter.so.2 将它的一个接口 foo 定义为 filtee filtee.so.1 的过滤器。
$ cat filter.c char *bar = "defined in filter"; $ cat mapfile $mapfile_version 2 SYMBOL_SCOPE { global: foo { TYPE=FUNCTION; FILTER=filtee.so.1 }; }; $ cc -o filter.so.2 -G -K pic -h filter.so.2 -M mapfile -R. filter.c $ elfdump -d filter.so.2 | egrep "SONAME|FILTER" [2] SONAME 0xd8 filter.so.2 [3] SUNW_FILTER 0xfb filtee.so.1 $ elfdump -y filter.so.2 | egrep "foo|bar" [1] F [3] filtee.so.1 foo [10] D <self> bar
在运行时,对过滤器符号 foo 的任何引用都会导致增加对 filtee filtee.so.1 的装入。运行时链接程序使用该 filtee 仅解析 filter.so.2 定义的符号 foo。对符号 bar 的引用始终使用 filter.so.2 中的符号,因为没有为此符号定义 filtee 处理。
例如,以下动态可执行文件 prog 引用符号 foo 和 bar,这两个符号在链接编辑过程中通过过滤器 filter.so.2 进行解析。执行 prog 会导致从 filtee filtee.so.1 中获取 foo,从过滤器 filter.so.2 中获取 bar。
$ cc -o prog main.c -R. filter.so.2 $ prog foo is defined in filtee: bar is defined in filter
在这些示例中,filtee filtee.so.1 仅与过滤器关联。不能使用该 filtee 从任何其他可能作为 prog 执行结果而装入的目标文件中实现符号查找。
标准过滤器提供了一种用于定义现有共享目标文件的子集接口的便捷机制。使用标准过滤器,可以跨多个现有共享目标文件创建接口组。标准过滤器还提供一种将接口重定向到实现的方法。在 Oracle Solaris OS 中,可以使用多个标准过滤器。
/usr/lib/libsys.so.1 过滤器提供标准 C 库 /usr/lib/libc.so.1 的子集。此子集表示位于必须由兼容应用程序导入的 C 库中兼容 ABI 的函数和数据项。
/lib/libxnet.so.1 过滤器使用多个 filtee。该库提供来自 /lib/libsocket.so.1、/lib/libnsl.so.1 和 /lib/libc.so.1 的套接字和 XTI 接口。
libc.so.1 定义运行时链接程序的接口过滤器。这些接口在以下两者之间提供了抽象:libc.so.1 的编译环境中引用的符号,以及 ld.so.1(1) 的运行时环境中生成的实际实现绑定。
libnsl.so.1 针对 libc.so.1 定义标准过滤器 gethostname(3C)。以前,libnsl.so.1 和 libc.so.1 针对该符号提供相同的实现。通过将 libnsl.so.1 建立为过滤器,只需要存在一种 gethostname() 实现。由于 libnsl.so.1 继续导出 gethostname(),因此此库的接口将继续保持与以前的发行版兼容。
由于在运行时从不引用标准过滤器中的代码,因此,无需向任何定义为过滤器的函数中添加内容。任何过滤器代码都可能需要重定位,这样会在运行时处理过滤器期间导致不必要的开销。建议将函数定义为空例程,或者直接从 mapfile 进行定义。请参见SYMBOL_SCOPE / SYMBOL_VERSION 指令。
在过滤器中生成数据符号时,始终将数据与节关联。可通过在可重定位目标文件中定义符号来生成此关联。也可以通过在 mapfile 中定义符号并使用 size 声明和 no value 声明来生成此关联。请参见SYMBOL_SCOPE / SYMBOL_VERSION 指令。生成的数据定义可确保正确建立来自动态可执行文件的引用。
链接编辑器执行某些更为复杂的符号解析时,需要了解符号的属性(包括符号大小)。因此,应该在过滤器中生成符号,以便符号属性与 filtee 中的符号属性匹配。维护属性一致性可确保链接编辑过程使用与运行时所用的符号定义兼容的方式来分析过滤器。请参见符号解析。
要生成辅助过滤器,应先定义要对其应用过滤的 filtee。以下示例将生成一个 filtee filtee.so.1,并提供符号 foo。
$ cat filtee.c char *foo() { return("defined in filtee"); } $ cc -o filtee.so.1 -G -K pic filtee.c
可以通过以下两种方法之一提供辅助过滤。要将共享目标文件提供的所有接口都声明为辅助过滤器,可使用链接编辑器的 -f 选项。要将共享目标文件的单个接口声明为辅助过滤器,可使用链接编辑器 mapfile 和 AUXILIARY 关键字。
以下示例将 filter.so.1 共享目标文件定义为辅助过滤器。filter.so.1 提供 foo 和 bar 符号,并且是 filtee filtee.so.1 的辅助过滤器。在此示例中,使用环境变量 LD_OPTIONS 来阻止编译器动程序解释 -f 选项。
$ cat filter.c char *bar = "defined in filter"; char *foo() { return ("defined in filter"); } $ LD_OPTIONS='-f filtee.so.1' \ cc -o filter.so.1 -G -K pic -h filter.so.1 -R. filter.c $ elfdump -d filter.so.1 | egrep "SONAME|AUXILIARY" [2] SONAME 0xee filter.so.1 [3] AUXILIARY 0xfb filtee.so.1
创建动态可执行文件或共享目标文件时,链接编辑器可将辅助过滤器 filter.so.1 引用为依赖项。链接编辑器使用此过滤器符号表中的信息来实现任何符号解析。但是,在运行时,对此过滤器符号的任何引用都会导致搜索 filtee filtee.so.1。如果找到了 filtee,运行时链接程序就会使用 filtee 解析由 filter.so.1 定义的任何符号。如果未找到此 filtee,或者在此 filtee 中未找到过滤器符号,则会使用过滤器中的原始符号。
例如,以下动态可执行文件 prog 引用符号 foo 和 bar;这两个符号在链接编辑过程中通过过滤器 filter.so.1 进行解析。执行 prog 会导致从 filtee filtee.so.1 中获取 foo,而不是从 filter.so.1 过滤器中获取。但是,bar 是从过滤器 filter.so.1 中获取的,因为此符号在 filtee filtee.so.1 中没有备选定义。
$ cat main.c extern char *bar, *foo(); void main() { (void) printf("foo is %s: bar is %s\n", foo(), bar); } $ cc -o prog main.c -R. filter.so.1 $ prog foo is defined in filtee: bar is defined in filter
在以下示例中,共享目标文件 filter.so.2 将接口 foo 定义为针对 filtee filtee.so.1 的一个辅助过滤器。
$ cat filter.c char *bar = "defined in filter"; char *foo() { return ("defined in filter"); } $ cat mapfile $mapfile_version 2 SYMBOL_SCOPE { global: foo { AUXILIARY=filtee.so.1 }; }; $ cc -o filter.so.2 -G -K pic -h filter.so.2 -M mapfile -R. filter.c $ elfdump -d filter.so.2 | egrep "SONAME|AUXILIARY" [2] SONAME 0xd8 filter.so.2 [3] SUNW_AUXILIARY 0xfb filtee.so.1 $ elfdump -y filter.so.2 | egrep "foo|bar" [1] A [3] filtee.so.1 foo [10] D <self> bar
在运行时,对过滤器符号 foo 的任何引用都会导致搜索 filtee filtee.so.1。如果找到 filtee,就会将 filtee 装入。然后,使用 filtee 来解析由 filter.so.2 定义的 foo。如果未找到 filtee,则将使用由 filter.so.2 定义的 foo 符号。对符号 bar 的引用始终使用 filter.so.2 中的符号,因为没有为此符号定义 filtee 处理。
例如,以下动态可执行文件 prog 引用符号 foo 和 bar,这两个符号在链接编辑过程中通过过滤器 filter.so.2 进行解析。如果存在 filtee filtee.so.1,执行 prog 将导致从 filtee filtee.so.1 中获取 foo,并从过滤器 filter.so.2 获取 bar。
$ cc -o prog main.c -R. filter.so.2 $ prog foo is defined in filtee: bar is defined in filter
如果不存在 filtee filtee.so.1,执行 prog 将导致从过滤器 filter.so.2 获取 foo 和 bar。
$ prog foo is defined in filter: bar is defined in filter
在这些示例中,filtee filtee.so.1 仅与过滤器关联。不能使用该 filtee 从任何其他可能作为 prog 执行结果而装入的目标文件中实现符号查找。
辅助过滤器提供了一种用于定义现有共享目标文件的备用接口的机制。在 Oracle Solaris OS 中使用此机制可以提供优化的硬件功能以及平台特定的共享目标文件。有关示例,请参见特定于功能的共享目标文件、特定于指令集的共享目标文件和特定于系统的共享目标文件。
可以在同一个共享目标文件中定义个别接口,这些接口可定义标准过滤器,以及定义辅助过滤器的个别接口。通过使用 mapfile 关键字 FILTER 和 AUXILIARY 来指定所需的 filtees,可以实现这种过滤器定义组合。
使用 -F 或 -f 选项将其所有接口都定义为过滤器的共享目标文件可以是标准过滤器,也可以是辅助过滤器。
共享目标文件可以定义单个接口以将其用作过滤器,同时还可以将目标文件的所有接口都定义为过滤器。在这种情况下,首先处理针对接口定义的单个过滤。如果无法针对单个接口过滤器建立 filtee,则针对过滤器的所有接口定义的 filtee 会在适用时提供回退。
例如,考虑 filter.so.1 过滤器。此过滤器使用链接编辑器的 -f 选项,针对 filtee filtee.so.1 将所有接口都定义为辅助过滤器。filter.so.1 还使用 mapfile 关键字 FILTER,针对 filtee foo.so.1 将单个接口 foo 定义为标准过滤器。filter.so.1 还使用 mapfile 关键字 AUXILIARY,针对 filtee bar.so.1 将单个接口 bar 定义为辅助过滤器。
对 foo 的外部引用会导致处理 filtee foo.so.1。如果在 foo.so.1 中找不到 foo,则不会进一步处理过滤器。在这种情况下,不会执行回退处理,因为已将 foo 定义为标准过滤器。
对 bar 的外部引用会导致处理 filtee bar.so.1。如果在 bar.so.1 中找不到 bar,则处理会回退到 filtee filtee.so.1。在这种情况下,会执行回退处理,因为已将 bar 定义为辅助过滤器。如果在 filtee.so.1 中找不到 bar,则最终会使用 filter.so.1 过滤器中的 bar 的定义来解析外部引用。
运行时链接程序处理过滤器时会延迟装入 filtee,直到引用过滤器符号。这种实现类似于执行 dlopen(3C) 的过滤器,在需要 filtee 时,对其每个 filtee 使用 RTLD_LOCAL 模式。这种实现考虑了由 ldd(1) 等生成的依赖项报告中存在的差异。
创建过滤器时,可以使用链接编辑器的 -z loadfltr 选项,实现在运行时立即处理 filtees。此外,通过将 LD_LOADFLTR 环境变量设置为任意值,可触发在某个进程中立即处理所有 filtee。