forked from Imagelibrary/rtems
1032 lines
28 KiB
C
1032 lines
28 KiB
C
/*
|
|
* 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.com/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);
|
|
}
|