Oracle® Solaris 11.2 链接程序和库指南

退出打印视图

更新时间: 2014 年 7 月
 
 

符号解析

符号解析的方式很广,有简单直观的,也有错综复杂的。大多数解析由链接编辑器执行,且没有任何提示。但是,某些重定位可能伴随有警告诊断,而某些可能导致致命错误状态。

最常见的简单解析方式需要将一个目标文件中的符号引用与另一个目标文件中的符号定义绑定起来。此种绑定可用于两个可重定位目标文件之间,也可用于一个可重定位目标文件与在共享目标文件依赖项中找到的第一个定义之间。复杂解析方式通常用于两个或多个可重定位目标文件之间。

这两种符号解析方式取决于符号的属性、提供符号的文件类型以及要生成的文件类型。有关符号属性的完整说明,请参见符号表节。但是,对于以下论述,标识了三种基本符号类型。

  • 未定义-文件中已引用但尚未指定存储地址的符号。

  • 暂定-已在文件中创建但尚未指定大小或分配存储空间的符号。这些符号在文件中显示为未初始化的 C 语言符号或 FORTRAN COMMON 块。

  • 已定义-已在文件中创建并且分配了存储地址和空间的符号。

形式最简单的符号解析需要使用优先级关系。此关系中,已定义符号优先于暂定符号,而暂定符号又优先于未定义符号。

以下 C 代码示例说明如何生成这些符号类型。未定义符号使用 u_ 作为前缀。暂定符号使用 t_ 作为前缀。已定义符号使用 d_ 作为前缀。

$ cat main.c
extern int u_bar;
extern int u_foo();

int t_bar;
int d_bar = 1;

int d_foo()
{
        return (u_foo(u_bar, t_bar, d_bar));
}
$ cc -o main.o -c main.c
$ elfdump -s main.o

Symbol Table Section:  .symtab
    index  value   size  type bind oth ver shndx          name
    ....
      [7]      0      0  FUNC GLOB  D    0 UNDEF          u_foo
      [8]   0x10   0x40  FUNC GLOB  D    0 .text          d_foo
      [9]    0x4    0x4  OBJT GLOB  D    0 COMMON         t_bar
     [10]      0    0x4  NOTY GLOB  D    0 UNDEF          u_bar
     [11]      0    0x4  OBJT GLOB  D    0 .data          d_bar

简单解析

简单符号解析是迄今最常见的解析方式。在这种情况下,将检测两个具有类似特征的符号,一个符号优先于另一个符号。此符号解析由链接编辑器执行,且没有任何提示。例如,对于具有相同绑定的符号,一个文件中的符号引用将绑定到另一个文件中的已定义或暂定符号定义。或者,一个文件中的暂定符号定义将绑定到另一个文件中的已定义符号定义。此种解析方式可用于两个可重定位目标文件之间,也可用于一个可重定位目标文件与在共享目标文件依赖项中找到的第一个定义之间。

要解析的符号可以具有全局绑定或弱绑定。处理可重定位目标文件时,弱绑定的优先级低于全局绑定。弱符号定义将在不给出任何提示的情况下被同一名称的全局定义所覆盖。

在可重定位目标文件与共享目标文件之间或者多个共享目标文件之间,还有一种简单符号解析方式,即插入。在这些情况下,如果多重定义了某个符号,链接编辑器会采用可重定位目标文件或多个共享目标文件之间的第一个定义,且不作任何提示。可重定位目标文件的定义或第一个共享目标文件的定义会在其他所有定义上插入。这种插入可用于覆盖其他共享目标文件提供的功能。可重定位目标文件与共享目标文件之间或多个共享目标文件之间的多重定义符号采用同样的处理方式。符号是弱绑定还是全局绑定无关紧要。无论哪种符号绑定,链接编辑器和运行时链接程序都行为一致,将其解析为第一个定义。

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

复杂解析

如果发现两个符号的名称相同,但属性不同,则将进行复杂解析。在这些情况下,链接编辑器将选择最适合的符号同时生成一条警告消息。此消息指出符号、发生冲突的属性以及包含符号定义的文件的标识。在以下示例中,包含数据项数组定义的两个文件有不同的大小要求。

$ cat foo.c
int array[1];
$ cat bar.c
int array[2] = { 1, 2 };
$ ld -r -o temp.o foo.c bar.c
ld: warning: symbol 'array' has differing sizes:
    (file foo.o value=0x4; file bar.o value=0x8);
    bar.o definition taken

如果符号的对齐要求不同,则会产生一条类似的诊断消息。在这两种情况下,使用链接编辑器的 –t 选项可以不进行诊断。

另一种形式的属性差异是符号的类型。在以下示例中,符号 bar() 已同时定义为数据项和函数。

$ cat foo.c
int bar()
{
        return (0);
}
$ cc -o libfoo.so -G -K pic foo.c
$ cat main.c
int bar = 1;

int main()
{
        return (bar);
}
$ cc -o main main.c -L. -lfoo
ld: warning: symbol 'bar' has differing types:
    (file main.o type=OBJT; file ./libfoo.so type=FUNC);
    main.o definition taken

注 -  此上下文中的符号类型是可以用 ELF 表示的分类。除非编程语言以最原始的方式使用数据类型,否则这些符号类型与数据类型无关。

在类似以上示例的情况下,在可重定位目标文件与共享目标文件之间进行解析时将采用可重定位目标文件定义。或者,在两个共享目标文件之间进行解析时采用第一个定义。在弱绑定符号或全局绑定符号之间进行这种解析时,还会生成警告。

链接编辑器的 –t 选项不会抑制符号类型之间的不一致性。

致命解析

无法解析的符号冲突会导致致命错误状态,并生成相应的错误消息。此消息会指示符号名称以及提供符号的文件的名称。不会生成任何输出文件。虽然致命状态足以导致链接编辑终止,但会先完成所有输入文件处理。通过这种方式,所有致命解析错误都可识别。

当两个可重定位目标文件都定义相同名称的非弱符号时,就会出现最常见的致命错误状态。

$ cat foo.c
int bar = 1;
$ cat bar.c
int bar()
{
        return (0);
}
$ ld -r -o temp.o foo.c bar.c
ld: fatal: symbol `bar' is multiply-defined:
    (file foo.o and file bar.o);

对于符号 bar 来讲,foo.cbar.c 的定义相冲突。因为链接编辑器无法确定哪个符号优先,所以链接编辑通常会终止,并生成一条错误消息。可以使用链接编辑器的 –z muldefs 选项抑制出现此错误状态。此选项允许采用第一个符号定义。