当前位置: 首页 > 工具软件 > GNU DDD > 使用案例 >

Hexagon Binutils GNU 手册(4)

漆雕深
2023-12-01

2.4 Sections and relocation

汇编语言程序的结构是以被称为节的单元为单位的。各部分被组装和连接在一起,形成可执行的程序。构成程序的各部分可以存储在一个单一的源文件中,也可以作为一个单独组装的文件集合来维护。
粗略地说,一个部分是一个地址范围,没有空隙;在这个地址范围内存储的所有数据对于某些特定的目的都是一样的。例如,下面的源代码定义了一个 "只读 “数据段:

.Ltext0:
.LC0:
.section     .data
.p2align     3
.string      "hello, world\n"
.string      "stddef.h"
...

链接器读取许多对象文件(部分程序),并将它们的内容结合起来,形成一个可运行的程序。当汇编器生成一个对象文件时,部分程序被假定从地址0开始。链接器为部分程序分配最终地址,这样不同的部分程序就不会重叠了。这实际上是一种过分的简化,但它足以解释汇编器如何使用部分程序。
链接器将你的程序的字节块移动到它们的运行时地址。这些块是作为刚性单位移动的:它们的长度不会改变,其中的字节顺序也不会改变。这样的一个刚性单元被称为一个节。将运行时地址分配给区块称为重定位。它包括调整对对象文件地址的引用,使它们指向正确的运行时地址。
一个由汇编器编写的对象文件至少有三个部分,其中任何部分都可以是空的。这些部分被命名为文本、数据和bss部分。
在对象文件中,文本部分从地址0开始,数据部分在文本部分之后,而bss部分在数据部分之后。
为了告知链接器在重新定位部分时哪些数据发生了变化,以及如何改变这些数据,汇编器还将所需的重新定位细节写入对象文件。为了执行重定位,链接器需要对对象文件中每次出现的地址引用提供以下信息:
地址引用在对象文件中的位置;
引用的长度(字节数;
该地址所引用的部分;
(地址)-(部分的起始地址)的数字值;
该引用是否与程序计数器(PC)有关;
事实上,汇编器所使用的每一个地址都被表达为:

(section) + (offset into section)

此外,大多数的表达式在汇编器中都有这种与节相关的性质。
注意 符号{secname N}是用来指定 “偏移N到secname部分”。
节的概念被扩展到未定义的节。任何在装配时节段未知的地址都被定义为{undefined U},其中U在以后被填入一个运行时地址。由于数字总是被定义的,产生未定义地址的唯一方法是引用一个未定义的符号。对一个命名的公共块的引用就是这样一个符号:它的值在装配时是未知的,所以它有未定义的部分。
注意 术语section通常用来描述链接程序中的部分组。链接器将所有部分程序的文本部分分配给链接程序中的连续地址。习惯上是指一个程序的文本部分,即所有部分程序的文本部分的所有地址。同样的术语惯例也用于数据和bss部分。
有些部分是由链接器操作的;其他部分只为汇编器使用而定义,除了在汇编期间没有任何意义。

2.4.1 Code and data sections

汇编器定义了包含程序代码和数据的标准部分。表2-1列出了标准的代码和数据部分:
Table 2-1 Code and data sections

Section NameDescription
.text.data.rodata这些部分包含了程序代码和数据。汇编器和链接器将这些部分视为功能上的等同。然而,当一个程序在运行时,通常文本部分是不可更改的。.text部分包含程序代码,运行中的程序的.data部分通常是可以改变的:例如,C变量通常存储在data部分。.rodata指定了一个只读的数据部分(用于存储常量数据)。额外的文本和数据部分是预定义的,直接引用Hexagon处理器特定的存储器(第2.4.4节),或者是用户定义的(第2.4.5节)。
.bss当你的程序开始运行时,这部分包含归零的字节。它被用来存储未初始化的变量或公共数据。每个部分程序的bss部分的长度是很重要的,但由于该部分一开始就包含归零字节,所以没有必要在对象文件中存储显式的零字节。bss部分是用来消除对象文件中的显式零。

图2-2显示了一个高层次的例子,说明两个部分程序的部分是如何在内存中重新定位的。内存地址被定义为位于水平轴上:

partial program #1:
partial program #2:
linked program:
    addresses:
+-----+----+--+
|ttttt|dddd|00|
+-----+----+--+
text   data bss
+---+---+---+
|TTT|DDD|000|
+---+---+---+
text data bss
+--+---+-----+--+----+---+-----+~~
|  |TTT|ttttt|  |dddd|DDD|00000|
+--+---+-----+--+----+---+-----+~~
0 ...

图2-2 节段类型重定位
注意 如果在源代码中没有指定章节,汇编器会自动将程序代码和数据分配到表2-1所列的章节中。
将程序代码和数据分配给表2-1中所列的章节。

2.4.2 Block storage section

块存储部分(缩写为 “bss”)用于本地公共变量的存储。你可以在bss部分分配地址空间,但你不能在程序执行前支配数据加载到其中。当你的程序开始运行时,bss部分的所有内容都是零字节。
上面的表2-1定义了bss部分。
.lcommon伪操作定义了bss部分的一个符号;见第2.6.41节。
.common伪操作可以用来声明一个公共符号,这是另一种形式的未初始化符号;见第2.6.12节。
另外,你可以用.section指令切换到bss部分
(第2.6.58节),并以通常的方式在该节中定义符号(尽管bss符号只能被赋值为零)。通常,bss部分只包含符号定义和.skip指令(第2.6.64节)。

2.4.3 Global sections

汇编器包括预定义的部分,用于将程序数据分配到全局数据区。
表2-2列出了全局数据区的预定义部分的名称。
Table 2-2 Processor global data sections

Section NameDescription
.sdata全局.数据部分(2.4.1节)
.sbss全局.bss部分(2.4.2节)。

2.4.4 Hexagon processor memory sections

汇编器包括预定义的文本和数据部分,用于将程序代码或数据分配给以下Hexagon处理器专用(或MSM专用)存储器:
外部总线接口(EBI)
紧密耦合的存储器(TCM)
堆栈式存储器接口(SMI)
除了指定存储器外,预定义部分还指定了存储代码/数据的缓存属性。
表2-3列出了Hexagon处理器专用存储器的预定义部分名称。
Table 2-3 Hexagon processor-specific memory sections

Section NameDescription
.ebi_code_cachedCode
.tcm_code_cachedCode
.smi_code_cachedCode
.ebi_data_cachedData (cached)
.tcm_data_cachedData (cached)
.smi_data_cachedData (cached)
.ebi_data_cached_wtData (write-through cached)
.tcm_data_cached_wtData (write-through cached)
.smi_data_cached_wtData (write-through cached)
.ebi_data_uncachedData (uncached)
.tcm_data_uncachedData (uncached)
.smi_data_uncachedData (uncached)

列入:

.section .tcm_code_cached
   .section .ebi_data_cached_wt

关于 Hexagon 处理器内存和内存缓存的更多信息,请参阅 Hexagon 程序员参考手册。
注意 当它们被使用时,Hexagon处理器内存部分必须被分配到特定的内存区域 - 这是用链接器完成的。更多信息见第 2.4.6 节。
程序代码和数据不能存储在同一区。

2.4.5 User-defined sections

用户定义的部分用于在非标准的内存配置中存储代码或数据。例如,一个程序可以定义一个专门的数据区来访问内存映射的硬件设备。
用户定义的区段是通过指定不同于预定义区段名称(表2-1至表2-3)而创建的。比如说:

.section my_data

The type of a user-defined section (code, data, read-only data: see Section 2.4.1) is automatically determined by the objects stored in the section. Table 2-4 shows the mapping.
Table 2-4 User-defined sections

Section ContentsSection Type
Code.text
Data.data
Literals or strings.rodata

用户定义的部分的类型通常包含在部分名称中,在名称前加上相应的预定义部分名称。例如。
.section .data.my_data
.section .text.my_code
.section .rodata.my_strings
注意 在默认情况下,链接器将所有具有类似类型的部分分配到链接程序中的连续地址。这种行为可以通过创建一个链接器脚本来改变(第3.3节)。

2.4.6 Section memory assignment

当它们被使用时,Hexagon处理器内存部分(第2.4.4节)必须被分配到特定的内存区域–这是用链接器完成的。
所有其他部分(包括任何用户定义的部分)在连接过程中被自动分配到EBI存储器。
注意 关于部分内存分配的更多信息见第3.4节。

2.4.7 Sub-sections

汇编的字节通常被分配到两个部分中的一个:代码或数据。一个程序可能包含独立的数据组,这些数据组在汇编源中并不连续,但你希望它们在目标文件中彼此相邻。汇编器允许你使用分段来达到这个目的。每个小节可以包含一个或多个编号的小节,小节编号范围从0到8192。一个被装配到特定子段的对象被分配到对象文件中的一个位置,这个位置与程序中所有被分配到同一子段的其他对象相邻。
例如,一个程序员可能希望将所有的程序常量存储在程序的文本部分,但不希望常量与正在汇编的程序代码混在一起。在这种情况下,分节指令’.text 0’可以出现在每一段被输出的代码之前,而分节指令’.text 1’则出现在每一组常量之前。
分节是可选的。如果你不使用分节,所有的项目都被默认为分节号为0。
每个小节都是零填充的,最多是四个字节的倍数。(在不同类型的汇编器上,子段的填充量可能不同)。
子段在对象文件中以数字顺序出现,从低到高。(所有这些都是为了与其他人的汇编器兼容。)注意,一个对象文件不包含子段的表示;链接器和其他操作对象文件的程序看不到它们的痕迹。他们只是把所有的文本子段看作一个文本部分,把所有的数据子段看作一个数据部分。
要指定你想把后续的语句组合到哪个小节中,请在".text expression “或”.data expression "语句中使用数字参数来指定。表达式参数指定的是一个绝对表达式(第2.3.8节)。如果你只指定了.text,那么就会假定’.text 0’。同样地,.data意味着’.data 0’。汇编从文本地址0开始。比如说:

.text 0     # The default subsection is text 0.
   .ascii "This lives in the first text subsection. *"
   .text 1
   .ascii "But this lives in the second text subsection."
   .data 0
   .ascii "This lives in the data section,"
   .ascii "in the first data subsection."
   .text 0
   .ascii "This lives in the first text section,"
   .ascii "immediately following the asterisk (*)."

每一节都有一个位置计数器,每组装到该节的一个字节都会递增一个。因为分段仅仅是限制在汇编器中的一种便利,所以没有分段位置计数器的概念。没有办法直接操作位置计数器;然而,.align指令可以改变它,任何标签定义都可以捕获它的当前值。正在汇编语句的那一节的位置计数器被称为活动位置计数器。

2.4.8 Assembler internal sections

这些部分仅用于汇编器的内部使用。它们在运行时没有任何意义。在大多数情况下,你并不需要了解这些部分;但它们可以在汇编器的警告信息中被提及,所以了解它们对汇编器的意义可能会有帮助。这些部分是用来允许你的汇编语言程序中的每个表达式的值都是一个与部分有关的地址。
ASSEMBLER-INTERNAL-LOGIC-ERROR!
发现了一个内部汇编器逻辑错误。这意味着在汇编器中存在着一个错误。
expr section
汇编器在内部将复杂的表达式存储为符号的组合。当它需要将一个表达式表示为一个符号时,它将其放在expr部分。

 类似资料: