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

退出打印视图

更新时间: 2014 年 7 月
 
 

生成标准过滤器

要生成标准过滤器,应先定义要对其应用过滤的 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 的过滤器。


注 -  由于未提供 foo() 的源代码,因此将使用 mapfile 关键字 FUNCTION 以确保创建 foo 的符号表项。
$ 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 中,可以使用多个标准过滤器。

/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 中的符号属性匹配。维护属性一致性可确保链接编辑过程使用与运行时所用的符号定义兼容的方式来分析过滤器。请参见符号解析。


注 -  链接编辑器使用所处理的第一个可重定位文件的 ELF 类来控制所创建的目标文件类。使用链接编辑器的 –64 选项可以仅根据 mapfile 创建 64 位过滤器。