This is an automated email from the ASF dual-hosted git repository.

xiaoxiang781216 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit 4cdd3cdbda47867c66971fdf0ebe6bcc3e75c5f1
Author: Marco Casaroli <[email protected]>
AuthorDate: Fri Aug 28 18:22:08 2026 +0200

    arch/arm, libs/libc/elf: Build FDPIC modules in the normal ELF build.
    
    With CONFIG_FDPIC selected, a module built by apps/Application.mk is now an
    FDPIC shared object.  Nothing about how a module is written or built
    changes: the same MODULE = m in the same Makefile, the same crt0 and the
    same linker script.
    
    Two things differ from the position independent build beside it.  The
    compiler is told -mfdpic -fPIC, and the link is done by 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 stock
    linker carries the armelf emulation alone and would turn every import into
    an R_ARM_JUMP_SLOT, one word, where the ABI wants an R_ARM_FUNCDESC_VALUE,
    which is two, a code address and the data base that goes with it.  Such a
    module links cleanly and then calls out of itself with the caller's data
    base still in r9.  That linker is in the CI image.
    
    gnu-elf.ld.in gains the two segments an FDPIC module needs, under
    CONFIG_FDPIC, because the loader places its read-only and writable segments
    independently, and names .dynamic, because a shared object is bound through
    it.  The sections themselves are untouched and so are the symbols crt0.c
    walks, so one script serves both and both build systems get it.
    
    .bss moves to the end of the script, for every configuration and not only
    FDPIC.  It held no file content but sat ahead of .got and .dynamic, which
    do, so the writable segment's p_filesz had to span it and the module file
    carried the whole of .bss.  A module with 16 KiB of .bss went from 26724 to
    10340 bytes, and its writable segment from p_filesz 0x40ac to 0xac against
    an unchanged p_memsz.  The loader reads p_filesz off the media, so it read
    those bytes too.
    
    Built for mps3-an547:picostest with apps/examples/elf, CONFIG_FDPIC both
    ways.  With it on, every module in apps/bin is ARM FDPIC with two PT_LOAD
    segments and enters at _start; hello++3, which has a static C++ object,
    carries DT_INIT_ARRAY and DT_FINI_ARRAY.  With it off the generated script
    has no PHDRS and the modules are what they were.
    
    Assisted-by: Claude Opus 5 (1M context) <[email protected]>
    Signed-off-by: Marco Casaroli <[email protected]>
---
 arch/arm/src/common/Toolchain.defs | 18 +++++++++++
 libs/libc/elf/gnu-elf.ld.in        | 64 +++++++++++++++++++++++++++++++-------
 2 files changed, 71 insertions(+), 11 deletions(-)

diff --git a/arch/arm/src/common/Toolchain.defs 
b/arch/arm/src/common/Toolchain.defs
index 155ef3f3d7c..949b7161573 100644
--- a/arch/arm/src/common/Toolchain.defs
+++ b/arch/arm/src/common/Toolchain.defs
@@ -642,11 +642,29 @@ ifeq ($(CONFIG_PIC),y)
   # after including this file, which would discard the flag.
 
   ARCHCFLAGS += --fixed-r9
+
+ifeq ($(CONFIG_FDPIC),y)
+  # An FDPIC module is a shared object whose two segments the loader places
+  # independently.  The stock compiler emits correct FDPIC objects for both C
+  # and C++, so only the link needs the arm-uclinuxfdpiceabi linker: the
+  # stock one carries the armelf emulation alone and would turn every import
+  # into a jump slot where the ABI wants a function descriptor.
+
+  FDPIC_CROSSDEV ?= arm-uclinuxfdpiceabi-
+  MODULELD = $(FDPIC_CROSSDEV)ld
+
+  CELFFLAGS += -mfdpic -fPIC -Wa,--noexecstack
+  CXXELFFLAGS += -mfdpic -fPIC -Wa,--noexecstack
+
+  LDELFFLAGS += -m armelf_linux_fdpiceabi -shared -z now
+else
   CELFFLAGS += $(PICFLAGS) -mpic-register=r9
   CXXELFFLAGS += $(PICFLAGS) -mpic-register=r9
 
   # Generate an executable elf, need to ignore undefined symbols
   LDELFFLAGS += --unresolved-symbols=ignore-in-object-files --emit-relocs
+endif
+
 else
   ifneq ($(CONFIG_BINFMT_ELF_EXECUTABLE),y)
     LDELFFLAGS += -r
diff --git a/libs/libc/elf/gnu-elf.ld.in b/libs/libc/elf/gnu-elf.ld.in
index 340a91df5a2..be0c8776050 100644
--- a/libs/libc/elf/gnu-elf.ld.in
+++ b/libs/libc/elf/gnu-elf.ld.in
@@ -39,6 +39,34 @@
 #  define SECTIONS_ALIGN 4
 #endif
 
+/* An FDPIC module is a shared object whose read-only and writable segments
+ * the loader places independently: the read-only one runs where the
+ * filesystem already holds it and only the writable one is copied to RAM,
+ * once per running instance.  So the two go into segments of their own, and
+ * .dynamic is named, because a shared object is bound through it.
+ *
+ * Everything else is common, the symbols crt0.c walks included, so a module
+ * is built and entered the same way whichever this is.
+ */
+
+#ifdef CONFIG_FDPIC
+#  define PHDR_TEXT :text
+#  define PHDR_DATA :data
+#  define DATA_ALIGN . = ALIGN(0x1000);
+
+PHDRS
+{
+  text    PT_LOAD FLAGS(5);    /* Read and execute */
+  data    PT_LOAD FLAGS(6);    /* Read and write   */
+  dynamic PT_DYNAMIC FLAGS(6);
+}
+
+#else
+#  define PHDR_TEXT
+#  define PHDR_DATA
+#  define DATA_ALIGN
+#endif
+
 SECTIONS
 {
   .text TEXT :
@@ -53,7 +81,7 @@ SECTIONS
       *(.jcr)
       . = ALIGN(SECTIONS_ALIGN);
       _etext = . ;
-    }
+    } PHDR_TEXT
 
   .rodata :
     {
@@ -64,7 +92,9 @@ SECTIONS
       *(.gnu.linkonce.r*)
       . = ALIGN(SECTIONS_ALIGN);
       _erodata = . ;
-    }
+    } PHDR_TEXT
+
+  DATA_ALIGN
 
   .data DATA :
     {
@@ -75,7 +105,7 @@ SECTIONS
       *(.gnu.linkonce.d*)
       . = ALIGN(SECTIONS_ALIGN);
       _edata = . ;
-    }
+    } PHDR_DATA
 
   .init_array :
     {
@@ -86,7 +116,7 @@ SECTIONS
       . = ALIGN(SECTIONS_ALIGN);
       _einit = .;
       _ectors = .;
-    }
+    } PHDR_DATA
 
   .fini_array :
     {
@@ -98,7 +128,24 @@ SECTIONS
       . = ALIGN(SECTIONS_ALIGN);
       _efini = .;
       _edtors = .;
-    }
+    } PHDR_DATA
+
+#ifdef CONFIG_FDPIC
+  .dynamic :
+    {
+      *(.dynamic)
+    } :data :dynamic
+#endif
+
+  .got :
+    {
+      *(.got*)
+    } PHDR_DATA
+
+  /* .bss holds no file content, so it comes last.  Everything ahead of it
+   * has content, thus the writable segment's p_filesz stops where .bss
+   * starts and the file does not carry it.
+   */
 
   .bss :
     {
@@ -111,12 +158,7 @@ SECTIONS
       *(COMMON)
       . = ALIGN(SECTIONS_ALIGN);
       _ebss = . ;
-    }
-
-  .got :
-    {
-      *(.got*)
-    }
+    } PHDR_DATA
 
     /* Stabs debugging sections.    */
 

Reply via email to