Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 50 additions & 8 deletions arch/arm/src/cmake/elf.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,59 @@ nuttx_mod_compile_options(-fvisibility=hidden -mlong-calls)
nuttx_elf_compile_options_ifdef(CONFIG_UNWINDER_ARM -fno-unwind-tables
-fno-asynchronous-unwind-tables)

# An ELF module needs r9 as its PIC base, so it must not also have the register
# fixed: GCC rejects that pair with "unable to use 'r9' for PIC register". This
# mirrors CELFFLAGS in common/Toolchain.defs, which filters --fixed-r9 back out
# of the inherited CFLAGS for the same reason.
if(CONFIG_FDPIC)

nuttx_elf_compile_options_ifdef(CONFIG_PIC -mpic-register=r9)
# 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.

nuttx_elf_link_options_ifdef(
CONFIG_PIC --unresolved-symbols=ignore-in-object-files --emit-relocs)
if(NOT FDPIC_CROSSDEV)
set(FDPIC_CROSSDEV arm-uclinuxfdpiceabi-)
endif()

# Say which linker is missing rather than failing later with a command that
# cannot be run.

find_program(FDPIC_LD "${FDPIC_CROSSDEV}ld")

if(NOT FDPIC_LD)
message(
FATAL_ERROR
"CONFIG_FDPIC needs ${FDPIC_CROSSDEV}ld, which is not on PATH. "
"It is in the NuttX CI image, and tools/ci/docker/linux/Dockerfile "
"shows how it is built. Set FDPIC_CROSSDEV to use a different prefix")
endif()

set(CMAKE_ELF_LD
"${FDPIC_LD}"
CACHE INTERNAL "Linker for FDPIC modules")

nuttx_elf_compile_options(-mfdpic -fPIC -Wa,--noexecstack)

nuttx_elf_link_options(-m armelf_linux_fdpiceabi -shared -z now)

nuttx_elf_link_options_ifdef(CONFIG_BINFMT_ELF_RELOCATABLE -r)
elseif(CONFIG_PIC)

# An ELF module needs r9 as its PIC base, so it must not also have the
# register fixed: GCC rejects that pair with "unable to use 'r9' for PIC
# register". This mirrors CELFFLAGS in common/Toolchain.defs, which filters
# --fixed-r9 back out of the inherited CFLAGS for the same reason.

nuttx_elf_compile_options(-mpic-register=r9)

nuttx_elf_link_options(--unresolved-symbols=ignore-in-object-files
--emit-relocs)

endif()

# Not with CONFIG_PIC: there the module is linked as an executable, which is
# what common/Toolchain.defs does too.

if(CONFIG_BINFMT_ELF_RELOCATABLE AND NOT CONFIG_PIC)
Comment thread
xiaoxiang781216 marked this conversation as resolved.
nuttx_elf_link_options(-r)
endif()

nuttx_mod_link_options(-r)

Expand Down
34 changes: 34 additions & 0 deletions arch/arm/src/common/Toolchain.defs
Original file line number Diff line number Diff line change
Expand Up @@ -642,11 +642,45 @@ 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

# Say which linker is missing rather than letting make report a command it
# cannot run. The report is deferred to the link itself rather than made
# here, so that a tree configured for FDPIC on a host without the linker can
# still be cleaned and reconfigured.

FDPIC_LD_FOUND := $(shell command -v $(MODULELD) 2> /dev/null)

ifeq ($(FDPIC_LD_FOUND),)
FDPIC_NO_LD_MSG = CONFIG_FDPIC needs $(FDPIC_CROSSDEV)ld, which is not \
on PATH. It is in the NuttX CI image, and \
tools/ci/docker/linux/Dockerfile shows how it is built. Set \
FDPIC_CROSSDEV to use a different prefix.

MODULELD = $(SHELL) -c 'echo "ERROR: $(FDPIC_NO_LD_MSG)" 1>&2; exit 1' --
endif

CELFFLAGS += -mfdpic -fPIC -Wa,--noexecstack
Comment thread
xiaoxiang781216 marked this conversation as resolved.
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
Expand Down
12 changes: 10 additions & 2 deletions cmake/nuttx_add_application.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,15 @@ function(nuttx_add_application)
if(TARGET STARTUP_OBJS)
add_dependencies(${TARGET} STARTUP_OBJS)
endif()
if(NOT "${CMAKE_LD}" MATCHES "gcc$")
# A module may need a different linker from the one that links the
# firmware: an FDPIC module does, because the stock linker cannot
# produce one. CMAKE_ELF_LD is that linker, and it is the ordinary one
# unless the architecture says otherwise.

if(NOT CMAKE_ELF_LD)
set(CMAKE_ELF_LD ${CMAKE_LD})
endif()
if(NOT "${CMAKE_ELF_LD}" MATCHES "gcc$")
set(USE_LINKER True)
endif()
if(STACKSIZE)
Expand Down Expand Up @@ -179,7 +187,7 @@ function(nuttx_add_application)
POST_BUILD
COMMAND
# add default link option
${CMAKE_LD} -T ${NUTTX_BINARY_DIR}/gnu-elf.ld
${CMAKE_ELF_LD} -T ${NUTTX_BINARY_DIR}/gnu-elf.ld
# add global MOD link option if dynlib link
$<$<BOOL:${DYNLIB_ELF_MODE}>:$<TARGET_PROPERTY:nuttx_global,NUTTX_MOD_APP_LINK_OPTIONS>>
# add global ELF link option if m&kernel link
Expand Down
64 changes: 53 additions & 11 deletions libs/libc/elf/gnu-elf.ld.in
Original file line number Diff line number Diff line change
Expand Up @@ -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 :
Expand All @@ -53,7 +81,7 @@ SECTIONS
*(.jcr)
. = ALIGN(SECTIONS_ALIGN);
_etext = . ;
}
} PHDR_TEXT

.rodata :
{
Expand All @@ -64,7 +92,9 @@ SECTIONS
*(.gnu.linkonce.r*)
. = ALIGN(SECTIONS_ALIGN);
_erodata = . ;
}
} PHDR_TEXT

DATA_ALIGN

.data DATA :
{
Expand All @@ -75,7 +105,7 @@ SECTIONS
*(.gnu.linkonce.d*)
. = ALIGN(SECTIONS_ALIGN);
_edata = . ;
}
} PHDR_DATA

.init_array :
{
Expand All @@ -86,7 +116,7 @@ SECTIONS
. = ALIGN(SECTIONS_ALIGN);
_einit = .;
_ectors = .;
}
} PHDR_DATA

.fini_array :
{
Expand All @@ -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 :
Comment thread
xiaoxiang781216 marked this conversation as resolved.
{
Expand All @@ -111,12 +158,7 @@ SECTIONS
*(COMMON)
. = ALIGN(SECTIONS_ALIGN);
_ebss = . ;
}

.got :
{
*(.got*)
}
} PHDR_DATA

/* Stabs debugging sections. */

Expand Down
Loading