From ebabdd57f3bd8a98fb4c5091319b907522445a8a Mon Sep 17 00:00:00 2001 From: Marco Casaroli Date: Wed, 7 Oct 2026 08:09:50 +0200 Subject: [PATCH] arch/arm: Keep GCC from making tail calls in an FDPIC module. With -mfdpic and -mlong-calls, GCC turns a call to an imported function in tail position into "ldr r3, [r9, #off]; bx r3". The GOT slot holds the address of the function descriptor, so the branch goes to the descriptor in RAM instead of through it, and the core faults. A normal call loads the code address and the data base from the descriptor first. GCC 13.2 and 15.3 both do this. Only an optimized build makes tail calls. The C++ library of apps/testing/fs/xipfs then faults in its constructor, which ends in a call to syslog(), and the test stops at "stage the C++ module". Pass -fno-optimize-sibling-calls with the other FDPIC flags, in the make build and the CMake build. Assisted-by: Claude Code:claude-opus-5-5 Signed-off-by: Marco Casaroli --- arch/arm/src/cmake/elf.cmake | 11 +++++++++-- arch/arm/src/common/Toolchain.defs | 15 +++++++++++---- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/arch/arm/src/cmake/elf.cmake b/arch/arm/src/cmake/elf.cmake index 465f442ae3f..45fb444a814 100644 --- a/arch/arm/src/cmake/elf.cmake +++ b/arch/arm/src/cmake/elf.cmake @@ -58,15 +58,22 @@ if(CONFIG_FDPIC) # GCC before 14 does not pass --fdpic to the assembler for -mfdpic, and the # assembler then rejects the FDPIC relocations, so pass it here. + # + # With -mlong-calls, GCC turns a call in tail position into a branch to the + # function descriptor rather than through it, which faults. Keep it from + # making tail calls. - nuttx_elf_compile_options(-mfdpic -fPIC -Wa,--noexecstack -Wa,--fdpic) + set(FDPIC_FLAGS -mfdpic -fPIC -Wa,--noexecstack -Wa,--fdpic + -fno-optimize-sibling-calls) + + nuttx_elf_compile_options(${FDPIC_FLAGS}) nuttx_elf_link_options(-m armelf_linux_fdpiceabi -shared -z now) # A shared library is an FDPIC shared object too, as LDMODULEFLAGS makes it in # common/Toolchain.defs - nuttx_mod_compile_options(-mfdpic -fPIC -Wa,--noexecstack -Wa,--fdpic) + nuttx_mod_compile_options(${FDPIC_FLAGS}) nuttx_mod_link_options(-m armelf_linux_fdpiceabi -shared -z now) diff --git a/arch/arm/src/common/Toolchain.defs b/arch/arm/src/common/Toolchain.defs index d681cd17278..787e048fcd9 100644 --- a/arch/arm/src/common/Toolchain.defs +++ b/arch/arm/src/common/Toolchain.defs @@ -686,9 +686,16 @@ ifeq ($(CONFIG_FDPIC),y) # GCC before 14 does not pass --fdpic to the assembler for -mfdpic, and the # assembler then rejects the FDPIC relocations, so pass it here. + # + # With -mlong-calls, GCC turns a call in tail position into a branch to the + # function descriptor rather than through it, which faults. Keep it from + # making tail calls. - CELFFLAGS += -mfdpic -fPIC -Wa,--noexecstack -Wa,--fdpic - CXXELFFLAGS += -mfdpic -fPIC -Wa,--noexecstack -Wa,--fdpic + FDPICFLAGS = -mfdpic -fPIC -Wa,--noexecstack -Wa,--fdpic \ + -fno-optimize-sibling-calls + + CELFFLAGS += $(FDPICFLAGS) + CXXELFFLAGS += $(FDPICFLAGS) LDELFFLAGS += -m armelf_linux_fdpiceabi -shared -z now @@ -700,8 +707,8 @@ ifeq ($(CONFIG_FDPIC),y) # The entry point is left to the caller: a module is entered at _start, # a library is only called into. - CMODULEFLAGS += -mfdpic -fPIC -Wa,--noexecstack -Wa,--fdpic - CXXMODULEFLAGS += -mfdpic -fPIC -Wa,--noexecstack -Wa,--fdpic + CMODULEFLAGS += $(FDPICFLAGS) + CXXMODULEFLAGS += $(FDPICFLAGS) LDMODULEFLAGS = -m armelf_linux_fdpiceabi -shared -z now \ -T $(call CONVERT_PATH,$(TOPDIR)/libs/libc/elf/gnu-elf.ld)