Skip to content

LA32R汇编编程简介

进程虚拟地址空间布局规范

通常来说,进程虚拟地址空间的布局是由指令系统、工具链和操作系统配合在一起共同决定的。ABI规范中并不会限定进程地址空间的布局规则。不过,为了让读者更好地了解这部分内容,我们将着重介绍Linux/LA32R中所采用的进程虚拟地址空间布局,并在此基础之上简要讨论一下嵌入式设备中常见的裸金属执行环境下的进程虚拟地址空间管理。

Linux系统下进程虚拟地址空间

大多数指令架构在Linux系统下一个进程可见的内存地址空间布局都是简单划分为用户空间(user space)和内核空间(kernel space)。以32位指令架构为例,X86-32在Linux系统下每个进程的4GB虚地址空间被大致划分为0x0 ~ 0xBFFFFFFF(3GB)的用户空间和0xC0000000 ~ 0xFFFFFFFF(1GB)的内核空间,而MIPS32在Linux系统下每个进程的4GB虚地址空间被大致划分为0x0 ~ 0x7FFFFFFF(2GB)的用户空间和0x80000000 ~ 0xFFFFFFFF(2GB)的内核空间。

对于LA32R来说,其在Linux系统下每个进程的4GB虚地址空间被大致划分为0x0 ~ 0x7FFFFFFF(2GB)的用户空间和0x80000000 ~ 0xFFFFFFFF(2GB)的内核空间。其中0x80000000以上的地址空间是所有进程共享的,其中通过直接映射窗口(Direct Mapping Window)配置出0x80000000 ~ 0x9FFFFFFF0xA0000000 ~ 0xBFFFFFFF两个各512MB大小、存储访问类型分别是强序非缓存和一致可缓存的直接映射区域,供内核高效率地访问系统中最低512MB物理地址空间。

LA32R进程地址空间分布

我们这里将主要讨论Linux系统下加载一个应用程序后的地址空间布局,所以我们将主要关注4GB虚拟地址空间下的用户空间部分。在Linux系统下,可执行程序采用ELF(Executable and Linkable Format)格式。在ELF文件中包含着一系列不同的段(section),不同的段用于存放不同类型的内容,其中最常见的段有:

  • .text段:用于保存程序中的代码片段。
  • .data段:用于保存已经初始化的全局变量和静态局部变量。
  • .bss段:用于保存未初始化的全局变量和静态局部变量。
  • .rodata段:用于保存只读的变量。

工具链在编译链接应用程序时,会按照默认配置的链接参数为上述各段设置好地址。运行程序时操作系统中的装载器将根据程序文件中记录的各段的内存地址信息,把代码和数据装入相应的虚拟内存地址。其中.text段会被放在最靠近0地址的位置(但是这个位置不会是0地址,地址0在多数操作系统中都会被设置为不可访问的地址,以便捕获空指针访问),然后向高地址依次摆放.rodata段、.data段、.bss段。装载器将ELF文件.text段中存放的代码,.rodata段数据和.data段变量的初始值复制到内存中。ELF文件中的.bss段中只记录各变量的大小,由装载器直接为变量分配所需的内存空间,然后清零。.bss段之上是堆(heap)空间。堆用于管理程序运行过程中动态分配的内存,如C程序中用malloc函数分配的内存就是放在堆空间进行管理的。其大体从低地址向高地址增长,不过堆的分配和释放行为比较复杂,并不是简单的超一个方向增长。从接近用户空间最高可访问地址向下的一段空间被用作该进程的栈。栈沿着高地址向低地址方向增长,它用作函数的临时工作空间,存储C程序中的非静态局部变量、子函数参数和返回地址等函数执行完毕就可以抛弃的数据。堆和栈之间的地址空间通常用来存放程序所用到的动态链接库,其具体地址由动态链接器在这段空间中寻找出合适的地址。

进程中用户地址空间分布

裸金属执行环境下的进程虚拟地址空间管理

嵌入式设备中常使用裸金属执行环境。此时设备上并不会运行操作系统,所有的软件功能实现在一个进程之中。由于裸金属执行环境的软件通常也是用汇编语言和C语言开发,那么自然也需要用工具链进行编译和链接。由于没有操作系统支持基于页表映射地址翻译模式,裸金属执行环境中通常采用直接地址映射翻译模式1。在此种地址映射方式下,物理地址和虚地址的最低28位是相同的。对于一个小型的嵌入式设备而言,其物理地址空间范围可能都不超过228字节,而其中能够存放代码、能放置栈空间地址区域都是有限的,且地址分布未必和Linux系统下用户进程存放.text段、栈的地址分布一致。因此,在编译链接裸金属执行环境时,通常都需要重新指定各类链接参数。譬如,chiplab项目中运行my_program的裸金属执行环境在编译时,就使用了chiplab/toolchains/system_newlib/pmon.ld作为新链接脚本。感兴趣的读者可以结合这个脚本学习体会一下。其实前面提到在Linux系统下链接器使用的默认配置的链接参数也是通过链接脚本控制的。以LA32R GCC交叉编译工具链为例,这个默认的链接脚本是loongarch32r-linux-gnusf/lib/ldscripts/elf32loongarch.x文件。


  1. 除了复位结束后的一小段代码,我们通常不建议软件运行在直接地址翻译模式下。