scripts: code_relocate: support section filter

One might want to select the symbols to be relocated inside a file or
a library. To do this, one can use the FILTER argument of
zephyr_code_relocate which must contain a regular expression of the
section names to be selected for relocation.

The test_function_in_sram2 test case in
`tests/application_development/code_relocation` has been updated to
verify that only one function `function_in_sram()` is relocated to ram
and that the function `function_not_relocated()` is not being relocated
when using relocation filter.

Signed-off-by: Sylvain Chouleur <sylvain.chouleur@gmail.com>
Signed-off-by: Torsten Rasmussen <Torsten.Rasmussen@nordicsemi.no>
This commit is contained in:
Sylvain Chouleur 2024-06-17 12:11:04 +02:00 committed by Benjamin Cabé
commit 4454734d12
6 changed files with 96 additions and 27 deletions

View file

@ -1453,7 +1453,7 @@ endmacro()
# - PHDR [program_header]: add program header. Used on Xtensa platforms.
function(zephyr_code_relocate)
set(options NOCOPY NOKEEP)
set(single_args LIBRARY LOCATION PHDR)
set(single_args LIBRARY LOCATION PHDR FILTER)
set(multi_args FILES)
cmake_parse_arguments(CODE_REL "${options}" "${single_args}"
"${multi_args}" ${ARGN})
@ -1530,7 +1530,7 @@ function(zephyr_code_relocate)
PROPERTY INTERFACE_SOURCES)
set_property(TARGET code_data_relocation_target
PROPERTY INTERFACE_SOURCES
"${code_rel_str}\n${CODE_REL_LOCATION}:${flag_list}:${file_list}")
"${code_rel_str}\n${CODE_REL_LOCATION}:${flag_list}:${file_list},${CODE_REL_FILTER}")
endfunction()
# Usage:

View file

@ -14,15 +14,23 @@ This script provides a robust way to re-order the memory contents without
actually having to modify the code. In simple terms this script will do the job
of ``__attribute__((section("name")))`` for a bunch of files together.
A regular expression filter can be used to select only the required sections to be relocated.
Details
*******
The memory region and file are given to the :ref:`gen_relocate_app.py` script in the form of a string.
The memory region and file are given to the :ref:`gen_relocate_app.py` script
through a file where each line specifies a list of files to be placed in the
given region.
An example of such a string is:
``SRAM2:/home/xyz/zephyr/samples/hello_world/src/main.c,SRAM1:/home/xyz/zephyr/samples/hello_world/src/main2.c``
An example of such a file is:
.. code-block:: none
SRAM2:/home/xyz/zephyr/samples/hello_world/src/main.c,
SRAM1:/home/xyz/zephyr/samples/hello_world/src/main2.c,
This script is invoked with the following parameters:
``python3 gen_relocate_app.py -i input_string -o generated_linker -c generated_code``
``python3 gen_relocate_app.py -i input_file -o generated_linker -c generated_code``
Kconfig :kconfig:option:`CONFIG_CODE_DATA_RELOCATION` option, when enabled in
``prj.conf``, will invoke the script and do the required relocation.
@ -97,6 +105,24 @@ This section shows additional configuration options that can be set in
zephyr_code_relocate(FILES ${sources} LOCATION SRAM)
zephyr_code_relocate(FILES $<TARGET_PROPERTY:my_tgt,SOURCES> LOCATION SRAM)
Section Filtering
=================
By default, all sections of the specified files will be relocated. If
``FILTER`` is used, a regular expression is provided to select only
the sections to be relocated.
The regular expression applies to sections names which can be used to
select the file's symbols when this one has been built with
``-ffunction-sections`` and ``-fdata-sections`` which is the case by
default.
.. code-block:: none
zephyr_code_relocate(FILES src/file1.c FILTER ".*\\.func1|.*\\.func2" LOCATION SRAM2_TEXT)
The example above will only relocate ``func1()`` and ``func2()`` of file ``src/file1.c``
NOKEEP flag
===========

View file

@ -8,21 +8,22 @@
"""
This script will relocate .text, .rodata, .data and .bss sections from required files
and places it in the required memory region. This memory region and file
are given to this python script in the form of a string.
are given to this python script in the form of a file.
A regular expression filter can be applied to select only the required sections from the file.
Example of such a string would be::
Example of content in such an input file would be::
SRAM2:COPY:/home/xyz/zephyr/samples/hello_world/src/main.c,\
SRAM1:COPY:/home/xyz/zephyr/samples/hello_world/src/main2.c, \
FLASH2:NOCOPY:/home/xyz/zephyr/samples/hello_world/src/main3.c
SRAM2:COPY:/home/xyz/zephyr/samples/hello_world/src/main.c,.*foo|.*bar
SRAM1:COPY:/home/xyz/zephyr/samples/hello_world/src/main2.c,.*bar
FLASH2:NOCOPY:/home/xyz/zephyr/samples/hello_world/src/main3.c,
One can also specify the program header for a given memory region:
SRAM2\\ :phdr0:COPY:/home/xyz/zephyr/samples/hello_world/src/main.c
SRAM2\\ :phdr0:COPY:/home/xyz/zephyr/samples/hello_world/src/main.c,
To invoke this script::
python3 gen_relocate_app.py -i input_string -o generated_linker -c generated_code
python3 gen_relocate_app.py -i input_file -o generated_linker -c generated_code
Configuration that needs to be sent to the python script.
@ -45,6 +46,7 @@ import sys
import argparse
import os
import glob
import re
import warnings
from collections import defaultdict
from enum import Enum
@ -225,7 +227,7 @@ def region_is_default_ram(region_name: str) -> bool:
return region_name == args.default_ram_region
def find_sections(filename: str) -> 'dict[SectionKind, list[OutputSection]]':
def find_sections(filename: str, symbol_filter: str) -> 'dict[SectionKind, list[OutputSection]]':
"""
Locate relocatable sections in the given object file.
@ -243,6 +245,9 @@ def find_sections(filename: str) -> 'dict[SectionKind, list[OutputSection]]':
out = defaultdict(list)
for section in sections:
if not re.search(symbol_filter, section.name):
# Section is filtered-out
continue
section_kind = SectionKind.for_section_named(section.name)
if section_kind is None:
continue
@ -501,9 +506,10 @@ def get_obj_filename(searchpath, filename):
# Extracts all possible components for the input string:
# <mem_region>[\ :program_header]:<flag_1>[;<flag_2>...]:<file_1>[;<file_2>...]
# Returns a 4-tuple with them: (mem_region, program_header, flags, files)
# <mem_region>[\ :program_header]:<flag_1>[;<flag_2>...]:<file_1>[;<file_2>...][,filter]
# Returns a 5-tuple with them: (mem_region, program_header, flags, files, filter)
# If no `program_header` is defined, returns an empty string
# If no `filter` is defined, returns an empty string
def parse_input_string(line):
# Be careful when splitting by : to avoid breaking absolute paths on Windows
mem_region, rest = line.split(':', 1)
@ -513,13 +519,19 @@ def parse_input_string(line):
mem_region = mem_region.rstrip()
phdr, rest = rest.split(':', 1)
# Split lists by semicolons, in part to support generator expressions
flag_list, file_list = (lst.split(';') for lst in rest.split(':', 1))
return mem_region, phdr, flag_list, file_list
flag_list, rest = rest.split(':', 1)
flag_list = flag_list.split(';')
# Create a dict with key as memory type and files as a list of values.
# Split file list by semicolons, in part to support generator expressions
file_list, symbol_filter = rest.split(',', 1)
file_list = file_list.split(';')
return mem_region, phdr, flag_list, file_list, symbol_filter
# Create a dict with key as memory type and (files, symbol_filter) tuple
# as a list of values.
# Also, return another dict with program headers for memory regions
def create_dict_wrt_mem():
# need to support wild card *
@ -529,11 +541,12 @@ def create_dict_wrt_mem():
input_rel_dict = args.input_rel_dict.read().splitlines()
if not input_rel_dict:
sys.exit("Disable CONFIG_CODE_DATA_RELOCATION if no file needs relocation")
for line in input_rel_dict:
if ':' not in line:
continue
mem_region, phdr, flag_list, file_list = parse_input_string(line)
mem_region, phdr, flag_list, file_list, symbol_filter = parse_input_string(line)
# Handle any program header
if phdr != '':
@ -556,12 +569,15 @@ def create_dict_wrt_mem():
if args.verbose:
print("Memory region ", mem_region, " Selected for files:", file_name_list)
# Apply filter on files
file_name_filter_list = [(f, symbol_filter) for f in file_name_list]
mem_region = "|".join((mem_region, *flag_list))
if mem_region in rel_dict:
rel_dict[mem_region].extend(file_name_list)
rel_dict[mem_region].extend(file_name_filter_list)
else:
rel_dict[mem_region] = file_name_list
rel_dict[mem_region] = file_name_filter_list
return rel_dict, phdrs
@ -585,13 +601,13 @@ def main():
for memory_type, files in rel_dict.items():
full_list_of_sections: 'dict[SectionKind, list[OutputSection]]' = defaultdict(list)
for filename in files:
for filename, symbol_filter in files:
obj_filename = get_obj_filename(searchpath, filename)
# the obj file wasn't found. Probably not compiled.
if not obj_filename:
continue
file_sections = find_sections(obj_filename)
file_sections = find_sections(obj_filename, symbol_filter)
# Merge sections from file into collection of sections for all files
for category, sections in file_sections.items():
full_list_of_sections[category].extend(sections)

View file

@ -17,7 +17,7 @@ endif()
# Code relocation feature
zephyr_code_relocate(FILES src/test_file1.c ${SRAM2_PHDR} LOCATION SRAM2)
zephyr_code_relocate(FILES ${CMAKE_CURRENT_SOURCE_DIR}/src/test_file2.c ${RAM_PHDR} LOCATION RAM)
zephyr_code_relocate(FILES ${CMAKE_CURRENT_SOURCE_DIR}/src/test_file2.c ${RAM_PHDR} LOCATION RAM FILTER ".*sram")
# Add custom library that we can relocate code for
add_subdirectory(test_lib)

View file

@ -23,12 +23,15 @@ __in_section(rodata, sram2, var) const uint32_t var_sram2_rodata = 100U;
__in_section(custom_section, static, var) uint32_t var_custom_data = 1U;
extern void function_in_sram(int32_t value);
extern void function_not_relocated(int32_t value);
void function_in_custom_section(void);
#define HAS_SRAM2_DATA_SECTION (CONFIG_ARM)
ZTEST(code_relocation, test_function_in_sram2)
{
extern uintptr_t __ram_text_reloc_start;
extern uintptr_t __ram_text_reloc_end;
extern uintptr_t __sram2_text_reloc_start;
extern uintptr_t __sram2_text_reloc_end;
extern uintptr_t __sram2_data_reloc_start;
@ -64,7 +67,20 @@ ZTEST(code_relocation, test_function_in_sram2)
"var_sram2_bss not in sram2_bss region");
/* Print values from sram */
printk("Address of function_in_sram %p\n", &function_in_sram);
zassert_between_inclusive((uintptr_t)&function_in_sram,
(uintptr_t)&__ram_text_reloc_start,
(uintptr_t)&__ram_text_reloc_end,
"function_in_sram is not in ram region");
function_in_sram(var_sram2_data);
/* Print values from non-relocated function */
printk("Address of function_not_relocated %p\n", &function_not_relocated);
zassert_between_inclusive((uintptr_t)&function_not_relocated,
(uintptr_t)&__text_region_start,
(uintptr_t)&__text_region_end,
"function_not_relocated is not in flash region");
function_not_relocated(var_sram2_data);
/* Call library function */
relocated_library();

View file

@ -19,3 +19,14 @@ void function_in_sram(int32_t value)
printk("Address of memcpy %p\n\n", &memcpy);
zassert_mem_equal(src, dst, 8, "memcpy compare error");
}
void function_not_relocated(int32_t value)
{
char src[8] = "data\n";
char dst[8];
printk("Hello World! %s\n", CONFIG_BOARD);
memcpy(dst, src, 8);
printk("Address of memcpy %p\n\n", &memcpy);
zassert_mem_equal(src, dst, 8, "memcpy compare error");
}