在嵌入式开发和系统底层编程中,理解程序如何从芯片上电到最终运行在指定内存位置,是解决各种“玄学”问题的关键。你是否遇到过程序在开发板上跑飞、Bootloader跳转后系统卡死、或者自己编写的裸机程序无法正确执行?这些问题背后,往往与“重定位”和“启动流程”这两个核心机制密切相关。本文将围绕ARM架构,深入浅出地剖析从芯片上电到应用程序运行的全过程,重点讲解重定位的原理与实现,以及Bootloader在其中扮演的角色。无论你是正在学习ARM裸机开发的新手,还是希望深入理解系统启动的嵌入式工程师,都能通过本文建立起清晰的知识框架,并掌握实际的代码分析与调试方法。
1. 核心概念解析:重定位、Bootloader与启动流程
在深入细节之前,我们首先需要厘清三个核心概念:它们是什么,以及它们之间如何协作。
1.1 什么是重定位?
重定位是链接器和加载器在程序执行前,对程序中的某些地址引用进行修正的过程。简单来说,就是解决“代码以为自己在哪”和“代码实际在哪”不一致的问题。
程序在编译链接时,会假设自己将被加载到一个特定的内存地址运行,这个地址称为 链接地址 或 虚拟内存地址 。例如,链接器可能被告知代码段从 0x80000000 开始。因此,程序中所有函数调用、全局变量访问的地址都是基于这个假设地址计算的。
然而,当芯片刚上电时,CPU通常从某个固定的物理地址(如ARM的 0x00000000 )开始取指执行。此时,程序实际被存放或执行的地址(称为 加载地址 或 物理地址 )与链接地址不同。如果直接执行,程序去访问 0x80000000 处的变量,但实际上变量在 0x00000000 附近,必然导致访问错误或系统崩溃。
重定位就是为了修正这些地址引用,让程序能在其实际被加载的物理地址上正确运行。 修正的信息存储在目标文件(如 .o 或 .elf )的 重定位表 中,常见的段有 .rel.text (代码段重定位表)、 .rel.data (数据段重定位表)或 .rel.dyn (动态重定位表)。
1.2 什么是Bootloader?
Bootloader,即引导加载程序,是系统上电后运行的第一段软件代码。它就像电脑的BIOS,负责初始化最基础的硬件(如时钟、内存控制器),为后续软件的运行准备好环境,并最终将系统控制权移交给操作系统或主应用程序。
在嵌入式系统中,Bootloader的核心职责通常包括:
- 硬件初始化 :设置CPU模式、关闭看门狗、配置系统时钟、初始化SDRAM等。
- 自举 :从存储介质(如Nor Flash、NAND Flash、SD卡)中加载后续程序到内存。
- 重定位 :将自己或要加载的程序从加载地址(如Flash)拷贝到链接地址(如SDRAM)并完成地址修正。
- 跳转 :最终跳转到重定位后的程序入口点,将CPU控制权交出。
常见的Bootloader如U-Boot、RedBoot,以及在STM32等MCU中常用的IAP程序,都属于此类。
1.3 什么是ARM启动流程?
ARM启动流程描述了从芯片上电复位开始,到操作系统内核或应用程序主函数 main() 被执行的完整过程。这是一个分层、分阶段的过程:
- ROM Code阶段 :芯片内部固化的代码,负责初始化最核心的硬件,并从启动设备(如SD卡、eMMC)加载第一级Bootloader到芯片内部SRAM。
- Bootloader阶段 :即上述的Bootloader执行阶段,完成更全面的硬件初始化和重定位工作。
- 操作系统内核加载阶段 :Bootloader将操作系统内核映像从存储设备加载到SDRAM的指定位置。
- 内核启动阶段 :内核自身进行解压、重定位(如果需要)、设置页表、初始化子系统,最后启动用户空间的第一个进程(如
init)。
理解整个流程,才能知道在哪个阶段可能出问题,以及重定位发生在哪个环节。
2. 环境准备与学习思路
本文侧重于原理讲解和代码分析,因此不严格依赖特定硬件。但为了便于理解,我们以通用的ARM Cortex-A系列处理器和ARM Cortex-M系列单片机为例进行说明。
学习建议环境:
- 理论分析 :任何文本编辑器即可。
- 实践验证 :
- ARM Cortex-M (如STM32) :建议使用Keil MDK或IAR for ARM集成开发环境,配合一块STM32开发板(如STM32F103、STM32F407)。这有助于理解裸机情况下的启动文件和重定位。
- ARM Cortex-A (如i.MX6ULL、RK3568) :建议使用Linux开发环境,安装交叉编译工具链(如
arm-linux-gnueabihf-gcc),并配合QEMU模拟器或真实开发板进行实验。这有助于理解复杂Bootloader(如U-Boot)的行为。
- 关键工具 :
- 交叉编译工具链 :用于编译生成ARM架构的可执行文件。
- objdump/readelf :用于分析ELF文件结构,查看节区、符号表和重定位表。命令如
arm-linux-gnueabihf-objdump -d(反汇编)、arm-linux-gnueabihf-readelf -S(查看节区)、arm-linux-gnueabihf-readelf -r(查看重定位表)。 - 链接器脚本(.ld文件) :控制程序的内存布局,是理解链接地址的关键。
本文代码示例将基于ARM汇编和C语言混合,并提供GCC汇编器格式和ARM Compiler 5/6(Ke
354



被折叠的 条评论
为什么被折叠?



