oracle动态链接,动态链接

动态链接

本节介绍用于创建运行程序的目标文件信息和系统操作。此处介绍的大多数信息适用于所有系统。特定于某处理器的信息位于带有相应标记的各节中。

可执行文件和共享目标文件静态表示应用程序。要执行这类程序,系统可使用这些文件创建动态程序表示形式(即进程映像)。进程映像具有包含其文本、数据、栈等内容的段。本节包括以下主要小节。

程序头,其中介绍了直接参与程序执行的目标文件结构。主数据结构是一种程序头表,用于定位文件中的段映像,并包含创建程序内存映像所需的其他信息。

程序装入(特定于处理器),其中介绍了用于将程序装入内存的信息。

运行时链接程序,其中介绍了用于指定和解析进程映像的目标文件之间的符号引用的信息。

程序头

可执行文件或共享目标文件的程序头表是一个结构数组。每种结构都描述了系统准备程序执行所需的段或其他信息。目标文件段包含一个或多个节,如段内容中所述。

程序头仅对可执行文件和共享目标文件有意义。文件使用 ELF 头的 e_phentsize 和 e_phnum 成员来指定各自的程序头大小。

程序头具有以下结构。请参见 sys/elf.h。typedef struct {

Elf32_Word p_type;

Elf32_Off p_offset;

Elf32_Addr p_vaddr;

Elf32_Addr p_paddr;

Elf32_Word p_filesz;

Elf32_Word p_memsz;

Elf32_Word p_flags;

Elf32_Word p_align;

} Elf32_Phdr;

typedef struct {

Elf64_Word p_type;

Elf64_Word p_flags;

Elf64_Off p_offset;

Elf64_Addr p_vaddr;

Elf64_Addr p_paddr;

Elf64_Xword p_filesz;

Elf64_Xword p_memsz;

Elf64_Xword p_align;

} Elf64_Phdr;p_type

此数组元素描述的段类型或解释此数组元素的信息的方式。表 7-25 中指定了类型值及其含义。

p_offset

相对段的第一个字节所在文件的起始位置的偏移。

p_vaddr

段的第一个字节在内存中的虚拟地址。

p_paddr

段在与物理寻址相关的系统中的物理地址。由于此系统忽略了应用程序的物理地址,因此该成员对于可执行文件和共享目标文件具有未指定的内容。

p_filesz

段的文件映像中的字节数,可以为零。

p_memsz

段的内存映像中的字节数,可以为零。

p_flags

与段相关的标志。表 7-26 中指定了类型值及其含义。

p_align

可装入的进程段必须具有 p_vaddr 和 p_offset 的同余值(以页面大小为模数)。此成员可提供一个值,用于在内存和文件中根据该值对齐各段。值 0 和 1 表示无需对齐。另外,p_align 应为 2 的正整数幂,并且 p_vaddr 应等于 p_offset(以 p_align 为模数)。请参见程序装入(特定于处理器)。

某些项用于描述进程段。其他项则提供补充信息,并且不会构成进程映像。除非明确指定了顺序,否则段的各项可以任何顺序显示。下表中列出了定义的类型值。

表 7-25 ELF 段类型

名称

PT_NULL

0

PT_LOAD

1

PT_DYNAMIC

2

PT_INTERP

3

PT_NOTE

4

PT_SHLIB

5

PT_PHDR

6

PT_TLS

7

PT_LOOS

0x60000000

PT_SUNW_UNWIND

0x6464e550

PT_SUNW_EH_FRAME

0x6474e550

PT_LOSUNW

0x6ffffffa

PT_SUNWBSS

0x6ffffffa

PT_SUNWSTACK

0x6ffffffb

PT_SUNWDTRACE

0x6ffffffc

PT_SUNWCAP

0x6ffffffd

PT_HISUNW

0x6fffffff

PT_HIOS

0x6fffffff

PT_LOPROC

0x70000000

PT_HIPROC

0x7fffffff

PT_NULL

未使用。没有定义成员值。使用此类型,程序头表可以包含忽略的项。

PT_LOAD

指定可装入段,通过 p_filesz 和 p_memsz 进行描述。文件中的字节会映射到内存段的起始位置。如果段的内存大小 (p_memsz) 大于文件大小 (p_filesz),则将多余字节的值定义为 0这些字节跟在段的已初始化区域后面。文件大小不能大于内存大小。程序头表中的可装入段的各项按升序显示,并基于 p_vaddr 成员进行排列。

PT_DYNAMIC

指定动态链接信息。请参见动态节。

PT_INTERP

指定要作为解释程序调用的以空字符结尾的路径名的位置和大小。对于动态可执行文件,必须设置此类型。此类型可出现在共享目标文件中。此类型不能在一个文件中多次出现。此类型(如果存在)必须位于任何可装入段的各项的前面。有关详细信息,请参见程序的解释程序。

PT_NOTE

指定辅助信息的位置和大小。有关详细信息,请参见注释节。

PT_SHLIB

保留类型,但具有未指定的语义。

PT_PHDR

指定程序头表在文件及程序内存映像中的位置和大小。此段类型不能在一个文件中多次出现。此外,仅当程序头表是程序内存映像的一部分时,才可以出现此段。此类型(如果存在)必须位于任何可装入段的各项的前面。有关详细信息,请参见程序的解释程序。

PT_TLS

指定线程局部存储模板。有关详细信息,请参见线程局部存储节。

PT_LOOS - PT_HIOS

此范围内包含的值保留用于特定于操作系统的语义。

PT_SUNW_UNWIND

此段包含栈扩展表。

PT_SUNW_EH_FRAME

此段包含栈扩展表。PT_SUNW_EH_FRAME 与 PT_SUNW_EH_UNWIND 等效。

PT_LOSUNW - PT_HISUNW

此范围内包含的值(包括这两个值)保留用于特定于 Sun 的语义。

PT_SUNWBSS

与 PT_LOAD 元素相同的属性,用于描述 .SUNW_bss 节。

PT_SUNWSTACK

描述进程栈。只能存在一个 PT_SUNWSTACK 元素。仅访问权限(如 p_flags 字段中所定义)有意义。

PT_SUNWDTRACE

PT_SUNWCAP

指定功能要求。有关详细信息,请参见功能节。

PT_LOPROC - PT_HIPROC

此范围内包含的值(包括这两个值)保留用于特定于处理器的语义。

注 -除非在其他位置具体要求,否则所有程序头的段类型都是可选的。文件的程序头表只能包含与其内容相关的那些元素。

基本地址

可执行文件和共享目标文件都有一个基本地址,该地址是与程序目标文件的内存映像关联的最低虚拟地址。基本地址的其中一种用途是在动态链接过程中重定位程序的内存映像。

可执行文件或共享目标文件的基本地址是在执行过程中通过以下三个值计算得出的:内存装入地址、最大页面大小和程序可装入段的最低虚拟地址。程序头中的虚拟地址可能并不表示程序内存映像的实际虚拟地址。请参见程序装入(特定于处理器)。

要计算基本地址,首先需要确定与 PT_LOAD 段的最低 p_vaddr 值关联的内存地址。然后,将内存地址截断为最大页面大小的最接近倍数,从而获取基本地址。根据装入内存的文件的类型,内存地址可能与 p_vaddr 值不匹配。

段权限

系统要装入的程序必须至少包含一个可装入段,即使文件格式并不要求此限制也是如此。系统创建可装入段的内存映像时,将会授予如 p_flags 成员中所指定的访问权限。PF_MASKPROC 掩码中包括的所有位都保留用于特定于处理器的语义。

表 7-26 ELF 段标志

名称

含义

PF_X

0x1

执行

PF_W

0x2

PF_R

0x4

PF_MASKPROC

0xf0000000

未指定

如果权限位是 0,则会拒绝该位的访问类型。实际内存权限取决于内存管理单元,该单元可随系统的不同而变化。尽管所有标志组合均有效,但系统仍可授予比请求更多的访问权限。不过,如果不显式指定写权限,则段在任何情况下都不会具有该权限。下表列出了确切的标志解释及允许的标志解释。

表 7-27 ELF 段权限

标志

确切解释

允许解释

0

拒绝所有访问

拒绝所有访问

PF_X

1

仅执行

读、执行

PF_W

2

只写

读、写、执行

PF_W + PF_X

3

写、执行

读、写、执行

PF_R

4

只读

读、执行

PF_R + PF_X

5

读、执行

读、执行

PF_R + PF_W

6

读、写

读、写、执行

PF_R + PF_W + PF_X

7

读、写、执行

读、写、执行

例如,典型的文本段具有读和执行权限,但没有写权限。数据段通常具有读、写和执行权限。

段内容

目标文件段由一节或多节组成,但此事实对程序头是透明的。另外,无论文件段包含一节还是包含多节,对程序装入都没有实际意义。但是,必须存在各种数据以便执行程序、进行动态链接等操作。下图使用一般术语说明了段内容。段中各节的顺序和成员关系可能会有所变化。

文本段包含只读指令和数据。数据段包含可写数据和指令。有关所有特殊节的列表,请参见表 7-10。

PT_DYNAMIC 程序头元素指向 .dynamic 节。.got 和 .plt 节还包含与位置无关的代码和动态链接的相关信息。

.plt 可以位于文本或数据段中,具体取决于处理器。有关详细消息,请参见全局偏移表(特定于处理器)和过程链接表(特定于处理器)。

类型为 SHT_NOBITS 的节不占用文件空间,但构成段的内存映像。通常,这些未初始化的数据驻留在段尾,从而使 p_memsz 大于关联程序头元素中的 p_filesz。

程序装入(特定于处理器)

系统创建或扩充进程映像时,系统会以逻辑方式将文件的段复制到虚拟内存段。系统以物理方式读取文件的时间和可能性取决于程序的执行行为、系统负载等。

除非进程在执行过程中引用了逻辑页,否则进程不需要物理页。进程通常会保留许多页面不对其进行引用。因此,延迟物理读取可以提高系统性能。要实际达到这种效率,可执行文件和共享目标文件必须具有文件偏移和虚拟地址同余(以页面大小为模数)的段映像。

32 位段的虚拟地址和文件偏移对模数 64 K (0x10000) 同余。64 位段的虚拟地址和文件偏移对模数 1 MB (0x100000) 同余。通过将各段与最大页面大小对齐,无论物理页大小如何,文件都适合进行换页。

缺省情况下,64 位 SPARC 程序与 0x100000000 的起始地址链接。整个程序位于 4 GB 以上的地址空间内,包括其文本、数据、堆、栈和共享目标文件依赖项。这有助于确保 64 位程序正确,因为如果程序截断其任何指针,则程序在其最低有效的 4

GB 地址空间中将出现错误。尽管 64 位程序在 4 GB 以上的地址空间内进行链接,但您仍可以使用 mapfile 和链接编辑器的 -M 选项,链接 4

GB 以下的地址空间内的程序。请参见 /usr/lib/ld/sparcv9/map.below4G。

下图显示了 SPARC 版本的可执行文件。

图 7-8 SPARC: 可执行文件(64 K 对齐)

c7feee042431bbb5f5151ee78fa961e2.png

下表定义了上图中可装入段的各元素。

表 7-28 SPARC: ELF 程序头段(64 K 对齐)

成员

文本

数据

p_type

PT_LOAD

PT_LOAD

p_offset

0x0

0x4000

p_vaddr

0x10000

0x24000

p_paddr

未指定

未指定

p_filesize

0x3a82

0x4f5

p_memsz

0x3a82

0x10a4

p_flags

PF_R + PF_X

PF_R + PF_W + PF_X

p_align

0x10000

0x10000

下图显示了 x86 版本的可执行文件。

图 7-9 32 位 x86: 可执行文件(64 K 对齐)

d3f1aac5a15d7ac82ace7a64831370b3.png

下表定义了上图中可装入段的各元素。

表 7-29 32 位 x86: ELF 程序头段(64 K 对齐)

成员

文本

数据

p_type

PT_LOAD

PT_LOAD

p_offset

0x0

0x4000

p_vaddr

0x8050000

0x8064000

p_paddr

未指定

未指定

p_filesize

0x32fd

0x3a0

p_memsz

0x32fd

0xdc4

p_flags

PF_R + PF_X

PF_R + PF_W + PF_X

p_align

0x10000

0x10000

此示例的文件偏移和虚拟地址以文本和数据的最大页面大小为模数同余。根据页面大小和文件系统块大小,最多可有四个文件页包含混合文本或数据。

第一个文本页包含 ELF 头、程序头表和其他信息。

最后一个文本页包含数据起始部分的副本。

第一个数据页包含文本结尾的副本。

最后一个数据页可以包含与运行的进程无关的文件信息。从逻辑上而言,系统会强制执行内存权限,如同每个段是完整而独立的一样。为确保地址空间中的每个逻辑页都具有单独一组权限,各段的地址会进行调整。在前面的示例中,包含文本结尾和数据起始部分的文件区域映射了两次:一次映射到文本的虚拟地址,另一次映射到数据的与之不同的虚拟地址。

注 -前面的示例反映了对文本段取整的典型的 Oracle Solaris OS 二进制文件。

数据段结尾要求对未初始化的数据进行特殊处理,系统将其定义为从零值开始。如果文件的最后一个数据页包含不属于逻辑内存页的信息,则必须将无关数据设置为零,而不是设置为可执行文件的未知内容。

其他三个页面中的混合内容在逻辑上不属于进程映像。没有指定系统是否会清除这些混合内容。以下各图中显示了此程序的内存映像,假定页面大小为 4 KB (0x1000)。为简单起见,这些图仅对一种页面大小进行说明。

图 7-10 32 位 SPARC: 进程映像段

368020313496105ae821fddbc106a0b0.png

图 7-11 x86: 进程映像段

d50689a5da82bc5ecbaa7fe98fc8074e.png

可执行文件和共享目标文件在段装入的某个方面有所不同。可执行文件段通常包含绝对代码。为使进程正确执行,段必须位于用于创建可执行文件的虚拟地址处。系统会使用未更改的 p_vaddr 值作为虚拟地址。

另一方面,共享目标文件段通常包含与位置无关的代码。使用此代码,段的虚拟地址在不同进程之间会进行更改,而不会使执行行为无效。

尽管系统会为各个进程选择虚拟地址,但仍会保持各段之间的相对位置。由于与位置无关的代码在各段之间使用相对地址,因此内存中虚拟地址之间的差值必须与文件中虚拟地址之间的差值匹配。

以下各表显示针对多个进程可能指定的共享目标文件虚拟地址,从而说明了固定的相对位置。此外,这些表中还包括基本地址计算。

表 7-30 32 位 SPARC: ELF 共享目标文件段地址示例

源代码

文本

数据

基本地址

文件

0x0

0x4000

0x0

进程 1

0xc0000000

0xc0024000

0xc0000000

进程 2

0xc0010000

0xc0034000

0xc0010000

进程 3

0xd0020000

0xd0024000

0xd0020000

进程 4

0xd0030000

0xd0034000

0xd0030000

表 7-31 32 位 x86: ELF 共享目标文件段地址示例

源代码

文本

数据

基本地址

文件

0x0

0x4000

0x0

进程 1

0x8000000

0x8004000

0x80000000

进程 2

0x80081000

0x80085000

0x80081000

进程 3

0x900c0000

0x900c4000

0x900c0000

进程 4

0x900c6000

0x900ca000

0x900c6000

程序的解释程序

启动动态链接的动态可执行文件或共享目标文件可以包含一个 PT_INTERP 程序头元素。在 exec(2) 过程中,系统将从 PT_INTERP 段检索路径名,并通过解释程序文件段创建初始进程映像。解释程序负责从系统接收控制并为应用程序提供环境。

在 Oracle Solaris OS 中,解释程序称为运行时链接程序,即 ld.so.1(1)。

运行时链接程序

创建启动动态链接的动态目标文件时,链接编辑器将向可执行文件中添加一个类型为 PT_INTERP 的程序头元素。该元素指示系统将运行时链接程序作为程序的解释程序进行调用。exec(2) 和运行时链接程序进行协作,为程序创建进程映像。

链接编辑器可为可执行文件和共享目标文件构造协助运行时链接程序运行的各种数据。这些数据位于可装入段中,从而使数据在执行过程中可用。这些段包括:

类型为 SHT_DYNAMIC 的 .dynamic 节,其中包含各种数据。位于该节起始位置的结构包含其他动态链接信息的地址。

类型为 SHT_PROGBITS 的 .got 和 .plt 节,其中分别包含以下两个表:全局偏移表和过程链接表。以下各节说明了运行时链接程序如何使用和更改这些表,以便为目标文件创建内存映像。

类型为 SHT_HASH 的 .hash 节,其中包含符号散列表。

共享目标文件可以占用虚拟内存地址,这些虚拟内存地址与文件的程序头表中记录的地址不同。运行时链接程序会重定位内存映像,从而在应用程序获取控制权之前更新绝对地址。

动态节

如果目标文件参与动态链接,则其程序头表将包含一个类型为 PT_DYNAMIC 的元素。此段包含 .dynamic 节。特殊符号 _DYNAMIC 用于标记包含以下结构的数组的节。请参见 sys/link.h。typedef struct {

Elf32_Sword d_tag;

union {

Elf32_Word d_val;

Elf32_Addr d_ptr;

Elf32_Off d_off;

} d_un;

} Elf32_Dyn;

typedef struct {

Elf64_Xword d_tag;

union {

Elf64_Xword d_val;

Elf64_Addr d_ptr;

} d_un;

} Elf64_Dyn;

对于此类型的每个目标文件,d_tag 将控制 d_un 的解释。d_val

这些目标文件表示具有各种解释的整数值。

d_ptr

这些目标文件表示程序虚拟地址。在执行过程中,文件虚拟地址可能与内存虚拟地址不匹配。对动态结构中包含的地址进行解释时,运行时链接程序会根据原始文件值和内存基本地址来计算实际地址。为确保一致性,文件不应包含用于更正动态结构中的地址的重定位项。

通常,每个动态标记的值决定了 d_un 联合的解释。借助此约定,第三方工具可进行更简单的动态标记解释。值为偶数的标记表示使用 d_ptr 的动态节项。值为奇数的标记表示使用 d_val 的动态节项,或该标记既不使用 d_ptr,也不使用 d_val。值包含在以下特殊兼容性范围中的标记不遵循这些规则。第三方工具必须逐项明确处理这些例外范围。

值小于特定值 DT_ENCODING 的标记。

值位于 DT_LOOS 和 DT_SUNW_ENCODING 之间的标记。

值位于 DT_HIOS 和 DT_LOPROC 之间的标记。

下表概述了可执行文件和共享目标文件的标记要求。如果某标记带有强制标志,则动态链接数组必须包含此类型的项。同样,可选表示该标记的项可以出现但不是必需的。

表 7-32 ELF 动态数组标记

名称

d_un

可执行文件

共享目标文件

DT_NULL

0

已忽略

强制

强制

DT_NEEDED

1

d_val

可选

可选

DT_PLTRELSZ

2

d_val

可选

可选

DT_PLTGOT

3

d_ptr

可选

可选

DT_HASH

4

d_ptr

强制

强制

DT_STRTAB

5

d_ptr

强制

强制

DT_SYMTAB

6

d_ptr

强制

强制

DT_RELA

7

d_ptr

强制

可选

DT_RELASZ

8

d_val

强制

可选

DT_RELAENT

9

d_val

强制

可选

DT_STRSZ

10

d_val

强制

强制

DT_SYMENT

11

d_val

强制

强制

DT_INIT

12

d_ptr

可选

可选

DT_FINI

13

d_ptr

可选

可选

DT_SONAME

14

d_val

已忽略

可选

DT_RPATH

15

d_val

可选

可选

DT_SYMBOLIC

16

已忽略

已忽略

可选

DT_REL

17

d_ptr

强制

可选

DT_RELSZ

18

d_val

强制

可选

DT_RELENT

19

d_val

强制

可选

DT_PLTREL

20

d_val

可选

可选

DT_DEBUG

21

d_ptr

可选

已忽略

DT_TEXTREL

22

已忽略

可选

可选

DT_JMPREL

23

d_ptr

可选

可选

DT_BIND_NOW

24

已忽略

可选

可选

DT_INIT_ARRAY

25

d_ptr

可选

可选

DT_FINI_ARRAY

26

d_ptr

可选

可选

DT_INIT_ARRAYSZ

27

d_val

可选

可选

DT_FINI_ARRAYSZ

28

d_val

可选

可选

DT_RUNPATH

29

d_val

可选

可选

DT_FLAGS

30

d_val

可选

可选

DT_ENCODING

32

未指定

未指定

未指定

DT_PREINIT_ARRAY

32

d_ptr

可选

已忽略

DT_PREINIT_ARRAYSZ

33

d_val

可选

已忽略

DT_MAXPOSTAGS

34

未指定

未指定

未指定

DT_LOOS

0x6000000d

未指定

未指定

未指定

DT_SUNW_AUXILIARY

0x6000000d

d_ptr

未指定

可选

DT_SUNW_RTLDINF

0x6000000e

d_ptr

可选

可选

DT_SUNW_FILTER

0x6000000e

d_ptr

未指定

可选

DT_SUNW_CAP

0x60000010

d_ptr

可选

可选

DT_SUNW_SYMTAB

0x60000011

d_ptr

可选

可选

DT_SUNW_SYMSZ

0x60000012

d_val

可选

可选

DT_SUNW_ENCODING

0x60000013

未指定

未指定

未指定

DT_SUNW_SORTENT

0x60000013

d_val

可选

可选

DT_SUNW_SYMSORT

0x60000014

d_ptr

可选

可选

DT_SUNW_SYMSORTSZ

0x60000015

d_val

可选

可选

DT_SUNW_TLSSORT

0x60000016

d_ptr

可选

可选

DT_SUNW_TLSSORTSZ

0x60000017

d_val

可选

可选

DT_SUNW_CAPINFO

0x60000018

d_ptr

可选

可选

DT_SUNW_STRPAD

0x60000019

d_val

可选

可选

DT_SUNW_CAPCHAIN

0x6000001a

d_ptr

可选

可选

DT_SUNW_LDMACH

0x6000001b

d_val

可选

可选

DT_SUNW_CAPCHAINENT

0x6000001d

d_val

可选

可选

DT_SUNW_CAPCHAINSZ

0x6000001f

d_val

可选

可选

DT_HIOS

0x6ffff000

未指定

未指定

未指定

DT_VALRNGLO

0x6ffffd00

未指定

未指定

未指定

DT_CHECKSUM

0x6ffffdf8

d_val

可选

可选

DT_PLTPADSZ

0x6ffffdf9

d_val

可选

可选

DT_MOVEENT

0x6ffffdfa

d_val

可选

可选

DT_MOVESZ

0x6ffffdfb

d_val

可选

可选

DT_POSFLAG_1

0x6ffffdfd

d_val

可选

可选

DT_SYMINSZ

0x6ffffdfe

d_val

可选

可选

DT_SYMINENT

0x6ffffdff

d_val

可选

可选

DT_VALRNGHI

0x6ffffdff

未指定

未指定

未指定

DT_ADDRRNGLO

0x6ffffe00

未指定

未指定

未指定

DT_CONFIG

0x6ffffefa

d_ptr

可选

可选

DT_DEPAUDIT

0x6ffffefb

d_ptr

可选

可选

DT_AUDIT

0x6ffffefc

d_ptr

可选

可选

DT_PLTPAD

0x6ffffefd

d_ptr

可选

可选

DT_MOVETAB

0x6ffffefe

d_ptr

可选

可选

DT_SYMINFO

0x6ffffeff

d_ptr

可选

可选

DT_ADDRRNGHI

0x6ffffeff

未指定

未指定

未指定

DT_RELACOUNT

0x6ffffff9

d_val

可选

可选

DT_RELCOUNT

0x6ffffffa

d_val

可选

可选

DT_FLAGS_1

0x6ffffffb

d_val

可选

可选

DT_VERDEF

0x6ffffffc

d_ptr

可选

可选

DT_VERDEFNUM

0x6ffffffd

d_val

可选

可选

DT_VERNEED

0x6ffffffe

d_ptr

可选

可选

DT_VERNEEDNUM

0x6fffffff

d_val

可选

可选

DT_LOPROC

0x70000000

未指定

未指定

未指定

DT_SPARC_REGISTER

0x70000001

d_val

可选

可选

DT_AUXILIARY

0x7ffffffd

d_val

未指定

可选

DT_USED

0x7ffffffe

d_val

可选

可选

DT_FILTER

0x7fffffff

d_val

未指定

可选

DT_HIPROC

0x7fffffff

未指定

未指定

未指定

DT_NULL

标记 _DYNAMIC 数组的结尾。

DT_NEEDED

以空字符结尾的字符串的 DT_STRTAB 字符串表偏移,用于提供所需依赖项的名称。动态数组可以包含多个此类型的项。尽管这些项与其他类型的项的关系不重要,但其相对顺序却很重要。请参见共享目标文件依赖项。

DT_PLTRELSZ

与过程链接表关联的重定位项的总大小(以字节为单位)。请参见过程链接表(特定于处理器)。

DT_PLTGOT

DT_HASH

符号散列表的地址。该表引用 DT_SYMTAB 元素指示的符号表。请参见散列表节。

DT_STRTAB

字符串表的地址。运行时链接程序所需的符号名称、依赖项名称和其他字符串位于该表中。请参见字符串表节。

DT_SYMTAB

符号表的地址。请参见符号表节。

DT_RELA

重定位表的地址。请参见重定位节。

目标文件可以有多个重定位节。为可执行文件或共享目标文件创建重定位表时,链接编辑器会连接这些节以形成一个表。尽管这些节在目标文件中可以保持独立,但运行时链接程序将看到一个表。运行时链接程序为可执行文件创建进程映像或将共享目标文件添加到进程映像中时,运行时链接程序将会读取该重定位表并执行关联操作。

此元素要求同时存在 DT_RELASZ 和 DT_RELAENT 元素。如果文件必须重定位,则可以存在 DT_RELA 或 DT_REL。

DT_RELASZ

DT_RELA 重定位表的总大小(以字节为单位)。

DT_RELAENT

DT_RELA 重定位项的大小(以字节为单位)。

DT_STRSZ

DT_STRTAB 字符串表的总大小(以字节为单位)。

DT_SYMENT

DT_SYMTAB 符号项的大小(以字节为单位)。

DT_INIT

初始化函数的地址。请参见初始化节和终止节。

DT_FINI

终止函数的地址。请参见初始化节和终止节。

DT_SONAME

以空字符结尾的字符串的 DT_STRTAB 字符串表偏移,用于标识共享目标文件的名称。请参见记录共享目标文件名称。

DT_RPATH

以空字符结尾的库搜索路径字符串的 DT_STRTAB 字符串表偏移。此元素的用途已被 DT_RUNPATH 取代。请参见运行时链接程序搜索的目录。

DT_SYMBOLIC

表示目标文件包含在其链接编辑过程中应用的符号绑定。此元素的用途已被 DF_SYMBOLIC 标志取代。请参见使用 -B symbolic 选项。

DT_REL

与 DT_RELA 类似,但其表中包含隐式加数。此元素要求同时存在 DT_RELSZ 和 DT_RELENT 元素。

DT_RELSZ

DT_REL 重定位表的总大小(以字节为单位)。

DT_RELENT

DT_REL 重定位项的大小(以字节为单位)。

DT_PLTREL

表示过程链接表指向的重定位项的类型(DT_REL 或 DT_RELA)。过程链接表中的所有重定位都必须使用相同的重定位项。请参见过程链接表(特定于处理器)。此元素要求同时存在 DT_JMPREL 元素。

DT_DEBUG

用于调试。

DT_TEXTREL

表示一个或多个重定位项可能会要求修改非可写段,并且运行时链接程序可以相应地进行准备。此元素的用途已被 DF_TEXTREL 标志取代。请参见与位置无关的代码。

DT_JMPREL

与过程链接表单独关联的重定位项的地址。请参见过程链接表(特定于处理器)。通过分隔这些重定位项,运行时链接程序可在装入启用了延迟绑定的目标文件时忽略这些项。此元素要求同时存在 DT_PLTRELSZ 和 DT_PLTREL 元素。

DT_POSFLAG_1

应用于紧邻的 DT_ 元素的各种状态标志。请参见表 7-35。

DT_BIND_NOW

表示在将控制权返回给程序之前,必须处理此目标文件的所有重定位项。通过环境或 dlopen(3C) 指定时,提供的此项优先于使用延迟绑定的指令。此元素的用途已被 DF_BIND_NOW 标志取代。请参见执行重定位的时间。

DT_INIT_ARRAY

初始化函数的指针数组的地址。此元素要求同时存在 DT_INIT_ARRAYSZ 元素。请参见初始化节和终止节。

DT_FINI_ARRAY

终止函数的指针数组的地址。此元素要求同时存在 DT_FINI_ARRAYSZ 元素。请参见初始化节和终止节。

DT_INIT_ARRAYSZ

DT_INIT_ARRAY 数组的总大小(以字节为单位)。

DT_FINI_ARRAYSZ

DT_FINI_ARRAY 数组的总大小(以字节为单位)。

DT_RUNPATH

以空字符结尾的库搜索路径字符串的 DT_STRTAB 字符串表偏移。请参见运行时链接程序搜索的目录。

DT_FLAGS

特定于此目标文件的标志值。请参见表 7-33。

DT_ENCODING

大于或等于 DT_ENCODING、小于或等于 DT_LOOS 的动态标记值遵循 d_un 联合的解释规则。

DT_PREINIT_ARRAY

预初始化函数的指针数组的地址。此元素要求同时存在 DT_PREINIT_ARRAYSZ 元素。仅在可执行文件中处理该数组。如果该数组包含在共享目标文件中,则会被忽略。请参见初始化节和终止节。

DT_PREINIT_ARRAYSZ

DT_PREINIT_ARRAY 数组的总大小(以字节为单位)。

DT_MAXPOSTAGS

正动态数组标记值的数量。

DT_LOOS - DT_HIOS

此范围内包含的值(包括这两个值)保留用于特定于操作系统的语义。所有这类值都遵循 d_un 联合的解释规则。

DT_SUNW_AUXILIARY

以空字符结尾的字符串的 DT_STRTAB 字符串表偏移,用于逐符号指定一个或多个辅助 filtee。请参见生成辅助过滤器。

DT_SUNW_RTLDINF

保留供运行时链接程序内部使用。

DT_SUNW_FILTER

以空字符结尾的字符串的 DT_STRTAB 字符串表偏移,用于逐符号指定一个或多个标准 filtee。请参见生成标准过滤器。

DT_SUNW_CAP

功能节的地址。请参见功能节。

DT_SUNW_SYMTAB

符号表的地址,其中包含用于扩充 DT_SYMTAB 所提供的符号的局部函数符号。这些符号始终在紧邻 DT_SYMTAB 所提供的符号之前的位置。请参见符号表节。

DT_SUNW_SYMSZ

DT_SUNW_SYMTAB 和 DT_SYMTAB 提供的符号表的组合大小。

DT_SUNW_ENCODING

大于或等于 DT_SUNW_ENCODING、小于或等于 DT_HIOS 的动态标记值遵循 d_un 联合的解释规则。

DT_SUNW_SORTENT

DT_SUNW_SYMSORT 和 DT_SUNW_TLSSORT 符号排序项的大小(以字节为单位)。

DT_SUNW_SYMSORT

符号表索引数组的地址,这些索引提供对 DT_SUNW_SYMTAB 所引用的符号表中函数和变量符号的排序访问。请参见符号排序节。

DT_SUNW_SYMSORTSZ

DT_SUNW_SYMSORT 数组的总大小(以字节为单位)。

DT_SUNW_TLSSORT

符号表索引数组的地址,这些索引提供对 DT_SUNW_SYMTAB 所引用的符号表中线程局部符号的排序访问。请参见符号排序节。

DT_SUNW_TLSSORTSZ

DT_SUNW_TLSSORT 数组的总大小(以字节为单位)。

DT_SUNW_CAPINFO

符号表索引数组的地址,这些索引提供符号与其功能要求之间的关联。请参见功能节。

DT_SUNW_STRPAD

动态字符串表末尾未使用的保留空间的总大小(以字节为单位)。如果目标文件中不存在 DT_SUNW_STRPAD,则没有保留空间可用。

DT_SUNW_CAPCHAIN

功能系列索引数组的地址。每个索引系列都以 0 项结尾。

DT_SUNW_LDMACH

生成目标文件的链接编辑器的计算机体系结构。DT_SUNW_LDMACH 使用与 ELF 头的 e_machine 字段相同的 EM_ 整数值。请参见ELF 头。DT_SUNW_LDMACH 用于标识生成目标文件的链接编辑器的类(32 位或 64 位)和平台。此信息不会用于运行时链接程序,而仅用于说明目的。

DT_SUNW_CAPCHAINENT

DT_SUNW_CAPCHAIN 项的大小(以字节为单位)。

DT_SUNW_CAPCHAINSZ

DT_SUNW_CAPCHAIN 链的总大小(以字节为单位)。

DT_SYMINFO

符号信息表的地址。此元素要求同时存在 DT_SYMINENT 和 DT_SYMINSZ 元素。请参见Syminfo 表节。

DT_SYMINENT

DT_SYMINFO 信息项的大小(以字节为单位)。

DT_SYMINSZ

DT_SYMINFO 表的总大小(以字节为单位)。

DT_VERDEF

版本定义表的地址。该表中的元素包含字符串表 DT_STRTAB 的索引。此元素要求同时存在 DT_VERDEFNUM 元素。请参见版本定义章节。

DT_VERDEFNUM

DT_VERDEF 表中的项数。

DT_VERNEED

版本依赖性表的地址。该表中的元素包含字符串表 DT_STRTAB 的索引。此元素要求同时存在 DT_VERNEEDNUM 元素。请参见版本依赖性节。

DT_VERNEEDNUM

DT_VERNEEDNUM 表中的项数。

DT_RELACOUNT

表示 RELATIVE 重定位计数,该计数是通过串联所有 Elf32_Rela 或 Elf64_Rela 重定位项生成的。请参见组合重定位节。

DT_RELCOUNT

表示 RELATIVE 重定位计数,该计数是通过串联所有 Elf32_Rel 重定位项生成的。请参见组合重定位节。

DT_AUXILIARY

以空字符结尾的字符串的 DT_STRTAB 字符串表偏移,用于指定一个或多个辅助 filtee。请参见生成辅助过滤器。

DT_FILTER

以空字符结尾的字符串的 DT_STRTAB 字符串表偏移,用于指定一个或多个标准 filtee。请参见生成标准过滤器。

DT_CHECKSUM

目标文件中选定的节的简单校验和。请参见 gelf_checksum(3ELF)。

DT_MOVEENT

DT_MOVETAB 移动项的大小(以字节为单位)。

DT_MOVESZ

DT_MOVETAB 表的总大小(以字节为单位)。

DT_MOVETAB

移动表的地址。此元素要求同时存在 DT_MOVEENT 和 DT_MOVESZ 元素。请参见移动节。

DT_CONFIG

以空字符结尾的字符串的 DT_STRTAB 字符串表偏移,用于定义配置文件。该配置文件仅在可执行文件中有意义,并且通常是特定于此目标文件的。请参见配置缺省搜索路径。

DT_DEPAUDIT

以空字符结尾的字符串的 DT_STRTAB 字符串表偏移,用于定义一个或多个审计库。请参见运行时链接程序审计接口。

DT_AUDIT

以空字符结尾的字符串的 DT_STRTAB 字符串表偏移,用于定义一个或多个审计库。请参见运行时链接程序审计接口。

DT_FLAGS_1

特定于此目标文件的标志值。请参见表 7-34。

DT_VALRNGLO-DT_VALRNGHI

此范围内包含的值(包括这两个值)使用动态结构的 d_un.d_val 字段。

DT_ADDRRNGLO - DT_ADDRRNGHI

此范围内包含的值(包括这两个值)使用动态结构的 d_un.d_ptr 字段。如果生成 ELF 目标文件后对其进行了任何调整,则必须相应地更新这些项。

DT_SPARC_REGISTER

DT_SYMTAB 符号表中 STT_SPARC_REGISTER 符号的索引。该符号表中的每个 STT_SPARC_REGISTER 符号都存在一个动态项。请参见寄存器符号。

DT_LOPROC - DT_HIPROC

此范围内包含的值(包括这两个值)保留用于特定于处理器的语义。

除动态数组末尾的 DT_NULL 元素以及 DT_NEEDED 和 DT_POSFLAG_1 元素的相对顺序以外,各项可以采用任何顺序显示。未显示在该表中的标记值为保留值。

表 7-33 ELF 动态标志 DT_FLAGS

名称

含义

DF_ORIGIN

0x1

要求 $ORIGIN 处理

DF_SYMBOLIC

0x2

要求符号解析

DF_TEXTREL

0x4

存在文本重定位项

DF_BIND_NOW

0x8

要求非延迟绑定

DF_STATIC_TLS

0x10

目标文件使用静态线程局部存储方案

DF_ORIGIN

表示目标文件要求 $ORIGIN 处理。请参见查找关联的依赖项。

DF_SYMBOLIC

表示目标文件包含在其链接编辑过程中应用的符号绑定。请参见使用 -B symbolic 选项。

DF_TEXTREL

表示一个或多个重定位项可能会要求修改非可写段,并且运行时链接程序可以相应地进行准备。请参见与位置无关的代码。

DF_BIND_NOW

表示在将控制权返回给程序之前,必须处理此目标文件的所有重定位项。通过环境或 dlopen(3C) 指定时,提供的此项优先于使用延迟绑定的指令。请参见执行重定位的时间。

DF_STATIC_TLS

表示目标文件包含使用静态线程局部存储方案的代码。在通过 dlopen(3C) 或延迟装入而动态装入的目标文件中,不能使用静态线程局部存储。

表 7-34 ELF 动态标志 DT_FLAGS_1

名称

含义

DF_1_NOW

0x1

执行完整的重定位处理。

DF_1_GLOBAL

0x2

未使用。

DF_1_GROUP

0x4

表示目标文件是组的成员。

DF_1_NODELETE

0x8

不能从进程中删除目标文件。

DF_1_LOADFLTR

0x10

确保立即装入 filtee。

DF_1_INITFIRST

0x20

首先进行目标文件初始化。

DF_1_NOOPEN

0x40

DF_1_ORIGIN

0x80

要求 $ORIGIN 处理。

DF_1_DIRECT

0x100

已启用直接绑定。

DF_1_INTERPOSE

0x400

目标文件是插入项。

DF_1_NODEFLIB

0x800

忽略缺省的库搜索路径。

DF_1_NODUMP

0x1000

不能使用 dldump(3C) 转储目标文件。

DF_1_CONFALT

0x2000

目标文件是配置替代项。

DF_1_ENDFILTEE

0x4000

filtee 终止过滤器搜索。

DF_1_DISPRELDNE

0x8000

已执行位移重定位。

DF_1_DISPRELPND

0x10000

位移重定位暂挂。

DF_1_NODIRECT

0x20000

目标文件包含非直接绑定。

DF_1_IGNMULDEF

0x40000

内部使用。

DF_1_NOKSYMS

0x80000

内部使用。

DF_1_NOHDR

0x100000

内部使用。

DF_1_EDITED

0x200000

目标文件在最初生成后已被修改。

DF_1_NORELOC

0x400000

内部使用。

DF_1_SYMINTPOSE

0x800000

存在各个符号插入项。

DF_1_GLOBAUDIT

0x1000000

建立全局审核。

DF_1_SINGLETON

0x2000000

存在单件符号。

DF_1_NOW

表示在将控制权返回给程序之前,必须处理此目标文件的所有重定位项。通过环境或 dlopen(3C) 指定时,提供的此标志优先于使用延迟绑定的指令。请参见执行重定位的时间。

DF_1_GROUP

表示目标文件是组的成员。此标志通过链接编辑器的 -B group 选项记录在目标文件中。请参见目标文件分层结构。

DF_1_NODELETE

表示不能从进程中删除目标文件。如果使用 dlopen(3C) 通过直接或依赖性方式将目标文件装入进程,则无法使用 dlclose(3C) 卸载该目标文件。此标志通过使用链接编辑器的 -z nodelete 选项记录在目标文件中。

DF_1_LOADFLTR

仅对过滤器有意义。表示立即处理所有关联 filtee。此标志通过使用链接编辑器的 -z loadfltr 选项记录在目标文件中。请参见filtee 处理。

DF_1_INITFIRST

表示在装入其他任何目标文件之前首先运行此目标文件的初始化节。此标志仅适用于专用系统库,并通过使用链接编辑器的 -z initfirst 选项记录在目标文件中。

DF_1_NOOPEN

表示无法使用 dlopen(3C) 将目标文件添加到正在运行的进程。此标志通过使用链接编辑器的 -z nodlopen 选项记录在目标文件中。

DF_1_ORIGIN

表示目标文件要求 $ORIGIN 处理。请参见查找关联的依赖项。

DF_1_DIRECT

表示目标文件应使用直接绑定信息。请参见附录 D。

DF_1_INTERPOSE

表示目标文件符号表将在除主装入目标文件(通常为可执行文件)外的所有符号之前插入。此标志通过使用链接编辑器的 -z interpose 选项进行记录。请参见运行时插入。

DF_1_NODEFLIB

表示此目标文件的依赖性搜索会忽略所有缺省的库搜索路径。此标志通过使用链接编辑器的 -z nodefaultlib 选项记录在目标文件中。请参见运行时链接程序搜索的目录。

DF_1_NODUMP

表示此目标文件不通过 dldump(3C) 进行转储。此选项的替代选项包括没有重定位项的目标文件,这些目标文件可能会包括在使用 crle(1) 生成的替代目标文件中。此标志通过使用链接编辑器的 -z nodump 选项记录在目标文件中。

DF_1_CONFALT

将此目标文件标识为 crle(1) 生成的配置替代目标文件。此标志可触发运行时链接程序来搜索配置文件 $ORIGIN/ld.config.app-name。

DF_1_ENDFILTEE

仅对 filtee 有意义。终止对其他任何 filtee 的过滤器搜索。此标志通过使用链接编辑器的 -z endfiltee 选项记录在目标文件中。请参见减少 filtee 搜索。

DF_1_DISPRELDNE

表示此目标文件应用了位移重定位。由于位移重定位记录在应用重定位后被丢弃,因此该目标文件中将不再存在这些记录。请参见位移重定位。

DF_1_DISPRELPND

表示此目标文件暂挂了位移重定位。由于此目标文件中存在位移重定位,因此可在运行时完成重定位。请参见位移重定位。

DF_1_NODIRECT

DF_1_IGNMULDEF

保留供内核运行时链接程序内部使用。

DF_1_NOKSYMS

保留供内核运行时链接程序内部使用。

DF_1_NOHDR

保留供内核运行时链接程序内部使用。

DF_1_EDITED

表示此目标文件在最初由链接编辑器构造后,已被编辑或被修改。此标志用于警告调试器,某个目标文件在最初生成后进行了更改。

DF_1_NORELOC

保留供内核运行时链接程序内部使用。

DF_1_SYMINTPOSE

表示目标文件包含应在除主装入目标文件(通常为可执行文件)外的所有符号之前插入的各个符号。使用 mapfile 和 INTERPOSE 关键字生成目标文件时记录此标志。请参见SYMBOL_SCOPE / SYMBOL_VERSION 指令。

DF_1_GLOBAUDIT

表示动态可执行文件要求全局审计。请参见记录全局审计程序。

DF_1_SINGLETON

表 7-35 ELF 动态位置标志 DT_POSFLAG_1

名称

含义

DF_P1_LAZYLOAD

0x1

标识延迟装入的依赖项。

DF_P1_GROUPPERM

0x2

标识组依赖性。

DF_P1_LAZYLOAD

将以下 DT_NEEDED 项标识为要延迟装入的目标文件。此标志通过使用链接编辑器的 -z lazyload 选项记录在目标文件中。请参见延迟装入动态依赖项。

DF_P1_GROUPPERM

将以下 DT_NEEDED 项标识为要作为组装入的目标文件。此标志通过使用链接编辑器的 -z groupperm 选项记录在目标文件中。请参见隔离组。

全局偏移表(特定于处理器)

通常,与位置无关的代码不能包含绝对虚拟地址。全局偏移表在专用数据中包含绝对地址。因此这些地址可用,并且不会破坏程序文本的位置独立性和共享性。程序使用与位置无关的地址来引用其 GOT 并提取绝对值。此方法可将与位置无关的引用重定向到绝对位置。

最初,GOT 包含其重定位项所需的信息。系统为可装入目标文件创建内存段后,运行时链接程序将会处理这些重定位项。某些重定位项的类型可以为 R_xxxx_GLOB_DAT,用于引用 GOT。

运行时链接程序可确定关联符号值,计算其绝对地址以及将相应的内存表各项设置为正确的值。尽管链接编辑器创建目标文件时绝对地址未知,但运行时链接程序知道所有内存段的地址,因此可以计算其中包含的符号的绝对地址。

如果程序要求直接访问某符号的绝对地址,则该符号将具有一个 GOT 项。由于可执行文件和共享目标文件具有不同的 GOT,因此一个符号的地址可以出现在多个表中。运行时链接程序在向进程映像中的任何代码授予控制权之前,将首先处理所有的 GOT 重定位项。此处理操作可确保绝对地址在执行过程中可用。

表项零保留用于存储动态结构(使用符号 _DYNAMIC 引用)的地址。使用此符号,运行时链接程序等程序可在尚未处理其重定位项的情况下查找各自的动态结构。此方法对于运行时链接程序尤其重要,因为它必须对自身进行初始化,而不依赖于其他程序来重定位其内存映像。

系统可为不同程序中的同一共享目标文件选择不同的内存段地址。系统甚至可以为同一程序的不同执行方式选择不同的库地址。但是,一旦建立进程映像,内存段即不会更改各地址。只要存在进程,其内存段就会位于固定的虚拟地址。

GOT 的格式和解释是特定于处理器的。符号 _GLOBAL_OFFSET_TABLE_ 可用于访问该表。此符号可以位于 .got 节的中间,以提供地址数组的负下标和非负下标。对于 32 位代码,符号类型是 Elf32_Addr 数组;对于 64 位代码,符号类型是

Elf64_Addr 数组。extern Elf32_Addr _GLOBAL_OFFSET_TABLE_[];

extern Elf64_Addr _GLOBAL_OFFSET_TABLE_[];

过程链接表(特定于处理器)

全局偏移表可将与位置无关的地址计算结果转换为绝对位置。同样,过程链接表也可将与位置无关的函数调用转换为绝对位置。链接编辑器无法解析不同动态目标文件之间的执行传输(如函数调用)。因此,链接编辑器会安排程序将控制权转移给过程链接表中的各项。这样,运行时链接程序就会重定向各项,而不会破坏程序文本的位置独立性和共享性。可执行文件和共享目标文件包含不同的过程链接表。

32 位 SPARC: 过程链接表

对于 32 位 SPARC 动态目标文件,过程链接表位于专用数据中。运行时链接程序可确定目标的绝对地址,并相应地修改过程链接表的内存映像。

过程链接表的前四项是保留项。尽管表 7-36 中显示了过程链接表的示例,但未指定这些项的原始内容。该表中的每一项都占用 3 个字(12 字节),并且表的最后一项后跟 nop 指令。

重定位表与过程链接表关联。_DYNAMIC 数组中的 DT_JMP_REL 项指定了第一个重定位项的位置。对于非保留的过程链接表的每一项,重定位表中都包含相同顺序的对应项。所有这些项的重定位类型均为 R_SPARC_JMP_SLOT。重定位偏移可指定关联的过程链接表项的第一个字节的地址。符号表索引会指向相应的符号。

为说明过程链接表,表 7-36 显示了四项。其中,前两项是初始保留项。第三项是对 name101 的调用。第四项是对 name102 的调用。此示例假定对应 name102 的项是表的最后一项。在该最后一项的后面是 nop 指令。左列显示了进行动态链接之前目标文件中的指令。右列说明了运行时链接程序会用于修复过程链接表各项的可能的指令序列。

表 7-36 32 位 SPARC: 过程链接表示例

目标文件

内存段

.PLT0:

unimp

unimp

unimp

.PLT1:

unimp

unimp

unimp

.PLT0:

save %sp, -64, %sp

call runtime_linker

nop

.PLT1:

.word identification

unimp

unimp

.PLT101:

sethi (.-.PLT0), %g1

ba,a .PLT0

nop

.PLT102:

sethi (.-.PLT0), %g1

ba,a .PLT0

nop

nop

.PLT101:

nop

ba,a name101

nop

.PLT102:

sethi (.-.PLT0), %g1

sethi %hi(name102), %g1

jmpl %g1+%lo(name102), %g0

nop

以下步骤介绍了运行时链接程序和程序如何通过过程链接表来共同解析符号引用。所介绍的这些步骤仅用于说明。没有指定运行时链接程序的准确执行时行为。

初始创建程序的内存映像时,运行时链接程序会更改初始过程链接表的各项。修改这些项是为了可将控制权转移给运行时链接程序自己的其中一个例程。运行时链接程序还会在第二项中存储一个字的标识信息。运行时链接程序获取控制权后,会检查该字以标识调用者。

过程链接表的其他所有项最初都会传输给第一项。因此,运行时链接程序会在首次执行表项时获取控制权。例如,该程序会调用 name101,以将控制权转移给标签 .PLT101。

sethi 指令可分别计算当前过程链接表各项和初始过程链接表各项(.PLT101 和 .PLT0)之间的距离。该值会占用 %g1 寄存器最高有效的 22 位。

接下来,ba,a 指令会跳至 .PLT0 以建立栈帧,然后调用运行时链接程序。

通过标识值,运行时链接程序可获取其用于目标文件的数据结构,包括重定位表。

通过将 %g1 值移位并除以过程链接表各项的大小,运行时链接程序可计算对应 name101 的重定位项的索引。重定位项 101 的类型为 R_SPARC_JMP_SLOT。此重定位偏移可指定 .PLT101 的地址,并且其符号表索引会指向 name101。因此,运行时链接程序可获取符号的实际值、展开栈、修改过程链接表项并将控制权转移给所需目标。

运行时链接程序不必在内存段列下创建指令序列。如果运行时链接程序创建了指令序列,则某些点需要更多说明。

要使代码可重复执行,可按特定顺序更改过程链接表的指令。如果运行时链接程序在修复函数的过程链接表项时收到信号,则信号处理代码必须能够调用具有可预测的正确结果的原始函数。

运行时链接程序更改三个字才能转换一项。对于指令执行,运行时链接程序只能自动更新一个字。因此,通过以相反顺序更新每个字可实现重复执行。如果仅在最后一个修补程序之前调用可重复执行的函数,则运行时链接程序会再次获取控制权。尽管两次调用运行时链接程序修改的过程链接表项都相同,但这些更改不会相互干扰。

过程链接表项的第一条 sethi 指令可以填充 jmp1 指令的延迟插槽。尽管 sethi 会更改 %g1 寄存器的值,但可以安全废弃以前的内容。

转换之后,过程链接表的最后一项 .PLT102 需要一条延迟指令用于其 jmp1。所需的结尾 nop 将填充此延迟插槽。

注 -为 .PLT101 和 .PLT102 显示的不同指令序列说明了如何优化关联目标的更新。

LD_BIND_NOW 环境变量更改动态链接行为。如果其值不为空,则运行时链接程序会在将控制权转移给程序之前处理 R_SPARC_JMP_SLOT 重定位项。

64 位 SPARC: 过程链接表

对于 64 位 SPARC 动态目标文件,过程链接表位于专用数据中。运行时链接程序可确定目标的绝对地址,并相应地修改过程链接表的内存映像。

过程链接表的前四项是保留项。尽管表 7-37 中显示了过程链接表的示例,但未指定这些项的原始内容。在该表中,前 32,768 项的每一项都占用 8 个字(32 字节),并且必须与 32 字节边界对齐。整个表必须与 256 字节边界对齐。如果所需项数大于 32,768,则其余各项由

6 个字(24 字节)和 1 个指针(8 字节)组成。指令以 160 项并后跟 160 个指针的块方式收集到一起。最后一组项和指针可以包含的项数少于 160。不需要进行填充。

注 -数字 32,768 和 160 分别基于分支和装入目标文件位移的限制,并且位移会向下舍入以使代码和数据之间的分区落到 256 字节边界上,从而提高高速缓存的性能。

重定位表与过程链接表关联。_DYNAMIC 数组中的 DT_JMP_REL 项指定了第一个重定位项的位置。对于非保留的过程链接表的每一项,重定位表中都包含相同顺序的对应项。所有这些项的重定位类型均为 R_SPARC_JMP_SLOT。对于前 32,767 个插槽,重定位偏移将指定关联过程链接表项的第一个字节的地址,并且加数字段为零。符号表索引会指向相应的符号。对于插槽 32,768 及其之后的插槽,重定位偏移将指定关联指针的第一个字节的地址。加数字段是未重定位的值 -(.PLTN + 4)。符号表索引会指向相应的符号。

为说明过程链接表,表 7-37 显示了若干项。前三项显示了初始保留项。接下来的三项显示了初始的 32,768 个项的示例以及可能的解析格式,这些格式分别应用于目标地址位于项上下 2 GB 的地址空间内、目标地址位于低位的 4 GB 地址空间内或目标地址位与其他任意位置的情形。最后两项显示了后续各项的示例,这些项由指令和指针对组成。左列显示了进行动态链接之前目标文件中的指令。右列说明了运行时链接程序会用于修复过程链接表各项的可能指令序列。

表 7-37 64 位 SPARC: 过程链接表示例

目标文件

内存段

.PLT0:

unimp

unimp

unimp

unimp

unimp

unimp

unimp

unimp

.PLT1:

unimp

unimp

unimp

unimp

unimp

unimp

unimp

unimp

.PLT2:

unimp

.PLT0:

save %sp, -176, %sp

sethi %hh(runtime_linker_0), %l0

sethi %lm(runtime_linker_0), %l1

or %l0, %hm(runtime_linker_0), %l0

sllx %l0, 32, %l0

or %l0, %l1, %l0

jmpl %l0+%lo(runtime_linker_0), %o1

mov %g1, %o0

.PLT1:

save %sp, -176, %sp

sethi %hh(runtime_linker_1), %l0

sethi %lm(runtime_linker_1), %l1

or %l0, %hm(runtime_linker_1), %l0

sllx %l0, 32, %l0

or %l0, %l1, %l0

jmpl %l0+%lo(runtime_linker_0), %o1

mov %g1, %o0

.PLT2:

.xword identification

.PLT101:

sethi (.-.PLT0), %g1

ba,a %xcc, .PLT1

nop

nop

nop; nop

nop; nop

.PLT102:

sethi (.-.PLT0), %g1

ba,a %xcc, .PLT1

nop

nop

nop; nop

nop; nop

.PLT103:

sethi (.-.PLT0), %g1

ba,a %xcc, .PLT1

nop

nop

nop

nop

nop

nop

.PLT101:

nop

mov %o7, %g1

call name101

mov %g1, %o7

nop; nop

nop; nop

.PLT102:

nop

sethi %hi(name102), %g1

jmpl %g1+%lo(name102), %g0

nop

nop; nop

nop; nop

.PLT103:

nop

sethi %hh(name103), %g1

sethi %lm(name103), %g5

or %hm(name103), %g1

sllx %g1, 32, %g1

or %g1, %g5, %g5

jmpl %g5+%lo(name103), %g0

nop

.PLT32768:

mov %o7, %g5

call .+8

nop

ldx [%o7+.PLTP32768 -

(.PLT32768+4)], %g1

jmpl %o7+%g1, %g1

mov %g5, %o7

...

.PLT32927:

mov %o7, %g5

call .+8

nop

ldx [%o7+.PLTP32927 -

(.PLT32927+4)], %g1

jmpl %o7+%g1, %g1

mov %g5, %o7

.PLT32768:

...

.PLT32927:

.PLTP32768

.xword .PLT0 -

(.PLT32768+4)

...

.PLTP32927

.xword .PLT0 -

(.PLT32927+4)

.PLTP32768

.xword name32768 -

(.PLT32768+4)

...

.PLTP32927

.xword name32927 -

(.PLT32927+4)

以下步骤介绍了运行时链接程序和程序如何通过过程链接表来共同解析符号引用。所介绍的这些步骤仅用于说明。没有指定运行时链接程序的准确执行时行为。

初始创建程序的内存映像时,运行时链接程序会更改初始过程链接表的各项。修改这些项是为了将控制权转移给运行时链接程序自己的例程。运行时链接程序还会在第三项中存储一个扩展字的标识信息。运行时链接程序获取控制权后,会检查该字以标识调用者。

过程链接表的其他所有项最初都会传输给第一项或第二项。这些项将建立栈帧并调用运行时链接程序。

通过标识值,运行时链接程序可获取其用于目标文件的数据结构,包括重定位表。

运行时链接程序会计算表插槽对应的重定位项的索引。

使用索引信息,运行时链接程序可获取符号的实际值、展开栈、修改过程链接表项并将控制权转移给所需目标。

运行时链接程序不必在内存段列下创建指令序列。如果运行时链接程序创建了指令序列,则某些点需要更多说明。

要使代码可重复执行,可按特定顺序更改过程链接表的指令。如果运行时链接程序在修复函数的过程链接表项时收到信号,则信号处理代码必须能够调用具有可预测的正确结果的原始函数。

运行时链接程序最多可更改八个字来转换一项。对于指令执行,运行时链接程序只能自动更新一个字。因此,通过以下方法可实现重复执行:首先将 nop 指令覆写为其替换指令,如果使用 64 位存储,则之后还要修补 ba,a 和 sethi。如果仅在最后一个修补程序之前调用可重复执行的函数,则运行时链接程序会再次获取控制权。尽管两次调用运行时链接程序修改的过程链接表项都相同,但这些更改不会相互干扰。

如果更改初始 sethi 指令,则只能将该指令替换为 nop。

按照更改第二种格式的项的方式更改指针是通过使用一个原子的 64 位存储器完成的。

注 -为 .PLT101、.PLT102 和 .PLT103 显示的不同指令序列说明了如何优化关联目标的更新。

LD_BIND_NOW 环境变量可更改动态链接行为。如果其值不为空,则运行时链接程序会在将控制权转移给程序之前处理 R_SPARC_JMP_SLOT 重定位项。

32 位 x86: 过程链接表

对于 32 位 x86 动态目标文件,过程链接表位于共享文本中,但使用专用全局偏移表中的地址。运行时链接程序可确定目标的绝对地址,并相应地修改全局偏移表的内存映像。这样,运行时链接程序就会重定向各项,而不会破坏程序文本的位置独立性和共享性。可执行文件和共享目标文件包含不同的过程链接表。

表 7-38 32 位 x86: 绝对过程链接表示例

.PLT0:

pushl got_plus_4

jmp *got_plus_8

nop; nop

nop; nop

.PLT1:

jmp *name1_in_GOT

pushl $offset

jmp .PLT0@PC

.PLT2:

jmp *name2_in_GOT

pushl $offset

jmp .PLT0@PC

表 7-39 32 位 x86: 与位置无关的过程链接表示例

.PLT0:

pushl 4(%ebx)

jmp *8(%ebx)

nop; nop

nop; nop

.PLT1:

jmp *name1@GOT(%ebx)

pushl $offset

jmp .PLT0@PC

.PLT2:

jmp *name2@GOT(%ebx)

pushl $offset

jmp .PLT0@PC

注 -如前面的示例所示,对于绝对代码和与位置无关的代码,过程链接表指令会使用不同的操作数寻址模式。但是,它们的运行时链接程序接口却相同。

以下步骤介绍了运行时链接程序和程序如何通过过程链接表和全局偏移表来协作解析符号引用。

初始创建程序的内存映像时,运行时链接程序会将全局偏移表中的第二项和第三项设置为特殊值。以下步骤说明了这些值。

如果过程链接表与位置无关,则全局偏移表的地址必须位于 %ebx 中。进程映像中的每个共享目标文件都有各自的过程链接表,并且控制权仅转移给位于同一目标文件内的过程链接表项。因此,调用函数在调用过程链接表项之前,必须首先设置全局偏移表基本寄存器。

例如,该程序会调用 name1,以将控制权转移给标签 .PLT1。

第一条指令会跳至全局偏移表项中对应于 name1 的地址。最初,全局偏移表包含以下 pushl 指令的地址,而不是 name1 的实际地址。

该程序将在栈中推送一个重定位偏移 (offset)。该重定位偏移是重定位表中一个 32 位的非负字节偏移。指定的重定位项的类型为 R_386_JMP_SLOT,其偏移指定了前面的 jmp 指令中使用的全局偏移表项。该重定位项还包含符号表索引,以供运行时链接程序用于获取引用的符号 name1。

推送该重定位偏移后,程序将跳至过程链接表中的第一项 .PLT0。pushl 指令会在栈中推送全局偏移表的第二项(got_plus_4 或 4(%ebx))的值,从而为运行时链接程序提供一个字的标识信息。然后,程序将跳至全局偏移表的第三项(got_plus_8 或 8(%ebx))中的地址,以继续跳至运行时链接程序。

运行时链接程序将展开栈、检查指定的重定位项、获取符号的值、在全局偏移项表中存储 name1 的实际地址并跳至目标。

过程链接表项的后续执行结果会直接传输给 name1,而不会再次调用运行时链接程序。位于 .PLT1 的 jmp 指令将跳至 name1,而不是对 pushl 指令失败。

LD_BIND_NOW 环境变量可更改动态链接行为。如果其值不为空,则运行时链接程序会在将控制权转移给程序之前处理 R_386_JMP_SLOT 重定位项。

x64: 过程链接表

对于 x64 动态目标文件,过程链接表位于共享文本中,但使用专用全局偏移表中的地址。运行时链接程序可确定目标的绝对地址,并相应地修改全局偏移表的内存映像。这样,运行时链接程序就会重定向各项,而不会破坏程序文本的位置独立性和共享性。可执行文件和共享目标文件包含不同的过程链接表。

表 7-40 x64: 过程链接表示例

.PLT0:

pushq GOT+8(%rip) # GOT[1]

jmp *GOT+16(%rip) # GOT[2]

nop; nop

nop; nop

.PLT1:

jmp *name1@GOTPCREL(%rip) # 16 bytes from .PLT0

pushq $index1

jmp .PLT0

.PLT2:

jmp *name2@GOTPCREL(%rip) # 16 bytes from .PLT1

pushl $index2

jmp .PLT0

以下步骤介绍了运行时链接程序和程序如何通过过程链接表和全局偏移表来协作解析符号引用。

初始创建程序的内存映像时,运行时链接程序会将全局偏移表中的第二项和第三项设置为特殊值。以下步骤说明了这些值。

进程映像中的每个共享目标文件都有各自的过程链接表,并且控制权仅转移给位于同一目标文件内的过程链接表项。

例如,该程序会调用 name1,以将控制权转移给标签 .PLT1。

第一条指令会跳至全局偏移表项中对应于 name1 的地址。最初,全局偏移表包含以下 pushq 指令的地址,而不是 name1 的实际地址。

该程序将在栈中推送一个重定位索引 (index1)。该重定位索引是重定位表中一个 32 位的非负索引。重定位表由 DT_JUMPREL 动态节项标识。指定的重定位项的类型为 R_AMD64_JMP_SLOT,其偏移指定了前面的 jmp 指令中使用的全局偏移表项。该重定位项还包含符号表索引,以供运行时链接程序用于获取引用的符号 name1。

推送该重定位索引后,程序将跳至过程链接表中的第一项 .PLT0。pushq 指令会在栈中推送全局偏移表的第二项 (GOT+8) 的值,从而为运行时链接程序提供一个字的标识信息。然后,程序将跳至第三个全局偏移表项 (GOT+16) 中的地址,以继续跳至运行时链接程序。

运行时链接程序将展开栈、检查指定的重定位项、获取符号的值、在全局偏移项表中存储 name1 的实际地址并跳至目标。

过程链接表项的后续执行结果会直接传输给 name1,而不会再次调用运行时链接程序。位于 .PLT1 的 jmp 指令将跳至 name1,而不是对 pushq 指令失败。

LD_BIND_NOW 环境变量可更改动态链接行为。如果其值不为空,则运行时链接程序会在将控制权转移给程序之前处理 R_AMD64_JMP_SLOT 重定位项。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值