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

退出打印视图

更新时间: 2014 年 7 月
 
 

标识功能要求

功能标识允许代码执行所需的系统属性。可以按优先级顺序使用以下功能。

  • 平台功能-用名称标识特定平台。

  • 计算机功能-用名称标识特定计算机硬件。

  • 硬件功能-用功能标志标识指令集扩展和其他硬件详细信息。

  • 软件功能-用功能标志反映软件环境的属性。

以上每项功能既可单独定义,也可共同构成一个功能组。

仅当某些功能可用时才能执行的代码应当借助关联的 ELF 目标文件中的功能节来确定这些要求。目标文件中的记录功能要求允许系统在尝试执行关联代码之前验证目标文件。这些要求还可以提供一个框架,以便系统从目标文件系列中选择最适合的目标文件。目标文件系列由相同目标文件的变体组成,每个变体具有不同的功能要求。

动态目标文件以及目标文件中的各个函数或初始化数据项可以与功能要求相关联。理论上,功能要求记录在编译器生成的可重定位目标文件中,并反映了编译时指定的选项或优化。链接编辑器结合所有输入可重定位目标文件的功能来创建输出文件的最终功能节。请参见功能节

此外,还可以在链接编辑器创建输出文件时定义功能。使用 mapfile 和链接编辑器的 –M 选项标识这些功能。使用 mapfile 定义的功能可以扩充或覆盖在所有输入可重定位目标文件中指定的功能。mapfile 通常用于扩充未生成必需的功能信息的编译器。

系统功能就是对运行中系统进行描述的功能。可以使用 uname(1) 以及 –i 选项和 –m 选项分别显示平台名称和计算机硬件名称。可以使用 isainfo(1)–v 选项显示系统硬件功能。运行时会将目标文件的功能要求与系统功能进行比较,以确定是否能装入目标文件,或者是否能在目标文件中使用符号。

目标文件功能就是与目标文件关联的功能。这些功能定义了整个目标文件的要求,并控制能否在运行时装入目标文件。如果系统无法满足目标文件所要求的功能,那么在运行时不能装入该目标文件。功能可用于提供给定目标文件的多个实例,每个实例均针对与目标文件要求匹配的系统进行了优化。运行时链接程序可以通过比较目标文件功能要求与系统提供的功能,从此类目标文件实例系列中透明地选择最佳实例。

符号功能即与目标文件中各个函数或初始化数据项关联的功能。这些功能定义了目标文件中一个或多个符号的要求,并控制在运行时能否使用符号。符号功能允许在单个目标文件中存在多个函数实例。每个函数实例均针对具有不同功能的系统进行了优化。符号功能还允许目标文件中存在多个初始化数据项实例。每个数据实例都可以定义特定于系统的数据。如果系统无法满足符号实例所要求的功能,那么在运行时不能使用该符号实例。相反,必须使用相同符号名的替代实例。通过符号功能,可以构造单个目标文件用在功能不断变化的系统上。函数系列可以为支持这些功能的系统提供经过优化的实例,并且为其他功能较弱的系统提供更为通用的实例。初始化数据项系列可以提供特定于系统的数据。运行时链接程序可以通过比较符号功能要求与系统提供的功能,从此类符号实例系列中透明地选择最佳实例。

目标文件功能和符号功能可用于为当前运行的系统选择最佳目标文件以及目标文件中的最佳符号。目标文件功能和符号功能是可选功能,彼此独立。但是,定义符号功能的目标文件也可以定义目标文件功能。这种情况下,任何功能符号系列都应当带有一个满足目标文件功能的符号实例。如果不存在目标文件功能,那么任何功能符号系列都应当带有一个不需要任何功能的符号实例。在给定系统无适用的功能实例的情况下,此符号实例会提供缺省实现。

以下 x86 示例显示了 foo.o 的目标文件功能。这些功能应用于整个目标文件。此示例中不存在任何符号功能。

$ elfdump -H foo.o

Capabilities Section:  .SUNW_cap

 Object Capabilities:
     index  tag               value
       [0]  CA_SUNW_HW_1      0x840  [ SSE  MMX ]

以下 x86 示例显示了 bar.o 的符号功能。这些功能应用于单个函数 foobar。每个符号存在两个实例,每个实例被指定给不同的功能集合。此示例中不存在任何目标文件功能。

$ elfdump -H bar.o

Capabilities Section:  .SUNW_cap

 Symbol Capabilities:
     index  tag               value
       [1]  CA_SUNW_HW_1      0x40  [ MMX ]

  Symbols:
     index    value     size  type bind oth ver shndx    name
      [25]        0     0x21  FUNC LOCL  D    0 .text    foo%mmx
      [26]     0x24     0x1e  FUNC LOCL  D    0 .text    bar%mmx

 Symbol Capabilities:
     index  tag               value
       [3]  CA_SUNW_HW_1      0x800  [ SSE ]

  Symbols:
     index    value     size  type bind oth ver shndx    name
      [33]     0x44     0x21  FUNC LOCL  D    0 .text    foo%sse
      [34]     0x68     0x1e  FUNC LOCL  D    0 .text    bar%sse

注 -  在此示例中,功能符号遵循在通用符号名后附加功能标识符的命名约定。此约定可由链接编辑器在将目标文件功能转换为符号功能时生成;以后将在将目标文件功能转换为符号功能一节中介绍此约定。

功能定义提供了许多种组合,可以使用这些组合来标识目标文件要求或目标文件中各个符号的要求。硬件功能可提供最大的灵活性。硬件功能定义硬件要求,但不决定某个特定的计算机硬件名称或平台名称。不过,有时候底层系统的某些属性只能根据计算机硬件名称或平台名称来确定。可以通过标识功能名称对非常具体的系统功能进行编码,但对已标识目标文件的使用可能会受限制。如果新计算机硬件名称或平台名称适用于目标文件,那么必须重新生成目标文件来标识新的功能名称。

以下各小节将介绍如何定义功能,以及链接编辑器如何使用功能。

标识平台功能

目标文件的平台功能标识了可在其上执行目标文件或目标文件中特定符号的系统的平台名称。可以定义多个平台功能。此标识非常具体,且优先级高于其他任何功能类型。

可通过实用程序 uname(1)–i 选项显示系统的平台名称。

可使用以下 mapfile 语法定义平台功能要求。

    $mapfile_version 2
        CAPABILITY {
                PLATFORM  = platform_name...;
                PLATFORM += platform_name...;
                PLATFORM -= platform_name...;
        };

可使用一个或多个平台名称限定 PLATFORM 属性。"+=" 形式的赋值将扩充输入目标文件所指定的平台功能,而 "=" 形式将覆盖这些功能。"-=" 形式的赋值用于从输出目标文件中排除平台功能。以下 SPARC 示例可将目标文件 foo.so.1 标识为特定于 SUNW,SPARC-Enterprise 平台。

$ cat mapfile
$mapfile_version 2
CAPABILITY {
        PLATFORM = 'SUNW,SPARC-Enterprise';
};
$ cc -o foo.so.1 -G -K pic -Mmapfile foo.c -lc
$ elfdump -H foo.so.1

Capabilities Section:  .SUNW_cap

 Object Capabilities:
     index  tag               value
       [0]  CA_SUNW_PLAT      SUNW,SPARC-Enterprise

可重定位目标文件可以定义平台功能。可将这些功能收集到一起,定义要生成的目标文件的最终功能要求。

通过使用 "=" 形式的赋值覆盖任何输入可重定位目标文件可能提供的任何平台功能,可以从 mapfile 显式控制目标文件的平台功能。结合使用空 PLATFORM 属性和 "=" 形式的赋值,可以有效地删除要生成的目标文件中的所有平台功能要求。

运行时链接程序会根据系统的平台名称验证动态目标文件中定义的平台功能要求。仅当目标文件中记录的平台名称之一与系统的平台名称相匹配时,才使用目标文件。

在某些情况下,针对特定平台编写代码会很有用,但开发硬件功能系列可提供更大的灵活性,因此建议采用。硬件功能系列可提供经过优化的代码,应用于更广泛的系统上。

标识计算机功能

目标文件的计算机功能可以标识可在其上执行目标文件或目标文件中特定符号的系统的计算机硬件名称。可以定义多个计算机功能。此标识的优先级低于平台功能定义,但高于其他任何功能类型。

可通过实用程序 uname(1)–m 选项显示系统的计算机硬件名称。

可使用以下 mapfile 语法定义计算机功能要求。

    $mapfile_version 2
        CAPABILITY {
                MACHINE  = machine_name...;
                MACHINE += machine_name...;
                MACHINE -= machine_name...;
        };

可使用一个或多个计算机硬件名称限定 MACHINE 属性。"+=" 形式的赋值将扩充输入目标文件所指定的计算机功能,而 "=" 形式将覆盖这些功能。"-=" 形式的赋值用于从输出目标文件中排除计算机功能。以下 SPARC 示例可将目标文件 foo.so.1 标识为特定于 sun4u 计算机硬件名称。

$ cat mapfile
$mapfile_version 2
CAPABILITY {
        MACHINE = sun4u;
};
$ cc -o foo.so.1 -G -K pic -Mmapfile foo.c -lc
$ elfdump -H foo.so.1

Capabilities Section:  .SUNW_cap

 Object Capabilities:
     index  tag               value
       [0]  CA_SUNW_MACH      sun4u

可重定位目标文件可以定义计算机功能。可将这些功能收集到一起,定义要生成的目标文件的最终功能要求。

通过使用 "=" 形式的赋值覆盖任何输入可重定位目标文件可能提供的任何计算机功能,从而从 mapfile 显式控制目标文件的计算机功能。结合使用空 MACHINE 属性和 "=" 形式的赋值,可以有效地删除要生成的目标文件中的所有计算机功能要求。

运行时链接程序会根据系统的计算机硬件名称验证动态目标文件中定义的计算机功能要求。仅当目标文件中记录的计算机名称之一与系统的计算机名称相匹配时,才使用目标文件。

在某些情况下,针对特定计算机编写代码会很有用,但开发硬件功能系列可提供更大的灵活性,因此建议采用。硬件功能系列可提供经过优化的代码,应用于更广泛的系统上。

标识硬件功能

目标文件的硬件功能可标识系统正确执行目标文件或特定符号所需的硬件要求。例如,标识需要在某些 x86 体系结构上可用的 MMXSSE 功能的代码。

可以使用以下 mapfile 语法标识硬件功能要求。

    $mapfile_version 2
        CAPABILITY {
                HW  = hwcap_flag...;
                HW += hwcap_flag...;
                HW -= hwcap_flag...;
        };

使用一个或多个标识限定 CAPABILITY 指令的 HW 属性,这些标记是硬件功能的符号表示。"+=" 形式的赋值可以扩充输入目标文件指定的硬件功能,而 "=" 形式可以覆盖这些功能。"-=" 形式的赋值用于从输出目标文件中排除硬件功能。

对于 SPARC 系统,硬件功能定义为 sys/auxv_SPARC.h 中的 AV_ 值。对于 x86 系统,硬件功能定义为 sys/auxv_386.h 中的 AV_ 值。

以下 x86 示例说明了如何将 MMXSSE 声明为目标文件 foo.so.1 所要求的硬件功能。

$ egrep "MMX|SSE" /usr/include/sys/auxv_386.h
#define AV_386_MMX    0x0040
#define AV_386_SSE    0x0800
$ cat mapfile
$mapfile_version 2
CAPABILITY {
        HW += SSE MMX;
};
$ cc -o foo.so.1 -G -K pic -Mmapfile foo.c -lc
$ elfdump -H foo.so.1

Capabilities Section:  .SUNW_cap

 Object Capabilities:
     index  tag               value
       [0]  CA_SUNW_HW_1      0x840  [ SSE  MMX ]

可重定位目标文件可以包含硬件功能值。链接编辑器结合多个输入可重定位目标文件中的任何硬件功能值。产生的 CA_SUNW_HW_1 值是对关联的输入值进行按位 OR 运算的结果。缺省情况下,这些值将与 mapfile 指定的硬件功能组合。

通过使用 "=" 形式的赋值覆盖任何输入可重定位目标文件可能提供的任何硬件功能,可以从 mapfile 显式控制目标文件的硬件功能要求。结合使用空 HW 属性和 "=" 形式的赋值,可以有效地删除要生成的目标文件的所有硬件功能要求。

以下示例可抑制输入可重定位目标文件 foo.o 定义的所有硬件功能数据,使其不包括在输出文件 bar.o 中。

$ elfdump -H foo.o

Capabilities Section:  .SUNW_cap

 Object Capabilities:
     index  tag               value
       [0]  CA_SUNW_HW_1      0x840  [ SSE  MMX ]
$ cat mapfile
$mapfile_version 2
CAPABILITY {
        HW = ;
};
$ ld -o bar.o -r -Mmapfile foo.o
$ elfdump -H bar.o
$ 

运行时链接程序会针对系统提供的硬件功能验证动态目标文件定义的任何硬件功能要求。如果无法满足任何硬件功能要求,就不会在运行时装入目标文件。例如,如果进程不能使用 SSE 功能,则 ldd(1) 将指示以下错误。

$ ldd prog
        foo.so.1 =>      ./foo.so.1  - hardware capability unsupported: 0x800 [ SSE ]
        ....

利用不同硬件功能的动态目标文件的多个变体可以提供使用过滤器的灵活运行时环境。请参见特定于功能的共享目标文件

硬件功能还可以用于标识单个目标文件中个别函数的功能。这种情况下,运行时链接程序可以根据当前系统功能选择要使用的最恰当的函数实例。请参见创建符号功能函数系列

标识软件功能

目标文件的软件功能标识对于调试或监视进程可能很重要的软件特征。软件功能也能影响进程的执行。目前,可识别的唯一软件功能与目标文件使用的帧指针以及进程地址空间限制有关。

目标文件可以声明已知其帧指针的使用状态。然后,将帧指针声明为正在使用或未使用来限定此状态。

64 位目标文件可以指明:在运行时,必须在 32 位地址空间内使用它们。

软件功能标志是在 sys/elf.h 中定义的。

#define  SF1_SUNW_FPKNWN    0x001
#define  SF1_SUNW_FPUSED    0x002
#define  SF1_SUNW_ADDR32    0x004

可以使用以下 mapfile 语法标识这些软件功能要求。

        $mapfile_version 2
CAPABILITY {
        SF  = sfcap_flags...;
        SF += sfcap_flags...;
        SF -= sfcap_flags...;
};

CAPABILITY 指令的 SF 属性可赋值为标记 FPKNWNFPUSEDADDR32 中的任意一个。

可重定位目标文件可以包含软件功能值。链接编辑器可以组合多个输入可重定位目标文件中的软件功能值。软件功能还可随 mapfile 提供。缺省情况下,任何 mapfile 值都将与可重定位目标文件提供的值组合。

通过使用 "=" 形式的赋值覆盖任何输入可重定位目标文件可能提供的任何软件功能,可以从 mapfile 显式控制目标文件的软件功能要求。结合使用空 SF 属性和 "=" 形式的赋值,可以有效地删除要生成的目标文件的所有软件功能要求。

以下示例可抑制输入可重定位目标文件 foo.o 定义的所有软件功能数据,使其不包括在输出文件 bar.o 中。

$ elfdump -H foo.o

Object Capabilities:
    index  tag               value
      [0]  CA_SUNW_SF_1      0x3  [ SF1_SUNW_FPKNWN  SF1_SUNW_FPUSED ]
$ cat mapfile
$mapfile_version 2
CAPABILITY {
        SF = ;
};
$ ld -o bar.o -r -Mmapfile foo.o
$ elfdump -H bar.o
$ 
软件功能帧指针处理

可按如下方法根据两个帧指针计算 CA_SUNW_SF_1 的值。

表 2-1  CA_SUNW_SF_1 帧指针标志组合状态表
输入文件 1
输入文件 2
SF1_SUNW_FPKNWN SF1_SUNW_FPUSED
SF1_SUNW_FPKNWN
<unknown>
SF1_SUNW_FPKNWN SF1_SUNW_FPUSED
SF1_SUNW_FPKNWN SF1_SUNW_FPUSED
SF1_SUNW_FPKNWN
SF1_SUNW_FPKNWN SF1_SUNW_FPUSED
SF1_SUNW_FPKNWN
SF1_SUNW_FPKNWN
SF1_SUNW_FPKNWN
SF1_SUNW_FPKNWN
<unknown>
SF1_SUNW_FPKNWN SF1_SUNW_FPUSED
SF1_SUNW_FPKNWN
<unknown>

此计算方法适用于每个可重定位目标文件值和 mapfile 值。如果不存在 .SUNW_cap 节、此节未包含 CA_SUNW_SF_1 值或者未设置 SF1_SUNW_FPKNWSF1_SUNW_FPUSED 标志,那么目标文件的帧指针软件功能是未知的。

软件功能地址空间限制处理

使用 SF1_SUNW_ADDR32 软件功能标志标识的 64 位目标文件可包含需要 32 位地址空间的经过优化的代码。按照此方式标识的 64 位目标文件可与任何其他 64 位目标文件交互操作,无论它们是否使用 SF1_SUNW_ADDR32 标志进行标识。在 64 位输入可重定位目标文件中出现的 SF1_SUNW_ADDR32 标志将传播到 CA_SUNW_SF_1 值,该值是为链接编辑器将创建的输出文件创建的。

64 位可执行文件中存在的 SF1_SUNW_ADDR32 标志可确保将关联进程限定于低 32 位地址空间。此限定地址空间包括进程栈和所有进程依赖项。在此类进程内,所有目标文件都在限定的 32 位地址空间内装入,无论其是否使用 SF1_SUNW_ADDR32 标志进行标识。

64 位共享目标文件可以包含 SF1_SUNW_ADDR32 标志。但是,限定的地址空间要求只能由包含 SF1_SUNW_ADDR32 标志的 64 位可执行文件建立。因此,64 位 SF1_SUNW_ADDR32 共享目标文件必须是 64 位 SF1_SUNW_ADDR32 可执行文件的依赖项。

链接编辑器在生成不受限的 64 位可执行文件时若遇到 64 位 SF1_SUNW_ADDR32 共享目标文件,将导致生成警告。

$ cc -m64 -o main main.c -lfoo
ld: warning: file libfoo.so: section .SUNW_cap: software capability ADDR32: \
    requires executable be built with ADDR32 capability

通过不受限的 64 位可执行文件创建的进程在运行时若遇到 64 位 SF1_SUNW_ADDR32 共享目标文件,将导致生成致命错误。

$ ldd main
    libfoo.so =>     ./libfoo.so  - software capability unsupported: 0x4  [ ADDR32 ]
    ....
$ main
ld.so.1: main: fatal: ./libfoo.so: software capability unsupported: 0x4  [ ADDR32 ]

可以使用 mapfileSF1_SUNW_ADDR32 生成可执行文件。

$ cat mapfile
$mapfile_version 2
CAPABILITY {
        SF += ADDR32;
};
$ cc -m64 -o main main.c -Mmapfile -lfoo
$ elfdump -H main

Object Capabilities:
    index  tag               value
      [0]  CA_SUNW_SF_1      0x4  [ SF1_SUNW_ADDR32 ]

创建符号功能函数系列

开发者通常需要在单个目标文件中提供多个函数实例,且每个实例都针对特定功能集合进行了优化。理想情况下,这些实例的选择和使用对于任何使用者都是透明的。可以创建一个通用的前端函数以提供外部接口。此通用实例与经过优化的实例可以一起组合到一个目标文件中。此通用实例可以使用 getisax(2) 来确定系统功能,然后调用适当的已优化函数实例来处理任务。虽然此模式可行,但它不仅缺乏通用性,还会产生运行时开销。

符号功能为构造此类目标文件提供了一种替代机制。此机制更简单、更有效,而且不需要编写额外的前端代码。可以创建多个函数实例,并将其与不同的功能关联。这些实例与适合任何系统的缺省函数实例可以一起组合到单个动态目标文件中。运行时链接程序使用符号功能信息,从此符号系列中选择执行最适当的符号。

在以下示例中,x86 目标文件 foobar.mmx.ofoobar.sse.o 包含相同的函数 foo()bar(),这两个函数已被编译为分别使用 MMXSSE 指令。

$ elfdump -H foobar.mmx.o

Capabilities Section:  .SUNW_cap

 Symbol Capabilities:
    index  tag               value
      [1]  CA_SUNW_ID        mmx
      [2]  CA_SUNW_HW_1      0x40  [ MMX ]

  Symbols:
     index    value      size  type bind oth ver shndx    name
      [10]        0      0x21  FUNC LOCL  D    0 .text    foo%mmx
      [16]     0x24      0x1e  FUNC LOCL  D    0 .text    bar%mmx

$ elfdump -H foobar.sse.o

Capabilities Section:  .SUNW_cap

 Symbol Capabilities:
    index  tag               value
      [1]  CA_SUNW_ID        sse
      [2]  CA_SUNW_HW_1      0x800  [ SSE ]

  Capabilities symbols:
     index    value      size  type bind oth ver shndx    name
      [16]        0      0x2f  FUNC LOCL  D    0 .text    foo%sse
      [18]     0x48      0x30  FUNC LOCL  D    0 .text    bar%sse

其中的每个目标文件都包含一个局部符号,可以标识功能函数 foo%*()bar%*()。此外,每个目标文件还会定义一个指向函数 foo()bar() 的全局引用。对 foo()bar() 的任何内部引用均通过这些全局引用进行重定位,这与外部引用是一样的。

现在可以将这两个目标文件与 foo()bar() 的缺省实例相组合。这些缺省实例可满足全局引用,并提供与任何目标文件功能兼容的实现。可以说这些缺省实例引导着每个功能系列。如果不存在任何目标文件功能,那么此缺省实例也应当不需要任何功能。实际上,存在三个 foo()bar() 的实例,全局实例提供缺省功能,局部实例提供在运行时所用的实现(如果关联的功能可用)。

$ cc -o libfoobar.so.1 -G foobar.o foobar.sse.o foobar.mmx.o
$ elfdump -sN.dynsym libfoobar.so.1 | egrep "foo|bar"
     [2]     0x700    0x21  FUNC LOCL  D    0 .text    foo%mmx
     [4]     0x750    0x2f  FUNC LOCL  D    0 .text    foo%sse
     [8]     0x784    0x1e  FUNC LOCL  D    0 .text    bar%mmx
     [9]     0x7b0    0x30  FUNC LOCL  D    0 .text    bar%sse
    [15]     0x7a0    0x14  FUNC GLOB  D    1 .text    foo
    [17]     0x7c0    0x14  FUNC GLOB  D    1 .text    bar

动态目标文件的功能信息会显示功能符号以及可用的功能系列。

$ elfdump -H libfoobar.so.1

Capabilities Section:  .SUNW_cap

 Symbol Capabilities:
   index  tag               value
     [1]  CA_SUNW_ID        mmx
     [2]  CA_SUNW_HW_1      0x40  [ MMX ]

  Symbols:
   index    value     size  type bind oth ver shndx    name
     [2]    0x700     0x21  FUNC LOCL  D    0 .text    foo%mmx
     [8]    0x784     0x1e  FUNC LOCL  D    0 .text    bar%mmx

 Symbol Capabilities:
   index  tag               value
     [4]  CA_SUNW_ID        sse
     [5]  CA_SUNW_HW_1      0x800  [ SSE ]

  Symbols:
   index    value     size  type bind oth ver shndx    name
     [4]    0x750     0x2f  FUNC LOCL  D    0 .text    foo%sse
     [9]    0x7b0     0x30  FUNC LOCL  D    0 .text    bar%sse

Capabilities Chain Section:  .SUNW_capchain

 Capabilities family: foo
  chainndx  symndx      name
         1  [15]        foo
         2  [2]         foo%mmx
         3  [4]         foo%sse

 Capabilities family: bar
  chainndx  symndx      name
         5  [17]        bar
         6  [8]         bar%mmx
         7  [9]         bar%sse

在运行时,对 foo()bar() 的所有引用最初都绑定到全局符号。不过,运行时链接程序将这些函数视为功能系列的引导实例。运行时链接程序会检查每个系列成员以确定是否有更好的功能函数。第一次调用函数时,此操作会发生一次性开销。对 foo()bar() 的后续调用直接绑定到第一次调用所选的函数实例。通过使用运行时链接程序调试功能,可以观察此函数选择内容。

在以下示例中,底层系统未提供 MMXSSE 支持。foo() 的引导实例不需要特殊的功能支持,因此可满足任何重定位引用。

$ LD_DEBUG=symbols  main
....
debug: symbol=foo;  lookup in file=./libfoo.so.1  [ ELF ]
debug:   symbol=foo[15]:  capability family default
debug:   symbol=foo%mmx[2]:  capability specific (CA_SUNW_HW_1):  [ 0x40  [ MMX ] ]
debug:   symbol=foo%mmx[2]:  capability rejected
debug:   symbol=foo%sse[4]:  capability specific (CA_SUNW_HW_1):  [ 0x800  [ SSE ] ]
debug:   symbol=foo%sse[4]:  capability rejected
debug:   symbol=foo[15]:  used

在以下示例中,MMX 可用,但 SSE 不可用。支持 MMXfoo() 实例可满足任何重定位引用。

$ LD_DEBUG=symbols  main
....
debug: symbol=foo;  lookup in file=./libfoo.so.1  [ ELF ]
debug:   symbol=foo[15]:  capability family default
debug:   symbol=foo%mmx[2]:  capability specific (CA_SUNW_HW_1):  [ 0x40  [ MMX ] ]
debug:   symbol=foo%mmx[2]:  capability candidate
debug:   symbol=foo%sse[4]:  capability specific (CA_SUNW_HW_1):  [ 0x800  [ SSE ] ]
debug:   symbol=foo%sse[4]:  capability rejected
debug:   symbol=foo[2]:  used

如果在同一个系统上可使用多个功能实例,则将使用一组优先规则来选择一个实例。

  • 定义平台名称的功能组优先于不定义平台名称的功能组。

  • 定义计算机硬件名称的功能组优先于不定义计算机硬件名称的功能组。

  • 较大的硬件功能值优先于较小的硬件功能值。

必须通过过程链接表项访问功能函数实例系列。请参见过程链接表(特定于处理器)。此过程链接引用需要运行时链接程序对函数进行解析。在此过程中,运行时链接程序可以处理关联的符号功能信息,并从可用的函数实例系列中选择最佳函数。

如果未使用符号功能,那么在某些情况下,链接编辑器无需过程链接表项即可解析代码引用。例如,在动态可执行文件中,可以在链接编辑时内部绑定可执行文件中存在的函数引用。共享目标文件中的隐藏函数和受保护函数也可以在链接编辑时内部绑定。在这些情况下,通常不需要运行时链接程序来解析这些函数的引用。

不过,如果使用了符号功能,就必须根据过程链接表项解析函数。为了使运行时链接程序参与适当的函数,同时维护只读文本段,此项是必需的。此机制导致功能函数的所有调用都通过过程链接表项间接进行。如果不使用符号功能,可能不一定要使用此间接方式。因此,在功能函数的调用开销与通过将功能函数用于其缺省对应项所获得的任何性能提高之间,应进行权衡。


注 -  虽然必须通过过程链接表项访问功能函数,但仍可将函数定义为隐藏函数或受保护函数。运行时链接程序会遵从这些可见性状态,并限制对这些函数的任何绑定。此行为导致的绑定与未将符号功能关联到函数时所生成的绑定相同。从外部目标文件无法绑定到隐藏函数。目标文件中对受保护函数的引用只能绑定到同一个目标文件中。

创建符号功能数据项系列

在同一个目标文件中可以提供多个初始化数据实例,其中每个实例均特定于一个系统。不过,通过功能接口提供此类数据通常更为简单,因此建议使用此方法。请参见创建符号功能函数系列。在单个可执行文件中提供多个初始化数据实例需要特别小心。

以下示例将初始化 foo.c 中的数据项 foo,使其指向一个计算机名称字符串。此文件可针对不同计算机进行编译,每个实例均标识有计算机功能。从文件 bar.c 中的 bar() 进行对此数据项的引用。然后,通过将 bar() 与 foo 的两个功能实例组合,创建共享目标文件 foobar.so.1

$ cat foo.c
char *foo = MACHINE;
$ cat bar.c
#include	<stdio.h>

extern char *foo = MACHINE;

void bar()
{
        (void) printf("machine: %s\n", foo);
}

$ elfdump -H foobar.so.1

Capabilities Section:  .SUNW_cap

 Symbol Capabilities:
   index  tag               value
     [1]  CA_SUNW_ID        sun4u
     [2]  CA_SUNW_MACH      sun4u

  Symbols:
   index    value     size  type bind oth ver shndx          name
     [1]  0x108d4      0x4  OBJT LOCL  D    0 .data          foo%sun4u

 Symbol Capabilities:
   index  tag               value
     [4]  CA_SUNW_ID        sun4v
     [5]  CA_SUNW_MACH      sun4v

  Symbols:
   index    value     size  type bind oth ver shndx          name
     [2]  0x108d8      0x4  OBJT LOCL  D    0 .data          foo%sun4v

应用程序可以引用 bar(),且运行时链接程序绑定到与底层系统关联的 foo 的实例。

$ uname -m
sun4u
$ main
machine: sun4u

此代码能否正确运行取决于已编译为与位置无关的代码,就像正常情况下可共享目标文件中的代码一样。请参见与位置无关的代码。与位置无关的数据引用为间接引用,运行时链接程序可通过此引用来查找所需的引用并更新数据段元素。这种数据库重定位更新会将文本段保留为只读。

不过,可执行文件中的代码通常为位置相关代码。此外,可执行文件中的数据引用在链接编辑时进行绑定。在可执行文件中,符号功能数据引用必须通过全局数据项保持未解析状态,以便运行时链接程序从符号功能系列中进行选择。如果将上一个示例 bar.c 中来自 bar() 的引用编译为位置相关的代码,则可执行文件的文本段必须在运行时进行重定位。缺省情况下,此状态会导致致命链接时错误。

$ cc -o main main.c bar.c foo.o foo.1.o foo.2.o ...
warning: Text relocation remains                referenced
    against symbol                  offset      in file
foo                                 0x0         bar.o
foo                                 0x8         bar.o

解决此错误状态的一种方法是将 bar.c 编译为与位置无关。但是,请注意可执行文件中所有符号功能数据项的所有引用都必须编译为与位置无关,否则此方法无效。

虽然可以使用符号功能机制访问数据,但如何使数据项成为目标文件公共接口的一部分是个问题。一种更为灵活的替代模式是将每个数据项封装在符号功能函数中。此函数提供了单独的数据访问方法。将数据隐藏在符号功能函数之后具有很大好处,可允许将数据定义为静态数据并保持专用状态。可以对上一个示例编码以使用符号功能函数。

$ cat foobar.c
cat bar.c
#include	<stdio.h>

static char *foo = MACHINE;

void bar()
{
        (void) printf("machine: %s\n", foo);
}
$ elfdump -H main

Capabilities Section:  .SUNW_cap

 Symbol Capabilities:
   index  tag               value
     [1]  CA_SUNW_ID        sun4u
     [2]  CA_SUNW_MACH      sun4u

  Symbols:
   index    value     size  type bind oth ver shndx          name
     [1]  0x1111c     0x1c  FUNC LOCL  D    0 .text          bar%sun4u

 Symbol Capabilities:
   index  tag               value
     [4]  CA_SUNW_ID        sun4v
     [5]  CA_SUNW_MACH      sun4v

  Symbols:
   index    value     size  type bind oth ver shndx          name
     [2]  0x11138     0x1c  FUNC LOCL  D    0 .text          bar%sun4v

$ uname -m
sun4u
$ main
machine: sun4u

将目标文件功能转换为符号功能

理论上,编译器可以生成标识有符号功能的目标文件。如果编译器无法创建符号功能,链接编辑器会提供一个解决方案。

可以使用链接编辑器将定义目标文件功能的可重定位目标文件转换为定义符号功能的可重定位目标文件。任何功能数据节都可使用链接编辑器 –z symbolcap 选项转换为定义符号功能。目标文件中的所有全局函数都将转换为局部函数,并与符号功能关联。所有全局初始化数据项都将转换为局部数据项,并与符号功能关联。这些转换的符号附加有指定为目标文件功能组部分的任何功能标识符。如果未定义功能标识符,就会附加缺省的组名。

对于每个原始全局函数或初始化数据项,将创建一个全局引用。此引用与所有重定位要求关联,在最终结合此目标文件来创建动态目标文件时,此引用将绑定到缺省的全局符号。


注 -  –z symbolcap 选项适用于包含目标文件功能节的目标文件。对于已包含符号功能的可重定位目标文件,或同时包含目标文件功能和符号功能的可重定位目标文件,此选项无效。此设计允许链接编辑器将多个目标文件仅与那些包含受此选项影响的目标文件功能的目标文件组合。

在以下示例中,x86 可重定位目标文件包含两个全局函数 foo()bar()。此目标文件已被编译为需要 MMXSSE 硬件功能。在这些示例中,功能组已采用功能标识符项进行命名。此标识符名称会附加到经过转换的符号名称后。如果没有此显式标识符,链接编辑器会附加一个缺省的功能组名称。

$ elfdump -H foo.o

Capabilities Section:  .SUNW_cap

 Object Capabilities:
   index  tag               value
     [0]  CA_SUNW_ID        sse,mmx
     [1]  CA_SUNW_HW_1      0x840  [ SSE MMX ]

$ elfdump -s foo.o | egrep "foo|bar"
    [25]         0   0x21  FUNC GLOB  D    0 .text    foo
    [26]      0x24   0x1e  FUNC GLOB  D    0 .text    bar

$ elfdump -r foo.o | fgrep foo
  R_386_PLT32                0x38       .rel.text             foo

接下来将这个可重定位目标文件转换为符号功能可重定位目标文件。

$ ld -r -o foo.1.o -z symbolcap foo.o
$ elfdump -H foo.1.o

Capabilities Section:  .SUNW_cap

 Symbol Capabilities:
   index  tag               value
     [1]  CA_SUNW_ID        sse,mmx
     [2]  CA_SUNW_HW_1      0x840  [ SSE MMX ]

  Symbols:
   index    value     size  type bind oth ver shndx    name
    [25]        0     0x21  FUNC LOCL  D    0 .text    foo%sse,mmx
    [26]     0x24     0x1e  FUNC LOCL  D    0 .text    bar%sse,mmx

$ elfdump -s foo.1.o | egrep "foo|bar"
    [25]        0     0x21  FUNC LOCL  D    0 .text    foo%sse,mmx
    [26]     0x24     0x1e  FUNC LOCL  D    0 .text    bar%sse,mmx
    [37]        0        0  FUNC GLOB  D    0 UNDEF    foo
    [38]        0        0  FUNC GLOB  D    0 UNDEF    bar

$ elfdump -r foo.1.o | fgrep foo
  R_386_PLT32                0x38       .rel.text             foo

现在可以将此目标文件与包含相同函数实例的其他目标文件组合,与不同符号功能关联,以生成可执行文件或共享目标文件。此外,应当提供每个函数的缺省实例(即不与任何符号功能关联的实例)以引导每个功能系列。此缺省实例可供所有外部引用使用,并确保函数的实例在任何系统上均可用。

在运行时,foo()bar() 的任何引用均定向到引导实例。不过,如果系统包含适当功能,运行时链接程序会从中选择最佳符号功能实例。

归档注意事项

归档库通常包含可重定位目标文件的集合。链接编辑器可以提取各个可重定位目标文件来解析未解析的符号引用。请参见归档处理

如果已将一系列功能可重定位目标文件添加到归档,任何对前置功能符号的引用只会提取定义该信号的通用可重定位目标文件。不会提取任何其他功能目标文件。

如果需要使用归档库部署功能目标文件,应创建单个功能系列可重定位目标文件。将任何功能目标文件和任何包含功能前置功能符号的通用目标文件合并为一个可重定位目标文件。将此包含整个功能系列集合的单个目标文件添加到归档中。

$ ld -r -o all.foo.o foo.o foo.1.o foo.2.o ....
$ ar -cr libfoo.o all.foo.o