Zephyr代码和数据重定位--2实现原理

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

本文说明Zephyr的代码和数据重定位功能实现原理。

Zephyr代码和数据重定位–1使用方法一文中“基本概念介绍”我们知道代码和数据的重定位是通过修改linker.ld和启动代码拷贝完成,Zephyr的实现就是将这一过程自动化,主要的流程如下cmake函数zephyr_code_relocate调用gen_relocate_app.py生成如下文件:

  • linker_relocate.ld
  • linker_sram_data_relocate.ld
  • linker_sram_bss_relocate.ld
  • code_relocation.c
    *.ld文件将被include到linker.ldcode_relocation.c提供的拷贝函数data_copy_xip_relocationzephyr/kernel/xip.c调用,提供bss初始化函数bss_zeroing_relocationzephyr/kernel/init.c调用。

文件生成

在CMakeList.txt中使用zephyr_code_relocate指定文件被重定位的地方

1
zephyr_code_relocate(FILES file1.c file2.c LOCATION SRAM_TEXT)

zephyr_code_relocate定义在cmake/modules/extensions.cmake内,实现解析如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
function(zephyr_code_relocate)
set(options NOCOPY) #可选参数
set(single_args LIBRARY LOCATION PHDR) //单一参数
set(multi_args FILES) //多参数
cmake_parse_arguments(CODE_REL "${options}" "${single_args}"
"${multi_args}" ${ARGN})

#解析参数后生成
#CODE_REL_FILES=file1.c file2.c
#CODE_REL_LOCATION=_TEXT
#CODE_REL_NOCOPY没有

# 参数验证
if(CODE_REL_UNPARSED_ARGUMENTS)
message(FATAL_ERROR "zephyr_code_relocate(${ARGV0} ...) "
"given unknown arguments: ${CODE_REL_UNPARSED_ARGUMENTS}")
endif()
if((NOT CODE_REL_FILES) AND (NOT CODE_REL_LIBRARY))
message(FATAL_ERROR
"zephyr_code_relocate() requires either FILES or LIBRARY be provided")
endif()
if(CODE_REL_FILES AND CODE_REL_LIBRARY)
message(FATAL_ERROR "zephyr_code_relocate() only accepts "
"one argument between FILES and LIBRARY")
endif()
if(NOT CODE_REL_LOCATION)
message(FATAL_ERROR "zephyr_code_relocate() requires a LOCATION argument")
endif()

#库和文件的结果都解析出来放到file_list中,
if(CODE_REL_LIBRARY)
# Use cmake generator expression to convert library to file list
set(genex_src_dir "$<TARGET_PROPERTY:${CODE_REL_LIBRARY},SOURCE_DIR>")
set(genex_src_list "$<TARGET_PROPERTY:${CODE_REL_LIBRARY},SOURCES>")
set(file_list
"${genex_src_dir}/$<JOIN:${genex_src_list},$<SEMICOLON>${genex_src_dir}/>")
else()
# Check if CODE_REL_FILES is a generator expression, if so leave it
# untouched.
string(GENEX_STRIP "${CODE_REL_FILES}" no_genex)
if(CODE_REL_FILES STREQUAL no_genex)
# no generator expression in CODE_REL_FILES, check if list of files
# is absolute
foreach(file ${CODE_REL_FILES})
if(NOT IS_ABSOLUTE ${file})
set(file ${CMAKE_CURRENT_SOURCE_DIR}/${file})
endif()
list(APPEND file_list ${file})
endforeach()
else()
# Generator expression is present in file list. Leave the list untouched.
set(file_list ${CODE_REL_FILES})
endif()
endif()

# 是否复制的标记放在copy_flag中
if(NOT CODE_REL_NOCOPY)
set(copy_flag COPY)
else()
set(copy_flag NOCOPY)
endif()

if(CODE_REL_PHDR)
set(CODE_REL_LOCATION "${CODE_REL_LOCATION}\ :${CODE_REL_PHDR}")
endif()

#最后的的结果放在code_data_relocation_target中
get_property(code_rel_str TARGET code_data_relocation_target
PROPERTY COMPILE_DEFINITIONS)
set_property(TARGET code_data_relocation_target
PROPERTY COMPILE_DEFINITIONS
"${code_rel_str}|${CODE_REL_LOCATION}:${copy_flag}:${file_list}")
endfunction()

生成的结果放在code_data_relocation_target变量中

1
SRAM_TEXT:COPY:file1.c;file2.c

在执行toolchain_ld_relocation时使用.zephyr/CMakeLists.txt在启用CONFIG_CODE_DATA_RELOCATION的情况下调用toolchain_ld_relocation

1
2
3
4
if(CONFIG_CODE_DATA_RELOCATION)
# @Intent: Linker script to relocate .text, data and .bss sections
toolchain_ld_relocation()
endif()

zephyr/cmake/linker/ld/target_relocation.cmake中定义了宏toolchain_ld_relocation,在该宏中调用脚本gen_relocate_app.py,实现解析如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
macro(toolchain_ld_relocation)
set(MEM_RELOCATION_LD "${PROJECT_BINARY_DIR}/include/generated/linker_relocate.ld")
set(MEM_RELOCATION_SRAM_DATA_LD
"${PROJECT_BINARY_DIR}/include/generated/linker_sram_data_relocate.ld")
set(MEM_RELOCATION_SRAM_BSS_LD
"${PROJECT_BINARY_DIR}/include/generated/linker_sram_bss_relocate.ld")
set(MEM_RELOCATION_CODE "${PROJECT_BINARY_DIR}/code_relocation.c")
set(MEM_REGION_DEFAULT_RAM RAM)

#调用gen_relocate_app.py 按照code_data_relocation_target生成重定位相关文件
add_custom_command(
OUTPUT ${MEM_RELOCATION_CODE} ${MEM_RELOCATION_LD}
COMMAND
${PYTHON_EXECUTABLE}
${ZEPHYR_BASE}/scripts/build/gen_relocate_app.py
$<$<BOOL:${CMAKE_VERBOSE_MAKEFILE}>:--verbose>
-d ${APPLICATION_BINARY_DIR}
-i \"$<TARGET_PROPERTY:code_data_relocation_target,COMPILE_DEFINITIONS>\"
-o ${MEM_RELOCATION_LD}
-s ${MEM_RELOCATION_SRAM_DATA_LD}
-b ${MEM_RELOCATION_SRAM_BSS_LD}
-c ${MEM_RELOCATION_CODE}
--default_ram_region ${MEM_REGION_DEFAULT_RAM}
DEPENDS app kernel ${ZEPHYR_LIBS_PROPERTY}
)

add_library(code_relocation_source_lib STATIC ${MEM_RELOCATION_CODE})
target_include_directories(code_relocation_source_lib PRIVATE
${ZEPHYR_BASE}/kernel/include ${ARCH_DIR}/${ARCH}/include)
target_link_libraries(code_relocation_source_lib zephyr_interface)
endmacro()

gen_relocate_app.py的路径为zephyr/scripts/build/gen_relocate_app.py主要参数:

  • -d 指向编译结果目录
  • -i 传入code_data_relocation_target,也就是这种形式SRAM_TEXT:COPY:file1.c;file2.c
  • -o 输出linker_relocate.ld
  • -s 输出linker_sram_data_relocate.ld
  • -b 输出linker_sram_bss_relocate.ld
  • -c 输出code_relocation.c
  • –default_ram_region 指定默认RAM区的名称为:RAM,如果不指定该参数默认为SRAM,该名称应出现在linker.ld的MEMORY中

gen_relocate_app.py的内容比较多,不列出分析,说明主要流程

get_obj_filename找出要重定位文件对应的obj文件
find_sections 找出obj中的section
assign_to_correct_mem_region 根据内存区域的段信息参数(_section, 例如SDRAM2_TEXT中的_TEXT)提取出需要的section信息
generate_linker_script 根据提取的section信息生成ld文件
generate_memcpy_code 根据提取的section文件生成拷贝c文件内容,同时会参考no_copy参数
dump_header_file 将c文件内容写入文件

gen_relocate_app.py生成的文件放在构建目录build/zephyr/include/generated/下,生成的ld文件有

  • linker_relocate.ld
  • linker_sram_data_relocate.ld
  • linker_sram_bss_relocate.ld
    主要就是做section的放置,这里不列出细节。
    生成的code_relocation.c主要内容如下, 可以看到就是各个section的拷贝:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    void data_copy_xip_relocation(void)
    {

    z_early_memcpy(&__dtcm_data_start, &__dtcm_data_rom_start,
    (size_t) &__dtcm_data_size);


    z_early_memcpy(&__itcm_text_start, &__itcm_text_rom_start,
    (size_t) &__itcm_text_size);


    z_early_memcpy(&__ram_text_start, &__ram_text_rom_start,
    (size_t) &__ram_text_size);


    z_early_memcpy(&__ram_rodata_start, &__ram_rodata_rom_start,
    (size_t) &__ram_rodata_size);


    z_early_memcpy(&__sram2_text_start, &__sram2_text_rom_start,
    (size_t) &__sram2_text_size);


    z_early_memcpy(&__sram2_rodata_start, &__sram2_rodata_rom_start,
    (size_t) &__sram2_rodata_size);


    z_early_memcpy(&__sram2_data_start, &__sram2_data_rom_start,
    (size_t) &__sram2_data_size);


    }

    void bss_zeroing_relocation(void)
    {

    z_early_memset(&__dtcm_bss_start, 0,
    (size_t) &__dtcm_bss_size);

    z_early_memset(&__sram2_bss_start, 0,
    (size_t) &__sram2_bss_size);

    }

生成文件的引用

ld文件的引用

不同soc在自己的linker.ld中引用生成ld文件,例如可以在zephyr/include/zephyr/arch/arm/aarch32/cortex_m/scripts/linker.ld中可以看到如下内容

1
2
3
4
5
6
7
8
9
10
11
12
13
#ifdef CONFIG_CODE_DATA_RELOCATION
#include <linker_sram_bss_relocate.ld>
#endif

#ifdef CONFIG_CODE_DATA_RELOCATION

#include <linker_relocate.ld>

#endif /* CONFIG_CODE_DATA_RELOCATION */

#ifdef CONFIG_CODE_DATA_RELOCATION
#include <linker_sram_data_relocate.ld>
#endif

c文件的应用

zephyr/cmake/linker/ld/target_relocation.cmake中通过下面方式将code_relocation.c加入构建

1
2
3
4
5
set(MEM_RELOCATION_CODE "${PROJECT_BINARY_DIR}/code_relocation.c")
add_library(code_relocation_source_lib STATIC ${MEM_RELOCATION_CODE})
target_include_directories(code_relocation_source_lib PRIVATE
${ZEPHYR_BASE}/kernel/include ${ARCH_DIR}/${ARCH}/include)
target_link_libraries(code_relocation_source_lib zephyr_interface)

code_relocation.c内的函数在启动代码中的调用如下:
zephyr/kernel/xip.c中执行代码和数据段拷贝

1
2
3
4
5
6
7
8
void z_data_copy(void)
{
#ifdef CONFIG_CODE_DATA_RELOCATION
extern void data_copy_xip_relocation(void);

data_copy_xip_relocation();
#endif /* CONFIG_CODE_DATA_RELOCATION */
}

zephyr/kernel/init.c中执行bss初始化

1
2
3
4
5
6
7
8
void z_bss_zero(void)
{
#ifdef CONFIG_CODE_DATA_RELOCATION
extern void bss_zeroing_relocation(void);

bss_zeroing_relocation();
#endif /* CONFIG_CODE_DATA_RELOCATION */
}

参考

https://docs.zephyrproject.org/latest/kernel/code-relocation.html