基本定义
LMA: LOAD MEMORY ADDRESS,加载地址
VMA: VIRTUAL MEMORY ADDRESS,虚拟地址
VMA 是运行时的地址,LMA是程序被加载时的地址。但是什么是被加载,为何需要区分这两个,链接程序究竟做了什么?在本文中,我记录我对这个问题的探索。
什么是加载?
加载意义不复杂,就是将二进制从文件,复制到指定的内存区域。但是,在嵌入式领域,将文件加载到嵌入式开发板上,也是一种加载。原因很简单,很多嵌入式开发板根本没有所谓的“磁盘”,那么程序保存的位置就只能是在内存中了。
为何需要区分两个地址?
为了明白为何需要区分这两个地址,需要先理解ROM和RAM。
ROM: 标准定义是只读存储器,一般认为它的数据不可修改、不会断电丢失。
RAM: 标准定义是随机访问存储器,一般认为它的数据可修改、会断电丢失。
我遇到LMA和VMA是在链接脚本中设置data段时,所以后面我将会基于嵌入式开发的data段进行原因分析。
可变全局变量,为了可以修改,执行时必须要存在 RAM 中。对于未初始化的可变全局变量所在的 RAM 空间就是直接写0。但是对于已初始化的可变全局变量则必须要记录其初始值,但是显然不可能在RAM内存中记录,只能在ROM中记录。
嵌入式设备一般是将 ROM 和RAM 提供统一的地址空间,但 ROM 和 RAM 有各自的地址范围。所以为了在加载时可变全局变量能够被加载到ROM中,执行时是在RAM中。这就出现了区分两个地址的需求了。
这个链接程序究竟做了什么?
对于LMA和VMA一样的情况,此处就不讨论。
对于LMA和VMA不一样的情况,根据测试观察。
链接程序在做地址计算分析时,全部采取了VMA。例如,获取一个全局可变变量的地址,其地址是基于VMA进行计算的,而非LMA。在执行加载时,则会将段的内容和数据写到LMA处。
具体细节:
- elf文件的program header table处,会有条目是将文件中的数据,加载到物理地址LMA,虚拟地址为VMA处。这个是elf文件加载程序实现的。
- 一般用户程序物理地址是直接忽略的。
- 嵌入式烧录程序,如果是烧录的是hex文件。一般而言,hex中的数据已经是根据program header table 变化后的数据了,烧录程序只需要根据hex中的地址写入数据到内存中即可。
- 嵌入式一般是缺少MMU的,所有VMA其实还是物理地址,烧录程序或hex转换程序会根据program header table 将程序写入到指定的物理地址。但是如果VMA和物理地址不一样,还需要在代码中手动执将数据从物理地址LMA处读取并写入到VMA地址中。
总结: 链接程序做的事情就是根据链接脚本,在program header table 中,创建相关的LOAD 条目。剩下的实际加载到指定的LMA,是由elf加载程序或烧录程序实现的。如果是嵌入式设备,LMA和VMA不一样,还需要手动拷贝复制数据。