MCU的内存主要有两类:
-
flash/ROM:只读、掉电数据不丢失,用于存储程序代码、常量、已初始化全局变量的初始值
-
RAM:可读写、掉电数据丢失,用于存储运行时的全局变量、局部变量、堆 / 栈等
这种地址映射是芯片厂商在设计时通过硬件电路(如内存控制器)固定的,软件无法修改。
C代码从编译到运行,需要经过三步:编译→汇编→链接,而其中链接器是"地址分配者",实际是依据链接脚本。
-
编译→汇编:将高级语言→底层指令,同时包含中间代码生成与优化,最终生成目标架构的汇编指令,汇编器将汇编代码翻译成ELF格式的可冲定位目标文件(这个时候仅有段内偏移,未绑定最终物理地址)
-
汇编→链接:本质是:"整合多文件、分配最终地址、修正符号引用 ",链接器处理输入的多个
.o文件(及可能的库文件lib.a),最终输出 .elf/.bin-
.text段(代码 + 只读数据)分配到FLASH区域
-
.data段(已初始化非const全局变量):RAM(可读写)
-
.bss段(未初始化的全局变量,初始值为0的全局变量,未初始化的静态变量):分配到RAM
-
.rodata段:const修饰的全局变量,存于ROM/FLASH
-
正是由于ELF有一个符号表(.symtab)去记录全局符号的最终物理地址,我们就可以通过objdump这个工具去找到某个全局变量的内存地址,如果是一个结构体,我们也可以先找到结构体的基地址,然后找到对应成员的偏移,就可以找到成员地址。这个实现的前提是芯片并没有虚拟内存,否则是无法实现的,下篇应该会说说MCU上电后的执行过程
https://mp.weixin.qq.com/s/3DLWcD1sqd_mwK_Pjo3jFw
https://mp.weixin.qq.com/s/3DLWcD1sqd_mwK_Pjo3jFw