新闻详情

新闻详情

首页 / 资讯中心 / 详情

Zephyr BSP: 33-Linker Memory Map Explanation

发布时间:2026/9/29 9:13:49来源:尧图网络
Zephyr BSP: 33-Linker Memory Map Explanation
摘要:本文是 Zephyr BSP 移植系列的第 33 篇,聚焦 Linker 与 Memory Map。文章从 Linker 在 BSP 中的定位讲起,对比普通 C 程序与 MCU 的差异,逐步拆解 MEMORY、SECTIONS、SYMBOLS 三大核心概念,深入分析 .data、.bss 的特殊性,并串联 Zephyr 特有的初始化 section、device section 与 iterable sections。随后梳理 SoC、Board、Linker 三者的协作关系,讲解 .map 文件的调试方法、Flash/RAM overflow 的典型错误、XIP 与 MPU/MMU 对 Linker 的依赖,最终把 21~33 篇内容闭环为完整的 SoC BSP 平台。Linker / Memory Map:Zephyr BSP 最后一道「硬件边界」前面你已经走完:21SoC Port Skeleton22CPU / Architecture23Startup24Interrupt Controller25Clock / Reset26Devicetree27Binding28UART Driver29GPIO / SPI / I2C / Timer30Board Support Package31Kconfig32CMake / Build System到了33 — Linker / Memory Map,我们开始处理一个非常关键的问题:Zephyr 编译出来的代码,最终到底应该被放到 Company SoC 的哪一块物理内存里?这一步实际上把:C/C++代码 ↓ Compiler ↓ Object files ↓ Linker ↓ ELF ↓ Flash / SRAM / ROM / XIP / RAM真正串起来。1. 先理解 Linker 在 BSP 里的位置整个 Zephyr BSP 可以粗略画成:Zephyr Application │ ▼ ┌─────────────┐ │ CMake │ └──────┬──────┘ │ ▼ ┌─────────────┐ │ Compiler │ │ gcc/clang │ └──────┬──────┘ │ .o / .a files │ ▼ ┌─────────────┐ │ Linker │ │ ld │ └──────┬──────┘ │ ▼ ELF │ ┌────────────┴────────────┐ ▼ ▼ Memory Layout Symbols │ ▼ ┌──────────────────┐ │ Flash / ROM │ │ SRAM │ │ Stack │ │ Heap │ │ Device regions │ └──────────────────┘所以:CMake 决定「编译哪些东西」,Linker 决定「这些东西最终放在哪里」。这两个概念一定不要混淆。2. 为什么普通 C 程序感觉不到 Linker在 PC 上:intmain(){printf("hello");}你通常只需要:gcc main.c-oappLinker 的事情被隐藏了。但 MCU 上则完全不同。假设 Company SoC:Flash 0x00000000 ───────────────── │ │512KB │ 0x00080000 ───────────────── SRAM 0x20000000 ───────────────── │ │128KB │ 0x20020000 ─────────────────那么 Linker 必须知道:.text → Flash .rodata → Flash .data → SRAM .bss → SRAM .stack → SRAM这就是:Memory Map3. 一个真实 MCU Memory Map假设我们的 Company SoC:CompanySoC-X1 ──────────────────────────────── 0x0000_0000 ┌──────────────────────────────┐ │ │ │ FLASH │ │ │ │ .vector_table │ │ .text │ │ .rodata │ │ │ │512KB │ │ │ └──────────────────────────────┘ 0x0008_0000 0x2000_0000 ┌──────────────────────────────┐ │ SRAM │ │ │ │ .data │ │ .bss │ │ heap │ │ stack │ │ │ │128KB │ │ │ └──────────────────────────────┘ 0x2002_0000 0x4000_0000 ┌──────────────────────────────┐ │ Peripheral │ │ │ │ UART │ │ GPIO │ │ SPI │ │ I2C │ │ TIMER │ │ │ └──────────────────────────────┘注意:Peripheral 不一定是 Linker section。它只是 CPU 的 memory-mapped address space。例如:UART0_BASE=0x40001000 GPIO_BASE=0x40002000 SPI0_BASE=0x40003000Devicetree:uart0:uart@40001000{reg=lt;0x400010000x1000gt;;};Driver:# define UART_BASE 0x40001000而:.text .data .bss .stack这些才是真正由 Linker 控制的 ELF sections。4. Linker 最重要的三个概念学习 Zephyr Linker 时,你必须先掌握以下三个概念:MEMORY SECTIONS SYMBOLS5. MEMORY:告诉 Linker「芯片有什么内存」最简单的 linker script:MEMORY{FLASH(rx):ORIGIN=0x00000000, LENGTH=512K SRAM(rwx):ORIGIN=0x20000000, LENGTH=128K}意思是:FLASH start=0x00000000 size=512KB SRAM start=0x20000000 size=128KB这实际上就是:把 Company SoC 的物理 Memory Map 告诉 Linker。6. SECTIONS:告诉 Linker「代码放哪里」例如:SECTIONS{.text:{*(.text*)}FLASH .rodata:{*(.rodata*)}FLASH .data:{*(.data*)}SRAM .bss:{*(.bss*)}SRAM}下面是一个完整的 CompanySoC-X1 链接脚本示例,把前面讲的 MEMORY、SECTIONS 以及 .data 的 LMA/VMA 处理整合到一起:/* * CompanySoC-X1 linker script * 完整示例:MEMORY + SECTIONS + .data LMA/VMA 处理 */ /* ========== 1. MEMORY:告诉 Linker 芯片有什么内存 ========== */ MEMORY { /* 片上 Flash:512 KB,起始地址 0x00000000,可读可执行 */ FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 512K /* 片上 SRAM:128 KB,起始地址 0x20000000,可读可写可执行 */ SRAM (rwx) : ORIGIN = 0x20000000, LENGTH = 128K } /* ========== 2. SECTIONS:告诉 Linker 代码放哪里 ========== */ SECTIONS { /* ---- 向量表:必须放在 Flash 起始位置 ---- */ .vector_table : { KEEP(*(.vector_table)) } FLASH /* ---- 代码段:只读,放 Flash ---- */ .text : { *(.text*) } FLASH /* ---- 只读数据:放 Flash ---- */ .rodata : { *(.rodata*) } FLASH /* * ---- .data:有初始值的全局/静态变量 ---- * * 关键点:LMA(Load Memory Address)在 Flash, * VMA(Virtual Memory Address)在 SRAM。 * * 语法:AT FLASH 表示"加载地址在 Flash", * SRAM 表示"运行地址在 SRAM"。 * * 启动时 startup code 必须把这段从 Flash 拷贝到 SRAM。 */ .data : { __data_start = .; /* 运行地址起点(VMA) */ *(.data*) __data_end = .; /* 运行地址终点(VMA) */ } SRAM AT FLASH /* 记录 .data 在 Flash 中的加载地址(LMA),供 startup 拷贝使用 */ __data_load_start = LOADADDR(.data); __data_load_end = LOADADDR(.data) + SIZEOF(.data); /* ---- .bss:未初始化/零初始化变量,只占 SRAM,Flash 不保存 ---- */ .bss (NOLOAD) : { __bss_start = .; *(.bss*) *(COMMON) __bss_end = .; } SRAM /* ---- 栈:放在 SRAM 末尾,向下增长 ---- */ .stack (NOLOAD) : { __stack_start = .; . = . + 4K; /* 预留 4 KB 栈空间 */ __stack_end = .; } SRAM }这段脚本里最值得关注的是.data的处理:.data │ ├── LMA(加载地址)→ Flash │ 初始值123保存在 Flash │ └── VMA(运行地址)→ SRAM 启动后 counter=123在 SRAM对应的 startup code 需要做两件事:/* 1. 把 .data 从 Flash 拷贝到 SRAM */memcpy(__data_start,__data_load_start,__data_end-__data_start);/* 2. 把 .bss 清零 */memset(__bss_start,0,__bss_end-__bss_start);这样,int counter = 123;的初始值保存在 Flash,运行时被拷贝到 SRAM;static int buffer[1024];则直接在 SRAM 清零,不占用 Flash 空间。意思:.text ↓ FLASH .rodata ↓ FLASH .data ↓ SRAM .bss ↓ SRAM7. 为什么 .data 很特殊?例如:int counter=123;这是:.data程序启动之前:Flash: counter initial value=123启动以后:SRAM: counter=123所以 .data 有:Load Address+Virtual/Runtime Address可以理解成:Flash │ │ initial value ▼ ┌────────────┐ │ .data │ └─────┬──────┘ │ copy ▼ SRAM ┌────────────┐ │ .data │ │counter=123│ └────────────┘这也是为什么 startup code 必须做:copy .data from FLASH → SRAM8. .bss 又不同例如:static int buffer[1024];如果没有显式初始化:static int buffer[1024];它通常进入:.bssFlash 不需要保存 4096 个字节的 0。所以:.bss ↓ SRAM启动时:memset(__bss_start,0, __bss_end - __bss_start);于是:.bss被清零。9. Zephyr 的 linker 比普通裸机复杂得多你不能简单认为 Zephyr 就是:.text .data .bss实际上 Zephyr 会有大量特殊 sections,例如:.text .rodata .data .bss .noinit .device .device_states .sw_isr_table .z_init_PRE_KERNEL_1 .z_init_PRE_KERNEL_2 .z_init_POST_KERNEL .z_init_APPLICATION .shell .log_const .log_backends .ARM.exidx其中有一些特别重要。10. z_init_* 和我们之前学的 Init Priority还记得前面:PRE_KERNEL_1 PRE_KERNEL_2 POST_KERNEL APPLICATION我们之前从:DEVICE_DT_DEFINE(...)一路追到了:device initialization现在从 Linker 的角度重新看。Zephyr 会把初始化函数放进 linker sections。概念上类似:.z_init_PRE_KERNEL_1 │ ▼ .z_init_PRE_KERNEL_2 │ ▼ .z_init_POST_KERNEL │ ▼ .z_init_APPLICATION因此:Zephyr 的初始化顺序不仅仅是 C 代码调用关系,也是 Linker section 布局的一部分。这就是为什么你前面学习:DEVICE_DT_DEFINE最后一定会碰到 linker。11. struct device 也和 Linker 有关系前面我们已经拆过:DEVICE_DT_DEFINE
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

iPhone配置学校邮箱全攻略:IMAP/SMTP参数、授权码与报错排查 2026/9/29 15:29:53

iPhone配置学校邮箱全攻略:IMAP/SMTP参数、授权码与报错排查

开学季总有人抱着新iPhone来找我:“学长,帮我看看学校邮箱怎么在手机上配?”这问题看着简单,真动起手来,卡在服务器参数、授权码、SSL端口上的人一抓一大把。网页版邮箱点开就能用,换成iOS自带邮件应用就报…

阅读更多 →
Python数据分析工具链实战:从环境搭建到业务洞察 2026/9/29 15:29:33

Python数据分析工具链实战:从环境搭建到业务洞察

1. 工具链全景规划:先想清楚再动手 做数据分析这些年,我最大的体会是: 多数人学Python半途而废,不是语法学不会,而是从一开始就把环境搞乱了。 今天想聊聊数据分析师日常真正会用到的Python工具组合,以及…

阅读更多 →
柔焦滤镜全解析:从光学原理到实拍参数,拍出高级感人像 2026/9/29 15:29:33

柔焦滤镜全解析:从光学原理到实拍参数,拍出高级感人像

从怼脸拍到退三步:柔焦滤镜到底在解决什么问题拍人像这几年,我越来越发现一个反常识的现象:很多人花大价钱买回来顶级镜头,结果拍出来的片子反而不如一支几百块的旧镜头耐看。问题不在解析力,而在观看方式上。顶级镜头…

阅读更多 →
React Native 接入鸿蒙:桥接实践与踩坑排查指南 2026/9/29 15:29:33

React Native 接入鸿蒙:桥接实践与踩坑排查指南

这两年做跨端开发,绕不开一个话题:React Native 怎么接鸿蒙。我前阵子把一个RN项目往鸿蒙设备上迁,期间要自己写鸿蒙(HarmonyOS)组件,还要把鸿蒙的系统能力暴露给React Native侧调用,踩的坑比预…

阅读更多 →
唱歌直播音频链路搭建:伴奏防混浊、歌词显示与设备选型全攻略 2026/9/29 15:29:33

唱歌直播音频链路搭建:伴奏防混浊、歌词显示与设备选型全攻略

直播间能看的东西挺多,但“真人唱歌直播间”绝对是最容易看出功底的一种。很多朋友第一次开播,架好手机、连上麦克风,放起伴奏张嘴就唱,结果观众听到的是混着外放伴奏的“罐头声”,人声发闷,伴奏糊成一团&a…

阅读更多 →
C++ std--valarray 用法实例详解 2026/9/29 15:29:33

C++ std--valarray 用法实例详解

前言std::valarray 是 C98 就进入标准库的数值数组类&#xff08;numeric array&#xff09;&#xff0c;头文件是 <valarray>。它的设计目标非常明确&#xff1a;面向数值计算&#xff0c;让 a b c * 2.0 这样的整体数组运算能像标量一样写出来&#xff0c;而不必手写…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉