c++嵌入式开发中如何使用链接器脚本(Linker Script)? (控制内存布局)
发布时间 - 2026-01-11 00:00:00 点击率:次嵌入式C++项目必须手写链接器脚本,因裸机/RTOS无OS管理内存布局,需精确控制.text/.data/.bss等段的物理地址;须用MEMORY定义Flash/RAM区域,SECTIONS中显式处理.init_array以保障全局对象构造,并导出_sdata/_sidata/_edata供startup复制.data。
为什么嵌入式 C++ 项目必须手写链接器脚本?
因为裸机或 RTOS 环境下没有操作系统接管内存布局,.text、.data、.bss 这些段默认会按工具链默认规则排布——很可能把代码塞进 RAM、把未初始化变量放在 Flash 地址上,直接导致启动失败或运行时崩溃。链接器脚本(通常叫 linker.ld)是唯一能精确控制每个段落物理地址和大小的手段。
如何定义 MEMORY 区域并映射到真实硬件?
必须先用 MEMORY 告诉链接器芯片的真实资源:哪些地址是 Flash、哪些是 RAM、起始和长度多少。漏写、写反顺序、地址重叠都会让 ld 报错或静默错配。
-
FLASH区域必须从芯片手册确认的 Boot 地址开始(比如0x08000000),长度不能超过实际 Flash 容量 -
RAM区域要避开栈/堆预留区(例如 STM32 的0x20000000开始,但前 4KB 可能被_estack占用) - 多个 RAM 区(如 DTCM/SRAM1/SRAM2)需分别命名,后续用
REGION显式指定段归属
MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 512K
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 128K
}
如何把 C++ 全局对象构造函数塞进 .init_array?
普通 C 项目不关心这个,但 C++ 的全局对象(含 static 局部对象)构造函数必须在 main() 前执行。GCC 用 .init_array 段收集这些函数指针,而默认链接脚本常忽略它——结果就是对象没构造、std::cout 或自定义单例直接崩。
- 必须显式声明
.init_array段,并确保它位于 RAM 中(因为函数指针要运行时读取) - 加上
PROVIDE定义__init_array_start和__init_array_end符号,C runtime 才能遍历调用 - 别忘了
.fini_array(析构函数),虽然嵌入式很少用,但留着更健壮
SECTIONS
{
.text : { *(.text) }
.rodata : { *(.rodata) }
.init_array : {
PROVIDE(__init_array_start = .);
KEEP(*(SORT(.init_array.*)))
KEEP(
*(.init_array))
PROVIDE(__init_array_end = .);
} > RAM
.data : { *(.data) } > RAM AT > FLASH
.bss : { *(.bss) } > RAM
}
为什么 .data 复制代码必须由 startup 文件触发?
链接脚本只定义布局,不生成复制逻辑。.data 在 Flash 里存初值,上电后得靠 startup 汇编/C 代码把它拷到 RAM 对应位置——否则 int x = 42; 的 x 在 RAM 里还是随机值。
- 链接脚本中
.data > RAM AT > FLASH是关键:前者指定运行时地址(RAM),后者指定加载时地址(Flash) - 必须导出
_sidata(Flash 中 .data 起始)、_sdata(RAM 中 .data 起始)、_edata(RAM 中 .data 结束)三个符号供 startup 使用 - C++ 的
constexpr全局变量若被放入.rodata,则无需复制;但带非 trivial 构造的静态对象仍依赖.init_array流程
SECTIONS
{
.data : {
_sdata = .;
*(.data)
_edata = .;
} > RAM AT > FLASH
_sidata = LOADADDR(.data);
}
链接器脚本不是“写完就能跑”的配置文件,它和 startup 代码、编译选项(比如 -fno-exceptions 是否影响 .init_array 内容)、甚至 C++ 标准库实现强耦合。一个字符写错(比如把 > 写成 >>)就可能让整个 .data 段被丢进未映射地址。调试时优先检查 map 文件里各段的 LMA 和 VMA 是否符合预期。
# 操作系统
# 工具
# 栈
# ai
# c++
# 配置文件
# 标准库
# 为什么
# Static
# 构造函数
# 析构函数
# 全局变量
# int
# 指针
# 堆
# map
# 对象
# stm32
# 塞进
# 放在
# 就能
# 多个
# 遍历
# 把它
# 能让
# 会让
# 很可能
# 就可
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
Laravel中间件起什么作用_Laravel Middleware请求生命周期与自定义详解
Laravel中间件如何使用_Laravel自定义中间件实现权限控制
大型企业网站制作流程,做网站需要注册公司吗?
Laravel distinct去重查询_Laravel Eloquent去重方法
C#如何调用原生C++ COM对象详解
零服务器AI建站解决方案:快速部署与云端平台低成本实践
JS去除重复并统计数量的实现方法
如何在Ubuntu系统下快速搭建WordPress个人网站?
中山网站推广排名,中山信息港登录入口?
Laravel策略(Policy)如何控制权限_Laravel Gates与Policies实现用户授权
Laravel怎么在Blade中安全地输出原始HTML内容
个人网站制作流程图片大全,个人网站如何注销?
微信小程序 scroll-view组件实现列表页实例代码
百度浏览器ai对话怎么关 百度浏览器ai聊天窗口隐藏
猪八戒网站制作视频,开发一个猪八戒网站,大约需要多少?或者自己请程序员,需要什么程序员,多少程序员能完成?
Laravel怎么实现软删除SoftDeletes_Laravel模型回收站功能与数据恢复【步骤】
图册素材网站设计制作软件,图册的导出方式有几种?
百度输入法ai面板怎么关 百度输入法ai面板隐藏技巧
Python文件操作最佳实践_稳定性说明【指导】
Angular 表单中正确绑定输入值以确保提交与验证正常工作
PHP 实现电台节目表的智能时间匹配与今日/明日轮播逻辑
Linux系统运维自动化项目教程_Ansible批量管理实战
如何彻底卸载建站之星软件?
网站制作企业,网站的banner和导航栏是指什么?
Laravel怎么实现验证码功能_Laravel集成验证码库防止机器人注册
JS中对数组元素进行增删改移的方法总结
phpredis提高消息队列的实时性方法(推荐)
微信公众帐号开发教程之图文消息全攻略
Laravel Vite是做什么的_Laravel前端资源打包工具Vite配置与使用
如何用AI一键生成爆款短视频文案?小红书AI文案写作指令【教程】
网站制作公司哪里好做,成都网站制作公司哪家做得比较好,更正规?
如何用PHP工具快速搭建高效网站?
成都品牌网站制作公司,成都营业执照年报网上怎么办理?
Laravel的HTTP客户端怎么用_Laravel HTTP Client发起API请求教程
如何在云虚拟主机上快速搭建个人网站?
如何安全更换建站之星模板并保留数据?
Python图片处理进阶教程_Pillow滤镜与图像增强
怎样使用JSON进行数据交换_它有什么限制
手机钓鱼网站怎么制作视频,怎样拦截钓鱼网站。怎么办?
如何在景安服务器上快速搭建个人网站?
如何生成腾讯云建站专用兑换码?
微信h5制作网站有哪些,免费微信H5页面制作工具?
标准网站视频模板制作软件,现在有哪个网站的视频编辑素材最齐全的,背景音乐、音效等?
Laravel怎么实现API接口鉴权_Laravel Sanctum令牌生成与请求验证【教程】
Laravel如何实现API版本控制_Laravel API版本化路由设计策略
如何在云主机快速搭建网站站点?
Laravel如何安装使用Debugbar工具栏_Laravel性能调试与SQL监控插件【步骤】
Laravel如何使用集合(Collections)进行数据处理_Laravel Collection常用方法与技巧
油猴 教程,油猴搜脚本为什么会网页无法显示?
深圳网站制作培训,深圳哪些招聘网站比较好?


*(.init_array))
PROVIDE(__init_array_end = .);
} > RAM
.data : { *(.data) } > RAM AT > FLASH
.bss : { *(.bss) } > RAM
}