This is an automated email from the ASF dual-hosted git repository. acassis pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit 9f1107862b9d32be74736aa21b0d515c74036173 Author: Marco Casaroli <[email protected]> AuthorDate: Sun Aug 23 23:31:37 2026 +0200 binfmt, arch/arm: Add the CONFIG_FDPIC option and the ABI header. The commits that follow teach the ELF loader to load an FDPIC object. This puts the option they hang off and the definitions they share in one place first, so each of them builds on its own. CONFIG_FDPIC depends on ARCH_HAVE_ELF_FDPIC, which an architecture selects when it has a PIC base register and the FDPIC relocations. Only armv7-m and armv8-m select it today, and it defaults off, so nothing changes for anyone who does not ask for it. include/nuttx/fdpic.h holds what both sides of the loader need: the two word function descriptor an FDPIC module passes instead of a code address, the test for whether the caller is such a module, and the call sequence that enters one with its own data base. All of it is behind CONFIG_FDPIC, thus the header is empty without it and a file may include it unconditionally. The call sequence itself is architecture specific, so arch/arm/include/arch.h supplies it as up_fdpic_invoke(), beside the other PIC base register macros. up_setpicbase() cannot serve here: the register has to hold the module's base for exactly one call and then go back, and nothing in C tells the compiler the register is live across that call, so the save, the install, the branch and the restore have to be one sequence. Built for mps3-an547:bl and mps3-an547:picostest, with CONFIG_FDPIC off, which is every configuration in the tree. Assisted-by: Claude Opus 5 (1M context) <[email protected]> Signed-off-by: Marco Casaroli <[email protected]> --- arch/Kconfig | 7 +++ arch/arm/Kconfig | 2 + arch/arm/include/arch.h | 39 +++++++++++++++ binfmt/Kconfig | 32 ++++++++++++ include/nuttx/fdpic.h | 129 ++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 209 insertions(+) diff --git a/arch/Kconfig b/arch/Kconfig index 3de2bd2caf4..b40bab2a0f0 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -710,6 +710,13 @@ config ARCH_HAVE_ELF_EXECUTABLE bool default n +config ARCH_HAVE_ELF_FDPIC + bool + default n + ---help--- + The architecture has a PIC base register and the ELF relocations + that an FDPIC object uses. + config ARCH_HAVE_TRUSTZONE bool default n diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 6a96c672816..9bbf8540d5d 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -1052,6 +1052,7 @@ config ARCH_ARMV7M default n select ARCH_HAVE_CPUINFO select ARCH_HAVE_DEBUG + select ARCH_HAVE_ELF_FDPIC select ARCH_HAVE_PERF_EVENTS config ARCH_CORTEXM3 @@ -1190,6 +1191,7 @@ config ARCH_ARMV8M default n select ARCH_HAVE_CPUINFO select ARCH_HAVE_DEBUG + select ARCH_HAVE_ELF_FDPIC select ARCH_HAVE_PERF_EVENTS config ARCH_CORTEXM23 diff --git a/arch/arm/include/arch.h b/arch/arm/include/arch.h index a91136c4662..da8429bfb59 100644 --- a/arch/arm/include/arch.h +++ b/arch/arm/include/arch.h @@ -81,6 +81,45 @@ do { \ ); \ } while (0) +#ifdef CONFIG_FDPIC + +/**************************************************************************** + * Name: up_fdpic_invoke + * + * Description: + * Call a module entry point with the module data base in the PIC base + * register. Put the caller data base back after the call. + * + * Input Parameters: + * arg - The one word argument, passed in r0. + * entry - The code address to enter. + * got - The module data base to install. + * + ****************************************************************************/ + +static inline void up_fdpic_invoke(uintptr_t arg, uintptr_t entry, + uintptr_t got) +{ + register uintptr_t r0v __asm__ ("r0") = arg; + + /* arg is already in r0. r4 goes on the stack with the PIC register to + * keep the push aligned to 8 bytes. + */ + + __asm__ __volatile__ + ( + "push {r4, " PIC_REG_STRING "}\n" /* Save the caller's base */ + "mov " PIC_REG_STRING ", %[got]\n" /* Install the module's base */ + "blx %[entry]\n" /* Enter the module */ + "pop {r4, " PIC_REG_STRING "}\n" /* Restore the caller's base */ + : "+r" (r0v) + : [entry] "r" (entry), [got] "r" (got) + : "r1", "r2", "r3", "r12", "lr", "cc", "memory" + ); +} + +#endif /* CONFIG_FDPIC */ + #endif /* CONFIG_PIC */ #ifdef CONFIG_ARCH_ADDRENV diff --git a/binfmt/Kconfig b/binfmt/Kconfig index 93844898da0..dd0678af577 100644 --- a/binfmt/Kconfig +++ b/binfmt/Kconfig @@ -60,6 +60,38 @@ config ELF_STACKSIZE default DEFAULT_TASK_STACKSIZE ---help--- This is the default stack size that will be used when starting ELF binaries. + +config FDPIC + bool "FDPIC modules" + default n + select PIC + depends on ARCH_HAVE_ELF_FDPIC + ---help--- + Load ELF modules built for the FDPIC ABI. + + An FDPIC module places its read-only and writable segments + independently, so its text can be executed directly out of flash + while only the writable segment is copied to RAM, once per running + instance. A filesystem that can show its media, such as XIPFS or + ROMFS, gives that result. On any other filesystem the loader copies + the text to RAM, and the module runs but shares nothing. + + Building a module needs 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. + + What this adds over the position independent ELF support already + present is a function pointer that carries its own data base, as a + two word descriptor rather than a bare code address. That is what + lets a module be called back on a thread it did not create, such as + the work queue worker that runs a SIGEV_THREAD notification. + + Selecting this makes ten libc and sched entry points that can + accept a callback from a module resolve such a descriptor before + storing or branching to it. Each costs a register read and a + branch on a path that is not hot. + + This implementation covers ARM Thumb-2. endif endif diff --git a/include/nuttx/fdpic.h b/include/nuttx/fdpic.h new file mode 100644 index 00000000000..27b997f7f90 --- /dev/null +++ b/include/nuttx/fdpic.h @@ -0,0 +1,129 @@ +/**************************************************************************** + * include/nuttx/fdpic.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_FDPIC_H +#define __INCLUDE_NUTTX_FDPIC_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <stdint.h> + +#include <nuttx/arch.h> +#include <nuttx/compiler.h> + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* A function descriptor: what a function pointer is under FDPIC. The + * firmware branches to a code address; a module passes one of these. + */ + +struct fdpic_desc_s +{ + uintptr_t entry; /* Address of the code */ + uintptr_t got; /* Data base to install before branching */ +}; + +/**************************************************************************** + * Inline Functions + ****************************************************************************/ + +#ifdef CONFIG_FDPIC + +/**************************************************************************** + * Name: fdpic_base + * + * Description: + * The data base of the calling context, from the PIC base register. + * Non-zero means the caller is an FDPIC module, zero means firmware. + * + ****************************************************************************/ + +static inline uintptr_t fdpic_base(void) +{ + uintptr_t base; + + up_getpicbase(&base); + return base; +} + +/**************************************************************************** + * Name: fdpic_callback + * + * Description: + * Resolve a function pointer from a caller that may be an FDPIC module. + * Only the entry point is taken: the data base is already in the register. + * + * Input Parameters: + * fn - The pointer as it was received. + * + * Returned Value: + * An address that can be branched to directly. + * + ****************************************************************************/ + +static inline FAR void *fdpic_callback(FAR void *fn) +{ + if (fn != NULL && fdpic_base() != 0) + { + return (FAR void *)((FAR struct fdpic_desc_s *)fn)->entry; + } + + return fn; +} + +/**************************************************************************** + * Name: fdpic_invoke + * + * Description: + * Call a resolved module entry point with the module data base in the PIC + * base register. For a callback that runs on a shared thread, which + * carries no module base. Elsewhere fdpic_callback() is enough. + * + * Input Parameters: + * arg - The one word argument. + * entry - The code address to enter, already resolved from the descriptor. + * got - The module data base to install. + * + ****************************************************************************/ + +static inline void fdpic_invoke(uintptr_t arg, uintptr_t entry, + uintptr_t got) +{ + up_fdpic_invoke(arg, entry, got); +} + +#else + +# define fdpic_base() (0) +# define fdpic_callback(fn) (fn) +# define fdpic_invoke(arg, entry, got) \ + ((void)(got), (((CODE void (*)(uintptr_t))(uintptr_t)(entry))(arg))) + +#endif /* CONFIG_FDPIC */ + +#endif /* __INCLUDE_NUTTX_FDPIC_H */
