/*
* COPYRIGHT (c) 2012 Chris Johns <chrisj@rtems.org>
*
* The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at
* http://www.rtems.org/license/LICENSE.
*/
/**
* @file
*
* @ingroup rtl
*
* @brief RTEMS Run-Time Linker Error
*/
#if HAVE_CONFIG_H
#include "config.h"
#endif
#include <errno.h>
#include <inttypes.h>
#include <stdlib.h>
#include <stdio.h>
#include <rtems/libio_.h>
#include <rtems/rtl/rtl.h>
#include "rtl-chain-iterator.h"
#include "rtl-obj.h"
#include "rtl-error.h"
#include "rtl-find-file.h"
#include "rtl-string.h"
#include "rtl-trace.h"
#if RTEMS_RTL_RAP_LOADER
#include "rtl-rap.h"
#define RTEMS_RTL_RAP_LOADER_COUNT 1
#else
#define RTEMS_RTL_RAP_LOADER_COUNT 0
#endif
#if RTEMS_RTL_ELF_LOADER
#include "rtl-elf.h"
#define RTEMS_RTL_ELF_LOADER_COUNT 1
#else
#define RTEMS_RTL_ELF_LOADER_COUNT 0
#endif
/**
* The table of supported loader formats.
*/
static rtems_rtl_loader_table_t loaders[RTEMS_RTL_ELF_LOADER_COUNT +
RTEMS_RTL_RAP_LOADER_COUNT] =
{
#if RTEMS_RTL_RAP_LOADER
{ rtems_rtl_rap_file_check, rtems_rtl_rap_file_load, rtems_rtl_rap_file_sig },
#endif
#if RTEMS_RTL_ELF_LOADER
{ rtems_rtl_elf_file_check, rtems_rtl_elf_file_load, rtems_rtl_elf_file_sig },
#endif
};
rtems_rtl_obj_t*
rtems_rtl_obj_alloc (void)
{
rtems_rtl_obj_t* obj = rtems_rtl_alloc_new (RTEMS_RTL_ALLOC_OBJECT,
sizeof (rtems_rtl_obj_t),
true);
if (obj)
{
/*
* Initialise the chains.
*/
rtems_chain_initialize_empty (&obj->sections);
}
return obj;
}
static void
rtems_rtl_obj_free_names (rtems_rtl_obj_t* obj)
{
if (rtems_rtl_obj_oname_valid (obj))
rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_OBJECT, (void*) obj->oname);
if (rtems_rtl_obj_aname_valid (obj))
rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_OBJECT, (void*) obj->aname);
if (rtems_rtl_obj_fname_valid (obj))
rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_OBJECT, (void*) obj->fname);
}
bool
rtems_rtl_obj_free (rtems_rtl_obj_t* obj)
{
if (obj->users || ((obj->flags & RTEMS_RTL_OBJ_LOCKED) != 0))
{
rtems_rtl_set_error (EINVAL, "cannot free obj still in use");
return false;
}
if (!rtems_chain_is_node_off_chain (&obj->link))
rtems_chain_extract (&obj->link);
rtems_rtl_alloc_module_del (&obj->text_base, &obj->const_base,
&obj->data_base, &obj->bss_base);
rtems_rtl_symbol_obj_erase (obj);
rtems_rtl_obj_free_names (obj);
if (obj->sec_num)
free (obj->sec_num);
if (obj->detail)
rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_OBJECT, (void*)obj->detail);
rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_OBJECT, obj);
return true;
}
bool
rtems_rtl_obj_unresolved (rtems_rtl_obj_t* obj)
{
return (obj->flags & RTEMS_RTL_OBJ_UNRESOLVED) != 0 ? true : false;
}
bool
rtems_rtl_parse_name (const char* name,
const char** aname,
const char** oname,
off_t* ooffset)
{
const char* laname = NULL;
const char* loname = NULL;
const char* colon;
const char* end;
/*
* Parse the name to determine if the object file is part of an archive or it
* is an object file. If an archive check the name for a '@' to see if the
* archive contains an offset.
*/
end = name + strlen (name);
colon = strrchr (name, ':');
if (colon == NULL || colon < strrchr(name, '/'))
colon = end;
loname = rtems_rtl_alloc_new (RTEMS_RTL_ALLOC_OBJECT, colon - name + 1, true);
if (!oname)
{
rtems_rtl_set_error (ENOMEM, "no memory for object file name");
return false;
}
memcpy ((void*) loname, name, colon - name);
/*
* If the pointers match there is no ':' delimiter.
*/
if (colon != end)
{
const char* at;
/*
* The file name is an archive and the object file name is next after the
* delimiter. Move the pointer to the archive name.
*/
laname = loname;
++colon;
/*
* See if there is a '@' to delimit an archive offset for the object in the
* archive.
*/
at = strchr (colon, '@');
if (at == NULL)
at = end;
loname = rtems_rtl_alloc_new (RTEMS_RTL_ALLOC_OBJECT, at - colon + 1, true);
if (!loname)
{
rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_OBJECT, (void*) laname);
rtems_rtl_set_error (ENOMEM, "no memory for object file name");
return false;
}
memcpy ((void*) loname, colon, at - colon);
if (at != end)
{
/*
* The object name has an archive offset. If the number
* does not parse 0 will be returned and the archive will be
* searched.
*/
*ooffset = strtoul (at + 1, 0, 0);
}
}
*oname = loname;
*aname = laname;
return true;
}
static bool
rtems_rtl_obj_parse_name (rtems_rtl_obj_t* obj, const char* name)
{
return rtems_rtl_parse_name (name, &(obj->aname), &(obj->oname), &(obj->ooffset));
}
static bool
rtems_rtl_seek_read (int fd, off_t off, size_t len, uint8_t* buffer)
{
if (lseek (fd, off, SEEK_SET) < 0)
return false;
if (read (fd, buffer, len) != len)
return false;
return true;
}
/**
* Scan the decimal number returning the value found.
*/
static uint64_t
rtems_rtl_scan_decimal (const uint8_t* string, size_t len)
{
uint64_t value = 0;
while (len && (*string != ' '))
{
value *= 10;
value += *string - '0';
++string;
--len;
}
return value;
}
/**
* Align the size to the next alignment point. Assume the alignment is a
* positive integral power of 2 if not 0 or 1. If 0 or 1 then there is no
* alignment.
*/
static size_t
rtems_rtl_sect_align (size_t offset, uint32_t alignment)
{
if ((alignment > 1) && ((offset & ~alignment) != 0))
offset = (offset + alignment) & ~(alignment - 1);
return offset;
}
/**
* Section size summer iterator data.
*/
typedef struct
{
uint32_t mask; /**< The selection mask to sum. */
size_t size; /**< The size of all section fragments. */
} rtems_rtl_obj_sect_summer_t;
static bool
rtems_rtl_obj_sect_summer (rtems_chain_node* node, void* data)
{
rtems_rtl_obj_sect_t* sect = (rtems_rtl_obj_sect_t*) node;
rtems_rtl_obj_sect_summer_t* summer = data;
if ((sect->flags & summer->mask) == summer->mask)
summer->size =
rtems_rtl_sect_align (summer->size, sect->alignment) + sect->size;
return true;
}
static size_t
rtems_rtl_obj_section_size (rtems_rtl_obj_t* obj, uint32_t mask)
{
rtems_rtl_obj_sect_summer_t summer;
summer.mask = mask;
summer.size = 0;
rtems_rtl_chain_iterate (&obj->sections,
rtems_rtl_obj_sect_summer,
&summer);
return summer.size;
}
/**
* Section alignment iterator data. The first section's alignment sets the
* alignment for that type of section.
*/
typedef struct
{
uint32_t mask; /**< The selection mask to look for alignment. */
uint32_t alignment; /**< The alignment of the section type. */
} rtems_rtl_obj_sect_aligner_t;
/**
* The section aligner iterator.
*/
static bool
rtems_rtl_obj_sect_aligner (rtems_chain_node* node, void* data)
{
rtems_rtl_obj_sect_t* sect = (rtems_rtl_obj_sect_t*) node;
rtems_rtl_obj_sect_aligner_t* aligner = data;
if ((sect->flags & aligner->mask) == aligner->mask)
{
aligner->alignment = sect->alignment;
return false;
}
return true;
}
static size_t
rtems_rtl_obj_section_alignment (rtems_rtl_obj_t* obj, uint32_t mask)
{
rtems_rtl_obj_sect_aligner_t aligner;
aligner.mask = mask;
aligner.alignment = 0;
rtems_rtl_chain_iterate (&obj->sections,
rtems_rtl_obj_sect_aligner,
&aligner);
return aligner.alignment;
}
static bool
rtems_rtl_obj_section_handler (uint32_t mask,
rtems_rtl_obj_t* obj,
int fd,
rtems_rtl_obj_sect_handler_t handler,
void* data)
{
rtems_chain_node* node = rtems_chain_first (&obj->sections);
while (!rtems_chain_is_tail (&obj->sections, node))
{
rtems_rtl_obj_sect_t* sect = (rtems_rtl_obj_sect_t*) node;
if ((sect->flags & mask) != 0)
{
if (!handler (obj, fd, sect, data))
return false;
}
node = rtems_chain_next (node);
}
return true;
}
bool
rtems_rtl_match_name (rtems_rtl_obj_t* obj, const char* name)
{
const char* n1 = obj->oname;
while ((*n1 != '\0') && (*n1 != '\n') && (*n1 != '/') &&
(*name != '\0') && (*name != '/') && (*n1 == *name))
{
++n1;
++name;
}
if (((*n1 == '\0') || (*n1 == '\n') || (*n1 == '/')) &&
((*name == '\0') || (*name == '/')))
return true;
return false;
}
bool
rtems_rtl_obj_find_file (rtems_rtl_obj_t* obj, const char* name)
{
const char* pname;
rtems_rtl_data_t* rtl;
/*
* Parse the name. The object descriptor will have the archive name and/or
* object name fields filled in. A find of the file will result in the file
* name (fname) field pointing to the actual file if present on the file
* system.
*/
if (!rtems_rtl_obj_parse_name (obj, name))
return false;
/*
* If the archive field (aname) is set we use that name else we use the
* object field (oname). If selected name is absolute we just point the aname
* field to the fname field to that name. If the field is relative we search
* the paths set in the RTL for the file.
*/
if (rtems_rtl_obj_aname_valid (obj))
pname = rtems_rtl_obj_aname (obj);
else
pname = rtems_rtl_obj_oname (obj);
rtl = rtems_rtl_lock ();
if (!rtems_rtl_find_file (pname, rtl->paths, &obj->fname, &obj->fsize))
{
rtems_rtl_set_error (ENOENT, "file not found");
rtems_rtl_unlock ();
return false;
}
rtems_rtl_unlock ();
return true;
}
bool
rtems_rtl_obj_add_section (rtems_rtl_obj_t* obj,
int section,
const char* name,
size_t size,
off_t offset,
uint32_t alignment,
int link,
int info,
uint32_t flags)
{
rtems_rtl_obj_sect_t* sect = rtems_rtl_alloc_new (RTEMS_RTL_ALLOC_OBJECT,
sizeof (rtems_rtl_obj_sect_t), true);
if (!sect)
{
rtems_rtl_set_error (ENOMEM, "adding allocated section");
return false;
}
sect->section = section;
sect->name = rtems_rtl_strdup (name);
sect->size = size;
sect->offset = offset;
sect->alignment = alignment;
sect->link = link;
sect->info = info;
sect->flags = flags;
sect->base = NULL;
rtems_chain_append (&obj->sections, §->node);
if (rtems_rtl_trace (RTEMS_RTL_TRACE_SECTION))
printf ("rtl: sect: %-2d: %s\n", section, name);
return true;
}
void
rtems_rtl_obj_erase_sections (rtems_rtl_obj_t* obj)
{
rtems_chain_node* node = rtems_chain_first (&obj->sections);
while (!rtems_chain_is_tail (&obj->sections, node))
{
rtems_rtl_obj_sect_t* sect = (rtems_rtl_obj_sect_t*) node;
rtems_chain_node* next_node = rtems_chain_next (node);
rtems_chain_extract (node);
rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_OBJECT, (void*) sect->name);
rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_OBJECT, sect);
node = next_node;
}
}
/**
* Section finder iterator data.
*/
typedef struct
{
rtems_rtl_obj_sect_t* sect; /**< The matching section. */
const char* name; /**< The name to match. */
int index; /**< The index to match. */
} rtems_rtl_obj_sect_finder_t;
static bool
rtems_rtl_obj_sect_match_name (rtems_chain_node* node, void* data)
{
rtems_rtl_obj_sect_t* sect = (rtems_rtl_obj_sect_t*) node;
rtems_rtl_obj_sect_finder_t* match = data;
if (strcmp (sect->name, match->name) == 0)
{
match->sect = sect;
return false;
}
return true;
}
rtems_rtl_obj_sect_t*
rtems_rtl_obj_find_section (rtems_rtl_obj_t* obj, const char* name)
{
rtems_rtl_obj_sect_finder_t match;
match.sect = NULL;
match.name = name;
rtems_rtl_chain_iterate (&obj->sections,
rtems_rtl_obj_sect_match_name,
&match);
return match.sect;
}
static bool
rtems_rtl_obj_sect_match_index (rtems_chain_node* node, void* data)
{
rtems_rtl_obj_sect_t* sect = (rtems_rtl_obj_sect_t*) node;
rtems_rtl_obj_sect_finder_t* match = data;
if (sect->section == match->index)
{
match->sect = sect;
return false;
}
return true;
}
rtems_rtl_obj_sect_t*
rtems_rtl_obj_find_section_by_index (rtems_rtl_obj_t* obj, int index)
{
rtems_rtl_obj_sect_finder_t match;
match.sect = NULL;
match.index = index;
rtems_rtl_chain_iterate (&obj->sections,
rtems_rtl_obj_sect_match_index,
&match);
return match.sect;
}
size_t
rtems_rtl_obj_text_size (rtems_rtl_obj_t* obj)
{
return rtems_rtl_obj_section_size (obj, RTEMS_RTL_OBJ_SECT_TEXT);
}
uint32_t
rtems_rtl_obj_text_alignment (rtems_rtl_obj_t* obj)
{
return rtems_rtl_obj_section_alignment (obj, RTEMS_RTL_OBJ_SECT_TEXT);
}
size_t
rtems_rtl_obj_const_size (rtems_rtl_obj_t* obj)
{
return rtems_rtl_obj_section_size (obj, RTEMS_RTL_OBJ_SECT_CONST);
}
uint32_t
rtems_rtl_obj_const_alignment (rtems_rtl_obj_t* obj)
{
return rtems_rtl_obj_section_alignment (obj, RTEMS_RTL_OBJ_SECT_CONST);
}
size_t
rtems_rtl_obj_data_size (rtems_rtl_obj_t* obj)
{
return rtems_rtl_obj_section_size (obj, RTEMS_RTL_OBJ_SECT_DATA);
}
uint32_t
rtems_rtl_obj_data_alignment (rtems_rtl_obj_t* obj)
{
return rtems_rtl_obj_section_alignment (obj, RTEMS_RTL_OBJ_SECT_DATA);
}
size_t
rtems_rtl_obj_bss_size (rtems_rtl_obj_t* obj)
{
return rtems_rtl_obj_section_size (obj, RTEMS_RTL_OBJ_SECT_BSS);
}
uint32_t
rtems_rtl_obj_bss_alignment (rtems_rtl_obj_t* obj)
{
return rtems_rtl_obj_section_alignment (obj, RTEMS_RTL_OBJ_SECT_BSS);
}
bool
rtems_rtl_obj_relocate (rtems_rtl_obj_t* obj,
int fd,
rtems_rtl_obj_sect_handler_t handler,
void* data)
{
uint32_t mask = RTEMS_RTL_OBJ_SECT_REL | RTEMS_RTL_OBJ_SECT_RELA;
return rtems_rtl_obj_section_handler (mask, obj, fd, handler, data);
}
bool
rtems_rtl_obj_load_symbols (rtems_rtl_obj_t* obj,
int fd,
rtems_rtl_obj_sect_handler_t handler,
void* data)
{
uint32_t mask = RTEMS_RTL_OBJ_SECT_SYM;
return rtems_rtl_obj_section_handler (mask, obj, fd, handler, data);
}
static size_t
rtems_rtl_obj_sections_loader (uint32_t mask,
rtems_rtl_obj_t* obj,
int fd,
uint8_t* base,
rtems_rtl_obj_sect_handler_t handler,
void* data)
{
rtems_chain_control* sections = &obj->sections;
rtems_chain_node* node = rtems_chain_first (sections);
size_t base_offset = 0;
bool first = true;
while (!rtems_chain_is_tail (sections, node))
{
rtems_rtl_obj_sect_t* sect = (rtems_rtl_obj_sect_t*) node;
if ((sect->size != 0) && ((sect->flags & mask) != 0))
{
if (!first)
base_offset = rtems_rtl_sect_align (base_offset, sect->alignment);
sect->base = base + base_offset;
if (rtems_rtl_trace (RTEMS_RTL_TRACE_LOAD_SECT))
printf ("rtl: loading: %s -> %8p (%zi)\n",
sect->name, sect->base, sect->size);
if ((sect->flags & RTEMS_RTL_OBJ_SECT_LOAD) == RTEMS_RTL_OBJ_SECT_LOAD)
{
if (!handler (obj, fd, sect, data))
{
sect->base = 0;
return false;
}
}
else if ((sect->flags & RTEMS_RTL_OBJ_SECT_ZERO) == RTEMS_RTL_OBJ_SECT_ZERO)
{
memset (base + base_offset, 0, sect->size);
}
else
{
sect->base = 0;
rtems_rtl_set_error (errno, "section has no load op");
return false;
}
base_offset += sect->size;
first = false;
}
node = rtems_chain_next (node);
}
return true;
}
bool
rtems_rtl_obj_load_sections (rtems_rtl_obj_t* obj,
int fd,
rtems_rtl_obj_sect_handler_t handler,
void* data)
{
size_t text_size;
size_t const_size;
size_t data_size;
size_t bss_size;
text_size = rtems_rtl_obj_text_size (obj) + rtems_rtl_obj_const_alignment (obj);
const_size = rtems_rtl_obj_const_size (obj) + rtems_rtl_obj_data_alignment (obj);
data_size = rtems_rtl_obj_data_size (obj) + rtems_rtl_obj_bss_alignment (obj);
bss_size = rtems_rtl_obj_bss_size (obj);
/*
* Let the allocator manage the actual allocation. The user can use the
* standard heap or provide a specific allocator with memory protection.
*/
if (!rtems_rtl_alloc_module_new (&obj->text_base, text_size,
&obj->const_base, const_size,
&obj->data_base, data_size,
&obj->bss_base, bss_size))
{
obj->exec_size = 0;
rtems_rtl_set_error (ENOMEM, "no memory to load obj");
return false;
}
obj->exec_size = text_size + const_size + data_size + bss_size;
if (rtems_rtl_trace (RTEMS_RTL_TRACE_LOAD_SECT))
{
printf ("rtl: load sect: text - b:%p s:%zi a:%" PRIu32 "\n",
obj->text_base, text_size, rtems_rtl_obj_text_alignment (obj));
printf ("rtl: load sect: const - b:%p s:%zi a:%" PRIu32 "\n",
obj->const_base, const_size, rtems_rtl_obj_const_alignment (obj));
printf ("rtl: load sect: data - b:%p s:%zi a:%" PRIu32 "\n",
obj->data_base, data_size, rtems_rtl_obj_data_alignment (obj));
printf ("rtl: load sect: bss - b:%p s:%zi a:%" PRIu32 "\n",
obj->bss_base, bss_size, rtems_rtl_obj_bss_alignment (obj));
}
/*
* Load all text then data then bss sections in seperate operations so each
* type of section is grouped together.
*/
if (!rtems_rtl_obj_sections_loader (RTEMS_RTL_OBJ_SECT_TEXT,
obj, fd, obj->text_base, handler, data) ||
!rtems_rtl_obj_sections_loader (RTEMS_RTL_OBJ_SECT_CONST,
obj, fd, obj->const_base, handler, data) ||
!rtems_rtl_obj_sections_loader (RTEMS_RTL_OBJ_SECT_DATA,
obj, fd, obj->data_base, handler, data) ||
!rtems_rtl_obj_sections_loader (RTEMS_RTL_OBJ_SECT_BSS,
obj, fd, obj->bss_base, handler, data))
{
rtems_rtl_alloc_module_del (&obj->text_base, &obj->const_base,
&obj->data_base, &obj->bss_base);
obj->exec_size = 0;
return false;
}
return true;
}
static void
rtems_rtl_obj_run_cdtors (rtems_rtl_obj_t* obj, uint32_t mask)
{
rtems_chain_node* node = rtems_chain_first (&obj->sections);
while (!rtems_chain_is_tail (&obj->sections, node))
{
rtems_rtl_obj_sect_t* sect = (rtems_rtl_obj_sect_t*) node;
if ((sect->flags & mask) == mask)
{
rtems_rtl_cdtor_t* handler;
size_t handlers = sect->size / sizeof (rtems_rtl_cdtor_t);
int c;
for (c = 0, handler = sect->base; c < handlers; ++c)
if (*handler)
(*handler) ();
}
node = rtems_chain_next (node);
}
}
void
rtems_rtl_obj_run_ctors (rtems_rtl_obj_t* obj)
{
return rtems_rtl_obj_run_cdtors (obj, RTEMS_RTL_OBJ_SECT_CTOR);
}
void
rtems_rtl_obj_run_dtors (rtems_rtl_obj_t* obj)
{
return rtems_rtl_obj_run_cdtors (obj, RTEMS_RTL_OBJ_SECT_DTOR);
}
/**
* Find a module in an archive returning the offset in the archive in the
* object descriptor.
*/
static bool
rtems_rtl_obj_archive_find (rtems_rtl_obj_t* obj, int fd)
{
#define RTEMS_RTL_AR_IDENT "!<arch>\n"
#define RTEMS_RTL_AR_IDENT_SIZE (sizeof (RTEMS_RTL_AR_IDENT) - 1)
#define RTEMS_RTL_AR_FHDR_BASE RTEMS_RTL_AR_IDENT_SIZE
#define RTEMS_RTL_AR_FNAME (0)
#define RTEMS_RTL_AR_FNAME_SIZE (16)
#define RTEMS_RTL_AR_SIZE (48)
#define RTEMS_RTL_AR_SIZE_SIZE (10)
#define RTEMS_RTL_AR_MAGIC (58)
#define RTEMS_RTL_AR_MAGIC_SIZE (2)
#define RTEMS_RTL_AR_FHDR_SIZE (60)
size_t fsize = obj->fsize;
off_t extended_file_names;
uint8_t header[RTEMS_RTL_AR_FHDR_SIZE];
bool scanning;
if (read (fd, &header[0], RTEMS_RTL_AR_IDENT_SIZE) != RTEMS_RTL_AR_IDENT_SIZE)
{
rtems_rtl_set_error (errno, "reading archive identifer");
return false;
}
if (memcmp (header, RTEMS_RTL_AR_IDENT, RTEMS_RTL_AR_IDENT_SIZE) != 0)
{
rtems_rtl_set_error (EINVAL, "invalid archive identifer");
return false;
}
/*
* Seek to the current offset in the archive and if we have a valid archive
* file header present check the file name for a match with the oname field
* of the object descriptor. If the archive header is not valid and it is the
* first pass reset the offset and start the search again in case the offset
* provided is not valid any more.
*
* The archive can have a symbol table at the start. Ignore it. A symbol
* table has the file name '/'.
*
* The archive can also have the GNU extended file name table. This
* complicates the processing. If the object's file name starts with '/' the
* remainder of the file name is an offset into the extended file name
* table. To find the extended file name table we need to scan from start of
* the archive for a file name of '//'. Once found we remeber the table's
* start and can direct seek to file name location. In other words the scan
* only happens once.
*
* If the name had the offset encoded we go straight to that location.
*/
if (obj->ooffset != 0)
scanning = false;
else
{
scanning = true;
obj->ooffset = RTEMS_RTL_AR_FHDR_BASE;
}
extended_file_names = 0;
while (obj->ooffset < fsize)
{
/*
* Clean up any existing data.
*/
memset (header, 0, sizeof (header));
if (!rtems_rtl_seek_read (fd, obj->ooffset, RTEMS_RTL_AR_FHDR_SIZE, &header[0]))
{
rtems_rtl_set_error (errno, "seek/read archive file header");
obj->ooffset = 0;
obj->fsize = 0;
return false;
}
if ((header[RTEMS_RTL_AR_MAGIC] != 0x60) ||
(header[RTEMS_RTL_AR_MAGIC + 1] != 0x0a))
{
if (scanning)
{
rtems_rtl_set_error (EINVAL, "invalid archive file header");
obj->ooffset = 0;
obj->fsize = 0;
return false;
}
scanning = true;
obj->ooffset = RTEMS_RTL_AR_FHDR_BASE;
continue;
}
/*
* The archive header is always aligned to an even address.
*/
obj->fsize = (rtems_rtl_scan_decimal (&header[RTEMS_RTL_AR_SIZE],
RTEMS_RTL_AR_SIZE_SIZE) + 1) & ~1;
/*
* Check for the GNU extensions.
*/
if (header[0] == '/')
{
off_t extended_off;
switch (header[1])
{
case ' ':
/*
* Symbols table. Ignore the table.
*/
break;
case '/':
/*
* Extended file names table. Remember.
*/
extended_file_names = obj->ooffset + RTEMS_RTL_AR_FHDR_SIZE;
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
/*
* Offset into the extended file name table. If we do not have the
* offset to the extended file name table find it.
*/
extended_off =
rtems_rtl_scan_decimal (&header[1], RTEMS_RTL_AR_FNAME_SIZE);
if (extended_file_names == 0)
{
off_t off = obj->ooffset;
while (extended_file_names == 0)
{
off_t esize =
(rtems_rtl_scan_decimal (&header[RTEMS_RTL_AR_SIZE],
RTEMS_RTL_AR_SIZE_SIZE) + 1) & ~1;
off += esize + RTEMS_RTL_AR_FHDR_SIZE;
if (!rtems_rtl_seek_read (fd, off,
RTEMS_RTL_AR_FHDR_SIZE, &header[0]))
{
rtems_rtl_set_error (errno,
"seeking/reading archive ext file name header");
obj->ooffset = 0;
obj->fsize = 0;
return false;
}
if ((header[RTEMS_RTL_AR_MAGIC] != 0x60) ||
(header[RTEMS_RTL_AR_MAGIC + 1] != 0x0a))
{
rtems_rtl_set_error (errno, "invalid archive file header");
obj->ooffset = 0;
obj->fsize = 0;
return false;
}
if ((header[0] == '/') && (header[1] == '/'))
{
extended_file_names = off + RTEMS_RTL_AR_FHDR_SIZE;
break;
}
}
}
if (extended_file_names)
{
/*
* We know the offset in the archive to the extended file. Read the
* name from the table and compare with the name we are after.
*/
#define RTEMS_RTL_MAX_FILE_SIZE (256)
char name[RTEMS_RTL_MAX_FILE_SIZE];
if (!rtems_rtl_seek_read (fd, extended_file_names + extended_off,
RTEMS_RTL_MAX_FILE_SIZE, (uint8_t*) &name[0]))
{
rtems_rtl_set_error (errno,
"invalid archive ext file seek/read");
obj->ooffset = 0;
obj->fsize = 0;
return false;
}
if (rtems_rtl_match_name (obj, name))
{
obj->ooffset += RTEMS_RTL_AR_FHDR_SIZE;
return true;
}
}
break;
default:
/*
* Ignore the file because we do not know what it it.
*/
break;
}
}
else
{
if (rtems_rtl_match_name (obj, (const char*) &header[RTEMS_RTL_AR_FNAME]))
{
obj->ooffset += RTEMS_RTL_AR_FHDR_SIZE;
return true;
}
}
obj->ooffset += obj->fsize + RTEMS_RTL_AR_FHDR_SIZE;
}
rtems_rtl_set_error (ENOENT, "object file not found");
obj->ooffset = 0;
obj->fsize = 0;
return false;
}
bool
rtems_rtl_obj_file_load (rtems_rtl_obj_t* obj, int fd)
{
int l;
for (l = 0; l < (sizeof (loaders) / sizeof (rtems_rtl_loader_table_t)); ++l)
{
if (loaders[l].check (obj, fd))
return loaders[l].load (obj, fd);
}
rtems_rtl_set_error (ENOENT, "no format loader found");
return false;
}
bool
rtems_rtl_obj_load (rtems_rtl_obj_t* obj)
{
int fd;
if (!rtems_rtl_obj_fname_valid (obj))
{
rtems_rtl_set_error (ENOMEM, "invalid object file name path");
return false;
}
fd = open (rtems_rtl_obj_fname (obj), O_RDONLY);
if (fd < 0)
{
rtems_rtl_set_error (ENOMEM, "opening for object file");
return false;
}
/*
* Find the object file in the archive if it is an archive that
* has been opened.
*/
if (rtems_rtl_obj_aname_valid (obj))
{
if (!rtems_rtl_obj_archive_find (obj, fd))
{
rtems_rtl_obj_caches_flush ();
close (fd);
return false;
}
}
/*
* Call the format specific loader. Currently this is a call to the ELF
* loader. This call could be changed to allow probes then calls if more than
* one format is supported.
*/
if (!rtems_rtl_obj_file_load (obj, fd))
{
rtems_rtl_obj_caches_flush ();
close (fd);
return false;
}
if (!_rtld_linkmap_add (obj)) /* For GDB */
{
close (fd);
return false;
}
rtems_rtl_obj_caches_flush ();
close (fd);
return true;
}
bool
rtems_rtl_obj_unload (rtems_rtl_obj_t* obj)
{
_rtld_linkmap_delete(obj);
rtems_rtl_symbol_obj_erase (obj);
return rtems_rtl_obj_free (obj);
}