Name
CYGPKG_OBJLOADER
— Extending the Object Loader
Description
The Object Loader package has a number of features that allow it to be extended. To support a new CPU architecture a new relocator needs to be written. If ELF files are to be read from a source that differs from those currently supported, then a new loader needs to be written. Finally, the mechanism by which the loader allocates the memory used to store loaded sections can be redirected by the application.
Adding New Relocators
When the loader loads a new module some locations in it must be adjusted to account for the address at which is it loaded. References to external symbols must also be installed. The location and nature of these modifications are described by one or more sections in the ELF file which contain a sequence of relocation records. The exact meaning of the relocations that these records define is architecture specific and is usually described as part of the ABI for that CPU type.
To define a new relocator for a CPU, it is necessary to add an extra
definition to the objloader.cdl
file, and add a
header and source file to the package. To support the XYZ CPU the
following must be added to
the CYGPKG_OBJLOADER_ARCHITECTURE
component:
cdl_option CYGBLD_OBJLOADER_ARCHITECTURE_XYZ { display "Support loading on XYZ processors" calculated CYGPKG_HAL_XYZ implements CYGINT_OBJLOADER_RELOCATOR define_proc { puts $::cdl_header "#include <cyg/objloader/relocate_xyz.h>" } compile relocate_xyz.c }
The relocate_xyz.h
file needs to define some
macros to customize the loader:
-
ELF_ARCH_MACHINE_TYPE
-
This defines the value that the
e_machine
machine type field of the ELF header. If this does not match, the module load will fail. -
ELF_ARCH_ENDIANNESS
-
This defines the value that the
EI_DATA
byte of thee_ident
field of the ELF header. It should be eitherELFDATA2LSB
orELFDATA2MSB
. If this does not match, the module load will fail. Architectures that are bi-endian need to test either a compiler or a HAL definition to select the correct endianness for the current build. -
CYG_LDR_MAKE_LOCAL_ADDRESS( addr, sym )
-
This macro is used to combine the section address of a symbol with
information from its symbol table entry. The
addr
argument is the base address of the section in which the symbol is defined. Thesym
is the symbol table entry; this is not a pointer, so fields should be accessed using the "." operator, not the "->" operator. The return value should be of type void *. This is an optional macro, if it is not defined here then a default definition will be used which simply adds thest_value
field of the symbol table entry to the address.
In addition to these, it may also contain definitions that are useful to the relocator. Typically the relocation record types and any support macros may be defined here.
The relocate_xyz.c
file contains two functions:
-
void cyg_ldr_flush_cache(void)
- This function is called to perform any cache flushing needed. The loader modifies code in memory that will subsequently be executed. It does this using data accesses, so it is essential that these updates are flushed from the data cache, and that stale entries are flushed from the instruction cache. This function must call appropriate HAL cache operations to ensure that this is done.
-
cyg_int32 cyg_ldr_relocate(cyg_int32 rel_type, cyg_uint32 flags, cyg_uint32 mem, cyg_int32 sym_value)
The loader will call this function for each relocation record in each relocation section found in the module.
The
rel_type
argument defines the relocation record type and will be one of the relocation types defined for the architecture. Most architecture ABIs define a large number of relocations, not all of which will be relevant to the use that eCos makes of the object file format. In general only a small subset of relocation types need to be handled which can usually be determined by inspecting the object files generated by compiling eCos.The
flags
argument contains flags that provide additional information about the relocation record. At present only one flag is defined:CYG_LDR_FLAG_RELA
which is set when the relocation is from a RELA record, otherwise it comes from a REL record.The
mem
argument contains the address of the location in memory to be relocated. It is constructed from the base address of the segment targeted by the relocation section, plus ther_offset
field from the relocation record.The
sym_value
argument contains the address of any symbol associated with the relocation record. If it was a RELA record, then the contents of ther_addend
field will have been added.
Adding new Loaders
The Object Loader package needs to fetch a module from some source to load it into memory. This is the job of a loader. A loader consists of an open function plus read, seek and close functions.
The loader open function is supplied as the first parameter
to cyg_ldr_open()
. It is called with the second
argument as a parameter. On success it returns a pointer to
a CYG_LDR_ELF_OBJECT object. On failure it
returns NULL.
The open function has a number of duties, best described by an annotated example for the ABC loader:
__externC CYG_LDR_ELF_OBJECT *cyg_ldr_open_abc(CYG_ADDRWORD arg) { // Allocate a CYG_LDR_ELF_OBJECT CYG_LDR_ELF_OBJECT * obj = (CYG_LDR_ELF_OBJECT *)cyg_ldr_malloc(sizeof(CYG_LDR_ELF_OBJECT)); // Allocate a private data descriptor. Depending on the nature of // the loader this may not be necessary. struct abc_desc *desc = cyg_ldr_malloc(sizeof(struct abc_desc)); // Check that the memory allocations worked if( obj == NULL || desc == NULL ) { if( obj != NULL ) free(obj); if( desc != NULL ) free(desc); cyg_ldr_last_error = "ERROR IN MALLOC"; return NULL; } // Perform any operations to enable access to the ELF file and // fill in the descriptor. If this fails then free both descriptor // and ELF object, set cyg_ldr_last_error and return NULL. // Clear the CYG_LDR_ELF_OBJECT memset( obj, 0, sizeof(CYG_LDR_ELF_OBJECT)); // Install private data pointer obj->ptr = (CYG_ADDRWORD)desc; // Install pointers to read, seek and close functions obj->read = cyg_ldr_abc_read; obj->seek = cyg_ldr_abc_seek; obj->close = cyg_ldr_abc_close; // Return completed object return obj; }
The read function will be called via the pointer in the ELF object whenever the Object Loader needs to read data from the file. It has the following definition:
static size_t cyg_ldr_abc_read(struct CYG_LDR_ELF_OBJECT* obj, size_t size, void* buf)
The obj
argument is the ELF object returned
from the open function. The size
argument gives
the number of bytes to be read and buf
points
to a location to store them. The function returns the number of bytes
read.
The seek function will be called via the pointer in the ELF object whenever the Object Loader needs to reposition the point in the file at which the next read will occur. It has the following definition:
static cyg_int32 cyg_ldr_abc_seek(struct CYG_LDR_ELF_OBJECT* obj, cyg_uint32 offset)
The obj
argument is the ELF object returned
from the open function. The offset
argument
gives the number of bytes from the start of the file to which the read
point should be moved. The function returns the new read offset. Some
sources may not be able to reposition the read pointer backwards, and
may only be capable of advancing it. If the reposition fails then this
function should return -1.
The close function will be called via the pointer in the ELF object when the Object Loader has finished with the file. It has the following definition:
static cyg_int32 cyg_ldr_abc_close(struct CYG_LDR_ELF_OBJECT* obj)
The obj
argument is the ELF object returned
from the open function. This function should close down access to the
file, free the private data descriptor if necessary and set
the obj->ptr
field to zero. It should not free the
ELF object itself, the Object Loader will do this itself later. If the
close succeeds then this function should return zero, and -1 if it
fails.
Redirecting Memory Allocation
All memory allocation in the Object Loader is made via
the cyg_ldr_malloc()
function and it is freed via
the cyg_ldr_free()
function. These have the
following prototypes:
__externC void *cyg_ldr_malloc(size_t) CYGBLD_ATTRIB_WEAK; __externC void cyg_ldr_free(void *) CYGBLD_ATTRIB_WEAK;
These functions by default simply call the standard
malloc()
and free()
heap
functions. However, they are defined with the weak
linker attribute. This means that the application can redefine these
functions to provide an alternative allocation and free mechanism if,
for example, the standard heap support has been omitted.
2024-12-10 | eCosPro Non-Commercial Public License |