casaroli opened a new pull request, #19584:
URL: https://github.com/apache/nuttx/pull/19584

   ## Summary
   
   Adds an FDPIC ELF loader so a downloadable module can execute **in place** 
out of memory-mapped flash: the read-only segment is mapped where it already 
sits on the media and never copied to RAM, and only the writable segment is 
copied, once per running instance.
   
   The in-tree ELF loader already does shared `.text` with private `.data` on a 
no-MMU target (`CONFIG_PIC`, `mps3-an547:picostest`), so that part is not new. 
What FDPIC adds is a function pointer that carries its own data base, as a 
two-word descriptor `{entry, GOT}` rather than a bare code address. Two things 
follow that a single base register per task cannot express: a module can be 
called back on a thread it never created, such as the work-queue worker that 
runs a `SIGEV_THREAD` notification; and two objects can hold distinct data 
bases at once, which is what makes `DT_NEEDED` shared libraries work with 
per-instance library data.
   
   Compared with NXFLAT: FDPIC is standard ELF, needs no `mknxflat`/`ldnxflat` 
and no linker script, puts `.rodata` in the RX segment on its own, supports 
shared libraries, and needs no workaround for a pointer to a `static` function. 
The cost is an `arm-uclinuxfdpiceabi` linker; the stock `arm-none-eabi` 
compiler emits correct FDPIC objects for both C and C++, so only the link needs 
it.
   
   The loader is `binfmt/fdpic.c` under `CONFIG_FDPIC` (depends on 
`CONFIG_ARCH_ARM`, selects `BINFMT_LOADABLE` and `PIC`). It requires a 
filesystem that answers `BIOC_XIPBASE`, such as XIPFS or ROMFS. 
`arch/arm/src/common/Toolchain.defs` adds `--fixed-r9` so the base firmware 
reserves the FDPIC register; without it a firmware callback into module code 
arrives with the wrong data base.
   
   Nine commits: the `DT_*_ARRAY` tags in `include/elf.h`; the loader; 
constructors and PLT relocation binding; descriptor resolution at the 
libc/sched entry points that accept a module callback; `SIGEV_THREAD` 
notifications; the developer guide in `Documentation/components/fdpic.rst`; the 
out-of-tree module build tooling in `tools/fdpic`; and a board configuration 
plus the demo's documentation.
   
   Depends on the matching apps change: opened alongside this one in 
apache/nuttx-apps, linked below
   
   ## Impact
   
   New feature, off by default. `CONFIG_FDPIC` defaults to `n`, so a tree that 
does not enable it is unaffected.
   
   Existing code touched in two places, both no-ops without `CONFIG_FDPIC`. 
First, ten libc/sched entry points that can accept a callback from a module now 
resolve an FDPIC descriptor before storing or branching to it: `qsort`, 
`bsearch`, `pthread_create`, `signal`, `sigaction`, 
`task_create`/`task_create_with_stack`, `task_spawn`, `pthread_once`, 
`scandir`, and `mq_notify`/`timer_create` with `SIGEV_THREAD`. Each is guarded 
by `#ifdef CONFIG_FDPIC`. Second, `Toolchain.defs` adds `--fixed-r9` to 
`ARCHCPUFLAGS`, again only under `CONFIG_FDPIC`, which costs the base firmware 
one register in that configuration.
   
   Adds one board configuration, `pimoroni-pico-2-plus:xipfs-fdpic`. It builds 
with a plain `arm-none-eabi` toolchain because the test modules are committed 
as prebuilt blobs, so CI covers it; it deliberately does not enable 
`CONFIG_NXFLAT`, which would need `mknxflat` and require excluding the 
configuration from the build list.
   
   Documentation: one new page, `Documentation/components/fdpic.rst`, added to 
the components toctree, plus the `fdpic` and `reject` sections in the xipfs 
test suite page.
   
   No change to any existing API, ABI, or default configuration. FDPIC is ARM 
Thumb-2 only; RISC-V has no FDPIC ABI, so a RISC-V target cannot use this 
loader.
   
   ## Testing
   
   Host: macOS 26.5.1 on arm64. Arm GNU Toolchain 15.2.Rel1 
(`arm-none-eabi-gcc` 15.2.1), `arm-uclinuxfdpiceabi-ld` from GNU Binutils 2.43, 
QEMU 11.0.3.
   
   **QEMU, `mps2-an500` (Cortex-M7, ARMv7E-M)** — the loader on a different 
core generation from the hardware below, with xipfs on a `rammtd` device that 
answers `BIOC_XIPBASE`. Built from `mps2-an500:xipfs` plus the FDPIC options. 
Full `xipfs_test` suite:
   
   ```
   ==== 130 passed, 0 failed ====
   ```
   
   The two FDPIC sections on their own:
   
   ```
   nsh> xipfs_test fdpic
   ==== 33 passed, 0 failed ====
   
   nsh> xipfs_test reject
   -- FDPIC loader rejections --
     PASS  a non-32-bit ELF class is refused
     PASS  a non-ET_DYN object is refused
     PASS  a module for the wrong machine is refused
     PASS  a module with RELA PLT relocations is refused
     PASS  a module whose needed library is absent is refused
     PASS  a module importing an unexported symbol is refused
     PASS  a module with too many DT_NEEDED entries is refused
   ==== 7 passed, 0 failed ====
   ```
   
   The `fdpicxip` demo, all four subcommands. `solib`, showing two instances 
sharing one copy of a library's text in flash while each gets its own copy of 
its data:
   
   ```
   nsh> fdpicxip solib
   === FDPIC shared library, executed in place ===
   
   staged libcounter.so (2052 bytes) and user (2732 bytes)
     libcounter.so text at 0x6000cd20
     user          text at 0x6000dd20
   
   spawning two instances, each bumping by its own seed...
   
   [while running] pins on the library's shared text = 2
   
   [user 1] library total = 3 (expected 3) -- PASS
   [user 2] library total = 6 (expected 6) -- PASS
   
   [after exit]    pins on the library's shared text = 0
   ```
   
   `cxx` additionally confirms each object's global constructors ran in 
dependency order before `main`, and `jmprel` that a module whose imports are 
all in `DT_JMPREL` binds and calls out.
   
   **Hardware, Pimoroni Pico Plus 2 (RP2350, Cortex-M33, ARMv8-M)** against 
real QSPI flash, `pimoroni-pico-2-plus:xipfs-fdpic`: full suite 130/130, 
`fdpic` 33/33, `reject` 7/7, and all four demo subcommands. The same module 
blobs run on both cores; they are built for `cortex-m3`, so one set serves v7-M 
and v8-M.
   
   **Simulator, `sim:xipfs`** on the host, which exercises the filesystem half 
only since the loader is ARM-only:
   
   ```
   ==== 90 passed, 0 failed ====
   ```
   
   **Loader assertions verified against deliberate breakage.** Each check in 
the `fdpic` section was confirmed to fail when the thing it names is broken — 
removing the `scandir` filter resolution, adding a second resolution of the 
comparison function, and removing the FDPIC register install around a 
`SIGEV_THREAD` callback each take the board down with a HardFault rather than 
printing a FAIL line, which is how a broken callback resolution manifests on 
Cortex-M.
   
   **`--fixed-r9` reaches the compiler**, checked per the guide, and prints 
that flag and no other reserved-register flag:
   
   ```
   $ make V=1 2>&1 | grep -o -- "--fixed-r[0-9]*" | sort -u
   --fixed-r9
   ```
   
   `tools/checkpatch.sh -f` passes on every changed C and header file.
   
   🤖 Generated with [Claude Code](https://claude.com/claude-code)
   
   https://claude.ai/code/session_01X77FVXXW4bvvBtfFmz1JPz
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to