eCosPro® Reference Manual
Prev |  | Next

# Object Loader

## Name

CYGPKG_OBJLOADER -- eCos Support for Dynamic Module Loading

## Synopsis

    #include <cyg/objloader/objload.h>

``void *cyg_ldr_open`(cyg_ldr_open_stream *open_stream, CYG_ADDRWORD data);`

``void cyg_ldr_close`(void *handle);`

``char *cyg_ldr_error`(void);`

``void *cyg_ldr_find_symbol`(void *handle, char *symbol);`

## Description

> Note: The Object Loader package does not support all processor architectures at present.

The Object Loader package provides support for dynamically loading executable modules into an eCos system. Modules may be loaded into memory from a variety of sources, linked in to the running system and entry points invoked to execute the code of the module. When the module is no longer required, it may be unloaded and the memory reused for other purposes or other modules.

This system is modelled most closely on the Linux kernel module mechanism, rather than Windows DLLs or Unix shared objects. As a result, it has a number of restrictions:

- Only modules written in C are supported. The Object Loader does not currently provide support for invoking static constructors and destructors, C++ exceptions, RTTI and other parts of the C++ runtime system.

- Automatic symbol resolution only works for references from a module into the main executable. References between modules are not supported, and resolution of unresolved symbols in the main executable to module symbols is not supported.

- Loaded modules need to be built using the same, or similar, configuration to the main system.

- Loaded modules should be built with the same or compatible compiler flags as the main system. There is one important exception. Some architectures including MIPS and Nios II implement a global pointer register. Small global variables are placed in an area of memory up to 64K. The gp register points at this area of memory, allowing the variables to be accessed directly using a single instruction instead of the two or more instructions that would otherwise be required. This technique cannot be used for a dynamically loaded module. Hence the use of gp-relative addressing must be suppressed with a compiler flag, typically -G0.

### Creating Loadable Modules

Modules can be just object files as generated by the compiler. In a Makefile including the `$(INSTALL_DIR)/include/pkgconf/ecos.mak` definitions file, the entry to build `module.o` might be:

    module.o: module.c
            $(ECOS_COMMAND_PREFIX)gcc -c -I$(INSTALL_DIR)/include $(ECOS_GLOBAL_CFLAGS) -o $@ $<
            $(ECOS_COMMAND_PREFIX)strip -g $@

The compile line generates a `.o` file. The -I option allows includes to be fetched from the eCos installation. The command prefix and global flags are stored in the `ecos.mak` file by the eCos build process. If the compile flags include -g or some other debug option then to save memory and maybe load time it is useful to pass the finished module through strip to limit the file contents to just the loadable ELF sections.

It is possible to create a module out of several object files by using the linker's ability to perform a partial link:

    module.o : file1.o file2.o file3.o
            $(ECOS_COMMAND_PREFIX)gcc $(subst --gc-sections,-r,$(ECOS_GLOBAL_LDFLAGS)) -L$(PREFIX)/lib -Tmodule.ld -o $@ $^
            $(ECOS_COMMAND_PREFIX)strip -g $@

The `module.ld` linker script is defined by the Object Loader package and is copied out to the install lib directory. It should be used when combining multiple files, or when advanced features such as HAL tables are used in a single object file.

If the module makes use of float, double, long long and some long arithmetic operations, then it should be partially linked against `libgcc` before loading. This can be done with the following makefile fragments:

    # Single source file module...
    module.o: module.c
            $(ECOS_COMMAND_PREFIX)gcc -c -I$(INSTALL_DIR)/include $(ECOS_GLOBAL_CFLAGS) -o $@.tmp $<
            $(ECOS_COMMAND_PREFIX)gcc $(subst --gc-sections,-r,$(ECOS_GLOBAL_LDFLAGS)) -L`dirname \`$(ECOS_COMMAND_PREFIX)gcc $(ECOS_GLOBAL_CFLAGS) -print-libgcc-file-name\`` -L$(PREFIX)/lib -Tmodule.ld -o $@ $@.tmp -lgcc
            $(ECOS_COMMAND_PREFIX)strip -g $@

    # Combine multiple object files...
    module.o : file1.o file2.o file3.o
            $(ECOS_COMMAND_PREFIX)gcc $(subst --gc-sections,-r,$(ECOS_GLOBAL_LDFLAGS)) -L`dirname \`$(ECOS_COMMAND_PREFIX)gcc $(ECOS_GLOBAL_CFLAGS) -print-libgcc-file-name\`` -L$(PREFIX)/lib -Tmodule.ld -o $@ $^ -lgcc
            $(ECOS_COMMAND_PREFIX)strip -g $@

#### Target Specific Considerations

There are a number of special considerations for particuar target architectures:

- Modules compiled for Thumb may be loaded into targets compiled for either ARM32 or Thumb. Thumb builds of eCos that use the object loader should have the "-mlong-calls" compiler option set. ARM32 builds should have thumb interworking enabled if thumb modules are to be loaded (the object loader module does this automatically). Thumb modules should be compiled with "-mthumb -mthumb-interwork -mlong-calls" compiler options.

- Modules compiled for the MIPS16 instruction set may be loaded into a MIPS target, so long as the processor supports the instruction set. To compile and link such a module, the "-mips16" compiler option must be substituted for "-mips32", along with "-fwritable-strings".

### Loading Modules

The function `cyg_ldr_open()` is used to load a module into memory. It takes two arguments. The first argument defines a module loader, while the second argument is a generic data item whose value depends on the loader. If the load is successful, then a non-NULL handle will be returned. A NULL pointer will be returned on failure.

If there is an error in the loading process, then the function `cyg_ldr_error()` will return a string describing the last error that occurred. Note that this is not thread-safe since there is only a single last error recorded for all load operations.

At present the following loaders are implemented:

CYG_LDR_FILESYSTEM

  This loader uses FILEIO operations to read an ELF file from a named file in a filesystem. For example, to read a module from the file "/lib/modules/module.o":

          mod_handle = cyg_ldr_open( CYG_LDR_FILESYSTEM, (CYG_ADDRWORD)"/lib/modules/module.o");

  This loader is included by default if the CYGPKG_IO_FILEIO package is included, although it can be omitted by disabling CYGPKG_OBJLOADER_LOADER_FS.

CYG_LDR_MEMORY

  This loader uses memory access primitives to read an ELF file from any addressable memory such as ROM, FLASH or RAM. For example to read a module from the location module_base:

          mod_handle = cyg_ldr_open( CYG_LDR_MEMORY, (CYG_ADDRWORD)&module_base);

  This loader is included by default, although it can be omitted by disabling CYGPKG_OBJLOADER_LOADER_MEM.

Loaders CYG_LDR_FTP, CYG_LDR_TFTP, CYG_LDR_HTTP and CYG_LDR_FLASH are defined, but not currently implemented.

### Unloading Modules

A module may be unloaded by calling `cyg_ldr_close()`, passing it the handle returned from `cyg_ldr_open()`. This will cause the memory occupied by the loader to be released. Any pointers into the code or data of the module will be rendered invalid and should not be used.

### Referencing Module Symbols

When a module is loaded, a symbol table listing all the external symbols that it defines is loaded with it. The function `cyg_ldr_find_symbol()` searches this table and returns a pointer to the location defined by a symbol. For example, to create a thread running from a function in a module:

        cyg_thread_entry_t *thread_entry;

        thread_entry = cyg_ldr_find_symbol( handle, "thread_entry");

        cyg_thread_create(THREAD_PRIORITY,
                          thread_entry,
                          0,
                          "Module Thread",
                          (void *)thread_stack,
                          THREAD_STACK_SIZE,
                          &thread_handle,
                          &thread_object);

Both functions and variables may be accessed in this way.

There is no mechanism for resolving dangling references in the main eCos application, or other modules, to symbols in a newly loaded module. The main eCos application must have all references resolved at link time. However, it is possible to simulate the effect of dynamic resolution by using function pointers. For example define a global function pointer to an initial dummy function:

    typedef int module_fn_t(int a, int b);

    module_fn_t dummy_fn;

    module_fn_t *module_fn = dummy_fn;

    int dummy_fn( int a, int b )
    {
        return -1;
    }

When the module is loaded the function pointer can be pointed at the function within the module, and pointed back to the dummy function when it is unloaded:

    void *mod_handle;

    void load_module(void)
    {
        mod_handle = cyg_ldr_open( CYG_LDR_FILESYSTEM, (CYG_ADDRWORD)"/lib/modules/module.o");

        module_fn = cyg_ldr_find_symbol( mod_handle, "module_fn");
    }

    void unload_module(void)
    {
        cyg_ldr_close( mod_handle );

        module_fn = dummy_fn;
    }

One could even implement a form of demand loading by combining `dummy_fn` and `load_module`:

    int dummy_fn( int a, int b )
    {
        mod_handle = cyg_ldr_open( CYG_LDR_FILESYSTEM, (CYG_ADDRWORD)"/lib/modules/module.o");

        module_fn = cyg_ldr_find_symbol( mod_handle, "module_fn");

        return module_fn( a, b );
    }

### Module Open and Close Functions

When a module is loaded the Object Loader will look for a symbol with the name "module_open" and if found will call it with the following prototype:

        void module_open( void );

Similarly, when `cyg_ldr_close()` is called, the Object Loader will look for a symbol named "module_close" and call it, with the same prototype.

### External References

When a module is loaded, the Object Loader package performs any relocations it requires and resolves any unresolved symbol references it contains. The load only succeeds if all of these can be completed. For these symbols to be resolved it is necessary for the main eCos executable to contain a symbol table defining the symbols to be resolved. Normal eCos executables do not contain such a symbol table since it would occupy an unreasonably large amount of memory. There is also no mechanism to persuade the linker to include a loadable symbol table into the executable. Hence it is necessary for the application to explicitly define a symbol table that maps symbol names to addresses.

The loader provides an empty table; the user can then define additional entries required by any loadable modules. In order to keep the size of the table to a minimum, the user can selectively include only those functions that are expected to be used by the loader to resolve all references. There are several macros defined in `objload.h` for defining table entries:

CYG_LDR_TABLE_FUN( name )

  This macro defines a table entry for a function with the given name.

CYG_LDR_TABLE_VAR( name )

  This macro defines a table entry for a data variable with the given name.

CYG_LDR_TABLE_ENTRY( entry_name, symbol_name, address )

  This is a low level macro that allow all aspects of a symbol table entry to be controlled. The entry_name argument defines the table entry object name (a C language requirement since anonymous objects are not permitted). The symbol_name argument is a string giving the symbol that will be matched by the loader. The address argument gives the memory location to which this symbol will resolve.

The `objload.h` file contains a number of macros that collect together groups of functions as a convenient way to include blocks of Kernel, C Library and FILEIO functionality. These include the following:

    CYG_LDR_TABLE_KAPI_ALARM()
    CYG_LDR_TABLE_KAPI_CLOCK()
    CYG_LDR_TABLE_KAPI_COND()
    CYG_LDR_TABLE_KAPI_COUNTER()
    CYG_LDR_TABLE_KAPI_EXCEPTIONS()
    CYG_LDR_TABLE_KAPI_FLAG()
    CYG_LDR_TABLE_KAPI_INTERRUPTS()
    CYG_LDR_TABLE_KAPI_MBOX()
    CYG_LDR_TABLE_KAPI_MEMPOOL_FIX()
    CYG_LDR_TABLE_KAPI_MEMPOOL_VAR()
    CYG_LDR_TABLE_KAPI_MUTEX()
    CYG_LDR_TABLE_KAPI_SCHEDULER()
    CYG_LDR_TABLE_KAPI_SEMAPHORE()
    CYG_LDR_TABLE_KAPI_THREAD()
    CYG_LDR_TABLE_STRING()
    CYG_LDR_TABLE_STDIO()
    CYG_LDR_TABLE_INFRA_DIAG()
    CYG_LDR_TABLE_FILEIO()
    CYG_LDR_TABLE_NET()

Prev | Home | Next
Object Loader | Up | Extending the Object Loader