xiaoxiang781216 commented on code in PR #19584:
URL: https://github.com/apache/nuttx/pull/19584#discussion_r3699543446
##########
binfmt/fdpic.c:
##########
@@ -1130,6 +1438,24 @@ static int fdpic_loadbinary(FAR struct binary_s *binp,
}
}
+ /* Constructors, now that every object can reach its own data.
+ *
+ * fdpic_release() can undo a load but cannot undo a constructor, so this
+ * sits ahead of the D-Space allocation rather than after it: the only way
+ * left to discard a load with constructors already run is a constructor
+ * list that fails partway through, and such a module is unusable anyway.
+ * Destructors are not run for the entries that did succeed -- a table this
+ * loader has just found malformed is not one to branch into on the way
+ * out.
+ */
+
+ ret = fdpic_runinit(head);
Review Comment:
it's wrong to call init in the caller context
##########
tools/fdpic/README.md:
##########
@@ -0,0 +1,61 @@
+# FDPIC module build tooling
+
+Everything needed to build an FDPIC module out of tree: a module is an ELF
+shared object whose read-only segment the target maps straight out of flash
+and executes in place, while its writable segment is copied to RAM once per
+running instance. It links against nothing -- libc and everything else are
+imported from the firmware's exported symbol table at load time.
+
+The loader that consumes these is `binfmt/fdpic.c`, enabled by `CONFIG_FDPIC`.
+The full description of the format, the toolchain and the load-time contract
+is in `Documentation/components/fdpic.rst`.
+
+## Contents
+
+| File | Purpose |
+| --- | --- |
+| `nuttx-fdpic.mk` | the module build itself; include it from a two-line
makefile |
+| `fdpic-verify.sh` | checks a built module's imports resolve against the
firmware |
+| `nuttx-exports.sh` | turns `libs/libc/exec_symtab.c` into a symbol list |
+| `fdpic-embed.py` | turns a built module into a C header, for carrying one in
an image |
+| `build-binutils.sh` | builds the `arm-uclinuxfdpiceabi` binutils, the one
from-source dependency |
Review Comment:
why not incorporate fdpic special process into the correct ELF file build?
##########
sched/task/task_create.c:
##########
Review Comment:
should we let elf call the base image symbol through PLT? so the PLT could
extract function pointer from function descriptor before invoking the real
function.
##########
include/nuttx/binfmt/fdpic.h:
##########
@@ -212,8 +212,85 @@ static inline FAR void *fdpic_callback(FAR void *fn)
return fn;
}
+
+/****************************************************************************
+ * Name: fdpic_base
+ *
+ * Description:
+ * The FDPIC data base of the calling context, taken from the FDPIC
+ * register. Non-zero means the caller is an FDPIC module; zero means base
+ * firmware. This is the same test fdpic_callback() makes, exposed for a
+ * caller that must decide whether a pointer it was handed is a descriptor
+ * before it stores it somewhere the register will no longer be correct --
+ * a callback that will run on a work-queue thread, for instance.
+ *
+ ****************************************************************************/
+
+static inline uintptr_t fdpic_base(void)
+{
+ uintptr_t base;
+
+ __asm__ __volatile__ ("mov %0, r9" : "=r"(base));
+
+ return base;
+}
+
+/****************************************************************************
+ * Name: fdpic_invoke
+ *
+ * Description:
+ * Call a resolved module entry point with the module's data base installed
+ * in the FDPIC register, and restore the caller's base afterwards.
+ *
+ * This is for the one case the register cannot already be correct: a
+ * callback that a module registered but that runs on a shared thread --
+ * the signal-notification work queue -- which carries no module's base.
+ * The base is captured at registration (fdpic_base(), in the module's
+ * context) and installed here around the call. Everywhere else the
+ * callback runs in a task that inherited the module's data space and only
+ * the entry point has to be resolved; there fdpic_callback() is enough.
+ *
+ * A context switch or interrupt during the call is safe: the FDPIC
+ * register is REG_PIC in the saved register context, so it is preserved
+ * and restored across a switch, and base firmware is built with it
+ * reserved so no interrupt handler disturbs it.
+ *
+ * Input Parameters:
+ * entry - The code address to enter (already resolved from the
+ * descriptor).
+ * arg - The single word argument, passed in r0.
+ * got - The module data base to install in the FDPIC register.
+ *
+ ****************************************************************************/
+
+static inline void fdpic_invoke(uintptr_t entry, uintptr_t arg,
+ uintptr_t got)
+{
+ register uintptr_t r0v __asm__ ("r0") = arg;
+
+ /* arg is pinned in r0 (the first argument and the call's scratch), so the
+ * asm needs registers only for entry and got -- kept deliberately few so
+ * the allocator has room on builds that reserve a frame pointer. r9 is
+ * saved on the stack rather than in a scratch register; r4 rides along
+ * only to keep the push 8-byte aligned and is restored untouched.
+ */
+
+ __asm__ __volatile__
+ (
+ "push {r4, r9}\n" /* Save the caller's FDPIC register */
+ "mov r9, %[got]\n" /* Install the module's data base */
+ "blx %[entry]\n" /* Enter the module */
+ "pop {r4, r9}\n" /* Restore the caller's FDPIC register */
+ : "+r" (r0v)
+ : [entry] "r" (entry), [got] "r" (got)
+ : "r1", "r2", "r3", "r12", "lr", "cc", "memory"
+ );
+}
#else
# define fdpic_callback(fn) (fn)
+# define fdpic_base() (0)
Review Comment:
why not use up_getpicbase and up_setpicbase
##########
Documentation/components/fdpic.rst:
##########
@@ -0,0 +1,490 @@
+=============
+FDPIC Modules
+=============
+
+FDPIC modules are ELF shared objects that 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; only the writable segment is copied, once
+per running instance. Shared libraries work, and so does C++ with global
+constructors.
+
+The loader is ``binfmt/fdpic.c``, enabled by ``CONFIG_FDPIC``. Modules are
+built out of tree with the tooling in ``tools/fdpic``.
+
+How it works
+============
+
+An FDPIC object has two ``PT_LOAD`` segments that may be placed independently
+of one another. Nothing in the code depends on the distance between them,
+which is what lets the loader leave the read-only segment in flash and put the
+writable one wherever RAM is available.
+
+Code reaches its own data through a base register -- **r9** on ARM -- holding
+the address of that object's GOT. A function pointer is therefore not enough
+to call a function: the callee needs its data base too. FDPIC represents a
+function pointer as a two-word **descriptor**:
+
+=========== ==============================================================
+Word Contents
+=========== ==============================================================
+``entry`` code address, including its Thumb bit
+``got`` data base to install in r9 before branching
+=========== ==============================================================
+
+Building those descriptors is most of what the loader does. Because each one
+names its own base, two objects can be in use at once with separate writable
+segments, and a pointer handed to the firmware carries everything needed to
+call back into the module later.
+
+A module links against nothing. libc and everything else are undefined
+imports, resolved at load time against other loaded objects first and the
+firmware's exported symbol table second.
+
+Load sequence
+-------------
+
+``binfmt/fdpic.c``, in order:
+
+#. Parse the ELF and program headers and check the FDPIC marker. Anything
+ else is declined quietly so another binfmt handler can try.
+
+#. Read ``PT_DYNAMIC`` from the file before either segment is placed: the
+ relocation count bounds the descriptor pool the writable allocation needs.
+
+#. Pin the read-only segment on the media with ``XIPFSIOC_PIN``. The returned
+ pointer *is* the execution address. The pin stops the filesystem
+ relocating code that is executing.
+
+#. Allocate the writable segment, copy ``.data`` from flash, zero ``.bss``.
+
+#. Walk ``DT_NEEDED`` and load each shared library the same way, depth capped
+ by ``FDPIC_MAX_DEPTH``.
+
+#. Apply relocations for every object, from both ``DT_REL`` and ``DT_JMPREL``.
+
+#. Run ``DT_INIT_ARRAY`` for every object, dependencies first.
+
+#. Hand the scheduler a ``struct dspace_s`` holding the module's GOT, which
+ ``up_initial_state()`` installs into r9.
+
+On unload the loader runs ``DT_FINI_ARRAY`` -- module first, then its
+libraries -- while their writable segments still exist, drops the pin, and
+frees the allocation. The pin release does not depend on the module calling
+``munmap``: ``mm_map_destroy()`` invokes each mapping's callback during group
+release, so a module that faults or is killed still drops its pin.
+
+Differences from NXFLAT and ELF PIC
+===================================
+
+All three run position-independent code from flash on a target with no MMU,
+and all three give several instances of one module a shared ``.text`` with
+private ``.data``. They differ in what a *pointer* can express and in what
+the toolchain has to provide.
+
+========================= ============== ============== ============
+Property NXFLAT ELF PIC (XIP) FDPIC
Review Comment:
it's better to add FDPIC support into ELF, just like how the origin ELF add
the support of PIC(XIP).
##########
tools/fdpic/nuttx-fdpic.mk:
##########
@@ -0,0 +1,221 @@
+############################################################################
+# tools/fdpic/nuttx-fdpic.mk
Review Comment:
where you support cmake
--
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]