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

退出打印视图

更新时间: 2014 年 7 月
 
 

System V 发行版 4(版本 1)mapfile


注 - 本附录介绍了原始的 System V 发行版 4 mapfile 语言(版本 1)。虽然此 mapfile 语法目前仍受支持,但是建议在新应用程序中使用Chapter 8, Mapfile中介绍的 mapfile语言版本 2。

链接编辑器会自动且智能地将可重定位目标文件中的输入节映射到正在创建的输出文件中的段。通过使用 –M 选项和关联的 mapfile,您可以更改链接编辑器提供的缺省映射。此外,使用 mapfile 还可以创建新段、修改属性以及提供符号版本控制信息。


注 - 使用 mapfile 选项时,您可以轻松创建不会执行的输出文件。链接编辑器可以在不使用 mapfile 选项的情况下生成正确的输出文件。

系统提供的 mapfile 样例位于 /usr/lib/ld 目录中。

mapfile 结构和语法

您可以在 mapfile 中输入以下基本类型的指令:

  • 段声明。

  • 映射指令。

  • 节到段的排序。

  • 大小符号声明。

  • 文件控制指令。

每个指令可以跨越多行,并且可以包含任意数量的空格(包括换行符),但空格后面要跟随一个分号。

通常,段声明后面跟有映射指令。您可以声明段,然后定义节成为该段的一部分所依据的标准。如果在未首先声明要映射到的段(内置段除外)的情况下输入映射指令或大小符号声明,则会为该段赋予缺省属性。此类段为隐式声明的段。

大小符号声明和文件控制指令可以出现在 mapfile 中的任何位置。

以下各节将针对每种指令类型进行介绍。对于所有语法讨论,以下表示法都适用。

  • 所有项都为固定宽度,所有冒号、分号、等号和 at (@) 符号都按原样输入。

  • 所有以斜体表示的项都是可替换的。

  • { .... }* 表示“零个或多个”。

  • { .... }+ 表示“一个或多个”。

  • [ .... ]  表示“可选”。

  • section_namessegment_names 遵守与 C 标识符相同的规则,其中将句点 (.) 视为一个字母。例如,.bss 为合法名称。

  • section_namessegment_namesfile_namessymbol_names 区分大小写。其他名称不区分大小写。

  • 除编号之前、名称或值的中间位置以外,空格或者换行符可以出现在其他任何位置。

  • 以 # 开始并以换行符结束的注释可以出现在任何允许出现空格的位置。

段声明

段声明可在输出文件中创建新段,或更改现有段的属性值。现有段可以是您先前定义的段,也可以是下面即将介绍的四个内置段之一。

段声明的语法如下:

        segment_name = {segment_attribute_value}*;

对于每个 segment_name,可以按任意顺序指定任何数量的 segment_attribute_values,但每个值都要由空格进行分隔。每个段属性只能有一个属性值。下表列出了段属性及其有效值。

表 B-1  mapfile 段属性
属性
segment_type
LOAD | NOTE | NULL | STACK
segment_flags
? [E] [N] [O] [R] [W] [X]
virtual_address
Vnumber
physical_address
Pnumber
length
Lnumber
rounding
Rnumber
alignment
Anumber

有四个内置段,其缺省属性值如下所示:

  • textLOAD?RX,未指定 virtual_addressphysical_addresslength,按 CPU 类型将 alignment 值设置为缺省值。

  • dataLOAD?RWX,未指定 virtual_addressphysical_addresslength,按 CPU 类型将 alignment 值设置为缺省值。

  • bss-已禁用,LOAD?RWX,未指定 virtual_addressphysical_addresslength,按 CPU 类型将 alignment 值设置为缺省值。

  • noteNOTE

缺省情况下,禁用 bss 段。任何类型为 SHT_NOBITS(此类型为节的唯一输入)的节都是在 data 段中捕获的。有关 SHT_NOBITS 节的完整说明,请参见Table 12–5。最简单的 bss 声明足以创建 bss 段。

        bss =;

任何 SHT_NOBITS 节都是由此段(而不是 data 段)捕获的。此段采用最简单的形式,并且使用与应用于任何其他段相同的缺省值对齐。还可以声明其他既可创建段又可为指定属性赋值的段属性。

链接编辑器的行为方式就好像在读入 mapfile 之前已经声明了这些段。请参见mapfile 选项缺省值

输入段声明时,请注意以下事项:

  • 数字可以是十六进制、十进制或八进制,所遵守的规则与 C 语言中的规则相同。

  • VPLRA 和数字之间不允许有空格。

  • segment_type 值可以是 LOADNOTENULLSTACK。如果未指定值,则缺省的段类型为 LOAD

  • segment_flags 的值包括 RWXO,分别表示可读、可写、可执行以及顺序。问号 (?) 与构成 segment_flags 值的单个标志之间不允许有空格。

  • LOAD 段的 segment_flags 值缺省为 RWX

  • 不能为 NOTE 段指定除 segment_type 以外的任何段属性值。

  • 允许有一个值为 STACKsegment_type。只能指定从 segment_flags 中选择的段访问要求。

  • 隐式声明的段采用以下缺省设置:segment_type 值为 LOADsegment_flags 值为 RWX,缺省的 virtual_addressphysical_addressalignment 值,且没有 length 限制。


    注 - 链接编辑器基于先前段的属性值计算当前段的地址和长度。
  • LOAD 段可以具有显式指定的 virtual_address 值或 physical_address 值,以及段的最大 length 值。

  • 如果某个段的 segment_flags 值为 ?,并且后面未跟任何内容,则该值缺省为不可读、不可写并且不可执行。

  • alignment 值用于计算段开头的虚拟地址。此对齐值仅影响指定了对齐的段。其他段仍采用缺省的对齐,除非更改了它们的 alignment 值。

  • 如果有任何 virtual_addressphysical_addresslength 属性值未设置,链接编辑器将在创建输出文件时计算这些值。

  • 如果没有为段指定 alignment 值,则对齐值将设置为内置的缺省值。此缺省值因 CPU 的不同而异,甚至也可能因软件修订版的不同而异。

  • 如果同时为段指定了 virtual_addressalignment 值,则 virtual_address 值优先。

  • 如果为段指定了 virtual_address 值,则程序头中的 alignment 字段将包含缺省的对齐值。

  • 如果为段设置了 rounding 值,则此段的虚拟地址将舍入为下一个符合给定值的地址。该值只影响指定了它的段。如果没有给定值,将不执行任何舍入操作。


注 - 如果指定了 virtual_address 值,段将放置在该虚拟地址处。对于系统内核,此方法可生成正确的结果。对于通过 exec(2) 启动的文件,此方法将生成错误的输出文件,因为段与其页边界的相对偏移量是错误的。

使用 ?E 标志可以创建空段。此空段没有关联的节。此段可为 LOAD 段或 NULL 段。只能为可执行文件指定空 LOAD 段。这些段必须有指定的大小和对齐方式。这些段将导致在进程启动时创建内存保留空间。空的 NULL 段可添加程序头项,后处理实用程序可以使用这些程序头项。这些段不应指定任何附加属性。LOAD 段和 NULL 段允许具有多个定义。

使用 ?N 标志可以控制是否将 ELF 头和任何程序头作为第一个可装入段的一部分包括在内。缺省情况下,ELF 头和程序头包括在第一个段内。这些头中的信息通常由运行时链接程序用在映射的映像中。使用 ?N 选项将导致从第一个段的第一个节开始计算映像的虚拟地址。

使用 ?O 标志可以控制输出文件中各节的顺序。此标志用于与编译器的 –xF 选项一起使用。使用 –xF 选项编译文件时,该文件中的每个函数将放置在与 .text 节具有相同属性的单独节中。这些节称为 .text%function_name

例如,使用 –xF 选项编译包含 main()foo()bar() 这三个函数的文件时,会生成一个可重定位的目标文件,并会将三个函数的文本放置在名为 .text%main.text%foo.text%bar 的节中。由于 –xF 选项强制实行每节一个函数,因此使用 ?O 标志控制各节的顺序实际上是控制函数的顺序。

请考虑以下用户定义的 mapfile

        text = LOAD ?RXO;
        text: .text%foo;
        text: .text%bar;
        text: .text%main;

第一个声明将 ?O 标志与缺省文本段进行关联。

如果源文件中函数定义的顺序为 mainfoobar,则最终的可执行文件所包含的函数顺序为 foobarmain

对于具有相同名称的静态函数,还必须使用文件名。?O 标志强制按 mapfile 中的要求对节进行排序。例如,如果静态函数 bar() 存在于 a.ob.o 文件中,并且要将 a.o 文件中的 bar() 函数放置在 b.o 文件中的 bar() 函数之前,则 mapfile 项将显示为:

        text: .text%bar: a.o;
        text: .text%bar: b.o;

虽然此语法允许具有以下项:

        text: .text%bar: a.o b.o;

但此项不能保证将 a.o 文件中的 bar() 函数放置在 b.o 中的 bar() 之前。由于结果不可靠,因此建议不要使用第二种格式。

映射指令

映射指令指示链接编辑器如何将输入节映射到输出段。本质上,就是指定要映射到的段,并指明节为了映射到指定的段而必须具备的属性。节为映射到特定段而必须具备的 section_attribute_values 集合称为此段的入口条件。节必须完全满足段的入口条件,才能置于输出文件的指定段中。

映射指令的语法如下:

        segment_name : {section_attribute_value}* [: {file_name}+];

对于 segment_name,可以按任意顺序指定任何数量的 section_attribute_values,其中每个值由空格进行分隔。每个节属性最多允许具有一个节属性值。您还可以通过 file_name 声明指定节必须来自某个特定的 .o 文件。下表列出了节属性及其有效值。

表 B-2  节属性
节属性
section_name
任何有效的节名称
section_type
$PROGBITS
$SYMTAB
$STRTAB
$REL
$RELA
$NOTE
$NOBITS
section_flags
? [[!]A] [[!]W] [[!]X]

输入映射指令时,请注意以下几点:

  • 最多只能从上面列出的 section_types 中选择一个 section_type。上面列出的 section_types 是内置类型。有关 section_types 的更多信息,请参见

  • section_flags 值包括 AWX,分别表示可分配、可写和可执行。如果个别标志之前加有一个叹号 (!),则链接编辑器将检查是否未设置此标志。构成 section_flags 值的问号、叹号和各个标志之间不允许有空格。

  • file_name 可以是任何形式为 *filenamearchive_name(component_name), 的合法文件名,例如,/lib/libc.a(printf.o)。链接编辑器不会检查文件名的语法。

  • 如果 file_name 的形式为 *filename,链接编辑器将决定命令行中文件的 basename(1)。此基本名称用于与指定的 file name 进行匹配。换言之,mapfile 中的 filename 只需要与命令行中文件名的最后一部分匹配即可。请参见映射示例

  • 如果在链接编辑过程中使用 –l 选项,并且 –l 选项后的库位于当前目录中,则必须在库的前面添加 ./ 或整个路径名(在 mapfile 中),以便创建匹配。

  • 对于特殊的输出段,可能显示多个指令行。例如,以下指令集是合法的:

            S1 : $PROGBITS;
            S1 : $NOBITS;

    为段输入多个映射指令行是为节属性指定多个值的唯一方法。

  • 一个节可以与多个入口条件匹配。在这种情况下,将使用 mapfile 中第一个遇到的具有该入口条件的段。例如,如果 mapfile 显示为:

            S1 : $PROGBITS;
            S2 : $PROGBITS;

    $PROGBITS 节将映射到 S1 段。

段内节的排序

使用以下表示法可以指定节在段中放置的顺序:

        segment_name | section_name1;
        segment_name | section_name2;
        segment_name | section_name3;

以上述形式命名的节将按照它们在 mapfile 中列出的顺序放置在任何未命名的节之前。

大小符号声明

使用大小符号声明,可以定义新的全局绝对符号,以代表指定段的大小(字节)。可以在目标文件中引用此符号。大小符号声明的语法如下:

        segment_name @ symbol_name;

symbol_name 可以是任何合法的 C 标识符。链接编辑器不会检查 symbol_name 的语法。

文件控制指令

使用文件控制指令,可以指定共享目标文件中有哪些版本定义在链接编辑期间可用。文件控制定义的语法如下:

        shared_object_name - version_name [ version_name .... ];

version_name 是指定 shared_object_name 中包含的版本定义名称。

映射示例

以下示例为用户定义的 mapfile。示例中左边的编号用于教学演示。实际上只有编号右边的信息会出现在 mapfile 中。

示例 B-1  用户定义的 mapfile
        1.  elephant : .data : peanuts.o *popcorn.o;
        2.  monkey : $PROGBITS ?AX;
        3.  monkey : .data;
        4.  monkey = LOAD V0x80000000 L0x4000;
        5.  donkey : .data;
        6.  donkey = ?RX A0x1000;
        7.  text = V0x80008000;

在此示例中处理四个单独的段。隐式声明的 elephant 段(第 1 行)从 peanuts.opopcorn.o 文件中接收所有 .data 节。请注意,*popcorn.o 能够匹配任何可以提供给链接编辑的 popcorn.o 文件。该文件无需位于当前目录中。另一方面,如果已将 /var/tmp/peanuts.o 提供给链接编辑,它将不会与 peanuts.o 相匹配,因为它没有 * 前缀。

隐式声明的 monkey 段(第 2 行)接收既有 $PROGBITS 属性又有可分配且可执行属性 (?AX) 的节,同时还接收所有尚未存在于 elephant 段中且名为 .data(第 3 行)的节。进入 monkey 段的 .data 节无需是 $PROGBITS 节或可分配且可执行的节,因为输入 section_typesection_flags 值的行与 section_name 值所在的行不是同一个行。

如第 2 行的 $PROGBITS“与”?AX 所示,同一行中的属性之间存在“与”的关系。如第 2 行的 $PROGBITS ?AX“或”第 3 行的 .data 所示,相同段不同行的属性之间存在“或”的关系。

在第 2 行中,monkey 段已进行如下隐式声明:segment_type 值为 LOADsegment_flags 值为 RWX,并且未指定 virtual_addressphysical_addresslengthalignment 值(使用缺省值)。在第 4 行中,monkeysegment_type 值设置为 LOAD。由于 segment_type 属性值未发生更改,因此不会发出任何警告。virtual_address 值设置为 0x80000000,最大 length 值设置为 0x4000

第 5 行隐式声明了 donkey 段。入口条件的目的是将所有 .data 节路由到此段。但是实际上,没有任何节进入此段,因为第 3 行中 monkey 的入口条件会捕获所有这些节。在第 6 行中,segment_flags 值设置为 ?RXalignment 值设置为 0x1000。由于这两个属性值都发生了更改,因此会发出警告。

第 7 行将文本段的 virtual_address 值设置为 0x80008000

为了进行说明,用户定义的 mapfile 示例设计为会发出警告。如果要更改指令的顺序以避免发出警告,请使用以下示例:

        1.  elephant : .data : peanuts.o *popcorn.o;
        4.  monkey = LOAD V0x80000000 L0x4000;
        2.  monkey : $PROGBITS ?AX;
        3.  monkey : .data;
        6.  donkey = ?RX A0x1000;
        5.  donkey : .data;
        7.  text = V0x80008000;

以下 mapfile 示例使用段内节的排序:

        1.  text = LOAD ?RXN V0xf0004000;
        2.  text | .text;
        3.  text | .rodata;
        4.  text : $PROGBITS ?A!W;
        5.  data = LOAD ?RWX R0x1000;

此示例中处理了 textdata 段。第 1 行声明 text 段的 virtual_address0xf0004000,并且没有将 ELF 头或任何程序头作为此段的地址计算的一部分包括在内。第 2 行和第 3 行启用段内节排序,并指定 .text.rodata 节是此段中的前两个节。结果是 .text 节的虚拟地址为 0xf0004000,并且 .rodata 节紧跟该地址之后。

构成 text 段的任何其他 $PROGBITS 节位于 .rodata 节之后。第 5 行声明了 data 段,并指定其虚拟地址必须从 0x1000 字节边界开始。构成 data 段的第一个节也驻留在文件映像内的 0x1000 字节边界上。

mapfile 选项缺省值

链接编辑器使用缺省的 segment_attribute_values 和对应的缺省映射指令定义了四个内置段(textdatabssnote)。虽然链接编辑器不使用实际的 mapfile 提供缺省值,但是缺省 mapfile 的模型可以帮助说明链接编辑器遇到 mapfile 时出现的情况。

以下示例说明 mapfile 在使用链接编辑器的缺省值时的表现。链接编辑器开始执行操作时,行为上好像已经读入了 mapfile。随后链接编辑器会读取 mapfile 并增大或更改缺省值。

        text = LOAD ?RX;
        text : ?A!W;
        data = LOAD ?RWX;
        data : ?AW;
        note = NOTE;
        note : $NOTE;

读入 mapfile 中的每个段声明时,都会将其与现有的段声明列表进行如下比较:

  1. 如果 mapfile 中尚不存在此段,但是存在具有相同段类型值的其他段,则在具有相同 segment_type 的所有现有段之前添加此段。

  2. 如果现有 mapfile 中没有任何段与刚读入的段的 segment_type 值相同,则按 segment_type 值添加该段以保持以下顺序:

    INTERP

    LOAD

    DYNAMIC

    NOTE

  3. 如果段的 segment_type 值为 LOAD,并且已经为这个可装入 (LOAD) 的段定义了 virtual_address 值,应将此段放置在任何没有定义 virtual_address 值或 virtual_address 值较大的可装入 (LOAD) 的段之前,但应放置在任何 virtual_address 值较小的段之后。

读入 mapfile 中的每个映射指令时,指令将添加在已经为同一个段指定的所有其他映射指令之后,但要在此段的缺省映射指令之前。

内部映射结构

映射结构是基于 ELF 的链接编辑器中最重要的数据结构之一。对应于模型缺省 mapfile 的缺省映射结构由链接编辑器使用。任何用户 mapfile 都可以增大或覆盖缺省映射结构中的特定值。

Figure B–1 说明了一个典型但某种程度上已简化的映射结构。“入口条件”框对应于缺省映射指令中的信息。“段属性描述符”框对应于缺省段声明中的信息。“输出节描述符”框提供了每段下的各个节的详细属性。节本身以循环方式显示。

图 B-1  简单的映射结构

image:简化的映射结构。

将节映射到段时,链接编辑器执行以下步骤:

  1. 读入节时,链接编辑器会检查入口条件列表以查看是否存在匹配。必须匹配所有指定的条件。

    Figure B–1 中,text 段下节的 section_type 值必须为 $PROGBITSsection_flags 值必须为 ?A!W。此节的名称无需为 .text,因为入口条件中未指定任何名称。节的 section_flag 值可以是 X,也可以是 !X,因为入口条件中未指定任何执行位。

    如果不与任何入口条件匹配,则将节放置在输出文件的末尾(位于所有其他段之后)。不会为此信息创建任何程序头项。

  2. 当节位于段下时,链接编辑器会检查此段中现有输出节描述符的列表,方式如下:

    如果节属性值与现有输出节描述符的属性值完全匹配,则将此节放置在与该输出节描述符关联的节列表的末尾。

    例如,section_name 值为 .data1section_type 值为 $PROGBITSsection_flags 值为 ?AWX 的节将进入Figure B–1 中第二个“入口条件”框,可以将其放置在 data 段中。此节与第二个“输出节描述符”框(.data1$PROGBITS?AWX)完全匹配,将被添加到与该框关联的列表的末尾。fido.orover.osam.o 中的 .data1 节说明了这一点。

    如果未找到匹配的输出节描述符,但是存在其他具有相同 section_type 的输出节描述符,则使用与节相同的属性值创建一个新的输出节描述符,并且此节与新的输出节描述符相关联。输出节描述符和节放置在节类型相同的最后一个输出节描述符之后。Figure B–1 中的 .data2 节便是按照此方式放置的。

    如果不存在其他具有所示节类型的输出节描述符,则将创建一个新的输出节描述符,并将节放置在此节中。


    注 - 如果输入节的用户定义类型值介于 SHT_LOUSERSHT_HIUSER 之间,则将其视为 $PROGBITS 节。mapfile 中没有命名此 section_type 值的方法,但是可以使用入口条件中的其他属性值规(section_flagssection_name)重定向这些节。
  3. 如果在读取所有命令行目标文件和库之后,段中没有任何节,则不会为该段生成任何程序头项。


注 - 类型为 $SYMTAB$STRTAB$REL$RELA 的输入节由链接编辑器在内部使用。引用这些节类型的指令只能将链接编辑器生成的输出节映射到段。