JavaScript is required to for searching.
跳过导航链接
退出打印视图
链接程序和库指南     Oracle Solaris 10 8/11 Information Library (简体中文)
search filter icon
search icon

文档信息

前言

1.  Oracle Solaris 链接编辑器介绍

2.  链接编辑器

调用链接编辑器

直接调用

使用编译器驱动程序

32 位链接编辑器和 64 位链接编辑器

跨链接编辑

指定链接编辑器选项

输入文件处理

归档处理

共享目标文件处理

与其他库链接

库命名约定

同时链接共享目标文件和归档

命令行中归档的位置

链接编辑器搜索的目录

使用命令行选项

使用环境变量

运行时链接程序搜索的目录

初始化节和终止节

符号处理

符号解析

简单解析

复杂解析

致命解析

未定义符号

生成可执行输出文件

生成共享目标文件输出文件

弱符号

输出文件中的暂定符号顺序

定义其他符号

使用 -u 选项定义其他符号

定义符号引用

定义绝对符号

定义暂定 (tentative) 符号

扩充符号定义

缩减符号作用域

删除符号

外部绑定

字符串表压缩

生成输出文件

标识功能要求

标识平台功能

标识计算机功能

标识硬件功能

标识软件功能

软件功能帧指针处理

创建符号功能函数系列

创建符号功能数据项系列

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

功能系列试验

重定位处理

位移重定位

桩目标文件

调试帮助

3.  运行时链接程序

4.  共享目标文件

5.  应用程序二进制接口与版本控制

6.  支持接口

7.  目标文件格式

8.  线程局部存储

9.  Mapfile

A.  链接编辑器快速参考

B.  版本控制快速参考

C.  使用动态字符串标记建立依赖性

D.  直接绑定

E.  System V 发行版 4(版本 1)Mapfile

F.  链接程序和库的更新及新增功能

索引

生成输出文件

在完成输入文件处理和符号解析并且没有出现致命错误后,链接编辑器将生成输出文件。链接编辑器首先生成完成输出文件必需的其他节。这些节包括所有输入文件中的符号表,这些符号表包含局部符号定义以及已解析的全局符号和弱符号信息。

此外,还包括运行时链接程序需要的任何输出重定位和动态信息节。确定所有输出节信息后,将计算输出文件总大小。然后,相应地创建输出文件映像。

创建动态可执行文件或共享目标文件时,通常会生成两个符号表。.dynsym 表及其关联的字符串表 .dynstr 包含寄存器符号、全局符号、弱符号和节符号。这些节成为在运行时作为进程映像一部分映射的 text 段的一部分。请参见 mmap(2)。使用此映射,运行时链接程序可以读取这些节,以便执行任何必需的重定位。

.symtab 表及其关联的字符串表 .strtab 包含在输入文件处理过程中收集的所有符号。这些节不能作为进程映像的一部分进行映射。使用链接编辑器的 -s 选项或在链接编辑后使用 strip(1) 甚至可以从映像中删除这些节。

生成符号表期间,将创建保留符号。这些符号对于链接进程有特殊意义。不能在代码中定义这些符号。

_etext

所有只读信息(通常指文本段)后面的第一个位置。

_edata

已初始化数据后面的第一个位置。

_end

所有数据后面的第一个位置。

_DYNAMIC

.dynamic 信息节的地址。

_END_

_end 相同。此符号具有局部作用域,它与 _START_ 符号一起提供一种确定目标文件地址范围的方法。

_GLOBAL_OFFSET_TABLE_

对链接编辑器提供的地址表(即 .got 节)的引用,与位置无关。此表由与位置无关的数据引用构造而成,这些数据引用出现在使用 -K pic 选项编译的目标文件中。请参见与位置无关的代码

_PROCEDURE_LINKAGE_TABLE_

对链接编辑器提供的地址表(即 .plt 节)的引用,与位置无关。此表由与位置无关的函数引用构造而成,这些数据引用出现在已使用 -K pic 选项编译的目标文件中。请参见与位置无关的代码

_START_

文本段中的第一个位置。此符号具有局部作用域,它与 _END_ 符号一起提供一种确定目标文件地址范围的方法。

生成可执行文件时,链接编辑器将查找其他符号,以定义可执行文件的入口点。如果使用链接编辑器的 -e 选项指定了一个符号,那么将使用该符号。否则,链接编辑器会查找保留符号名称 _start,然后查找 main

标识功能要求

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

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

仅当某些功能可用时才能执行的代码应当借助关联的 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]  0x00000000 0x00000021  FUNC LOCL  D    0 .text    foo%mmx
      [26]  0x00000024 0x0000001e  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]  0x00000044 0x00000021  FUNC LOCL  D    0 .text    foo%sse
      [34]  0x00000068 0x0000001e  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

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

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

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

可重定位目标文件可以包含软件功能值。链接编辑器可以组合多个输入可重定位目标文件中的软件功能值。软件功能还可随 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 标志,那么目标文件的帧指针软件功能是未知的。

创建符号功能函数系列

开发人员通常需要在单个目标文件中提供多个函数实例,且每个实例都针对特定功能集合进行了优化。理想情况下,这些实例的选择和使用对于任何使用者都是透明的。可以创建一个通用的前端函数以提供外部接口。此通用实例与经过优化的实例可以一起组合到一个目标文件中。此通用实例可以使用 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]  0x00000000 0x00000021  FUNC LOCL  D    0 .text    foo%mmx
      [16]  0x00000024 0x0000001e  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]  0x00000000 0x0000002f  FUNC LOCL  D    0 .text    foo%sse
      [18]  0x00000048 0x00000030  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]  0x00000700 0x00000021  FUNC LOCL  D    0 .text    foo%mmx
       [4]  0x00000750 0x0000002f  FUNC LOCL  D    0 .text    foo%sse
       [8]  0x00000784 0x0000001e  FUNC LOCL  D    0 .text    bar%mmx
       [9]  0x000007b0 0x00000030  FUNC LOCL  D    0 .text    bar%sse
      [15]  0x000007a0 0x00000014  FUNC GLOB  D    1 .text    foo
      [17]  0x000007c0 0x00000014  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]  0x00000700 0x00000021  FUNC LOCL  D    0 .text    foo%mmx
       [8]  0x00000784 0x0000001e  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]  0x00000750 0x0000002f  FUNC LOCL  D    0 .text    foo%sse
       [9]  0x000007b0 0x00000030  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 不可用。foo()MMX 功能实例可满足任何重定位引用。

$ LD_DEBUG=symbols main
....
debug: symbol=foo;  lookup in file=./libfoo.so.1  [ ELF ]
debug:   symbol=foo[15]:  capability family default
debug:   symbol=foo[2]:  capability specific (CA_SUNW_HW_1):  [ 0x40  [ MMX ] ]
debug:   symbol=foo[2]:  capability candidate
debug:   symbol=foo[4]:  capability specific (CA_SUNW_HW_1):  [ 0x800  [ SSE ] ]
debug:   symbol=foo[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]  0x000108d4 0x00000004  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]  0x000108d8 0x00000004  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]  0x0001111c 0x0000001c  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]  0x00011138 0x0000001c  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]  0x00000000 0x00000021  FUNC GLOB  D    0 .text    foo
      [26]  0x00000024 0x0000001e  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]  0x00000000 0x00000021  FUNC LOCL  D    0 .text    foo%sse,mmx
      [26]  0x00000024 0x0000001e  FUNC LOCL  D    0 .text    bar%sse,mmx

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

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

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

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

功能系列试验

正常情况下,设计和生成目标文件是为了能在给定体系结构的所有系统上执行该目标文件。但是,经常要对具有特殊功能的个别系统进行优化。可以使用之前各节介绍的机制,用代码需要执行的功能来标识经过优化的代码。

要测试经过优化的实例,必须使用可提供所需功能的系统。对于每个系统,运行时链接程序会确定可用的功能,然后选择功能最强的实例。为帮助进行测试和实验,可以让运行时链接程序使用替代功能集合,而不是系统提供的功能集合。此外,可以指定应仅根据这些替代功能验证特定文件。

替代功能集合源自系统功能,可以重新初始化,或者添加或删除功能。

以下环境变量系列可用于创建并计划使用替代功能集合。

LD_PLATCAP={name}

标识替代平台名称。

LD_MACHCAP={name}

标识替代计算机硬件名称。

LD_HWCAP=[+-]{token | number},....

标识替代硬件功能值。

LD_SFCAP=[+-]{token | number},....

标识替代软件功能值。

LD_CAP_FILES=file,....

标识应根据替代功能进行验证的文件。

功能环境变量 LD_PLATCAPLD_MACHCAP 分别接受定义平台名称的字符串和计算机硬件名称的字符串。请参见标识平台功能标识计算机功能

功能环境变量 LD_HWCAPLD_SFCAP 接受逗号分隔的标记列表作为功能的符号表现形式。请参见标识硬件功能标识软件功能。标记也可以是数值。

"+" 前缀将导致将其后的功能添加到替代功能中。"-" 前缀将导致从替代功能中删除其后的功能。缺少 "+-" 将导致其后的功能替换替代功能。

删除某个功能会导致增加对要模拟的功能环境的限制。通常情况下,当功能实例系列可用时,还会提供一个非特定于功能的通用实例。因此,可以使用限制程度较高的功能环境来强制使用功能较弱的代码实例或通用代码实例。

添加功能会导致进一步强化要模拟的功能环境。应谨慎创建此环境,但可将其用于测试某个功能系列的框架。例如,可以创建函数系列以使用 mapfile 定义其预期功能。这些函数可以使用 printf(3C) 确认其执行情况。然后,采用各种功能组合验证并测试关联目标文件的创建。在对函数的实际功能要求进行编码之前,功能系列的此原型设计可以证明是有用的。不过,如果实例系列中的代码要求正确执行特定功能,但系统并未提供此功能,而是将其设置为替代功能,那么代码实例将无法正确执行。

如果不使用 LD_CAP_FILES 建立替代功能集合,那么会根据替代功能对进程的所有特定于功能的目标文件进行验证。此方法也应谨慎使用,因为许多系统目标文件要求正确执行系统功能。功能的任何更改都可能导致系统目标文件无法正确执行。

功能实验的最佳环境是使用可提供目标文件要使用的所有功能的系统。LD_CAP_FILES 还应当用于隔离要实验的目标文件。随后可以使用 "-" 语法禁用功能,从而使得能够试验功能系列的各种实例。系统的真实功能会完全支持每个实例。

例如,假设有两个 x86 功能目标文件 libfoo.solibbar.so。这些目标文件包含针对 SSE2 指令进行优化的功能函数、针对 MMX 指令进行优化的函数,以及不需要功能的通用函数。底层系统同时提供了 SSE2MMX。缺省情况下,使用经过全面优化的 SSE2 函数。

可以使用 LD_HWCAP 定义删除 SSE2 功能,从而限制 libfoo.solibbar.so 使用针对 MMX 指令进行优化的函数。定义 LD_CAP_FILES 最灵活的方法是使用所需文件的基名。

$ LD_HWCAP=-sse2 LD_CAP_FILES=libfoo.so,libbar.so ./main

可以通过删除 SSE2MMX 功能,进一步将 libfoo.solibbar.so 限制为仅使用通用函数。

$ LD_HWCAP=-sse2,mmx LD_CAP_FILES=libfoo.so,libbar.so ./main

注 - 可以使用运行时链接程序诊断来观察应用程序的可用功能以及已设置的所有替代功能。

$ LD_DEBUG=basic LD_HWCAP=-sse2,mmx,cx8 ./main
....
02328: hardware capabilities (CA_SUNW_HW_1) - 0x5c6f  \
    [ SSE3 SSE2 SSE FXSR MMX CMOV SEP CX8 TSC FPU ]
02328: alternative hardware capabilities (CA_SUNW_HW_1) - 0x4c2b  \
    [ SSE3 SSE FXSR CMOV SEP TSC FPU ]
....