NXFLAT

>>> Under Construction <<<

Last Updated: March 20, 2011



Table of Contents

1.0 Overview

1.1 Functionality

1.2 Background

1.3 Limitations

1.4 Supported Processors

1.5 Development Status
2.0 NXFLAT Toolchain

1.2 Building the NXFLAT Toolchain

1.2 mknxflat

1.3 ldnxflat

1.4 Making an NXFLAT module
3.0. Binary Loader APIs
Appendix A. No GOT Operation
Appendix B. PIC Text Workaround

1.0 Overview

f

1.1 Functionality

NXFLAT is a customized and simplified version of binary format implemented a few years ago called XFLAT With the NXFLAT binary format you will be able to do the following:

This allows you to extend the NuttX base code after it has been written into FLASH. One motivation for implementing NXFLAT is support clean CGI under an HTTPD server.

This feature is especially attractive when combined with the NuttX ROMFS support: ROMFS allows you to execute programs in place (XIP) in flash without copying anything other than the .data section to RAM. In fact, the initial NXFLAT release will work only on ROMFS.

This NuttX feature includes:

1.2 Background

NXFLAT is derived from XFLAT. XFLAT is a toolchain add that provides full shared library and XIP executable support for processors that have no Memory Management Unit (MMU1). NXFLAT is greatly simplified for the deeply embedded environment targeted by Nuttx:

Rather, the NXFLAT module only imports symbol values. In the NXFLAT model, the (PIC2) NXFLAT module resides in a FLASH file system and when it is loaded at run time, it is dynamically linked only to the (non-PIC) base NuttX code: The base NuttX exports a symbol table; the NXFLAT module imports those symbol value to dynamically bind the module to the base code.

1.3 Limitations

1.4 Supported Processors

As mentioned above, the NXFLAT toolchain is only available for ARM and Cortex-M3 (thumb2) targets. Furthermore, NXFLAT has only been tested on the Eagle-100 LMS6918 Cortex-M3 board.

1.5 Development Status

The initial release of NXFLAT was made in NuttX version 0.4.9. Testing is limited to the tests found under apps/examples/nxflat in the source tree. Some known problems exist (see the TODO list). As such, NXFLAT is currently in an early alpha phase.

2.0 NXFLAT Toolchain

1.2 Building the NXFLAT Toolchain

In order to use NXFLAT, you must use special NXFLAT tools to create the binary module in FLASH. To do this, you will need to download the buildroot package and build it on your Linux or Cygwin machine. The buildroot can be downloaded from Sourceforge. You will need version 0.1.7 or later.

Here are some general build instructions:

1.2 mknxflat

mknxflat is used to build a thunk file. See below for usage.

1.3 ldnxflat

ldnxflat is use to link your object files along with the thunk file generated by mknxflat to produce the NXFLAT binary module. See below for usage.

    Usage: ldnxflat [options] <bfd-filename>
    
    Where options are one or more of the following.  Note
    that a space is always required between the option and
    any following arguments.
    
      -d Use dynamic symbol table [Default: symtab]
      -e <entry-point>
         Entry point to module [Default: _start]
      -o <out-filename>
         Output to <out-filename> [Default: <bfd-filename>.nxf]
      -s <stack-size>
         Set stack size to <stack-size> [Default: 4096]
      -v Verbose output. If -v is applied twice, additional
         debug output is enabled [Default: no verbose output].
    

1.4 Making an NXFLAT module

Below is a snippet from an NXFLAT make file (simplified from NuttX Hello, World! example.

      Target 1 hello.r1: hello.o


      abc-elf-ld -r -d -warn-common -o $@ $^
      Target 2 hello-thunk.S: hello.r1


      mknxflat -o $@ $^
      Target 3 hello.r2: hello-thunk.S


      abc-elf-ld -r -d -warn-common -T binfmt/libnxflat/gnu-nxflat.ld -no-check-sections -o $@ hello.o hello-thunk.o
      Target 4 hello: hello.r2


      ldnxflat -e main -s 2048 -o $@ $^

    Target 1. This target links all of the module's object files together into one relocatable object. Two relocatable objects will be generated; this is the first one (hence, the suffic .r1). In this "Hello, World!" case, there is only a single object file, hello.o, that is linked to produce the hello.r1 object.

    When the module's object files are compiled, some special compiler CFLAGS must be provided. First, the option -fpic is required to tell the compiler to generate position independent code (other GCC options, like -fno-jump-tables might also be desirable). For ARM compilers, two additional compilation options are required: -msingle-pic-base and -mpic-register=r10.

    Target 2. Given the hello.r1 relocatable object, this target will invoke mknxflat to make the thunk file, hello-thunk.S. This thunk file contains all of the information needed to create the imported function list.

    Target 3 This target is similar to Target 1. In this case, it will link together the module's object files (only hello.o here) along with the assembled thunk file, hello-thunk.o to create the second relocatable object, hello.r2. The linker script, gnu-nxflat.ld is required at this point to correctly position the sections. This linker script produces two segments: An I-Space (Instruction Space) segment containing mostly .text and a D-Space (Data Space) segment containing .got, .data, and .bss sections. The I-Space section must be origined at address 0 (so that the segment's addresses are really offsets into the I-Space segment) and the D-Space section must also be origined at address 0 (so that segment's addresses are really offsets into the I-Space segment). The option -no-check-sections is required to prevent the linker from failing because these segments overlap.

    Target 4. Finally, this target will use the hello.r2 relocatable object to create the final, NXFLAT module hello by executing ldnxflat.

    3.0 Binary Loader APIs

    Relevant Header Files:

    • The interface to the binary loader is described in the header file include/nuttx/binfmt.h. A brief summary of the APIs prototyped in that header file are listed below.
    • NXFLAT APIs needed to register NXFLAT as a binary loader appear in the header file include/nuttx/nxflat.h.
    • The format of an NXFLAT object itself is described in the header file: include/nuttx/nxflat.h.

    binfmt Registration These first interfaces are used only by a binary loader module, such as NXFLAT itself. NXFLAT (or any future binary loader) calls register_binfmt() to incorporate itself into the system. In this way, the binary loader logic is dynamically extensible to support any kind of loader. Normal application code should not be concerned with these interfaces.

      int register_binfmt(FAR struct binfmt_s *binfmt)

        Description: Register a loader for a binary format

        Returned Value: This is a NuttX internal function so it follows the convention that 0 (OK) is returned on success and a negated errno is returned on failure.

      int unregister_binfmt(FAR struct binfmt_s *binfmt)

        Description: Register a loader for a binary format

        Returned Value: This is a NuttX internal function so it follows the convention that 0 (OK) is returned on success and a negated errno is returned on failure.

    NXFLAT Initialization These interfaces are specific to NXFLAT. Normally, an application needs only call nxflat_initialize() during its initialization to have full NXFLAT support.

      int nxflat_initialize(void)

        Description: NXFLAT support is built unconditionally. However, it order to use this binary format, this function must be called during system format in order to register the NXFLAT binary format. This function calls register_binfmt() appropriately.

        Returned Value: This is a NuttX internal function so it follows the convention that 0 (OK) is returned on success and a negated errno is returned on failure.

      void nxflat_uninitialize(void)

        Description: Unregister the NXFLAT binary loader

        Returned Value: None

    Binary Loader Interfaces. The remaining APIs are called by user applications to maintain modules in the file system.

      int load_module(FAR struct binary_s *bin)

        Description: Load a module into memory, bind it to an exported symbol take, and prep the module for execution.

        Returned Value: This is an end-user function, so it follows the normal convention: Returns 0 (OK) on success. On failure, it returns -1 (ERROR) with errno set appropriately.

      int unload_module(FAR const struct binary_s *bin)

        Description: Unload a (non-executing) module from memory. If the module has been started (via exec_module()), calling this will be fatal.

        Returned Value: This is a NuttX internal function so it follows the convention that 0 (OK) is returned on success and a negated errno is returned on failure.

      int exec_module(FAR const struct binary_s *bin, int priority)

        Description: Execute a module that has been loaded into memory by load_module().

        Returned Value: This is an end-user function, so it follows the normal convention: Returns the PID of the exec'ed module. On failure, it.returns -1 (ERROR) and sets errno appropriately.

    Appendix A. No GOT Operation

    When GCC generate position independent code, new code sections will appear in your programs. One of these is the GOT (Global Offset Table) and, in ELF environments, another is the PLT (Procedure Lookup Table. For example, if your C code generated (ARM) assembly language like this without PIC:

              ldr     r1, .L0         <-- Fetch the offset to 'x'
              ldr     r0, [r10, r1]   <-- Load the value of 'x' with PIC
                                      offset
              ...
      .L0:    .word   x               <-- Offset to 'x'
      

    Then when PIC is enabled (say with the -fpic compiler option), it will generate code like this:

              ldr     r1, .L0         <-- Fetch the offset to the GOT entry
              ldr     r1, [r10,r1]    <-- Fetch the (relocated) address
                                      of 'x' from the GOT
              ldr     r0, [r1, #0]    <-- Fetch the value of 'x'
              ...
      .L1     .word   x(GOT)          <-- Offset to entry in the GOT
      

    See reference

    Notice that the generates an extra level of indirection through the GOT. This indirection is not needed by NXFLAT and only adds more RAM usage and execution time.

    NXFLAT (like XFLAT) can work even better with the GOT. Patches again older version of GCC exist to eliminate the GOT indirections. Several are available here if you are inspired to port them to a new GCC version.

    Appendix B. PIC Text Workaround

    There is a problem with the memory model in GCC that prevents it from being used as you need to use it in the NXFLAT context. The problem is that GCC PIC model assumes that the executable lies in a flat, contiguous (virtual) address space like:

      Virtual
      .text
      .got
      .data
      .bss

    It assumes that the PIC base register (usually r10 for ARM) points to the base of .text so that any address in .text, .got, .data, .bss can be found with an offset from the same base address. But that is not the memory arrangement that we need in the XIP embedded environment. We need two memory regions, one in FLASH containing shared code and on per task in RAM containing task-specific data:

      FlashRAM
      .text .got

      .data

      .bss

    The PIC base register needs to point to the base of the .got and only addresses in the .got, .data, and .bss sections can be accessed as an offset from the PIC base register. See also this XFLAT discussion.

    Patches again older version of GCC exist to correct this GCC behavior. Several are available here if you are inspired to port them to a new GCC version.