nuttx/Documentation/components/iterable_sections.rst
Jorge Guzman f595ba31e2
Some checks are pending
Build Documentation / build-html (push) Waiting to run
MemBrowse Memory Report / changes-filter (push) Waiting to run
MemBrowse Memory Report / load-targets (push) Waiting to run
MemBrowse Memory Report / identical (push) Blocked by required conditions
MemBrowse Memory Report / analyze (push) Blocked by required conditions
include/nuttx/macro.h: Let FOREACH_ARG() paste the argument index
FOREACH_ARG() hands the position of each argument as the expression
"count - N", which can be used as a value but cannot be pasted into an
identifier, because "##" pastes a single token.  Hand it out as a
literal instead, so that an action macro can build a symbol name out of
it, which is what a subsystem needs when the link order of its objects
has to follow the declaration order.

The list is reversed through REVERSE_ARG() so that the position becomes
the level of the chain, and each step emits the recursion before its own
action so that the actions still come out in the order the arguments
were given.  This is how the same problem is solved upstream in Zephyr,
whose FOR_EACH_IDX() also reverses the list before walking a per arity
chain.

The index values are unchanged, so the arithmetic use keeps working:
NOTE_PRINTF_TYPES(), the only user in tree, produces the same tags for
every argument count.  It now supplies the leading zero itself, because
FOREACH_ARG() no longer expands to "0" when the list is empty.  An empty
list expanding to nothing is what lets an action expanding to
declarations be used at file scope.

Assisted-by: Claude Code
Signed-off-by: Jorge Guzman <jorge.gzm@gmail.com>
2026-09-01 17:55:52 -03:00

174 lines
6.6 KiB
ReStructuredText

=================
Iterable Sections
=================
Iterable sections provide **link-time registration** of ``struct``
instances: an instance defined with :c:macro:`STRUCT_SECTION_ITERABLE` in
any compilation unit is placed in a dedicated linker input section. The
linker collects all instances into a contiguous, name-sorted array
delimited by ``_<type>_list_start``/``_<type>_list_end`` symbols, which
the code can then iterate like a plain C array -- no runtime registration
calls, no central list to maintain.
This is the same mechanism used by the Zephyr RTOS ``STRUCT_SECTION_*``
macros. The first user of this infrastructure is the zbus message bus
port (``apps/system/zbus``, from nuttx-apps).
C API
=====
The macros are provided by ``include/nuttx/iterable_sections.h``:
.. code-block:: c
#include <nuttx/iterable_sections.h>
struct my_entry
{
const char *name;
int value;
};
/* In any .c file (const places the instance in ROM): */
const STRUCT_SECTION_ITERABLE(my_entry, entry_foo) =
{
.name = "foo",
.value = 42,
};
/* In the file that iterates: declare the section boundaries once, at
* file scope, then loop with a caller-declared pointer.
*/
STRUCT_SECTION_DECLARE(my_entry);
void print_entries(void)
{
FAR struct my_entry *entry;
STRUCT_SECTION_FOREACH(my_entry, entry)
{
printf("%s = %d\n", entry->name, entry->value);
}
}
Available macros:
* ``STRUCT_SECTION_ITERABLE(type, varname)`` -- define an instance inside
the iterable section ``._<type>.static.<varname>``. The variable name
is part of the input section name, so the linker's ``SORT_BY_NAME()``
defines the iteration order. A subsystem that needs a specific order
encodes it in the name, usually with a fixed width index:
``FOREACH_ARG()`` from ``nuttx/macro.h`` hands the position of each item
to the definition macro as a literal that can be pasted into the name.
* ``STRUCT_SECTION_DECLARE(type)`` -- declare the boundary symbols (file
scope), required before iterating.
* ``STRUCT_SECTION_FOREACH(type, iterator)`` -- for-loop over all
instances; ``iterator`` is a pointer declared by the caller, as with
``list_for_every_entry()``.
* ``STRUCT_SECTION_GET(type, i, dst)`` -- random access by index.
* ``STRUCT_SECTION_COUNT(type, dst)`` -- number of instances.
* ``STRUCT_SECTION_START/END/START_EXTERN/END_EXTERN`` -- direct access
to the boundary symbols.
Linker integration
==================
The collection step needs linker script support. Two mechanisms are
available; both rely on the fact that the linker scripts listed in
``ARCHSCRIPT`` are preprocessed with CPP (arm, arm64, risc-v, xtensa,
x86_64 and tricore), so ``#include`` and ``#ifdef CONFIG_*`` work inside
them.
Board script include (first-class mechanism)
--------------------------------------------
The board linker script includes the central fragments, which expand to
nothing unless a subsystem using iterable sections is enabled:
.. code-block:: text
.text :
{
...
*(.gnu.linkonce.r.*)
#include <nuttx/linker/common-rom.ld>
_etext = ABSOLUTE(.);
} > flash
.data :
{
_sdata = ABSOLUTE(.);
...
#include <nuttx/linker/common-ram.ld>
. = ALIGN(4);
_edata = ABSOLUTE(.);
} > sram AT > flash
* ``common-rom.ld`` collects the read-only (``const``) iterable sections
and must be included inside the read-only output section (typically
``.text``, before ``_etext``).
* ``common-ram.ld`` collects mutable *initialized* iterable sections and
must be included inside ``.data`` (between ``_sdata`` and ``_edata``)
so the startup FLASH-to-RAM copy initializes the entries.
* Subsystems add their sections to these central files, guarded by their
Kconfig option (see ``include/nuttx/linker/common-rom.ld`` for the zbus
example).
Supplementary INSERT script (zero-touch mode)
---------------------------------------------
With ``CONFIG_ITERABLE_SECTIONS_LINKER_INSERT`` the central script
``include/nuttx/linker/common-insert.ld`` is added before the board
script by the build system (``tools/Config.mk`` for Make, the top-level
``CMakeLists.txt`` for CMake) and supplements it through the GNU ld
``INSERT AFTER`` command, so **no board script modification is needed**.
The file defines one output section, ``.iterable_sections``, inserted
after ``.text``; subsystems add their ``ITERABLE_SECTION()`` blocks
inside it, guarded by their Kconfig option, exactly as in
``common-rom.ld`` (which expands to nothing in this mode).
This mode has constraints, discovered the hard way and worth knowing
before choosing it:
* GNU ld only (``INSERT`` is not supported by the macOS ld64).
* The INSERT script must come *before* the board script on the linker
command line. Adding it via ``ARCHSCRIPT`` from ``tools/Config.mk``
guarantees that, because ``Config.mk`` is included by the board
``Make.defs`` before it appends its own script. (The reversed order
fails with ``.text not found for insert``.)
* GNU ld assigns an INSERTed output section to a ``MEMORY`` region by
*attribute matching in declaration order*, not by inheriting the anchor
section's region. The ROM/flash region must therefore be the first
region compatible with read-only sections. Boards declaring a generic
``rwx`` region at a lower address first (e.g. an ITCM at ``0x0``) are
incompatible with this mode and must use the board script include.
* Giving the inserted section an explicit address is **not** a fix: a
section with an explicit address does not consume the memory region,
so the next region-allocated section overlaps it.
Alignment rules
===============
Instances are aligned to the natural alignment of their type
(``STRUCT_SECTION_ITERABLE`` adds ``__aligned__(__alignof__(type))``), and
``sizeof`` is always a multiple of ``alignof``, so the collected section
can be indexed as a plain array with no padding between entries from
different compilation units. The fragments additionally align the list
boundaries to 4 bytes.
Adding a new iterable type
==========================
1. Define the instances with ``STRUCT_SECTION_ITERABLE(mytype, name)``.
2. Add ``ITERABLE_SECTION(mytype)`` to
``include/nuttx/linker/common-rom.ld`` (const) or ``common-ram.ld``
(mutable initialized), guarded by the subsystem Kconfig option.
3. Iterate with ``STRUCT_SECTION_FOREACH(mytype, it)`` after
``STRUCT_SECTION_DECLARE(mytype);`` at file scope.
Caveat on generated linker scripts: the preprocessed ``.ld.tmp`` files
only depend on the board script and ``.config``; after editing the
central fragments during development, remove the ``.tmp`` files (or run
``make clean``) to force regeneration.