链接程序和库指南

与位置无关的代码

动态可执行文件中的代码通常与位置相关,并且与内存中的固定地址关联。相反,共享库可装入不同进程中的不同地址。位置无关代码不与特定地址关联。这种无关性允许在每个使用此类代码的进程中的不同地址有效地执行代码。建议在创建共享库时使用与位置无关的代码。

使用 -K pic 选项编译器可以生成与位置无关的代码。

如果共享库根据位置相关代码生成,则在运行时可能需要修改文本段。通过此修改,可以为已装入目标文件的位置指定可重定位引用。文本段的重定位需要将此段重映射为可写段。这种修改需要预留交换空间,并且会形成此进程的文本段专用副本。此文本段不再供多个进程共享。通常,位置相关代码比相应的与位置无关的代码需要更多的运行时重定位。总体而言,处理文本重定位的开销可能会严重降低性能。

根据与位置无关的代码生成共享库时,会通过共享库数据段中的数据间接生成可重定位引用。文本段中的代码不需要进行任何修改。所有重定位更新都会应用于数据段中的相应项。有关特定间接技术的更多详细信息,请参见全局偏移表(特定于处理器)过程链接表(特定于处理器)

如果存在文本重定位,运行时链接程序便会尝试处理这些重定位。但是,某些重定位无法在运行时实现。

通常,x64 位置相关代码序列生成的代码只能装入内存的低 32 位。任何地址的高 32 位必须全部为零。由于共享库通常装入内存高位,因此需要地址的高 32 位。这样,x64 共享库中位置相关代码便无法满足重定位要求。在共享库中使用此类代码会导致出现运行时重定位错误。


$ prog

ld.so.1: prog: fatal: relocation error: R_AMD64_32: file \

    libfoo.so.1: symbol (unknown): value 0xfffffd7fff0cd457 does not fit

与位置无关的代码可以装入内存中的任何区域,从而可以满足 x64 共享库的要求。

这种情况不同于用于 64 位 SPARCV9 代码的缺省 ABS64 模式。这种位置相关代码通常兼容整个 64 位地址范围。 因此,位置相关代码序列可以存在于 SPARCV9 共享库中。针对 64 位 SPARCV9 代码使用 ABS32 模式或 ABS44 模式仍会导致无法在运行时解析的重定位。但是,这两种模式都需要运行时链接程序对文本段进行重定位。

无论运行时链接程序功能如何,也无论重定位要求的差异如何,共享库都应该使用与位置无关的代码生成。

可以根据文本段确定需要重定位的共享库。以下示例使用 dump(1) 确定是否存在 TEXTREL 项动态项。


$ cc -o libfoo.so.1 -G -R. foo.c

$ dump -Lv libfoo.so.1 | grep TEXTREL

[9]     TEXTREL  0

注 –

TEXTREL 项的值无关紧要。共享库中存在此项表示存在文本重定位。


要防止创建包含文本重定位的共享库,请使用链接编辑器的 -z text 标志。此标志会导致链接编辑器生成指示将位置相关代码源用作输入的诊断。以下示例显示位置相关代码如何导致无法生成共享库。


$ cc -o libfoo.so.1 -z text -G -R. foo.c

Text relocation remains                       referenced

    against symbol                  offset      in file

foo                                 0x0         foo.o

bar                                 0x8         foo.o

ld: fatal: relocations remain against allocatable but \

non-writable sections

将根据文本段生成两个重定位,因为通过文件 foo.o 生成了位置相关代码。如有可能,这些诊断会指明执行重定位所需的任何符号引用。在这种情况下,将根据符号 foobar 进行重定位。

如果包括手写汇编程序代码,但不包括相应的位置无关原型,则在共享库中也会出现文本重定位。


注 –

可能需要使用一些简单的源文件进行实验,以确定启用位置无关性的编码序列。请使用编译器功能来生成中间汇编程序输出。


SPARC: -K pic-K PIC 选项

对于 SPARC 二进制文件,-K pic 选项与备用 -K PIC 选项之间的细微差异会影响对全局偏移表项的引用。请参见全局偏移表(特定于处理器)

全局偏移表是一个指针数组,对于 32 位(4 个字节)和 64 位(8 个字节)目标文件其项大小为常量。以下代码序列使用 -K pic 引用项:


        ld    [%l7 + j], %o0    ! load &j into %o0

其中,%l7 是执行引用的目标文件的 _GLOBAL_OFFSET_TABLE_ 符号的预计算值。

此代码序列为全局偏移表项提供了 13 位位移常量。因此,此位移为 32 位目标文件提供了 2048 个唯一项,为 64 位目标文件提供了 1024 个唯一项。如果创建目标文件需要的项数多于可用项数,则链接编辑器会生成以下致命错误:


$ cc -K pic -G -o lobfoo.so.1 a.o b.o ... z.o

ld: fatal: too many symbols require `small' PIC references:

        have 2050, maximum 2048 -- recompile some modules -K PIC.

要克服这种错误情况,请使用 -K PIC 选项编译某些输入可重定位目标文件。此选项为全局偏移表项提供了 32 位常量:


        sethi %hi(j), %g1

        or    %g1, %lo(j), %g1    ! get 32–bit constant GOT offset

        ld    [%l7 + %g1], %o0    ! load &j into %o0

可以使用带有 -G 选项的 elfdump(1) 查看目标文件的全局偏移表要求。还可以使用链接编辑器调试标记 -D got,detail 在链接编辑过程中检查这些项的处理。

理论上,使用 -K pic 模型对经常访问的数据项有益。可以使用这两种模型引用单个项。但是,确定哪些可重定位目标文件应该使用其中一个选项进行编译可能会相当耗时,并且不会显著改善性能。通常,使用 -K PIC 选项可轻松重新编译所有的可重定位目标文件。