diff --git a/Documentation/guides/fully_linked_elf.rst b/Documentation/guides/fully_linked_elf.rst new file mode 100644 index 0000000000..69e3d2b715 --- /dev/null +++ b/Documentation/guides/fully_linked_elf.rst @@ -0,0 +1,533 @@ +ELF Programs – No Symbol Tables +=============================== + +You can easily extend the firmware in your released, embedded system using ELF +programs provided via a file system (for example, an SD card or downloaded into +on-board SPI FLASH). In order to support such post-release updates, your +released firmware would have to support execution of fully linked, relocatable +ELF programs loaded into RAM (see, for example, ``apps/examples/elf``). + +The files shown in this Wiki page can be downloaded `here `_. + +Alan Carvalho de Assis has also made a video based on this example in the +YouTube `NuttX Channel `_. + +Creating the Export Package +--------------------------- + +At the time that you release the firmware, you should create and save an +export package. The export package is all that you need to create +post-release, add-on modules for your embedded system. Let's illustrate this +using the ``STM32F4-Discovery`` networking ``NSH`` configuration with the +``STM32F4DIS-BB`` baseboard. (This demonstration assumes that you also have +support for some externally modifiable media in the board configuration, such +as removable media like an SD card, or a USB FLASH stick, an internal file +system remotely accessible via USB MSC, FTP, or any remote file system (NFS). +The networking ``NSH`` configuration uses the SD card on the STM32 baseboard +for this demonstration. Other ``NSH`` configurations could be used, provided +that you supply the necessary file system support in some fashion.) + +(No baseboard? You can add file system support to the basic ``STM32F4-Discovery`` +board by following these instructions: +`USB FLASH drive `_ +or `SD card `_.) + +.. code-block:: shell + + $ make distclean + $ tools/configure.sh -c stm32f4discovery:netnsh + $ make menuconfig + +Your released firmware would have to have been built with a few important +configuration settings: + +1. Disable networking (Only because it is not needed in this example): + +.. code-block:: shell + + # CONFIG_NET is not set + +2. Enable basic ELF binary support with no built-in symbol table support: + +.. code-block:: shell + + CONFIG_ELF=y + CONFIG_LIBC_EXECFUNCS=y + # CONFIG_EXECFUNCS_HAVE_SYMTAB is not set + +3. Enable PATH variable support: + +.. code-block:: shell + + CONFIG_BINFMT_EXEPATH=y + CONFIG_PATH_INITIAL="/bin" + # CONFIG_DISABLE_ENVIRON not set + +4. Enable execution of ELF files from the ``NSH`` command line: + +.. code-block:: shell + + CONFIG_NSH_FILE_APPS=y + +.. note:: + + You must enable some application that uses ``printf()``. This is necessary + to assure that the symbol ``printf()`` is included in the base system. + Here we assume that you include the "Hello, World!" example from + ``apps/examples/hello``: + +.. code-block:: shell + + CONFIG_EXAMPLES_HELLO=y + +Then we can build the NuttX firmware image and the export package: + +.. code-block:: shell + + $ make + $ make export + +When ``make export`` completes, you will find a ZIP'ed package in the top-level +NuttX directory called ``nuttx-export-x.y.zip`` (for version ``x.y``). The +version is determined by the ``.version`` file in the same directory. The +content of this ZIP file is the following directory structure: + +.. code-block:: shell + + nuttx-export-x.x + |- arch/ + |- build/ + |- include/ + |- libs/ + |- startup/ + |- System.map + `- .config + +The Add-On Build Directory +-------------------------- + +In order to create the add-on ELF program, you will need (1) the export +package, (2) the program build ``Makefile``, (3) a linker script used by the +``Makefile``, and (4) a Bash script to create a linker script. That +``Makefile`` and Bash Script are discussed in the following paragraphs. + +.. note:: + + These example files implicitly assume a GNU tool chain is used and, in at + least one place, that the target is an ARMv7-M platform. A non-GNU tool + chain would probably require a significantly different ``Makefile`` and + linker script. There is at least one ARMv7-M specific change that would + have to be made for other platforms in the script that creates the linker + script (``mkdefines.sh``). + +Hello Example +------------- + +To keep things manageable, let's use a concrete example. Suppose the ELF +program that we wish to add to the release code is the single source file +``hello.c``: + +.. code-block:: c + + #include + + int main(int argc, char **argv) + { + printf("Hello from Add-On Program!\n"); + return 0; + } + +Let's say that we have a directory called ``addon`` and it contains the +``hello.c`` source file, a ``Makefile`` that will create the ELF program, and a +Bash script called ``mkdefines.sh`` that will create a linker script. + +Building the ELF Program +------------------------ + +The first step in creating the ELF program is to unzip the Export Package. We +start with our ``addon`` directory containing the following: + +.. code-block:: shell + + $ cd addon + $ ls + gnu-elf.ld hello.c Makefile mkdefines.sh nuttx-export-7.25.zip + +Where: + +- ``gnu-elf.ld`` is the linker script. +- ``hello.c`` is our example source file. +- ``Makefile`` will build our ELF program and symbol table. +- ``mksymtab.h`` is the Bash script that will create the symbol table for the + ELF program. +- ``nuttx-export-7.25.zip`` is the Export Package for NuttX-7.25. + +We unzip the Export Package like: + +.. code-block:: shell + + $ unzip nuttx-export-7.25.zip + +Then we have a new directory called ``nuttx-export-7.25`` that contains all of +the content from the released NuttX code that we need to build the ELF +program. + +The Makefile +------------ + +The ELF program is created simply as: + +.. code-block:: shell + + $ make + +This uses the following ``Makefile`` to generate several files: + +- ``hello.o``: The compiled ``hello.c`` object. +- ``hello.r``: A "partially linked" ELF object that still has undefined + symbols. +- ``hello``: The fully linked, relocatable ELF program. +- ``linker.ld``: A linker script created by ``mkdefines.sh``. + +Only the resulting ``hello`` is needed. + +Below is the ``Makefile`` used to create the ELF program: + +.. code-block:: shell + + include nuttx-export-7.25/build/Make.defs + + # Long calls are need to call from RAM into FLASH + + ARCHCFLAGS += -mlong-calls + ARCHWARNINGS = -Wall -Wstrict-prototypes -Wshadow -Wundef + ARCHOPTIMIZATION = -Os -fno-strict-aliasing -fno-strength-reduce -fomit-frame-pointer + ARCHINCLUDES = -I. -isystem nuttx-export-7.25/include + + CFLAGS = $(ARCHCFLAGS) $(ARCHWARNINGS) $(ARCHOPTIMIZATION) $(ARCHINCLUDES) -pipe + + CROSSDEV = arm-none-eabi- + CC = $(CROSSDEV)gcc + LD = $(CROSSDEV)ld + STRIP = $(CROSSDEV)strip --strip-unneeded + + # Setup up linker command line options + + LDRELFLAGS = -r + + LDELFFLAGS = -r -e main + LDELFFLAGS += -T defines.ld -T gnu-elf.ld + + # This might change in a different environment + + OBJEXT ?= .o + + # This is the generated ELF program + + BIN = hello + REL = hello.r + + # These are the sources files that we use + + SRCS = hello.c + OBJS = $(SRCS:.c=$(OBJEXT)) + + # Build targets + + all: $(BIN) + .PHONY: clean + + $(OBJS): %$(OBJEXT): %.c + $(CC) -c $(CFLAGS) -o $@ $< + + System.map: nuttx-export-7.25/System.map + cat nuttx-export-7.25/System.map | sed -e "s/\r//g" >System.map + + $(REL): $(OBJS) + $(LD) $(LDRELFLAGS) -o $@ $< + + defines.ld: System.map $(REL) + ./mkdefines.sh System.map "$(REL)" >defines.ld + + $(BIN): defines.ld $(REL) + $(LD) $(LDELFFLAGS) -o $@ $(REL) + $(STRIP) $(REL) + + clean: + rm -f $(BIN) + rm -f $(REL) + rm -f defines.ld + rm -f System.map + rm -f *.o + +The Linker Script +----------------- + +Two linker scripts are used. One is a normal file (we'll call it the main +linker script), and the other, ``defines.ld``, is created on-the-fly as +described in the next section. + +The main linker script, ``gnu-elf.ld``, contains the following: + +.. code-block:: shell + + SECTIONS + { + .text 0x00000000 : + { + _stext = . ; + *(.text) + *(.text.*) + *(.gnu.warning) + *(.stub) + *(.glue_7) + *(.glue_7t) + *(.jcr) + _etext = . ; + } + + .rodata : + { + _srodata = . ; + *(.rodata) + *(.rodata1) + *(.rodata.*) + *(.gnu.linkonce.r*) + _erodata = . ; + } + + .data : + { + _sdata = . ; + *(.data) + *(.data1) + *(.data.*) + *(.gnu.linkonce.d*) + _edata = . ; + } + + .bss : + { + _sbss = . ; + *(.bss) + *(.bss.*) + *(.sbss) + *(.sbss.*) + *(.gnu.linkonce.b*) + *(COMMON) + _ebss = . ; + } + + /* Stabs debugging sections. */ + + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_info 0 : { *(.debug_info) } + .debug_line 0 : { *(.debug_line) } + .debug_pubnames 0 : { *(.debug_pubnames) } + .debug_aranges 0 : { *(.debug_aranges) } + } + +Creating the ``defines.ld`` Linker Script +----------------------------------------- + +The additional linker script ``defines.ld`` is created through a three-step +process: + +1. The ``Makefile`` generates a partially linked ELF object, ``hello.r``. +2. The ``Makefile`` then invokes the ``mkdefines.sh`` script, which generates + the ``defines.ld`` linker script that provides values for all of the + undefined symbols. +3. Finally, the ``Makefile`` produces the fully linked, relocatable ``hello`` + ELF object using the ``defines.ld`` linker script. + +Below is the version of ``mkdefines.sh`` used in this demo: + +.. code-block:: bash + + #!/bin/bash + + usage="Usage: $0 " + + # Check for the required path to the System.map file + + sysmap=$1 + if [ -z "$sysmap" ]; then + echo "ERROR: Missing " + echo "" + echo $usage + exit 1 + fi + + # Check for the required partially linked file + + relprog=$2 + if [ -z "$relprog" ]; then + echo "ERROR: Missing " + echo "" + echo $usage + exit 1 + fi + + # Verify the System.map and the partially linked file + + if [ ! -r "$sysmap" ]; then + echo "ERROR: $sysmap does not exist" + echo "" + echo $usage + exit 1 + fi + + if [ ! -r "$relprog" ]; then + echo "ERROR: $relprog does not exist" + echo "" + echo $usage + exit 1 + fi + + # Extract all of the undefined symbols from the partially linked file and create a + # list of sorted, unique undefined variable names. + + varlist=`nm $relprog | fgrep ' U ' | sed -e "s/^[ ]*//g" | cut -d' ' -f2 | sort - | uniq` + + # Now output the linker script that provides a value for all of the undefined symbols + + for var in $varlist; do + map=`grep " ${var}$" ${sysmap}` + if [ -z "$map" ]; then + echo "ERROR: Variable $var not found in $sysmap" + echo "" + echo $usage + exit 1 + fi + + varaddr=`echo ${map} | cut -d' ' -f1` + echo "${var} = 0x${varaddr} | 0x00000001;" + done + +This script uses the ``nm`` utility to find all of the undefined symbols in the +ELF object, then searches for the address of each undefined symbol in the +``System.map`` that was created when the released firmware was built. Finally, +it uses the symbol name and the symbol address to create each symbol table +entry. + +.. note:: + + - For the ARMv7-M architecture, bit 0 of the address must be set to indicate + thumb mode. If you are using a different architecture that requires + normal aligned addresses, you will need to change the following line by + eliminating the ORed value: + + .. code-block:: shell + + echo "${var} = 0x${varaddr} | 0x00000001;" + + - If the new ELF module uses a symbol that is not provided in the base + firmware and, hence, not included in the ``System.map`` file, this script + will fail. In that case, you will need to provide the missing logic + within the ELF program itself, if possible. + + - The technique as described here is only valid in the FLAT build mode. It + could probably also be extended to work in the PROTECTED mode by + substituting ``User.map`` for ``System.map``. + +Here is an example ``defines.ld`` created by ``mkdefines.sh``: + +.. code-block:: shell + + printf = 0x0800aefc | 0x00000001 ; + +Replacing an NSH Built-In Function +---------------------------------- + +Files can be executed by ``NSH`` from the command line by simply typing the +name of the ELF program. This requires: + +1. That the feature be enabled with``CONFIG_NSH_FILE_APP=y`` +2. That support for the PATH variable is enabled (``CONFIG_BINFMT_EXEPATH=y`` and + ``CONFIG_PATH_INITIAL`` set to the mount point of the file system that + may contain ELF programs). + +Suppose, for example, I have a built-in application called ``hello``. Before +installing the new replacement ``hello`` ELF program in the file system, this +is the version of ``hello`` that ``NSH`` will execute: + +.. code-block:: shell + + nsh> hello + Hello, World! + nsh> + +In the above configuration, ``NSH`` will first attempt to run the program called +``hello`` from the file system. This will fail because we have not yet placed +our custom ``hello`` ELF program in the file system. So instead, ``NSH`` will +fall back and execute the built-in application called ``hello``. + +In this way, any command known to ``NSH`` can be replaced by an ELF program +installed in a mounted file system directory that is found via the PATH +variable. + +Now suppose that we do add our custom ``hello`` to the file system. When +``NSH`` attempts to run the program called ``hello`` from the file system, it +will run successfully. The built-in version will be ignored. It has been +replaced with the version in the file system: + +.. code-block:: shell + + nsh> mount -t vfat /dev/mmcsd0 /bin + nsh> hello + Hello from Add-On Program! + nsh> + +Version Dependency +------------------ + +.. note:: + + This technique generates ELF programs using fixed addresses from the + ``System.map`` file of a versioned release. The generated ELF programs can + only be used with that specific firmware version. A crash will most likely + result if used with a different firmware version, because the addresses + from the ``System.map`` will not match the addresses in a different version + of the firmware. + +The alternative approach using :doc:`Symbol Tables ` is more +or less version independent. + +Tightly Coupled Memories +------------------------ + +Most MCUs based on ARMv7-M family processors support some kind of Tightly +Coupled Memory (TCM). These TCMs have somewhat different properties for +specialized operations. Depending on the bus matrix of the processor, you may +not be able to execute programs from TCM. For instance, the ``STM32 F4`` +supports Core Coupled Memory (CCM), but since it is tied directly to the D-bus, +it cannot be used to execute programs! On the other hand, the ``STM32F3`` has a +CCM that is accessible to both the D-Bus and the I-Bus, in which case it +should be possible to execute programs from this TCM. + +.. image:: ./image/system_arch_stm32f42xx_and_f43xx.png + +.. image:: ./image/system_arch_stm32f303xBC_and_f358xC.png + +When ELF programs are loaded into memory, the memory is allocated from the +heap via a standard memory allocator. By default with the ``STM32 F4``, the +CCM is included in ``HEAP`` and will typically be allocated first. If CCM +memory is allocated to hold the ELF program, a hard-fault will occur +immediately when you try to execute the ELF program in memory. + +Therefore, it is necessary on ``STM32 F4`` platforms to include the following +configuration setting: + +.. code-block:: shell + + CONFIG_STM32_CCMEXCLUDE=y + +With that setting, the CCM memory will be excluded from the heap, and so will +never be allocated for ELF program memory. diff --git a/Documentation/guides/image/system_arch_stm32f303xBC_and_f358xC.png b/Documentation/guides/image/system_arch_stm32f303xBC_and_f358xC.png new file mode 100644 index 0000000000..fdfdb8ed0e Binary files /dev/null and b/Documentation/guides/image/system_arch_stm32f303xBC_and_f358xC.png differ diff --git a/Documentation/guides/image/system_arch_stm32f42xx_and_f43xx.png b/Documentation/guides/image/system_arch_stm32f42xx_and_f43xx.png new file mode 100644 index 0000000000..5556c82204 Binary files /dev/null and b/Documentation/guides/image/system_arch_stm32f42xx_and_f43xx.png differ diff --git a/Documentation/guides/index.rst b/Documentation/guides/index.rst index 52e2cced76..b20ba5b022 100644 --- a/Documentation/guides/index.rst +++ b/Documentation/guides/index.rst @@ -56,4 +56,7 @@ Guides signal_events_interrupt_handlers.rst signaling_sem_priority_inheritance.rst smaller_vector_tables.rst - port.rst \ No newline at end of file + port.rst + updating_release_system_elf.rst + partially_linked_elf.rst + fully_linked_elf.rst \ No newline at end of file diff --git a/Documentation/guides/partially_linked_elf.rst b/Documentation/guides/partially_linked_elf.rst new file mode 100644 index 0000000000..507e6e2c63 --- /dev/null +++ b/Documentation/guides/partially_linked_elf.rst @@ -0,0 +1,429 @@ +ELF Programs – With Symbol Tables +================================= + +Updating a Release System with ELF Programs – With Symbol Tables +----------------------------------------------------------------- + +You can easily extend the firmware in your released, embedded system using +ELF programs provided via a file system. For example, an SD card or, perhaps, +downloaded into on-board SPI FLASH. + +In order to support such post-release updates, your released firmware must +support execution of ELF programs loaded into RAM and symbol tables also +provided via the file system (see `apps/examples/elf`). + +The files shown in this Wiki page can be downloaded +`here `_ + +Creating a Symbol Table +----------------------- + +There are several ways to create an application symbol table. Only two are +compatible with the example provided here: + +1. **Board-specific Bring-up Logic** + Build a symbol table into the base firmware and add it to your + board-specific bring-up logic. This technique is typically used in kernel + mode with ``CONFIG_USER_INITPATH=y``. + + In this setup, the system does not initialize using a standard C call like + ``nsh_main()``. Instead, it starts with an ``init`` ELF program, similar to + how Linux initializes. The configuration option + ``CONFIG_EXECFUNCS_SYMTAB_ARRAY`` initializes the system with a minimal set + of symbols required by the ``init`` program. Once initialized, the ``init`` + program would typically call ``boardctl()`` to put the final symbol table in + place. + + To enable this method, you must: + + - Set ``CONFIG_EXECFUNCS_HAVE_SYMTAB=y`` in your configuration. + - Provide a symbol table with the global name ``CONFIG_EXECFUNCS_SYMTAB_ARRAY`` with the variable name ``CONFIG_EXECFUNCS_NSYMBOLS_VAR`` that holds the number of symbol entries. The default symbol table name is ``g_symtab``. + + In this example, let's illustrate this using an STM32F4-Discovery + configuration. We will assume that you have modified the + ``boards/arm/stm32/stm32fdiscovery/src/stm32_bringup.c`` file, adding the + following: + + .. code-block:: c + + #include + #include + + const struct symtab_s g_symtab[] = { + {"printf", (FAR void *)printf} + }; + + int g_nsymbols = 1; + + This is a simple symbol table containing only the symbol string "printf," + whose value is the address of the function ``printf()``. + + There is, of course, a lot more that could be said about generating symbol + tables. NuttX provides specialized tools in the ``tools/`` directory and + instructions elsewhere for generating more extensive symbol tables. However, + this example keeps things simple to focus on the core functionality. + +2. **Application Logic** + Alternatively, the symbol table can be provided dynamically by the + application itself, using the ``boardctl()`` system interface. The specific + ``boardctl()`` command to use is ``BOARDIOC_APP_SYMTAB``. This command + provides the symbol table in the same way as the board-specific logic but + allows for application-level control. + + To use this approach, you need to: + - Enable the configurations ``CONFIG_LIB_BOARDCTL=y`` and ``CONFIG_BOARDCTL_APP_SYMTAB=y``. + - Include application logic to provide the symbol table. If ``CONFIG_EXAMPLES_NSH_SYMTAB=y`` is set, NSH can handle this automatically. + +Export Package +-------------- + +At the time of firmware release, you should create and save an export package. +This export package contains all the necessary files required to create +post-release add-on modules for your embedded system. + +For demonstration purposes, we use the STM32F4-Discovery with the network NSH +configuration. This setup assumes that you have the STM32F4DIS-BB baseboard. +The demonstration also requires support for externally modifiable media, such +as: + +- Removable media, like an SD card or USB flash drive. +- An internal file system remotely accessible via USB MSC, FTP, or other + protocols. +- A remote file system, such as NFS. + +In this demonstration, the networking NSH configuration uses the SD card on +the STM32 baseboard. Other NSH configurations can also be used, provided they +supply the necessary file system support. + +(No baseboard? You can add file system support to the basic ``STM32F4-Discovery`` +board by following these instructions: +`USB FLASH drive `_ +or `SD card `_.) + +Example for STM32F4-Discovery: + +.. code-block:: shell + + $ make distclean + $ tools/configure.sh -c stm32f4discovery:netnsh + $ make menuconfig + +Required configurations: + +- Disable networking: ``# CONFIG_NET is not set`` +- Enable ELF binary support: ``CONFIG_ELF=y``, ``CONFIG_LIBC_EXECFUNCS=y``, + ``CONFIG_EXECFUNCS_HAVE_SYMTAB=y``, ``CONFIG_EXECFUNCS_SYMTAB_ARRAY="g_symtab"`` and + ``CONFIG_EXECFUNCS_NSYMBOLS_VAR="g_nsymbols"`` +- Enable PATH variable support: ``CONFIG_BINFMT_EXEPATH=y``, + ``CONFIG_PATH_INITIAL="/bin"`` +- Enable execution from NSH: ``CONFIG_NSH_FILE_APPS=y`` + +Then, build the NuttX firmware image and the export package: + +.. code-block:: shell + + $ make + $ make export + +When ``make export`` completes, you will find a ZIP package in the top-level +NuttX directory called ``nuttx-export-x.y.zip`` (where x.y corresponds to the +version, determined by the .version file in the same directory). The contents +of this ZIP file are organized as follows: + +.. code-block:: text + + nuttx-export-x.x + |- arch/ + |- build/ + |- include/ + |- libs/ + |- startup/ + |- System.map + `- .config + +Add-On Build Directory +----------------------- + +In order to create the add-on ELF program, you will need: + +1. The export package. +2. A program build Makefile. +3. A linker script used by the Makefile. + +The example Makefile discussed below assumes the use of a GNU toolchain. Note +that non-GNU toolchains would likely require a significantly different +Makefile and linker script. + +Hello Example +------------- + +To keep things manageable, let's use a concrete example. Suppose the ELF +program that we wish to add to the release code is the simple +source file ``hello.c``: + +.. code-block:: c + + #include + + int main(int argc, char **argv) + { + printf("Hello from Add-On Program!\n"); + return 0; + } + +Let's say that we have a directory called ``addon`` that contains the following: + +1. The ``hello.c`` source file. +2. A Makefile to build the ELF program. +3. A linker script called ``gnu-elf.ld`` needed by the Makefile. +4. The export package ``nuttx-export-7.25.zip``. + + +Building the ELF Program +------------------------ + +The first step in creating the ELF program is to unzip the export +package. Starting in the ``addon`` directory: + +.. code-block:: shell + + $ cd addon + $ ls + gnu-elf.ld hello.c Makefile nuttx-export-7.25.zip + +Where: +- ``gnu-elf.ld`` is the linker script. +- ``hello.c`` is the example source file. +- ``Makefile`` builds the ELF program. +- ``nuttx-export-7.25.zip`` is the export package from NuttX 7.25. + +Unzip the export package as follows: + +.. code-block:: shell + + $ unzip nuttx-export-7.25.zip + +This creates a new directory called ``nuttx-export-7.25``, containing +all the content from the released NuttX code required to build +the ELF program. + + +The Makefile +------------ + +To build the ELF program, simply run: + +.. code-block:: shell + + $ make + +This uses the following Makefile to generate several files: +- ``hello.o``: The compiled object file for ``hello.c``. +- ``hello``: The linked ELF program. + +Only the resulting ``hello`` file is needed. + +The Makefile used to create the ELF program is as follows: + +.. code-block:: shell + + include nuttx-export-7.25/build/Make.defs + + # Long calls are need to call from RAM into FLASH + + ARCHCFLAGS += -mlong-calls + ARCHWARNINGS = -Wall -Wstrict-prototypes -Wshadow -Wundef + ARCHOPTIMIZATION = -Os -fno-strict-aliasing -fno-strength-reduce -fomit-frame-pointer + ARCHINCLUDES = -I. -isystem nuttx-export-7.25/include + + CFLAGS = $(ARCHCFLAGS) $(ARCHWARNINGS) $(ARCHOPTIMIZATION) $(ARCHINCLUDES) -pipe + + CROSSDEV = arm-none-eabi- + CC = $(CROSSDEV)gcc + LD = $(CROSSDEV)ld + STRIP = $(CROSSDEV)strip --strip-unneeded + + # Setup up linker command line options + + LDELFFLAGS = -r -e main + LDELFFLAGS += -T gnu-elf.ld + + # This might change in a different environment + + OBJEXT ?= .o + + # This is the generated ELF program + + BIN = hello + + # These are the sources files that we use + + SRCS = hello.c + OBJS = $(SRCS:.c=$(OBJEXT)) + + # Build targets + + all: $(BIN) + .PHONY: clean + + $(OBJS): %$(OBJEXT): %.c + $(CC) -c $(CFLAGS) $< -o $@ + + $(BIN): $(OBJS) + $(LD) $(LDELFFLAGS) -o $@ $^ + $(STRIP) $(BIN) + + clean: + rm -f $(BIN) + rm -f *.o + +The Linker Script +----------------- + +The linker script that I am using in this example, gnu-elf.ld, +contains the following: + +.. code-block:: shell + + SECTIONS + { + .text 0x00000000 : + { + _stext = . ; + *(.text) + *(.text.*) + *(.gnu.warning) + *(.stub) + *(.glue_7) + *(.glue_7t) + *(.jcr) + _etext = . ; + } + + .rodata : + { + _srodata = . ; + *(.rodata) + *(.rodata1) + *(.rodata.*) + *(.gnu.linkonce.r*) + _erodata = . ; + } + + .data : + { + _sdata = . ; + *(.data) + *(.data1) + *(.data.*) + *(.gnu.linkonce.d*) + _edata = . ; + } + + .bss : + { + _sbss = . ; + *(.bss) + *(.bss.*) + *(.sbss) + *(.sbss.*) + *(.gnu.linkonce.b*) + *(COMMON) + _ebss = . ; + } + + /* Stabs debugging sections. */ + + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_info 0 : { *(.debug_info) } + .debug_line 0 : { *(.debug_line) } + .debug_pubnames 0 : { *(.debug_pubnames) } + .debug_aranges 0 : { *(.debug_aranges) } + } + +Replacing NSH Built-In Functions +-------------------------------- + +Files can be executed by NSH from the command line by simply typing the name +of the ELF program. This requires (1) that the feature be enabled with +``CONFIG_NSH_FILE_APP=y`` and (2) that support for the PATH variable is +enabled with ``CONFIG_BINFMT_EXEPATH=y`` and ``CONFIG_PATH_INITIAL`` set to +the mount point of the file system that may contain ELF programs. + +In this example, there is no application in the base firmware called +``hello``. So attempts to run ``hello`` will fail: + +.. code-block:: shell + + nsh> hello + nsh: hello: command not found + nsh> + +But if we mount the SD card containing the ``hello`` image that we created +above, then we can successfully execute the ``hello`` command: + +.. code-block:: shell + + nsh> mount -t vfat /dev/mmcsd0 /bin + nsh> ls /bin + /bin: + System Volume Information/ + hello + nsh> hello + Hello from Add-On Program! + nsh> + +Here we showed how you can add a new command to NSH to a product without +modifying the base firmware. We can also replace or update an existing +built-in application in this way: + +In the above configuration, NSH will first attempt to run the program called +``hello`` from the file system. This will fail because we have not yet put +our custom ``hello`` ELF program in the file system. So instead, NSH will +fallback and execute the built-in application called ``hello``. In this way, +any command known to NSH can be replaced from an ELF program installed in a +mounted file system directory that can be found via the PATH variable. + +After we do add our custom ``hello`` to the file system, when NSH attempts to +run the program called ``hello`` from the file system it will run +successfully. The built-in version will be ignored. It has been replaced with +the version in the file system. + +Tightly Coupled Memories +------------------------ + +Most MCUs based on ARMv7-M family processors support some kind of Tightly +Coupled Memory (TCM). These TCMs have somewhat different properties for +specialized operations. Depending on the bus matrix of the processor, you +may not be able to execute programs from the TCM. For instance, the STM32 F4 +supports Core Coupled Memory (CCM), but since it is tied directly to the +D-bus, it cannot be used to execute programs! On the other hand, the STM32F3 +has a CCM that is accessible to both the D-Bus and the I-Bus, in which case +it should be possible to execute programs from this TCM. + +.. image:: ./image/system_arch_stm32f42xx_and_f43xx.png + +.. image:: ./image/system_arch_stm32f303xBC_and_f358xC.png + +When ELF programs are loaded into memory, the memory is allocated from the +heap via a standard memory allocator. By default with the STM32 F4, the CCM +is included in the heap and will typically be allocated first. If CCM memory +is allocated to hold the ELF program in memory, then a hard-fault will occur +immediately when you try to execute the ELF program in memory. + +Therefore, it is necessary on STM32 F4 platforms to include the following +configuration setting: + +.. code-block:: shell + + CONFIG_STM32_CCMEXCLUDE=y + +With that setting, the CCM memory will be excluded from the heap and so will +never be allocated for ELF program memory. diff --git a/Documentation/guides/updating_release_system_elf.rst b/Documentation/guides/updating_release_system_elf.rst new file mode 100644 index 0000000000..2b13913e7c --- /dev/null +++ b/Documentation/guides/updating_release_system_elf.rst @@ -0,0 +1,25 @@ +Updating a Release System with ELF Programs +============================================ + +You can enhance the functionality of your released embedded system by adding +ELF programs, which can be loaded from a file system. These programs can be +stored on an SD card or downloaded into on-board SPI FLASH, allowing for +easy updates or extensions to the system's firmware. + +There are two ways you can accomplish this: + +Partially linked +---------------- +This describes building the partially linked, relocatable ELF program that +depends on a symbol table provided by the base firmware in FLASH. + +Reference: +- See :doc:`Partially Linked ELF Programs ` + +Fully linked +------------ +This describes building a fully linked, relocatable ELF program that does +not depend on any symbol table information. + +Reference: +- See :doc:`Fully Linked ELF Programs `