除输入文件中提供的符号外,还可以为链接编辑提供其他符号引用或定义。使用链接编辑器的 -u 选项可以生成符号引用的最简单形式。链接编辑器的 -M 选项和关联的 mapfile 可以提供更大的灵活性。使用此 mapfile,可以定义符号引用和各种符号定义。
-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。
此简单示例假定 lib1.a 中的可重定位目标文件 foo.o 既没有直接引用也没有间接引用符号 bar。如果 lib1.a 引用 bar,则在处理 lib1.a 期间还会从中提取可重定位目标文件 bar.o。有关链接编辑器处理归档多遍的论述,请参见归档处理。
可以使用链接编辑器的 -M 选项和关联的 mapfile 提供一组更丰富的符号定义。符号定义 mapfile 的项使用以下语法。
[ name ] { scope: symbol [ = [ type ] [ value ] [ size ] [ attribute ] ]; } [ dependency ]; |
此组符号定义的标签(如果存在)标识映像中的版本定义。 请参见第 5 章,应用程序二进制接口与版本控制。
指示要生成的输出文件中符号绑定的可见性。在链接编辑过程中,使用 mapfile 定义的所有符号都将视为全局符号。将针对任何其他具有相同名称的符号(从所有输入文件中获取)解析这些符号。以下定义和别名定义在要创建的目标文件中符号的可见性:
此范围的符号对所有外部目标文件都可见。在运行时绑定从目标文件内部对这种符号的引用,从而允许进行插入。
此范围的符号对所有外部目标文件都可见。在链接编辑时绑定从目标文件内部对这种符号的引用,从而防止在运行时插入。此范围定义与具有 STV_PROTECTED 可见性的符号产生相同的效果。 请参见表 7–20。
此范围的符号缩减为具有本地绑定的符号。此范围的符号对其他外部目标文件不可见。此范围定义与具有 STV_HIDDEN 可见性的符号产生相同的效果。 请参见表 7–20。
此范围的符号是 hidden。删除这些符号的符号表项。
所需符号的名称。如果该名称未后跟符号属性 type、value、size 或 extern 之一,则将创建符号引用。此引用与本节前面所述的使用 -u 选项生成的引用完全相同。如果符号名称后跟任何符号属性,则将使用关联的属性生成符号定义。
当符号属于 local 范围时,可以将此符号名称定义为特殊自动缩减指令 "*"。此指令将任何 mapfile 中未显式定义为 global 的所有全局符号降级为要生成的动态库中的本地绑定。
指示符号类型属性。此属性可以为 data、function 或 COMMON。前两种类型的属性会产生绝对符号定义。 请参见符号表节。后一种类型的属性会产生暂定符号定义。
指示值属性。此属性的格式为 Vnumber。
指示大小属性。此属性的格式为 Snumber。
此关键字提供符号的其他属性:
指示在外部对要创建的目标文件定义符号。 此属性可以与 DIRECT 或 NODIRECT 属性配合使用,以建立单独的直接或非直接引用。还可以使用此选项抑制将使用 -z defs 选项标记的未定义符号。
指示应直接绑定到此符号。此属性可以与 EXTERN 属性配合使用以控制绑定到外部符号。 请参见直接绑定。
指示不应直接绑定到此符号。此状态适用于要创建的目标文件中的引用或外部引用中的引用。此属性可以与 EXTERN 属性配合使用以控制绑定到外部符号。 请参见直接绑定。
指示此符号在共享库 name 中是一个过滤器。 请参见生成标准过滤器。过滤器符号不需要输入可重定位目标文件提供任何后备实现。因此,使用此指令并定义符号的类型可以创建绝对符号表项。
指示此符号在共享库 name 中是一个辅助过滤器。 请参见生成辅助过滤器。
表示此定义继承的版本定义。 请参见第 5 章,应用程序二进制接口与版本控制。
如果指定了版本定义或自动缩减指令,则将在创建的映像中记录版本控制信息。如果此映像是可执行文件或共享库,则还会应用任何符号缩减。
如果要创建的映像是可重定位目标文件,则缺省情况下不会应用符号缩减。在这种情况下,任何符号缩减都将记录在版本控制信息中。当最终使用可重定位目标文件来生成可执行文件或共享库时,将应用这些符号缩减。在生成可重定位目标文件时,可以使用链接编辑器的 -B reduce 选项强制执行符号缩减。
第 5 章,应用程序二进制接口与版本控制中提供了版本控制信息的更详细说明。
为了确保接口定义的稳定性,定义符号名称时不提供通配符扩展功能。
以下各小节提供了多个使用 mapfile 语法的示例。
以下示例说明如何定义三种符号引用。然后,使用这些引用提取归档成员。虽然可以通过对链接编辑指定多个 -u 选项来实现归档提取,但此示例还说明了如何将符号的最终范围缩减到局部。
$ cat foo.c foo() { (void) printf("foo: called from lib.a\n"); } $ cat bar.c bar() { (void) printf("bar: called from lib.a\n"); } $ cat main.c extern void foo(), bar(); main() { foo(); bar(); } $ ar -rc lib.a foo.o bar.o main.o $ cat mapfile { local: foo; bar; global: main; }; $ cc -o prog -M mapfile lib.a $ prog foo: called from lib.a bar: called from lib.a $ nm -x prog | egrep "main$|foo$|bar$" [28] |0x00010604|0x00000024|FUNC |LOCL |0x0 |7 |foo [30] |0x00010628|0x00000024|FUNC |LOCL |0x0 |7 |bar [49] |0x0001064c|0x00000024|FUNC |GLOB |0x0 |7 |main |
在缩减符号范围一节中更详细地说明了将符号范围从全局缩减为局部的重要性。
以下示例说明如何定义两种绝对符号定义。然后,使用这些定义解析输入文件 main.c 中的引用。
$ cat main.c extern int foo(); extern int bar; main() { (void) printf("&foo = %x\n", &foo); (void) printf("&bar = %x\n", &bar); } $ cat mapfile { global: foo = FUNCTION V0x400; bar = DATA V0x800; }; $ cc -o prog -M mapfile main.c $ prog &foo = 400 &bar = 800 $ nm -x prog | egrep "foo$|bar$" [37] |0x00000800|0x00000000|OBJT |GLOB |0x0 |ABS |bar [42] |0x00000400|0x00000000|FUNC |GLOB |0x0 |ABS |foo |
从输入文件获取函数或数据项的符号定义时,这些符号定义通常与数据存储元素关联。mapfile 定义不足以构造此数据存储,因此,这些符号必须保持为绝对值。如果在共享库中定义符号,则应避免绝对定义。 请参见扩充符号定义。
还可以使用 mapfile 定义 COMMON 或暂定符号。与其他类型的符号定义不同,暂定符号在文件中不占用存储空间,而定义在运行时必须分配的存储空间。因此,定义此类型的符号有助于要生成的输出文件的存储分配。
暂定符号与其他类型的符号的一个不同特征在于,暂定符号的 value 属性指示其对齐要求。因此,可以使用 mapfile 定义重新对齐从链接编辑的输入文件中获取的暂定定义。
以下示例给出了两个暂定符号的定义。符号 foo 定义新的存储区域,而符号 bar 实际上用于更改文件 main.c 中相同暂定定义的对齐方式。
$ cat main.c extern int foo; int bar[0x10]; main() { (void) printf("&foo = %x\n", &foo); (void) printf("&bar = %x\n", &bar); } $ cat mapfile { global: foo = COMMON V0x4 S0x200; bar = COMMON V0x100 S0x40; }; $ cc -o prog -M mapfile main.c ld: warning: symbol `bar' has differing alignments: (file mapfile value=0x100; file main.o value=0x4); largest value applied $ prog &foo = 20940 &bar = 20900 $ nm -x prog | egrep "foo$|bar$" [37] |0x00020900|0x00000040|OBJT |GLOB |0x0 |16 |bar [42] |0x00020940|0x00000200|OBJT |GLOB |0x0 |16 |foo |
使用链接编辑器的 -t 选项可以不诊断此符号解析。
应避免在共享库中创建绝对数据符号。从动态可执行文件对共享库中数据项的外部引用通常需要创建复制重定位。 请参见复制重定位。要提供此重定位,应该在目标文件中定义数据项,以便将符号定义与数据存储关联。
可以过滤数据符号。 请参见作为过滤器的共享库。要提供此过滤,可以使用 mapfile 定义扩充目标文件定义。以下示例创建包含函数和数据定义的过滤器。虽然可以在 mapfile 中显式创建函数定义,但数据定义将扩充输入可重定位目标文件提供的定义。
$ cat bar.c int bar = 0; $ cat mapfile { global: foo = FUNCTION FILTER filtee.so.1; bar = FILTER filtee.so.1; local: *; }; $ cc -o filter.so.1 -G -Kpic -h filter.so.1 -M mapfile -R. bar.c $ nm -x filter.so.1 | egrep "foo|bar" [39] |0x000102b0|0x00000004|OBJT |GLOB |0 |12 |bar [45] |0x00000000|0x00000000|FUNC |GLOB |0 |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 中的定义。