Doc: loading ELF program
Some checks are pending
Build Documentation / build-html (push) Waiting to run

Migrate the Updating a release System with ELF programs with child pages
to official wiki
This commit is contained in:
Ludovic Vanasse 2024-12-28 19:40:59 -05:00 committed by Xiang Xiao
parent f0a3c43a54
commit 04c7391162
6 changed files with 991 additions and 1 deletions

View file

@ -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 <https://cwiki.apache.org/confluence/download/attachments/139629402/elfprog-nosymtab.tar.gz?version=1&modificationDate=1576735520000&api=v2>`_.
Alan Carvalho de Assis has also made a video based on this example in the
YouTube `NuttX Channel <https://www.youtube.com/watch?v=oL6KAgkTb8M>`_.
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 <https://www.youtube.com/watch?v=5hB5ZXpRoS4>`_
or `SD card <https://www.youtube.com/watch?v=H28t4RbOXqI>`_.)
.. 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 <stdio.h>
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 <system-map> <relprog>"
# Check for the required path to the System.map file
sysmap=$1
if [ -z "$sysmap" ]; then
echo "ERROR: Missing <system-map>"
echo ""
echo $usage
exit 1
fi
# Check for the required partially linked file
relprog=$2
if [ -z "$relprog" ]; then
echo "ERROR: Missing <program-list>"
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 <fully_linked_elf>` 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.

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

View file

@ -56,4 +56,7 @@ Guides
signal_events_interrupt_handlers.rst signal_events_interrupt_handlers.rst
signaling_sem_priority_inheritance.rst signaling_sem_priority_inheritance.rst
smaller_vector_tables.rst smaller_vector_tables.rst
port.rst port.rst
updating_release_system_elf.rst
partially_linked_elf.rst
fully_linked_elf.rst

View file

@ -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 <https://cwiki.apache.org/confluence/download/attachments/139629402/elfprog-wsymtab.tar.gz?version=1&modificationDate=1576735523000&api=v2>`_
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 <stdio.h>
#include <nuttx/binfmt/symtab.h>
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 <https://www.youtube.com/watch?v=5hB5ZXpRoS4>`_
or `SD card <https://www.youtube.com/watch?v=H28t4RbOXqI>`_.)
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 <stdio.h>
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.

View file

@ -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 <partially_linked_elf>`
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 <fully_linked_elf>`