除输入文件中提供的符号外,还可以为链接编辑提供其他全局符号引用或全局符号定义。生成符号引用的最简单形式是使用链接编辑器的 –u 选项。使用链接编辑器的 –M 选项和关联的 mapfile 的灵活性更大。使用此 mapfile,可以定义全局符号引用和各种全局符号定义。可以指定符号可见性和类型之类的符号属性。有关可用选项的完整说明,请参见SYMBOL_SCOPE / SYMBOL_VERSION 指令。
–u 选项提供了一种在链接编辑命令行中生成全局符号引用的机制。可以使用此选项完全从归档执行链接编辑。选择要从多个归档中提取的目标文件时,此选项还可以提供更多灵活性。有关归档提取的概述,请参见归档处理一节。
例如,可能要从可重定位目标文件 main.o 生成动态可执行文件(此目标文件引用了符号 foo 和 bar)。您希望从 lib1.a 中包含的可重定位目标文件 foo.o 获取符号定义 foo,并从 lib2.a 中包含的可重定位目标文件 bar.o 获取符号定义 bar。
但是,归档 lib1.a 中也包含定义符号 bar 的可重定位目标文件。此可重定位目标文件的功能可能与 lib2.a 中提供的可重定位目标文件不同。要指定需要的归档提取,可以使用以下链接编辑。
$ cc -o prog -L. -u foo -l1 main.o -l2
–u 选项生成对符号 foo 的引用。此引用将导致从归档 lib1.a 中提取可重定位目标文件 foo.o。对符号 bar 的第一次引用出现在 main.o 中,这是在处理了 lib1.a 之后遇到的。因此,将从归档 lib2.a 获取可重定位目标文件 bar.o。
以下示例说明如何定义三种符号引用。然后,使用这些引用提取归档成员。虽然可以通过对链接编辑指定多个 –u 选项来实现归档提取,但此示例还说明了如何将符号的最终作用域缩减到局部。
$ cat foo.c #include <stdio.h> void foo() { (void) printf("foo: called from lib.a\n"); } $ cat bar.c #include <stdio.h> void bar() { (void) printf("bar: called from lib.a\n"); } $ cat main.c extern void foo(), bar(); void main() { foo(); bar(); } $ cc -c foo.c bar.c main.c $ ar -rc lib.a foo.o bar.o main.o $ cat mapfile $mapfile_version 2 SYMBOL_SCOPE { local: foo; bar; global: main; }; $ cc -o prog -M mapfile lib.a $ prog foo: called from lib.a bar: called from lib.a $ elfdump -sN.symtab prog | egrep 'main$|foo$|bar$' [29] 0x10f30 0x24 FUNC LOCL H 0 .text bar [30] 0x10ef8 0x24 FUNC LOCL H 0 .text foo [55] 0x10f68 0x24 FUNC GLOB D 0 .text main
在缩减符号作用域一节中更详细地说明了将符号作用域从全局缩减为局部的重要性。
以下示例说明如何定义两种绝对符号定义。然后,使用这些定义解析输入文件 main.c 中的引用。
$ cat main.c #include <stdio.h> extern int foo(); extern int bar; void main() { (void) printf("&foo = 0x%p\n", &foo); (void) printf("&bar = 0x%p\n", &bar); } $ cat mapfile $mapfile_version 2 SYMBOL_SCOPE { global: foo { TYPE=FUNCTION; VALUE=0x400 }; bar { TYPE=DATA; VALUE=0x800 }; }; $ cc -o prog -M mapfile main.c $ prog &foo = 0x400 &bar = 0x800 $ elfdump -sN.symtab prog | egrep 'foo$|bar$' [45] 0x800 0 OBJT GLOB D 0 ABS bar [69] 0x400 0 FUNC GLOB D 0 ABS foo
从输入文件获取函数或数据项的符号定义时,这些符号定义通常与数据存储元素关联。mapfile 定义不足以构造此数据存储,因此,这些符号必须保持为绝对值。与 size 关联、但无 value 的简单 mapfile 定义会导致创建数据存储。这种情况下,符号定义将带有节索引。但是,带有 value 的 mapfile 定义会导致创建绝对符号。如果在共享目标文件中定义符号,应当避免绝对定义。请参见扩充符号定义。
还可以使用 mapfile 来定义 COMMON(即暂定)符号。与其他类型的符号定义不同,暂定符号在文件中不占用存储空间,而定义在运行时必须分配的存储空间。因此,定义此类型的符号有助于要生成的输出文件的存储分配。
暂定符号与其他符号类型的一个不同特性在于,暂定符号的 value 属性会指示其对齐要求。因此,可以使用 mapfile 定义重新对齐从链接编辑的输入文件中获取的暂定定义。
以下示例给出了两个暂定符号的定义。符号 foo 定义新的存储区域,而符号 bar 实际上用于更改文件 main.c 中相同暂定定义的对齐方式。
$ cat main.c #include <stdio.h> extern int foo; int bar[0x10]; void main() { (void) printf("&foo = 0x%p\n", &foo); (void) printf("&bar = 0x%p\n", &bar); } $ cat mapfile $mapfile_version 2 SYMBOL_SCOPE { global: foo { TYPE=COMMON; VALUE=0x4; SIZE=0x200 }; bar { TYPE=COMMON; VALUE=0x102; SIZE=0x40 }; }; $ cc -o prog -M mapfile main.c ld: warning: symbol 'bar' has differing alignments: (file mapfile value=0x102; file main.o value=0x4); largest value applied $ prog &foo = 0x21264 &bar = 0x21224 $ elfdump -sN.symtab prog | egrep 'foo$|bar$' [45] 0x21224 0x40 OBJT GLOB D 0 .bss bar [69] 0x21264 0x200 OBJT GLOB D 0 .bss foo
应避免在共享目标文件中创建绝对数据符号。从动态可执行文件对共享目标文件中数据项的外部引用通常需要创建复制重定位。请参见复制重定位。要提供此重定位,应当将数据项与数据存储关联。可通过在可重定位目标文件中定义符号来生成此关联。也可以通过在 mapfile 中定义符号并使用 size 声明和 no value 声明来生成此关联。请参见SYMBOL_SCOPE / SYMBOL_VERSION 指令。
可以过滤数据符号。请参见作为过滤器的共享目标文件。要提供此过滤,可以通过 mapfile 定义扩充目标文件定义。以下示例创建包含函数和数据定义的过滤器。
$ cat mapfile $mapfile_version 2 SYMBOL_SCOPE { global: foo { TYPE=FUNCTION; FILTER=filtee.so.1 }; bar { TYPE=DATA; SIZE=0x4; FILTER=filtee.so.1 }; local: *; }; $ cc -o filter.so.1 -G -Kpic -h filter.so.1 -M mapfile -R. $ elfdump -sN.dynsym filter.so.1 | egrep 'foo|bar' [1] 0x105f8 0x4 OBJT GLOB D 1 .data bar [7] 0 0 FUNC GLOB D 1 ABS foo $ elfdump -y filter.so.1 | egrep 'foo|bar' [1] F [0] filtee.so.1 bar [7] F [0] filtee.so.1 foo
在运行时,会将从外部目标文件到以上任一符号的引用解析为 filtee 中的定义。