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. However, some later ARM variants do not need the "-mthumb-interwork" option since this is implicit in the architecture. For such targets this option need not be given.
- Modules compiled for ARM, Thumb or Thumb 2 may require the "-mlong-calls" compiler option if the module to be loaded will occupy a different region of the address space to the rest of the program. The most frequent scenario causing this to arise is if the main program runs from FLASH memory, but with the module loaded into RAM. If in doubt, use the option as it is always safe, and the only downside is a small code size and runtime execution penalty on function calls.
- 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".
- Modules compiled for NIOS II processors must be compiled with the "-G0" compiler option. This ensures that loaded modules do not make assumptions about the accessibility of small initialised data (".sdata") or small zero-initialised data (".sbss") relative to the address it was loaded at.
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 disablingCYGPKG_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). Thesymbol_name
argument is a string giving the symbol that will be matched by the loader. Theaddress
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()
2024-12-10 | eCosPro Non-Commercial Public License |