Zephyr的分散加载文件--组成分析

Creative Commons
本作品采用知识共享署名

本文分析说明zephyr下gnu ld分散加载文件的组成。

概述

分散加载文件用于告诉链接器如何在ROM和RAM中放置代码中的各个段,对于PC应用只会运行在统一的用户空间地址上,开发者不需要考虑各个段的放置位置,另外使用的是标准库,不会添加额外的段,因此一般只会使用默认的分散加载文件,不会去实现和修改分散加载文件。但在对于嵌入式软件的开发,存在bootloader/APP/硬件中断向量表的地址指定,另外不同的功能库实现可能会定义不同的段,所以有修改定制分散加载文件的需求。因此了解Zephyr的分散加载文件组成很有必要。本文主要分析zephyr的linker.ld由那些文件组成,不涉及细节section分析,zephyr最后使用的分散加载文件会生成在build/zephyr/linker.cmd,该文件里面可以看到最终的链接加载情况。

组成来源

分类

Zephyr的分散加载文件来源可以分为三类
1.由soc读取配置组合成linker.ld
2.由board的实现者实现linker.ld
3.由app的实现者实现linker.ld

由board的实现者实现linker.ld,要求board的移植者非常了解Zephyr各功能模块的分散加载情况,并进行组合实现。
由app的实现者实现linker.ld,要求app的开发者非常了解app使用的Zephyr各功能模块的分散加载情况,并进行组合实现。
除非非常情况Zephyr各个Section,一般情况下并不推荐使用2,3方式, 但通过了解soc读取配置组合linker.ld后参考写出zephyr的linker.ld也不是难事。因此本文主要分析soc下的linker.ld,

Zephyr如何指定linker.ld

在zephyr/CMakeList.txt中指定使用linker文件,可以从以下流程看出Zephyr的分散加载文件来源

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
if(CONFIG_HAVE_CUSTOM_LINKER_SCRIPT) 		#如果配置用户分散加载文件,则使用用户的分散加载文件
set(LINKER_SCRIPT ${APPLICATION_SOURCE_DIR}/${CONFIG_CUSTOM_LINKER_SCRIPT})
if(NOT EXISTS ${LINKER_SCRIPT})
set(LINKER_SCRIPT ${CONFIG_CUSTOM_LINKER_SCRIPT})
assert_exists(CONFIG_CUSTOM_LINKER_SCRIPT)
endif()
else()
# Try a board specific linker file
# 如果用户没有配置分散加载文件,使用board下面的linker.ld
set(LINKER_SCRIPT ${BOARD_DIR}/linker.ld)
if(NOT EXISTS ${LINKER_SCRIPT})
# If not available, try an SoC specific linker file
# 如果没有指定就使用soc下的linker.ld
set(LINKER_SCRIPT ${SOC_DIR}/${ARCH}/${SOC_PATH}/linker.ld)
endif()
endif()

组成分析

本节将以mm_feather为例,分析分散加载文件的组成

Step1 确认顶层分散加载文件

应用不指定linker.ld, mm_feather board文件夹内也没有指定linker.ld,因此使用的时soc下的linker.ld
mm_feather使用的时nxp rt1062,arch是arm的cortex-m,因此${SOC_DIR}/${ARCH}/${SOC_PATH}/linker.ld)对应的就是soc/arm/nxp_imx/rt/linker.ld
其内容为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//包含头文件,会因应配置项和设备数宏
#include <autoconf.h>
#include <devicetree.h>

#define IS_CHOSEN_SRAM(x) (DT_DEP_ORD(DT_NODELABEL(x)) == DT_DEP_ORD(DT_CHOSEN(zephyr_sram)))

//和soc相关的内存区域定义
MEMORY
{
#if (DT_REG_SIZE(DT_NODELABEL(ocram)) > 0) && !IS_CHOSEN_SRAM(ocram)
OCRAM (wx) : ORIGIN = DT_REG_ADDR(DT_NODELABEL(ocram)), LENGTH = DT_REG_SIZE(DT_NODELABEL(ocram))
#endif
#if (DT_REG_SIZE(DT_NODELABEL(sdram0)) > 0) && !IS_CHOSEN_SRAM(sdram0)
SDRAM (wx) : ORIGIN = DT_REG_ADDR(DT_NODELABEL(sdram0)), LENGTH = DT_REG_SIZE(DT_NODELABEL(sdram0))
#endif
}

//包含cortex-m架构相关的ld文件,在include文件夹下
#include <arch/arm/aarch32/cortex_m/scripts/linker.ld>

Step2 收集确认包含的ld文件

ld文件中包含的头文件,基本就是为了引用配置,观察其组成,我们主要是查看其包含了那些ld。在include/arch/arm/aarch32/cortex_m/scripts/linker.ld中,该文件中含有所有分散加载的信息,包含了存储区域,输入输出段。其中输入输出段有很大一部分是通过include其它ld文件完成,这是我们本文分析的重点。
打开include/arch/arm/aarch32/cortex_m/scripts/linker.ld在其中搜索列出如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <linker/rel-sections.ld>
#include <snippets-rom-start.ld>
#include <linker_relocate.ld>
#include <linker/kobject-text.ld>
#include <linker/common-rom.ld>
#include <linker/thread-local-storage.ld>
#include <snippets-rodata.ld>
#include <linker/kobject-rom.ld>
#include <linker/cplusplus-rom.ld>
#include <snippets-ram-sections.ld>
#include <app_smem.ld>
#include <linker_sram_bss_relocate.ld>
#include <linker/common-noinit.ld>
#include <snippets-rwdata.ld>
#include <linker_sram_data_relocate.ld>
#include <linker/common-ram.ld>
#include <linker/kobject-data.ld>
#include <linker/cplusplus-ram.ld>
#include <snippets-data-sections.ld>
#include <linker_sram_bss_relocate.ld>
#include <snippets-noinit.ld>
#include <snippets-sections.ld>
#include <linker/debug-sections.ld>

可以分为4类

1. 代码中存在的文件

这些文件是原生zephyr代码中就有的文件,全都放在include/linker下,
按用途分类说明:

  • C++使用
    cplusplus-rom.ld
    cplusplus-ram.ld
  • 符号偏移重定位
    rel-sections.ld
  • 内核对象
    kobject-text.ld
    kobject-rom.ld
    kobject-data.ld
  • zephyr通用
    common-rom.ld
    common-noinit.ld
    common-ram.ld
  • TLS使用
    thread-local-storage.ld
  • 调试信息
    debug-sections.ld

2. 由CMake函数生成

CMake将控制生成以下文件,通过zephyr_linker_sources/zephyr_linker_sources_ifdef可以将指定的ld文件inculde到对应的生成文件内,这些ld文件可以由用户定义。

  • 由ROM_START指定的文件
    snippets-rom-start.ld
    被放入到ROMABLE_REGION,位于ROM的最开始,通常用于指定中断向量表或是soc要求的特殊header
  • 由RODATA指定的文件
    snippets-rodata.ld
    被放到ROMABLE_REGION,用于指定只读数据
  • 由RAM_SECTIONS指定的文件
    snippets-ram-sections.ld
    被放到RAMABLE_REGION,用于指定不需要初始化的的内存部分
  • 由RWDATA指定的文件
    snippets-rwdata.ld
    被放到RAMABLE_REGION,用于指定需要初始化的的内存部分
  • 由DATA_SECTIONS指定的文件
    snippets-data-sections.ld
    用于指定专门的需要初始化的内存部分
  • 由NOINIT指定的文件
    snippets-noinit.ld
    被放到RAMABLE_REGION中,用于指定专门的不需要初始化的内存部分
  • 由SECTIONS指定的文件
    snippets-sections.ld
    被放到有效内存区的最后,用于指定特殊的一些section,例如中断向量映射表

以下是rt1062的实例分析,通过搜索和rt1062相关的如下
arch/arm/core/aarch32/CMakeLists.txt:

1
zephyr_linker_sources(ROM_START SORT_KEY 0x0vectors vector_table.ld)

生成的snippets-rom-start.ld中会包含vector_table.ld

arch/arm/core/aarch32/cortex_m/CMakeLists.txt

1
2
3
4
5
6
7
8
9
zephyr_linker_sources_ifdef(CONFIG_GEN_ISR_TABLES
SECTIONS
${ZEPHYR_BASE}/include/linker/intlist.ld
)

zephyr_linker_sources_ifdef(CONFIG_ARCH_HAS_RAMFUNC_SUPPORT
RAM_SECTIONS
ramfunc.ld
)

生成的snippets-ram-sections.ld会包含ramfunc.ld

1
2
3
4
zephyr_linker_sources_ifdef(CONFIG_NOCACHE_MEMORY
RAM_SECTIONS
nocache.ld
)

生成的snippets-sections.ld会包含intlist.ld
生成的snippets-ram-sections.ld会包含nocache.ld

soc/arm/nxp_imx/rt/CMakeLists.txt

1
2
zephyr_linker_sources_ifdef(CONFIG_NXP_IMX_RT_BOOT_HEADER
ROM_START SORT_KEY 0 boot_header.ld)

生成的snippets-rom-start.ld中会包含boot_header.ld
其它的生成文件将不会包含ld,被包含的ld文件可以自行在zephyr中查找。
这里可以看到生成的snippets-rom-start.ld会同时包含boot_header.ld和vector_table.ld,这两个的顺序由SORT_KEY决定,boot_header.ld为0,vector_table.ld为0x0vectors,因此boot_header.ld排在最前面

3. 由脚本生成

在开启用户空间后CONFIG_USERSPACE=y后,由app_memdomain.h中宏定义出的section会被script/gen_app_partitions.py生成出来放到app_smem_aligned.ld或app_smem_unaligned.ld内.
app_smem.ld只是include这两个ld文件

1
2
3
4
5
6
7
8
#ifdef LINKER_APP_SMEM_UNALIGNED
#define APP_SMEM_LD <app_smem_unaligned.ld>
#else
#define APP_SMEM_LD <app_smem_aligned.ld>
#endif

#include APP_SMEM_LD
#undef APP_SMEM_LD

4. relaction指定

在CONFIG_CODE_DATA_RELOCATION=y时,由gen_relocate_app.py解析出app relocate section生成下列文件
linker_relocate.ld
linker_sram_bss_relocate.ld
linker_sram_data_relocate.ld

结语

本文分析了从zephyr分散加载文件的总入口linker.ld分析了其include的各个ld文件,为分析zephyr分散加载文件的细节和修改添加提供了入口方向。