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:
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
the e_ident field of the ELF header. It
should be either ELFDATA2LSB
or ELFDATA2MSB. 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. The sym
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 the
st_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.
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 the
r_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 the r_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:
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:
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:
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:
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.