Skip to content

Commit

Permalink
[nrf fromtree] scripts: code_relocate: support section filter
Browse files Browse the repository at this point in the history
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 <[email protected]>
Signed-off-by: Torsten Rasmussen <[email protected]>
(cherry picked from commit 4454734d127ad5519c2f8d72e002d5471add4d24)
  • Loading branch information
schouleu authored and carlescufi committed Feb 14, 2025
1 parent 1e22329 commit d5aefbe
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 26 deletions.
4 changes: 2 additions & 2 deletions cmake/modules/extensions.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -1452,7 +1452,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})
Expand Down Expand Up @@ -1529,7 +1529,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:
Expand Down
34 changes: 30 additions & 4 deletions doc/kernel/code-relocation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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 file is:

.. code-block:: none
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``
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.
Expand Down Expand Up @@ -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
===========

Expand Down
54 changes: 35 additions & 19 deletions scripts/build/gen_relocate_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -45,6 +46,7 @@
import argparse
import os
import glob
import re
import warnings
from collections import defaultdict
from enum import Enum
Expand Down Expand Up @@ -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.
Expand All @@ -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
Expand Down Expand Up @@ -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)
Expand All @@ -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))
flag_list, rest = rest.split(':', 1)
flag_list = flag_list.split(';')


# 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
return mem_region, phdr, flag_list, file_list, symbol_filter


# Create a dict with key as memory type and files as a list of values.
# 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 *
Expand All @@ -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 != '':
Expand All @@ -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

Expand All @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
16 changes: 16 additions & 0 deletions tests/application_development/code_relocation/src/test_file1.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();

Expand Down
11 changes: 11 additions & 0 deletions tests/application_development/code_relocation/src/test_file2.c
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}

0 comments on commit d5aefbe

Please sign in to comment.