LOAD_SEGMENT / NOTE_SEGMENT / NULL_SEGMENT 指令
SIZE_SYMBOL 属性(仅限 LOAD_SEGMENT)
SYMBOL_SCOPE / SYMBOL_VERSION 指令
Mapfile 指令可以超出一行,并且可以具有任意数量的空格,包括换行符。
对于所有语法讨论,以下表示法都适用。
空格或换行符可以出现在除名称或值的中间位置以外的任何位置。
以井号 (#) 开始并以换行符结束的注释可以出现在任何可以出现空格的位置。链接编辑器不会解释注释,注释仅用于提供说明。
所有指令均以分号 (;) 结束。{...} 节中的最后一个分号可以省略。
所有项都为固定宽度,所有冒号 (:)、分号 (;)、赋值运算符(=、+=、-=)和花括号 {...} 都按原样输入。
所有以斜体表示的项都是可替换的。
方括号 [ ... ] 用于表示可选语法。方括号不是表达形式的一部分,不会显示在实际指令中。
名称是区分大小写的字符串。表 9-2 列出了 mapfile 中的常见名称和其他字符串。可以通过三种不同形式指定名称。
非引用
非引用名称是一个字母和数字序列。第一个字符必须是字母,后面可以没有或跟有多个字母或数字。百分号 (%)、斜杠 (/)、句点 (.) 和下划线 (_) 将视为字母。美元符号 ($) 和连字符 (-) 将视为数字。
单引号
在单引号 (') 中,名称可以包含除单引号或换行符以外的任何字符。所有字符将解释为字面字符。指定文件路径或其他包含非引用名称中所不允许的常规可打印字符的名称时,这种引用形式会很方便。
双引号
在双引号 (") 中,名称可以包含除双引号或换行符以外的任何字符。反斜杠 (\) 是转义符,其工作方式与在 C 编程语言的串文字中的使用方式类似。带有反斜杠前缀的字符将替换为其所代表的字符,如表 9-1 中所示。除了表 9-1 中所示的字符外,任何字符跟在反斜杠后都是错误的。
value 表示数字值,可以是十六进制、十进制或八进制,并遵循 C 语言的整型常数所使用的规则。所有值都是无符号整数值,对于 32 位输出目标文件是 32 位的,对于 64 位输出目标文件是 64 位的。
segment_flags 将内存访问权限指定为表 9-3 中所列一个或多个值的空格分隔列表,对应于 <sys/elf.h> 中定义的 PF_ 值。
表 9-1 双引号文本转义序列
|
表 9-2 Mapfile 中的名称和其他广泛使用的字符串
|
表 9-3 段标志
|
mapfile 中的第一个非注释、非空行应是 mapfile 版本声明。该声明确定文件其余部分使用的 mapfile 语言的版本。本手册中所探讨的 mapfile 语言为版本 2。
$mapfile_version 2
未以版本声明开始的 mapfile 假定为采用为 AT&T 的 System V Release 4 Unix (SVR4) 定义的原始 mapfile 语言。链接编辑器保留了处理这种 mapfile 的功能。附录 E 说明了其语法。
mapfile 中的行可以设置条件以只应用于特定 ELFCLASS(32 或 64 位)或计算机类型。
$if expr ... [$elif expr] ... [$else] ... $endif
条件输入表达式将计算得出一个逻辑 true 或 false 值。每个指令($if、$elif、$else 和 $endif)都单独出现在一行中。$if 和后续 $elif 行中的表达式将依序计算,直至找到计算结果为 true 的表达式。值为 false 的行后面的文本将被放弃。成功指令行后面的文本将得到正常处理。这里的文本是指不属于条件结构的任何内容。找到成功的 $if 或 $elif 并处理了其文本后,后续的 $elif 和 $else 行及其文本将被放弃。如果所有表达式都为零,并且有一个 $else,则会正常处理该 $else 之后的文本。
$if 指令的作用域不能跨越多个 mapfile。$if 指令必须由使用该 $if 指令的 mapfile 中的匹配 $endif 终止,否则链接编辑器将发出错误。
链接编辑器会维护一个内部名称表,这些名称可用在由 $if 和 $elif 计算的逻辑表达式中。启动时,该表将使用下表中适用于要创建的输出目标文件的每个名称进行初始化。
表 9-4 预定义的条件表达式名称
|
名称区分大小写,并且必须完全按照所示形式使用。例如,定义了 true,但未定义 TRUE。其中的任何名称都可以由自身用作逻辑表达式。例如:
$if _ELF64 ... $endif
该示例的计算结果为 true,并且在输出目标文件为 64 位时允许链接编辑器处理所含的文本。虽然这些逻辑表达式中不允许使用数字值,但值 1 和 0 例外,它们的计算结果分别为 true 和 false。
任何未定义的名称计算结果将为 false。使用未定义的名称 false 标记应无条件跳过的输入行是一种常见做法。
$if false ... $endif
使用下表中所示的运算符可以编写更复杂的逻辑表达式。
表 9-5 条件表达式运算符
|
表达式从左至右计算。子表达式先于外围表达式进行计算。
例如,在为 x86 平台生成 64 位目标文件时,将计算以下构造中的行。
$if _ELF64 && _x86 ... $endif
$add 指令可用于向链接编辑器的已知名称表中添加新名称。在上述示例中,定义名称 amd64 以表示 64 位 x86 目标文件可能会便于简化 $if 指令。
$if _ELF64 && _x86 $add amd64 $endif
这可用于简化上述示例。
$if amd64 ... $endif
$clear 指令是 $add 指令的逆操作。它用于从内部表中删除名称。
$clear amd64
$add 指令的作用一直持续到使用该 $add 的 mapfile 的最后,并且对于由相同链接操作中的链接编辑器处理的任何后续 mapfile 均可见。如果不希望这样,可在包含 $add 的 mapfile 的最后使用 $clear 删除该定义。
最后,$error 指令会导致链接编辑器将行中的所有剩余文本打印为致命错误,并停止链接操作。$error 指令可用于确保将目标文件移植到新计算机类型的程序员将无法在无提示的情况下生成缺少必要 mapfile 定义的错误目标文件。
$if _sparc ... $elif _x86 ... $else $error unknown machine type $endif
C 语言程序员会发现用于 mapfile 条件输入的语法与 C 预处理程序宏语言类似。这种相似性是有意为之的。不过,mapfile 条件输入指令远不如 C 预处理程序提供的指令强大,这也是有意如此的。它们只能提供支持跨平台环境中的链接操作所需的最基本的功能。
这两种语言之间存在显著差异,其中包括:
C 预处理程序可定义完整的宏语言,并且这些宏适用于源文本以及由 #if 和 #elif 预处理程序语句计算的表达式。链接编辑器 mapfile 不实现宏功能。
C 预处理程序计算的表达式涉及各种数字类型以及一组丰富的运算符。Mapfile 逻辑表达式涉及布尔值 true 和 false,以及一组有限的运算符。
C 预处理程序表达式涉及任意数字值,可能定义为宏,并使用 defined() 计算是否定义了给定宏,同时生成 true(非零)或 false(零)值。Mapfile 逻辑表达式只处理布尔值,并且直接使用名称而不含 defined() 运算。如果指定的名称存在于链接编辑器的已知名称表中,则视为 true,否则视为 false。
如果需要进行更复杂的宏处理,则应考虑使用外部宏处理程序,例如 m4(1)。
Mapfile 指令可用于指定输出目标文件的很多方面。这些指令具有通用的语法,即为属性使用名称值对,并使用 {...} 构造表示分层结构和分组。
mapfile 指令的语法以下列通用形式为基础。
最简单的形式是一个不带值的指令名称。
directive;
第二种形式是带有一个值或由空格分隔的值列表的指令名称。
directive = value...;
除了所示的 "=" 赋值运算符外,还允许使用 "+=" 和 "-=" 形式的赋值。"=" 运算符将给定指令设置为给定值或值列表。"+=" 运算符用于将右侧值添加到当前值中,"-=" 运算符用于删除值。
更复杂的指令可以处理将多个属性放在花括号 {...} 中以便将其分组为一个单元的项目。
directive [name] { attribute [directive = value]; ... } [name];
左括号 ({) 前面可以有一个名称,用于命名给定语句的结果。类似地,在右括号 (}) 后面、终止分号 (;) 之前可以带有一个或多个可选名称。这些名称用于表示所定义的项目与其他命名项目具有某种关系。
请注意,分组中的属性的格式采用与前面所述的带有值的简单指令相同的语法,即带有一个赋值运算符 (=, +=, -=) 并后跟一个值,或是一个由空格分隔的值列表,并由分号 (;) 终止。
指令可以具有属性,后者又可以具有子属性。在这种情况下,子属性也分组在嵌套的花括号 {...} 中以反映此分层结构。
directive [name] { attribute { subatribute [= value]; ... }; } [name...];
mapfile 语法对于这种嵌套所允许的深度没有限制。嵌套深度只取决于指令的要求。