链接程序和库指南

简单解析

到目前为止,简单符号解析是最常见的一种解析形式。在这种情况下,将检测两个具有类似特征的符号,一个符号优先于另一个符号。此符号解析由链接编辑器执行,且没有任何提示。例如,对于具有相同绑定的符号,一个文件中的符号引用将绑定到另一个文件中已定义或暂定符号定义。或者,一个文件中的暂定符号定义将绑定到另一个文件中的已定义符号定义。

要解析的符号可以具有全局绑定或弱绑定。弱绑定的优先级低于全局绑定,因此,将根据略有改变的基本规则解析具有不同绑定的符号。

通常可以通过编译器单独定义弱符号或将它们定义为全局符号的别名。一种机制使用 #pragma 定义:


$ cat main.c

#pragma weak    bar

#pragma weak    foo = _foo



int             bar = 1;



_foo()

{

        return (bar);

}

$ cc -o main.o -c main.c

$ nm -x main.o

[Index]   Value      Size      Type  Bind  Other Shndx   Name

...............

[7]     |0x00000000|0x00000004|OBJT |WEAK |0x0  |3      |bar

[8]     |0x00000000|0x00000028|FUNC |WEAK |0x0  |2      |foo

[9]     |0x00000000|0x00000028|FUNC |GLOB |0x0  |2      |_foo

请注意,对弱别名 foo 指定了与全局符号 _foo 相同的属性。此关系由链接编辑器维护,并将导致在输出映像中对符号指定相同的值。在符号解析过程中,定义的弱符号会被名称相同的任何全局定义覆盖,且没有任何提示。

在可重定位目标文件与共享库之间或者多个共享库之间进行的另一种形式的简单符号解析是插入。在这些情况下,如果多重定义了某个符号,则链接编辑器会采用可重定位目标文件或多个共享库之间的第一个定义,且不作任何提示。可重定位目标文件的定义或第一个共享库的定义插入进来取代所有其他定义。这种插入可用于覆盖其他共享库提供的功能。

弱符号和符号插入的组合可以提供有用的编程方法。例如,标准 C 库提供了多个允许您重新定义的服务。但是,ANSI C 定义了一组必须出现在系统中的标准服务。在严格遵循规则的程序中不能替换这些服务。

例如,函数 fread(3C) 是一个 ANSI C 库函数,而系统函数 read(2) 不是。遵循 ANSI 的C 程序必须可以重新定义 read(2),并且仍以可预测的方法使用 fread(3C)

此处的问题是,在标准 C 库中 read(2)fread(3C) 实现的基础。因此,重新定义 read(2) 的程序可能会混淆 fread(3C) 实现。为了避免出现这种情况,ANSI C 声明实现只能使用为该实现保留的名称。使用以下 #pragma 指令可定义这种保留名称。使用此名称可生成函数 read(2) 的别名。


#pragma weak read = _read

因此,您可以非常自由地定义自己的 read() 函数,而不会破坏 fread(3C) 实现,而 fread(3C) 是使用 _read() 函数实现的。

链接编辑器在链接共享库或标准 C 库的归档版本时,可以轻松重新定义 read()。在前一种情况下,可以执行插入操作。在后一种情况下,由于 C 库中 read(2) 的定义较弱,所以允许默认覆盖该定义。

使用链接编辑器的 -m 选项可将所有插入的符号引用列表和节装入地址信息写入标准输出中。