summaryrefslogtreecommitdiffstats
path: root/cpukit/libdl/rtl-unresolved.c
diff options
context:
space:
mode:
authorChris Johns <chrisj@rtems.org>2014-10-26 18:09:41 -0700
committerChris Johns <chrisj@rtems.org>2014-10-31 11:04:15 +1100
commitae5fe7e6bca2874c5f1ef077204bb63124fb3db3 (patch)
tree90a6e9e7b414ed3713011267b1fee404b5f6093f /cpukit/libdl/rtl-unresolved.c
parentAdded missing stm32f4xxxx_adc.h (diff)
downloadrtems-ae5fe7e6bca2874c5f1ef077204bb63124fb3db3.tar.bz2
cpukit: Add libdl with the Runtime Loader (RTL) code.
This is a merge of the RTL project.
Diffstat (limited to 'cpukit/libdl/rtl-unresolved.c')
-rw-r--r--cpukit/libdl/rtl-unresolved.c471
1 files changed, 471 insertions, 0 deletions
diff --git a/cpukit/libdl/rtl-unresolved.c b/cpukit/libdl/rtl-unresolved.c
new file mode 100644
index 0000000000..0dbde0a48a
--- /dev/null
+++ b/cpukit/libdl/rtl-unresolved.c
@@ -0,0 +1,471 @@
+/*
+ * 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 rtems_rtl
+ *
+ * @brief RTEMS Run-Time Linker Object File Unresolved Relocations Table.
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <errno.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <rtems/rtl/rtl.h>
+#include "rtl-error.h"
+#include "rtl-unresolved.h"
+#include "rtl-trace.h"
+
+static rtems_rtl_unresolv_block_t*
+rtems_rtl_unresolved_block_alloc (rtems_rtl_unresolved_t* unresolved)
+{
+ /*
+ * The block header contains a record.
+ */
+ size_t size =
+ (sizeof(rtems_rtl_unresolv_block_t) +
+ (sizeof(rtems_rtl_unresolv_rec_t) * (unresolved->block_recs - 1)));
+ rtems_rtl_unresolv_block_t* block =
+ rtems_rtl_alloc_new (RTEMS_RTL_ALLOC_EXTERNAL, size, true);
+ if (block)
+ rtems_chain_append (&unresolved->blocks, &block->link);
+ else
+ rtems_rtl_set_error (ENOMEM, "no memory for unresolved block");
+ return block;
+}
+
+static size_t
+rtems_rtl_unresolved_name_recs (const char* name)
+{
+ size_t length = strlen (name);
+ return ((length + sizeof(rtems_rtl_unresolv_name_t) - 1) /
+ sizeof(rtems_rtl_unresolv_name_t));
+}
+
+static int
+rtems_rtl_unresolved_rec_index (rtems_rtl_unresolv_block_t* block,
+ rtems_rtl_unresolv_rec_t* rec)
+{
+ return (rec - &block->rec) / sizeof (rtems_rtl_unresolv_rec_t);
+}
+
+static rtems_rtl_unresolv_rec_t*
+rtems_rtl_unresolved_rec_first (rtems_rtl_unresolv_block_t* block)
+{
+ return &block->rec;
+}
+
+static rtems_rtl_unresolv_rec_t*
+rtems_rtl_unresolved_rec_next (rtems_rtl_unresolv_rec_t* rec)
+{
+
+ switch (rec->type)
+ {
+ case rtems_rtl_unresolved_empty:
+ /*
+ * Empty returns NULL. The end of the records in the block.
+ */
+ rec = NULL;
+ break;
+
+ case rtems_rtl_unresolved_name:
+ /*
+ * Determine how many records the name occupies. Round up.
+ */
+ rec += ((rec->rec.name.length + sizeof(rtems_rtl_unresolv_name_t) - 1) /
+ sizeof(rtems_rtl_unresolv_name_t));
+ break;
+
+ case rtems_rtl_unresolved_reloc:
+ ++rec;
+ break;
+
+ default:
+ break;
+ }
+
+ return rec;
+}
+
+static bool
+rtems_rtl_unresolved_rec_is_last (rtems_rtl_unresolv_block_t* block,
+ rtems_rtl_unresolv_rec_t* rec)
+{
+ int index = (rec - &block->rec) / sizeof (rec);
+ return !rec || (index >= block->recs) || (rec->type == rtems_rtl_unresolved_empty);
+}
+
+static rtems_rtl_unresolv_rec_t*
+rtems_rtl_unresolved_rec_first_free (rtems_rtl_unresolv_block_t* block)
+{
+ return &block->rec + block->recs;
+}
+
+static int
+rtems_rtl_unresolved_find_name (rtems_rtl_unresolved_t* unresolved,
+ const char* name,
+ bool update_refcount)
+{
+ size_t length = strlen (name);
+ int index = 1;
+
+ rtems_chain_node* node = rtems_chain_first (&unresolved->blocks);
+ while (!rtems_chain_is_tail (&unresolved->blocks, node))
+ {
+ rtems_rtl_unresolv_block_t* block = (rtems_rtl_unresolv_block_t*) node;
+ rtems_rtl_unresolv_rec_t* rec = rtems_rtl_unresolved_rec_first (block);
+
+ while (!rtems_rtl_unresolved_rec_is_last (block, rec))
+ {
+ if (rec->type == rtems_rtl_unresolved_name)
+ {
+ if ((rec->rec.name.length == length)
+ && (strcmp (rec->rec.name.name, name)))
+ {
+ if (update_refcount)
+ ++rec->rec.name.refs;
+ return index;
+ }
+ ++index;
+ }
+ rec = rtems_rtl_unresolved_rec_next (rec);
+ }
+
+ node = rtems_chain_next (node);
+ }
+
+ return 0 - index;
+}
+
+/**
+ * Struct to pass relocation data in the interator.
+ */
+typedef struct rtems_rtl_unresolved_reloc_data_s
+{
+ uint16_t name; /**< Name index. */
+ rtems_rtl_unresolv_rec_t* name_rec; /**< Name record. */
+ rtems_rtl_obj_sym_t* sym; /**< The symbol record. */
+} rtems_rtl_unresolved_reloc_data_t;
+
+static bool
+rtems_rtl_unresolved_resolve_reloc (rtems_rtl_unresolv_rec_t* rec,
+ void* data)
+{
+ if (rec->type == rtems_rtl_unresolved_reloc)
+ {
+ rtems_rtl_unresolved_reloc_data_t* rd;
+ rd = (rtems_rtl_unresolved_reloc_data_t*) data;
+
+ if (rec->rec.reloc.name == rd->name)
+ {
+ if (rtems_rtl_trace (RTEMS_RTL_TRACE_UNRESOLVED))
+ printf ("rtl: unresolv: resolve reloc: %s\n", rd->name_rec->rec.name.name);
+
+ rtems_rtl_obj_relocate_unresolved (&rec->rec.reloc, rd->sym);
+
+ /*
+ * Set the object pointer to NULL to indicate the record is not used
+ * anymore. Update the reference count of the name. The sweep after
+ * relocating will remove the reloc records with obj set to NULL and
+ * names with a reference count of 0.
+ */
+ rec->rec.reloc.obj = NULL;
+ if (rd->name_rec && rd->name_rec->rec.name.refs)
+ --rd->name_rec->rec.name.refs;
+ }
+ }
+ return false;
+}
+
+static bool
+rtems_rtl_unresolved_resolve_iterator (rtems_rtl_unresolv_rec_t* rec,
+ void* data)
+{
+ if (rec->type == rtems_rtl_unresolved_name)
+ {
+ rtems_rtl_unresolved_reloc_data_t* rd;
+ rd = (rtems_rtl_unresolved_reloc_data_t*) data;
+
+ ++rd->name;
+
+ if (rtems_rtl_trace (RTEMS_RTL_TRACE_UNRESOLVED))
+ printf ("rtl: unresolv: lookup: %d: %s\n", rd->name, rec->rec.name.name);
+
+ rd->sym = rtems_rtl_symbol_global_find (rec->rec.name.name);
+
+ if (rd->sym)
+ {
+ if (rtems_rtl_trace (RTEMS_RTL_TRACE_UNRESOLVED))
+ printf ("rtl: unresolv: found: %s\n", rec->rec.name.name);
+
+ rd->name_rec = rec;
+
+ rtems_rtl_unresolved_interate (rtems_rtl_unresolved_resolve_reloc, rd);
+
+ rd->name_rec = NULL;
+ rd->sym = NULL;
+ }
+ }
+
+ return false;
+}
+
+static void
+rtems_rtl_unresolved_clean_block (rtems_rtl_unresolv_block_t* block,
+ rtems_rtl_unresolv_rec_t* rec,
+ size_t count,
+ size_t recs_per_block)
+{
+ size_t index = rtems_rtl_unresolved_rec_index (block, rec);
+ size_t bytes =
+ (block->recs - index - count) * sizeof (rtems_rtl_unresolv_rec_t);
+ if (bytes)
+ memmove (rec, rec + count, bytes);
+ --block->recs;
+ bytes = count * sizeof (rtems_rtl_unresolv_rec_t);
+ memset (&block->rec + block->recs, 0, bytes);
+}
+
+static void
+rtems_rtl_unresolved_compact (void)
+{
+ rtems_rtl_unresolved_t* unresolved = rtems_rtl_unresolved ();
+ if (unresolved)
+ {
+ /*
+ * Iterate backwards over the blocks removing any used records. A block is
+ * compacted moving up the block.
+ */
+ rtems_chain_node* node = rtems_chain_last (&unresolved->blocks);
+ while (!rtems_chain_is_head (&unresolved->blocks, node))
+ {
+ rtems_chain_node* prev = rtems_chain_previous (node);
+ rtems_rtl_unresolv_block_t* block = (rtems_rtl_unresolv_block_t*) node;
+ rtems_rtl_unresolv_rec_t* rec = rtems_rtl_unresolved_rec_first (block);
+
+ while (!rtems_rtl_unresolved_rec_is_last (block, rec))
+ {
+ bool next = true;
+
+ if (rec->type == rtems_rtl_unresolved_name)
+ {
+ if (rec->rec.name.refs == 0)
+ {
+ size_t name_recs = rtems_rtl_unresolved_name_recs (rec->rec.name.name);
+ rtems_rtl_unresolved_clean_block (block, rec, name_recs,
+ unresolved->block_recs);
+ next = false;
+ }
+ }
+ else if (rec->type == rtems_rtl_unresolved_reloc)
+ {
+ if (!rec->rec.reloc.obj)
+ {
+ rtems_rtl_unresolved_clean_block (block, rec, 1,
+ unresolved->block_recs);
+ next = false;
+ }
+ }
+
+ if (next)
+ rec = rtems_rtl_unresolved_rec_next (rec);
+ }
+
+ if (block->recs == 0)
+ {
+ rtems_chain_extract (node);
+ free (block);
+ }
+
+ node = prev;
+ }
+ }
+}
+
+bool
+rtems_rtl_unresolved_table_open (rtems_rtl_unresolved_t* unresolved,
+ size_t block_recs)
+{
+ unresolved->marker = 0xdeadf00d;
+ unresolved->block_recs = block_recs;
+ rtems_chain_initialize_empty (&unresolved->blocks);
+ return true;
+}
+
+void
+rtems_rtl_unresolved_table_close (rtems_rtl_unresolved_t* unresolved)
+{
+ rtems_chain_node* node = rtems_chain_first (&unresolved->blocks);
+ while (!rtems_chain_is_tail (&unresolved->blocks, node))
+ {
+ rtems_chain_node* next = rtems_chain_next (node);
+ free (node);
+ node = next;
+ }
+}
+
+bool
+rtems_rtl_unresolved_interate (rtems_rtl_unresolved_iterator_t iterator,
+ void* data)
+{
+ rtems_rtl_unresolved_t* unresolved = rtems_rtl_unresolved ();
+ if (unresolved)
+ {
+ rtems_chain_node* node = rtems_chain_first (&unresolved->blocks);
+ while (!rtems_chain_is_tail (&unresolved->blocks, node))
+ {
+ rtems_rtl_unresolv_block_t* block = (rtems_rtl_unresolv_block_t*) node;
+ rtems_rtl_unresolv_rec_t* rec = rtems_rtl_unresolved_rec_first (block);
+
+ while (!rtems_rtl_unresolved_rec_is_last (block, rec))
+ {
+ if (iterator (rec, data))
+ return true;
+ rec = rtems_rtl_unresolved_rec_next (rec);
+ }
+
+ node = rtems_chain_next (node);
+ }
+ }
+ return false;
+}
+
+bool
+rtems_rtl_unresolved_add (rtems_rtl_obj_t* obj,
+ const uint16_t flags,
+ const char* name,
+ const uint16_t sect,
+ const rtems_rtl_word_t* rel)
+{
+ rtems_rtl_unresolved_t* unresolved;
+ rtems_chain_node* node;
+ rtems_rtl_unresolv_block_t* block;
+ rtems_rtl_unresolv_rec_t* rec;
+ int name_index;
+ size_t name_recs;
+
+ if (rtems_rtl_trace (RTEMS_RTL_TRACE_UNRESOLVED))
+ printf ("rtl: unresolv: add: %s(s:%d) -> %s\n",
+ rtems_rtl_obj_oname (obj), sect, name);
+
+ unresolved = rtems_rtl_unresolved ();
+ if (!unresolved)
+ return false;
+
+ /*
+ * Find the first block with a spare record.
+ */
+ node = rtems_chain_first (&unresolved->blocks);
+ block = NULL;
+ while (!rtems_chain_is_tail (&unresolved->blocks, node))
+ {
+ block = (rtems_rtl_unresolv_block_t*) node;
+ if (block->recs < unresolved->block_recs)
+ break;
+ block = NULL;
+ node = rtems_chain_next (node);
+ }
+
+ /*
+ * No blocks with any spare records, allocate a new block.
+ */
+ if (!block)
+ {
+ block = rtems_rtl_unresolved_block_alloc (unresolved);
+ if (!block)
+ return false;
+ }
+
+ name_index = rtems_rtl_unresolved_find_name (unresolved, name, true);
+ name_recs = rtems_rtl_unresolved_name_recs (name);
+
+ /*
+ * An index less than 0 means the name is present and "0 - index" is the next
+ * index to use.
+ */
+ if (name_index < 0)
+ {
+ rtems_rtl_unresolv_block_t* name_block = block;
+
+ /*
+ * Is there enough room to fit the name ? It not add a new block.
+ */
+ if (name_recs > (unresolved->block_recs - block->recs))
+ {
+ name_block = rtems_rtl_unresolved_block_alloc (unresolved);
+ if (!name_block)
+ return false;
+ }
+
+ rec = rtems_rtl_unresolved_rec_first_free (name_block);
+ rec->type = rtems_rtl_unresolved_name;
+ rec->rec.name.refs = 1;
+ rec->rec.name.length = strlen (name) + 1;
+ memcpy ((void*) &rec->rec.name.name[0], name, rec->rec.name.length + 1);
+ block->recs += name_recs;
+ name_index = 0 - name_index;
+
+ /*
+ * If the name block is the reloc block and it is full allocate a new
+ * block for the relocation record.
+ */
+ if ((block == name_block) && (block->recs >= unresolved->block_recs))
+ {
+ block = rtems_rtl_unresolved_block_alloc (unresolved);
+ if (!block)
+ return false;
+ }
+ }
+
+ rec = rtems_rtl_unresolved_rec_first_free (block);
+ rec->type = rtems_rtl_unresolved_reloc;
+ rec->rec.reloc.obj = obj;
+ rec->rec.reloc.flags = flags;
+ rec->rec.reloc.name = name_index;
+ rec->rec.reloc.sect = sect;
+ rec->rec.reloc.rel[0] = rel[0];
+ rec->rec.reloc.rel[1] = rel[1];
+ rec->rec.reloc.rel[2] = rel[2];
+
+ ++block->recs;
+
+ return true;
+}
+
+void
+rtems_rtl_unresolved_resolve (void)
+{
+ rtems_rtl_unresolved_reloc_data_t rd;
+ if (rtems_rtl_trace (RTEMS_RTL_TRACE_UNRESOLVED))
+ printf ("rtl: unresolv: global resolve\n");
+ rd.name = 0;
+ rd.name_rec = NULL;
+ rd.sym = NULL;
+ rtems_rtl_unresolved_interate (rtems_rtl_unresolved_resolve_iterator, &rd);
+ rtems_rtl_unresolved_compact ();
+}
+
+bool
+rtems_rtl_unresolved_remove (rtems_rtl_obj_t* obj,
+ const char* name,
+ const uint16_t sect,
+ const rtems_rtl_word_t* rel)
+{
+ rtems_rtl_unresolved_t* unresolved;
+ unresolved = rtems_rtl_unresolved ();
+ if (!unresolved)
+ return false;
+ return false;
+}
+