我们在做嵌入式系统开发时,会经常遇到加载地址、链接地址和运行地址的概念,可能会感到很困惑,搞不清它们三者的关系。希望此文能帮助大家彻底理解三者的关系。
一.概念
1.1.加载地址
加载地址,即Load Memory Address(LMA),是指程序或数据在存储介质(如Flash、ROM)中的物理存放地址。
它是指静态存储位置,由链接脚本中的 AT 指令显式指定,若不指定则默认与链接地址(即预期的运行地址)相同。
这里的“加载”一词容易造成误解,它是指从哪个地址开始加载,而不是指加载到哪个地址。例如,系统启动时,一般需从flash加载代码到RAM中运行,假设从flash的地址A加载到RAM的地址B运行,则加载地址是指A。
例如,以下为链接脚本片段:
SECTIONS {.text 0x10000000 : { *(.text) } /* 指定链接地址为0x10000000 */.data : AT(0x1000) { *(.data) } /* 指定加载地址为0x1000*/
}
(1)代码段(.text)的链接地址为 0x10000000,也是代码的预期运行地址。
(2)数据段(.data)存储在Flash的 0x1000,即加载地址为0x1000。若不指定AT,则加载地址默认为0x10000000。
1.2.链接地址
链接地址,即Link Address,是编译链接时程序员预期的代码运行地址,由链接脚本中的起始地址定义。所有符号(函数、变量)的地址均基于此地址生成。
链接地址可通过链接脚本指定,也可通过链接参数-Ttext指定。
SECTIONS {.text 0x2000 : { *(.text) } /* 将代码段链接到0x2000地址 */.data : { *(.data) } /* 数据段地址紧接.text段末尾 */
}
若同时使用链接脚本和链接器参数指定链接地址,则编译器会优先使用脚本中的设置。
1.3.运行地址
运行地址,即Virtual Memory Address(VMA),是程序实际执行时的地址。这个地址一般指内存地址,但也有直接在flash上执行代码的。
二.它们的关系
2.1 三者相同
若代码直接在存储介质中运行(如NOR Flash),三者通常相同。
2.2 加载地址 ≠ 运行地址
代码从Flash加载到RAM运行时,加载地址为Flash地址,运行地址为RAM地址。
2.3 链接地址 ≠ 运行地址
位置无关的代码,可在任意地址运行,它的实际运行地址可以不等于链接地址。
例如,内核代码的启动部分一般为位置无关码,它通过相对跳转执行(如 B、BL 指令),不依赖链接地址。而后部分为位置相关码,依赖链接地址,需通过重定位修正。
2.4 链接地址 ≠ 加载地址
常见于代码存储在ROM但链接到RAM的场景,需在启动时复制代码到RAM。