summaryrefslogtreecommitdiffstats
path: root/cpukit/libdl/rtl-obj.c
diff options
context:
space:
mode:
Diffstat (limited to 'cpukit/libdl/rtl-obj.c')
-rw-r--r--cpukit/libdl/rtl-obj.c1031
1 files changed, 1031 insertions, 0 deletions
diff --git a/cpukit/libdl/rtl-obj.c b/cpukit/libdl/rtl-obj.c
new file mode 100644
index 0000000000..d4ee63c035
--- /dev/null
+++ b/cpukit/libdl/rtl-obj.c
@@ -0,0 +1,1031 @@
+/*
+ * 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, &sect->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);
+}