🏆本文收录于 《全栈Bug调优(实战版)》 专栏,该专栏专注于分享我在真实项目开发中遇到的各类疑难Bug及其深层成因,并系统提供高效、可复现的解决思路和实操方案。无论你是刚入行的新手开发者,还是拥有多年项目经验的资深工程师,本专栏都将为你提供一条系统化、高质量的问题排查与优化路径,助力你加速成长,攻克技术壁垒,迈向技术价值最大化与职业发展的更高峰🚀!
📌 特别说明: 文中部分技术问题来源于真实生产环境及网络公开案例,均经过精挑细选与系统化整理,并结合多位一线资深架构师和工程师多年实战经验沉淀,提炼出多种经过验证的高可行性解决方案,供开发者们参考与借鉴。
欢迎 关注、收藏并订阅本专栏,持续更新的干货内容将与您同行,让我们携手精进,技术跃迁,步步高升!

📢 问题描述
问题来源:https://ask.csdn.net/questions/xxx
问题描述:keil5编译出现段重复报错:.\Objects\JjProject.sct(7): error L6235E: More than one section matches sele…如何解决?

📣 请知悉:如下方案不保证一定适配你的问题!
如下是针对上述问题进行专业角度剖析答疑,不喜勿喷,仅供参考:

✅️问题理解
根据题主提供的图片信息,您遇到的是Keil5编译器中的链接器脚本错误L6235E:
.\Objects\JjProject.sct(7): error L6235E: More than one section matches selector - cannot all be FIRST/LAST.
错误分析:
这个错误表明在您的散布加载描述文件(Scatter-loading Description File,即.sct
文件)中,存在多个内存段选择器冲突的问题。具体来说:
- FIRST/LAST选择器冲突:在链接器脚本中,有多个内存段都使用了
FIRST
或LAST
属性,但同一类型的选择器在同一个加载域中只能有一个 - 内存段重复定义:可能存在相同的内存段被多次定义或引用
- 段属性配置错误:在
+RO
、+RW
、+ZI
等属性设置中出现了冲突
问题根本原因:
STM32项目中的内存映射配置错误,导致链接器无法正确分配代码和数据到指定的内存区域。
✅️问题解决方案
方案一:检查并修复.sct文件
; 正确的STM32散布加载文件示例
; JjProject.sct
; 定义加载域 - ROM区域
LR_IROM1 0x08000000 0x00080000 {
; 执行域 - FLASH区域
ER_IROM1 0x08000000 0x00080000 {
*.o (RESET, +First) ; 确保只有一个FIRST
*(InRoot$$Sections)
.ANY (+RO) ; 所有只读代码和常量
.ANY (+XO) ; 执行代码
}
; 执行域 - RAM区域
RW_IRAM1 0x20000000 0x00020000 {
.ANY (+RW +ZI) ; 读写和零初始化数据
}
}
; 如果有其他特殊内存区域(如CCM RAM)
LR_IRAM2 0x10000000 0x00010000 {
RW_IRAM2 0x10000000 UNINIT 0x00010000 {
.ANY (+ZI) ; 仅零初始化数据
}
}
方案二:使用Keil5默认内存配置
如果自定义配置有问题,可以先使用Keil5的默认配置:
步骤1:删除或重命名当前.sct文件
# 将JjProject.sct重命名为JjProject.sct.bak
步骤2:在Keil5中重新配置内存
- 右击项目 →
Options for Target
- 选择
Linker
选项卡 - 取消勾选
Use Memory Layout from Target Dialog
- 在
Target
选项卡中重新配置内存映射
步骤3:STM32典型内存配置
// 在Target选项中设置
ROM1:
Start: 0x08000000
Size: 0x80000 // 512KB Flash
RAM1:
Start: 0x20000000
Size: 0x20000 // 128KB RAM
方案三:手动修复具体错误
基于您的错误信息,重点检查第7行附近的代码:
; 错误示例(需要修复)
LR_IROM1 0x08000000 0x00080000 {
ER_IROM1 0x08000000 0x00080000 {
*.o (RESET, +First) ; 第一个FIRST
*.o (STARTUP, +First) ; 错误:第二个FIRST - 这里会报错
.ANY (+RO)
}
}
; 正确修复后
LR_IROM1 0x08000000 0x00080000 {
ER_IROM1 0x08000000 0x00080000 {
*.o (RESET, +First) ; 只保留一个FIRST
*.o (STARTUP) ; 移除+First属性
.ANY (+RO)
}
}
方案四:使用图形化配置工具
// 使用STM32CubeMX重新生成项目配置
// 1. 打开STM32CubeMX
// 2. 选择对应的STM32芯片型号
// 3. 配置外设和时钟
// 4. 生成Keil5项目
// 5. 使用生成的.sct文件替换当前文件
方案五:完整的调试步骤
// 1. 备份当前项目
// 2. 清理编译输出
#include "stm32f4xx.h"
// 3. 创建最小化的.sct文件进行测试
LR_IROM1 0x08000000 0x00020000 { ; 减小ROM大小进行测试
ER_IROM1 0x08000000 0x00020000 {
*.o (RESET, +First)
*(InRoot$$Sections)
.ANY (+RO)
}
RW_IRAM1 0x20000000 0x00005000 { ; 减小RAM大小进行测试
.ANY (+RW +ZI)
}
}
// 4. 逐步增加内存大小和功能模块
✅️问题延伸
1. STM32内存映射深入理解
2. 散布加载文件语法详解
; 加载域语法
LoadRegion_Name base_address max_size [attributes] {
; 执行域语法
ExecRegion_Name base_address [attributes] [max_size] {
; 输入段描述
module_selector(section_selector, [attributes])
.ANY (+RO) ; 任意只读段
.ANY (+RW) ; 任意读写段
.ANY (+ZI) ; 任意零初始化段
}
}
; 常用属性说明
; +First - 必须放在执行域开始位置
; +Last - 必须放在执行域结束位置
; +RO - 只读属性(代码和常量)
; +RW - 读写属性(已初始化数据)
; +ZI - 零初始化属性(未初始化数据)
; +XO - 执行代码属性
; UNINIT - 不初始化属性
3. 常见的段属性冲突场景
; 场景1:多个FIRST冲突
*.o (RESET, +First) ; 第一个FIRST
*.o (STARTUP, +First) ; 错误:重复的FIRST
; 场景2:段地址重叠
ER_IROM1 0x08000000 0x00080000 { }
ER_IROM2 0x08040000 0x00080000 { } ; 错误:地址重叠
; 场景3:大小超出限制
RW_IRAM1 0x20000000 0x00020000 {
.ANY (+RW +ZI) ; 如果实际需要超过128KB就会出错
}
4. 高级内存配置技巧
; 支持多Bank Flash的配置
LR_IROM1 0x08000000 0x00100000 {
; Bank1: 引导代码
ER_IROM1 0x08000000 0x00080000 {
*.o (RESET, +First)
bootloader.o (+RO)
}
; Bank2: 应用代码
ER_IROM2 0x08080000 0x00080000 {
application.o (+RO)
.ANY (+RO)
}
; 快速访问RAM
RW_IRAM1 0x20000000 0x00020000 {
critical_data.o (+RW +ZI)
.ANY (+RW +ZI)
}
; CCM RAM(仅数据,不能执行代码)
RW_IRAM2 0x10000000 UNINIT 0x00010000 {
buffer.o (+ZI)
}
}
✅️问题预测
1. 内存不足问题预测
可能出现的问题:
- 代码量增长导致Flash空间不足
- 全局变量过多导致RAM空间不足
- 堆栈溢出导致程序异常
预防措施:
// 内存使用监控代码
#include "stm32f4xx.h"
// 获取堆栈使用情况
uint32_t get_stack_usage(void) {
extern uint32_t _estack; // 栈顶地址
uint32_t stack_top = (uint32_t)&_estack;
uint32_t current_sp;
__asm volatile ("mov %0, sp" : "=r" (current_sp));
return stack_top - current_sp;
}
// 获取堆使用情况
uint32_t get_heap_usage(void) {
extern uint32_t _heap_start;
extern uint32_t _heap_end;
return (uint32_t)&_heap_end - (uint32_t)&_heap_start;
}
// 在主函数中监控
int main(void) {
// 初始化代码...
while(1) {
uint32_t stack_used = get_stack_usage();
uint32_t heap_used = get_heap_usage();
// 发送到调试串口或存储
printf("Stack used: %lu bytes\n", stack_used);
printf("Heap used: %lu bytes\n", heap_used);
HAL_Delay(1000);
}
}
2. 多核心处理器适配问题
预测场景:
- 从单核STM32迁移到双核STM32H7系列
- 需要为不同核心分配独立的内存区域
解决方案:
; STM32H7双核内存配置示例
; Cortex-M7核心配置
LR_IROM1 0x08000000 0x00100000 {
ER_IROM1 0x08000000 0x00100000 {
*.o (RESET, +First)
m7_code.o (+RO) ; M7专用代码
.ANY (+RO)
}
; M7专用RAM
RW_IRAM1 0x20000000 0x00020000 {
m7_data.o (+RW +ZI)
}
}
; Cortex-M4核心配置
LR_IROM2 0x08100000 0x00100000 {
ER_IROM2 0x08100000 0x00100000 {
m4_code.o (+RO) ; M4专用代码
}
; M4专用RAM
RW_IRAM2 0x30000000 0x00048000 {
m4_data.o (+RW +ZI)
}
}
; 共享内存区域
LR_SHARED 0x38000000 0x00010000 {
RW_SHARED 0x38000000 UNINIT 0x00010000 {
shared_data.o (+ZI) ; 核心间通信数据
}
}
3. 编译器版本兼容性问题
可能的问题:
- Keil5升级后语法变化
- ARM编译器版本差异导致的链接错误
- 新版本对内存对齐要求更严格
预防措施:
// 版本兼容性检查
#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6000000)
// ARM Compiler 6.x 配置
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wpadded"
#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 5000000)
// ARM Compiler 5.x 配置
#pragma push
#pragma anon_unions
#endif
// 内存对齐属性
typedef struct {
uint32_t data1;
uint16_t data2;
uint8_t data3;
} __attribute__((packed)) DataStruct_t;
#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6000000)
#pragma clang diagnostic pop
#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 5000000)
#pragma pop
#endif
✅️小结
问题核心:
您遇到的L6235E错误是由于Keil5链接器脚本中的段选择器冲突导致的,主要原因是在.sct
文件中多个内存段使用了相同的FIRST
或LAST
属性。
关键解决步骤:
- 立即解决方案:检查
JjProject.sct
文件第7行附近,确保只有一个段使用+First
属性 - 标准化配置:使用STM32典型的内存映射配置模板
- 工具辅助:利用STM32CubeMX重新生成项目配置
- 逐步调试:通过最小化配置逐步定位问题
最佳实践要点:
- 唯一性原则:每个加载域中
FIRST
和LAST
选择器必须唯一 - 地址对齐:确保内存段地址按照ARM Cortex-M的要求对齐
- 大小合理:内存段大小不能超过芯片的实际硬件限制
- 备份策略:修改前务必备份工作版本的配置文件
紧急处理建议:
如果急需解决,建议先删除当前的.sct
文件,让Keil5使用默认的内存配置,然后重新编译。这样可以快速恢复编译功能,后续再优化内存布局。
代码审查要点:
- 检查是否有重复的
+First
或+Last
属性 - 验证内存地址范围是否与STM32芯片手册一致
- 确认代码段和数据段的大小在合理范围内
这个问题虽然看起来复杂,但按照系统化的方法排查,通常可以快速解决。关键是理解STM32的内存架构和Keil5链接器的工作原理。
希望如上措施及解决方案能够帮到有需要的你。
PS:如若遇到采纳如下方案还是未解决的同学,希望不要抱怨&&急躁,毕竟影响因素众多,我写出来也是希望能够尽最大努力帮助到同类似问题的小伙伴,即把你未解决或者产生新Bug黏贴在评论区,我们大家一起来努力,一起帮你看看,可以不咯。
若有对当前Bug有与如下提供的方法不一致,有个不情之请,希望你能把你的新思路或新方法分享到评论区,一起学习,目的就是帮助更多所需要的同学,正所谓「赠人玫瑰,手留余香」。
🧧🧧 文末福利,等你来拿!🧧🧧
如上问题有的来自我自身项目开发,有的收集网站,有的来自读者…如有侵权,立马删除。再者,针对此专栏中部分问题及其问题的解答思路或步骤等,存在少部分搜集于全网社区及人工智能问答等渠道,若最后实在是没能帮助到你,还望见谅!并非所有的解答都能解决每个人的问题,在此希望屏幕前的你能够给予宝贵的理解,而不是立刻指责或者抱怨!如果你有更优解,那建议你出教程写方案,一同学习!共同进步。
ok,以上就是我这期的Bug修复内容啦,如果还想查找更多解决方案,你可以看看我专门收集Bug及提供解决方案的专栏《全栈Bug调优(实战版)》,都是实战中碰到的Bug,希望对你有所帮助。到此,咱们下期拜拜。
码字不易,如果这篇文章对你有所帮助,帮忙给 bug菌 来个一键三连(关注、点赞、收藏) ,您的支持就是我坚持写作分享知识点传播技术的最大动力。
同时也推荐大家关注我的硬核公众号:「猿圈奇妙屋」 ;以第一手学习bug菌的首发干货,不仅能学习更多技术硬货,还可白嫖最新BAT大厂面试真题、4000G Pdf技术书籍、万份简历/PPT模板、技术文章Markdown文档等海量资料,你想要的我都有!
🫵 Who am I?
我是bug菌,CSDN | 掘金 | InfoQ | 51CTO | 华为云 | 阿里云 | 腾讯云 等社区博客专家,C站博客之星Top30,华为云多年度十佳博主,掘金多年度人气作者Top40,掘金等各大社区平台签约作者,51CTO年度博主Top12,掘金/InfoQ/51CTO等社区优质创作者;全网粉丝合计 30w+;更多精彩福利点击这里;硬核微信公众号「猿圈奇妙屋」,欢迎你的加入!免费白嫖最新BAT互联网公司面试真题、4000G PDF电子书籍、简历模板等海量资料,你想要的我都有,关键是你不来拿。

-End-