Skip to content
7 changes: 7 additions & 0 deletions arch/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -724,6 +724,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
Expand Down
2 changes: 2 additions & 0 deletions arch/arm/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -1096,6 +1096,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
Expand Down Expand Up @@ -1245,6 +1246,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
Expand Down
39 changes: 39 additions & 0 deletions arch/arm/include/arch.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,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:
* entry - The code address to enter.
* arg - The one word argument, passed in r0.
* got - The module data base to install.
*
****************************************************************************/

static inline void up_fdpic_invoke(uintptr_t entry, uintptr_t arg,
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
Expand Down
12 changes: 12 additions & 0 deletions arch/arm/include/elf.h
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,18 @@
#define R_ARM_THM_TLS_DESCSEQ16 129 /* Thumb16 */
#define R_ARM_THM_TLS_DESCSEQ32 130 /* Thumb32 */

/* FDPIC relocations. Values from the ARM FDPIC ABI as implemented by
* binutils (include/elf/arm.h).
*/

#define R_ARM_GOTFUNCDESC 161 /* Data GOT entry holding a descriptor */
#define R_ARM_GOTOFFFUNCDESC 162 /* Data GOT-relative descriptor */
#define R_ARM_FUNCDESC 163 /* Data Address of a descriptor */
#define R_ARM_FUNCDESC_VALUE 164 /* Data The descriptor itself: {code, GOT} */
#define R_ARM_TLS_GD32_FDPIC 165 /* Data */
#define R_ARM_TLS_LDM32_FDPIC 166 /* Data */
#define R_ARM_TLS_IE32_FDPIC 167 /* Data */

/* Processor specific values for the Phdr p_type field. */

#define PT_ARM_EXIDX (PT_LOPROC + 1) /* ARM unwind segment. */
Expand Down
32 changes: 32 additions & 0 deletions binfmt/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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.

FDPIC is specified only for ARM Thumb-2.
endif
endif

Expand Down
7 changes: 7 additions & 0 deletions include/elf.h
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@
#define ELFOSABI_MODESTO 11 /* Novell Modesto. */
#define ELFOSABI_OPENBSD 12 /* OpenBSD. */
#define ELFOSABI_ARM_AEABI 64 /* ARM EABI */
#define ELFOSABI_ARM_FDPIC 65 /* ARM FDPIC */
#define ELFOSABI_ARM 97 /* ARM */
#define ELFOSABI_STANDALONE 255 /* Standalone (embedded) application */

Expand Down Expand Up @@ -278,6 +279,12 @@
#define DT_TEXTREL 22 /* d_un=ignored */
#define DT_JMPREL 23 /* d_un=d_ptr */
#define DT_BINDNOW 24 /* d_un=ignored */
#define DT_INIT_ARRAY 25 /* d_un=d_ptr */
#define DT_FINI_ARRAY 26 /* d_un=d_ptr */
#define DT_INIT_ARRAYSZ 27 /* d_un=d_val */
#define DT_FINI_ARRAYSZ 28 /* d_un=d_val */
#define DT_PREINIT_ARRAY 32 /* d_un=d_ptr */
#define DT_PREINIT_ARRAYSZ 33 /* d_un=d_val */
#define DT_LOPROC 0x70000000 /* d_un=unspecified */
#define DT_HIPROC 0x7fffffff /* d_un= unspecified */

Expand Down
129 changes: 129 additions & 0 deletions include/nuttx/fdpic.h
Original file line number Diff line number Diff line change
@@ -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:
* entry - The code address to enter, already resolved from the descriptor.
* arg - The one word argument.
* got - The module data base to install.
*
****************************************************************************/

static inline void fdpic_invoke(uintptr_t entry, uintptr_t arg,
uintptr_t got)
{
up_fdpic_invoke(entry, arg, got);
}

#else

# define fdpic_base() (0)
# define fdpic_callback(fn) (fn)
# define fdpic_invoke(entry, arg, got) \
((void)(got), (((CODE void (*)(uintptr_t))(uintptr_t)(entry))(arg)))

#endif /* CONFIG_FDPIC */

#endif /* __INCLUDE_NUTTX_FDPIC_H */
40 changes: 40 additions & 0 deletions include/nuttx/lib/elf.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,15 @@
# define CONFIG_LIBC_ELF_MAXDEPEND 0
#endif

/* A compacting filesystem gives its media address with a pin that holds the
* blocks in place. The loader holds the pin through a file reference,
* because the unload runs on another task. Flat build only.
*/

#if defined(CONFIG_FS_XIPFS) && defined(CONFIG_BUILD_FLAT)
# define HAVE_LIBC_ELF_PIN 1
#endif

#ifndef CONFIG_LIBC_ELF_ALIGN_LOG2
# define CONFIG_LIBC_ELF_ALIGN_LOG2 2
#endif
Expand Down Expand Up @@ -123,6 +132,7 @@ typedef CODE int (*mod_uninitializer_t)(FAR void *arg);
* nexports - The number of symbols in the exported symbol table.
*/

struct file;
struct symtab_s;
struct mod_info_s
{
Expand Down Expand Up @@ -252,6 +262,36 @@ struct mod_loadinfo_s
* skip the copy.
*/

/* FDPIC state.
*
* fdpic - True if e_ident[EI_OSABI] marked this an FDPIC object.
* textpin - True if a filesystem pin holds the read-only segment, which
* the loader gives back at unload.
*/

bool fdpic;
bool textpin;

#ifdef HAVE_LIBC_ELF_PIN
/* The file the pin is held through, handed to the module once it loads. */

FAR struct file *pinfile;
#endif

/* The object's data base, from DT_PLTGOT. An FDPIC module runs with this
* in the PIC base register.
*/

uintptr_t gotaddr;

/* Pool of function descriptors behind the writable segment. Reserved
* when the segment is sized, and bounded by the relocation count.
*/

uintptr_t descpool;
uint16_t ndesc; /* Capacity */
uint16_t usedesc; /* Next free slot */

/* Address environment.
*
* addrenv - This is the handle created by addrenv_allocate() that can be
Expand Down
Loading
Loading