@@ -22,12 +22,12 @@ Program Header:
22
22
>
23
23
> 一般来说,一个程序按照功能不同会分为下面这些段:
24
24
>
25
- > - $\text{ .text}$ 段,即代码段,存放汇编代码;
26
- > - $\text{ .rodata}$ 段,即只读数据段,顾名思义里面存放只读数据,通常是程序中的常量;
27
- > - $\text{ .data}$ 段,存放被初始化的可读写数据,通常保存程序中的全局变量;
28
- > - $\text{ .bss}$ 段,存放被初始化为 $0$ 的可读写数据,与 $\text{ .data}$ 段的不同之处在于我们知道它要被初始化为 $0 $ ,因此在可执行文件中只需记录这个段的大小以及所在位置即可,而不用记录里面的数据。
29
- > - $\text{ stack}$ ,即栈,用来存储程序运行过程中的局部变量,以及负责函数调用时的各种机制。它从高地址向低地址增长;
30
- > - $\text{ heap}$ ,即堆,用来支持程序** 运行过程中** 内存的** 动态分配** ,比如说你要读进来一个字符串,在你写程序的时候你也不知道它的长度究竟为多少,于是你只能在运行过程中,知道了字符串的长度之后,再在堆中给这个字符串分配内存。
25
+ > - * .text* 段,即代码段,存放汇编代码;
26
+ > - * .rodata* 段,即只读数据段,顾名思义里面存放只读数据,通常是程序中的常量;
27
+ > - * .data* 段,存放被初始化的可读写数据,通常保存程序中的全局变量;
28
+ > - * .bss* 段,存放被初始化为 $$ 0 $$ 的可读写数据,与 * .data* 段的不同之处在于我们知道它要被初始化为 $$ 0 $ $ ,因此在可执行文件中只需记录这个段的大小以及所在位置即可,而不用记录里面的数据。
29
+ > - * stack* ,即栈,用来存储程序运行过程中的局部变量,以及负责函数调用时的各种机制。它从高地址向低地址增长;
30
+ > - * heap* ,即堆,用来支持程序** 运行过程中** 内存的** 动态分配** ,比如说你要读进来一个字符串,在你写程序的时候你也不知道它的长度究竟为多少,于是你只能在运行过程中,知道了字符串的长度之后,再在堆中给这个字符串分配内存。
31
31
>
32
32
> 内存布局,也就是指这些段各自所放的位置。一种典型的内存布局如下:
33
33
>
@@ -88,19 +88,19 @@ SECTIONS
88
88
89
89
时至今日我们已经不太可能将所有代码都写在一个文件里面。在编译过程中,我们的编译器和链接器已经给每个文件都自动生成了一个内存布局。这里,我们的链接工具所要做的是最终将各个文件的内存布局装配起来生成整个程序的内存布局。
90
90
91
- 我们首先使用 ` OUTPUT_ARCH ` 指定了架构,随后使用 ` ENTRY_POINT ` 指定了 ** 入口点** 为 ` _start ` ,即程序第一条被执行的指令所在之处。在这个链接脚本中我们并未看到 ` _start ` ,回忆上一章,我们为了移除运行时环境依赖,重写了 C runtime 的入口 ` _start ` 。所以,链接脚本宣布整个程序会从那里开始运行。
91
+ 我们首先使用 * OUTPUT_ARCH* 指定了架构,随后使用 * ENTRY_POINT* 指定了 ** 入口点** 为 ` _start ` ,即程序第一条被执行的指令所在之处。在这个链接脚本中我们并未看到 ` _start ` ,回忆上一章,我们为了移除运行时环境依赖,重写了 C runtime 的入口 ` _start ` 。所以,链接脚本宣布整个程序会从那里开始运行。
92
92
93
- 链接脚本的整体写在 ` SECTION{ } ` 中,里面有多个形如 $\text{ output section: \ { input section list \} }$ 的语句,每个都描述了一个整个程序内存布局中的一个输出段 $\text{ output section}$ 是由各个文件中的哪些输入段 $\text{ input section}$ 组成的。
93
+ 链接脚本的整体写在 * SECTION{ }* 中,里面有多个形如 * output section:{ input section list } * 的语句,每个都描述了一个整个程序内存布局中的一个输出段 * output section* 是由各个文件中的哪些输入段 * input section* 组成的。
94
94
95
- 我们可以用 $* ()$ 来表示将各个文件中所有符合括号内要求的输入段放在当前的位置。而括号内,你可以直接使用段的名字,也可以包含通配符 $* $ 。
95
+ 我们可以用 $$ *() $$ 来表示将各个文件中所有符合括号内要求的输入段放在当前的位置。而括号内,你可以直接使用段的名字,也可以包含通配符 $$ * $ $ 。
96
96
97
97
单独的一个 ` . ` 为 ** 当前地址 (Location Counter)** ,可以对其赋值来从设置的地址继续向高地址放置各个段。如果不进行赋值的话,则默认各个段会紧挨着向高地址放置。将一个 ** 符号** 赋值为 ` . ` 则会记录下这个符号的地址。
98
98
99
- 到这里我们大概看懂了这个链接脚本在做些什么事情。首先是从 ` BASE_ADDRESS ` 即 ` 0x80200000 ` 开始向下放置各个段,依次是 $\text{ .text, .rodata, .data, .stack, .bss}$ 。同时我们还记录下了每个段的开头和结尾地址,如 $\text{.text}$ 段的开头、结尾地址分别就是符号 $\text{ stext, etext}$ 的地址,我们接下来会用到。
99
+ 到这里我们大概看懂了这个链接脚本在做些什么事情。首先是从 * BASE_ADDRESS* 即 ` 0x80200000 ` 开始向下放置各个段,依次是 * .text, .rodata, .data, .stack, .bss* 。同时我们还记录下了每个段的开头和结尾地址,如 $\text{.text}$ 段的开头、结尾地址分别就是符号 * stext, etext* 的地址,我们接下来会用到。
100
100
101
101
> OpenSBI 将自身放在 ` 0x80000000 ` ,完成初始化后会跳转到 ` 0x80200000 ` ,因此 ` _start ` 必须位于这个地址。` .text ` 为代码段标识,其第一个函数就是 ` _start ` 。
102
102
103
- 这里面有两个输入段与其他长的不太一样,即 $$ \text{ .text.entry,.bss.stack} $$ ,似乎编译器不会自动生成这样名字的段。事实上,它们是我们在后面自己定义的。
103
+ 这里面有两个输入段与其他长的不太一样,即 * .text.entry,.bss.stack* ,似乎编译器不会自动生成这样名字的段。事实上,它们是我们在后面自己定义的。
104
104
105
105
### 使用链接脚本
106
106
0 commit comments