跳到主要内容

1. 引言与概述

1.1 文档说明​

本文档旨在详细介绍 ChipON KF32 系列芯片在 LLVM/LLD 工具链体系下链接脚本(Linker Script,.ld 文件)的完整使用方法,包括内存区域配置(MEMORY)、段布局控制(SECTIONS)、各输出段的设计原理、与 GNU ld 的行为差异,以及常量定位、变量定位、低功耗数据保持、堆栈调整等常规使用方法,并附常见问题排查方案。

无论是使用默认脚本直接编译,还是需要为 Bootloader 双应用、指定地址常量/函数、低功耗保持变量等特殊应用调整脚本布局的用户,均可通过本文档掌握链接脚本的原理与修改方法。

1.2 链接脚本的作用​

编译的最后一步由链接脚本控制:脚本定义的段规则决定链接器从各目标文件中读取哪些输入段、如何合并成输出段、最终生成什么内存布局的目标文件。默认脚本已覆盖常规应用的布局需求,通过调整脚本可以实现特殊的应用程序布局,例如:

  • Bootloader 双应用程序:为引导程序与应用程序划分独立的 Flash 区域;
  • 指定函数/常量的固定地址:将关键代码或数据放置到确定的 Flash/RAM 地址;
  • 差异化应用的 RAM 变量定位:将变量放置到指定 RAM 地址,或利用低功耗保持区在唤醒后保留数据。

1.3 适用范围​

本文档适用于所有使用 ChipON KF32 LLVM 工具链进行开发的用户。文中示例均以常用型号 KF32A158MQVSF(512K PFlash / 64K RAM / 32K DFlash) 为例;不同型号之间主要是资源大小与配置区域地址的差别,段规则部分完全一致,其他型号仅需按产品规格书调整 MEMORY 中的区域起始地址与长度(见 第 2 章 内存区域与布局控制)。

KF32 输出文件格式为 elf32。RAM 设计中部分地址空间为低功耗唤醒不丢失区域(本型号为前 32K,具体以产品规格书为准),可通过变型 startup(控制拷贝循环跳过 .lpdata 段)配合脚本实现(见 5.3 低功耗保持数据应用)。

1.4 术语与定义​

  • 链接脚本(Linker Script):文本格式的链接规则描述文件,控制输入段到输出段的合并方式与最终内存布局,ChipON 工程中为 .ld 文件。
  • 输入段(Input Section):编译产生的目标文件(.o)中的段,如 .text、.data、.rodata。
  • 输出段(Output Section):链接脚本 SECTIONS 中定义的段,由一个或多个输入段合并而成,如脚本中的 .text、.data、.bss。
  • VMA(Virtual Memory Address):虚拟地址,即程序运行时段所在的地址,通过 > region 指定。
  • LMA(Load Memory Address):装载地址,即段内容在烧录映像(HEX/BIN)中存放的地址,通过 AT > region 指定。
  • 定位计数器(Location Counter,.):脚本中的特殊符号,表示当前布局位置。在 = 运算中出现的 . 即为定位计数器。
  • 孤儿段(Orphan Section):脚本中未显式列出的输入段;使用含 SECTIONS 的脚本时按错误处理。
  • 死段优化(--gc-sections):链接器默认开启的优化,未被引用的代码/数据不分配空间;KEEP 修饰的输入段可豁免。
  • NOLOAD:输出段修饰符,标记该段不装载(ELF 中为 NOBITS 类型),不占用 HEX 内容。

1.5 默认段归属​

默认情况下,各类内容的段归属如下:

内容段空间
代码函数.textflash
全局初始化常量 / 局部常量.rdata / .rodataflash
全局已初始化变量.dataram(初值装载在 flash .text 之后)
全局未初始化变量.bss 或 COMMONram
RAM 中的函数/常量.indata / .inrdata / .inrodataram
C++ 全局构造/析构函数表.preinit_array / .init_array / .fini_arrayflash

1.6 脚本必需符号​

以下符号为工具实现的组成部分,构成脚本与启动代码(startup)之间的接口,修改脚本时必须保留:

符号含义使用者
__text_end__代码段结束startup 拷贝源
__data_start__ / __data_end__已初始化数据段的运行地址范围startup 拷贝目标
__bss_start__ / __bss_end__未初始化数据段的清零范围startup 清零循环
__allot_end__堆区起始堆初始化
__initial_sp初始栈顶指针(RAM 顶地址)向量表 / SP 初始化
__lpdata_start__ / __lpdata_end__低功耗保持区范围变型 startup 跳过拷贝
__vec_end__中断向量表结束布局标记
__init_class_start / __init_class_endC++ 全局构造函数表范围startup C++ 模式逐个调用构造函数
必需符号的约束
  • startup 代码依赖上述符号完成数据拷贝与清零,删除或改名将导致程序无法运行。
  • __data_start__ 必须位于 ram 中 .data 的起始处。
  • 若需保留 RAM 前 N 字节,将 MEMORY 中 ram 的 ORIGIN 增加 N、LENGTH 减少 N(见 第 2 章 内存区域与布局控制)。

1.7 程序映像的构成​

程序生成时,ram 段的全局初始化部分通过 AT > flash 装载到 flash 中 .text 之后,即代码与全局初值共同构成完整的程序内容(可在 HEX 文件中看到)。startup 函数在 main 之前以 __text_end__ 为源地址、__data_start__ ~ __data_end__ 为目标地址完成数据拷贝,并清零 .bss 区。

.bss、.heap 与 .stack 段标记为 NOLOAD,属 NOBITS 段,不占用 HEX 内容,运行时由 startup 清零或留待使用,不会在 HEX 中写入无意义的 0 填充。