共享对象和运行时链接
缺省情况下,程序得到链接以便从共享对象导入的符号引用被绑定到装入时的那个定义。
即使该程序或其所需的其他共享对象定义相同的符号也是如此。
- 运行时链接程序
- 允许对被适当链接的程序重新绑定符号的共享对象
通过以 -brtl 选项链接程序以在程序中包含运行时链接程序。 该选项有以下作用:
- 对该运行时链接程序的引用被添加到您的程序。 当程序开始执行时,启动代码 (/lib/crt0.o) 将在调用主函数前调用运行时链接程序。
- 全部是共享对象的输入文件作为程序的从属在程序的装入程序部分中列示。 共享对象按在命令行中指定的相同顺序列示。 这会促使系统装入程序装入所有这些共享对象,以便运行时链接程序能使用其定义。 如果没有使用 -brtl 选项,那么不列示程序没有引用的共享对象,即使它提供了该程序使用的别的共享对象可能需要的定义。
- 如果压缩文档指定自动装入共享对象成员,那么仅列示包含在归档里的共享对象。 通过下列行创建文件来指定归档成员 foo.o 的自动装入:
将文件作为成员添加到归档。# autoload #! (foo.o) - 在动态方式下,使用 -l 标志指定的输入文件可能以 .so和 .a结尾。 即,在要搜索的任何目录中找到的第一个 libfoo.so 或 libfoo.a 将满足对 -lfoo 的引用。 除非使用了 -bstatic 选项,缺省情况下动态方式是有效的。
运行时链接程序在使用静态链接时模仿 ld 命令的行为(除了只能使用导出符号来解析符号)。 即使在使用运行时链接时,系统装入程序必须能够在主程序及其依靠的任何模块中装入并解析所有的符号引用。 因此,如果从模块除去定义,且主程序拥有对该定义的引用,那么该程序不会执行,即使该符号的其他定义存在于别的模块中。
运行时链接程序可重新绑定所有从其他模块导入的符号引用。 只有在该模块由对该符号启用的运行时链接构建时,在与其相同的模块中定义的符号的引用才能被重新绑定。
AIX 4.2或更高版本的共享模块已为大多数导出变量启用了运行时链接。 仅对通过函数指针调用的函数启用函数的运行时链接。 例如,作为已经装运的调用,不能重新绑定对 /lib/libc.a 中的共享对象 shr.o 内的 malloc 子例程的调用,即使 malloc 的定义在主程序或其他共享模块中存在。 您可以通过运行 rtl_enable 命令来链接大多数交付的共享模块,以启用函数和变量的运行时链接。
运行时链接程序的操作
主程序由系统装入程序按普通方式装入并解析。 如果可执行程序由于任何原因而无法装入,exec() 子例程发生错误且根本不调用运行时链接程序。 如果主程序成功装入,控制转到运行时链接程序,它按如下所述重新绑定符号。 运行时链接程序完成时调用初始化例程,如果没有问题,再调用主函数。
运行时链接程序以宽度优先搜索顺序来处理模块,首先处理主可执行文件的模块,接着处理该主可执行文件的直接从属模块(按照每个模块的装入程序段中所列的从属模块的顺序)。 也在搜索符号的定义实例时使用该顺序。 符号的“定义实例”通常为符号的第一个实例,但有两个例外。 如果符号的第一个实例为没有解析、延迟的导入,那么不存在定义实例。 如果第一个实例为 BSS 符号(即类型 XTY_CM 的符号,表示未初始化的变量),且还有一个既不是 BBS 符号也不是未解析、被延迟导入的符号的实例,该符号的第一个这样的实例为定义实例。
每个模块的装入程序段列示了导入符号(它们通常在另一个特定的模块中被定义)和导出符号(它们通常在该模块本身中被定义)。 被导入和导出的符号称为“passed-through”导入。 这样的符号似乎是在一个模块中被定义的,虽然实际是在另一个模块中定义的。
符号还可被标记为“延迟的导入”。 运行时链接程序从不重新绑定延迟的导入符号的引用。 这些符号的解析必须由系统装入程序执行,或者通过调用 loadbind() 或以 load() 或 dlopen() 显式装入新的模块。
可始终重新绑定导入符号(除延迟的导入外)的引用。 系统装入程序将已经解析了大多数的导入。 每个导入符号的引用被重新绑定到符号的定义实例。 如果不存在定义实例,错误消息将被显示至标准错误。 此外,如果导入符号的 typechecking 散列字符串与定义符号的散列字符串不匹配,也将显示错误消息。
导出符号的引用也会被重新绑定到其定义实例,只要这些引用出现在装入程序段的重新定位表中。 (如上所述,passed-through 和其他导入一起被处理。) 根据链接模块的方法,导出符号的某些引用在链接时被绑定,且不能被重新绑定。 由于导出符号在导出模块中被定义,将始终存在一个该符号的定义实例,除非第一个实例为已延迟的导入,这样在重新绑定导出符号时就不太可能发生错误(但这种可能性仍然存在)。 导入中,如果在重新绑定符号时 typechecking 散列字符串不匹配则有错误被显示出来。
无论何时重新绑定符号,一个从属项将从使用该符号的模块被添加到定义该符号的模块。 该从属项防止从地址空间永久除去模块。 在由 dlopen 子例程装入的模块定义在试图用 dlclose 子例程卸装模块时仍然在使用的符号时,这一点是重要的。
装入程序段符号表不包含任何有关对齐或符号长度的信息。 这样,在符号被重新绑定到太短或没有正确对齐的实例时,不会检测到错误。 在此情况下可能发生执行错误。
处理完所有模块后,如果发生任何运行时链接错误,那么运行时链接程序将调用 exit 子例程,并传递退出代码144(0x90). 否则执行将通过调用初始化例程或 main() 继续进行。
在启用了运行时链接的情况下创建共享对象
要创建为运行时链接而启用的共享对象,请使用 -G 标记来链接。 使用该标记时,将发生以下操作:
- 为导出的符号提供了nosymbolic属性,以便运行时链接程序可以重新绑定对符号的所有引用。
- 运行未定义的符号(请参阅 -berok 选项)。 这样的符号在从名为“..”的符号模块导入时被标记。 从“..” 导入的符号必须由运行时链接程序在其被使用前解析,因为系统装入程序不会解析这些符号。
- 为输出文件提供了模块类型SRE就像指定了-bM:SRE选项一样。
- 所有在命令行上列示的共享对象被列为输出模块的从属,用的方式与以 -brtl 选项链接程序时所述的相同。
- 如果归档中的共享对象具有autoload属性。
使用 -G 标记所暗含的 -berok 选项可以掩盖链接时可能检测到的错误。 如果打算在链接模块时定义所有被引用的符号,应该在 -G 标记后使用 -bernotok 选项。 对于未定义的符号这将导致有错误被报告。