summaryrefslogtreecommitdiffstats
path: root/cpukit/libdl/rtl.c
diff options
context:
space:
mode:
Diffstat (limited to 'cpukit/libdl/rtl.c')
-rw-r--r--cpukit/libdl/rtl.c637
1 files changed, 637 insertions, 0 deletions
diff --git a/cpukit/libdl/rtl.c b/cpukit/libdl/rtl.c
new file mode 100644
index 0000000000..c55cd94084
--- /dev/null
+++ b/cpukit/libdl/rtl.c
@@ -0,0 +1,637 @@
+/*
+ * 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_rtld
+ *
+ * @brief RTEMS Run-Time Link Editor
+ *
+ * This is the RTL implementation.
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <rtems/libio_.h>
+
+#include <rtems/rtl/rtl.h>
+#include "rtl-allocator.h"
+#include "rtl-error.h"
+#include "rtl-string.h"
+#include "rtl-trace.h"
+
+/**
+ * Semaphore configuration to create a mutex.
+ */
+#define RTEMS_MUTEX_ATTRIBS \
+ (RTEMS_PRIORITY | RTEMS_BINARY_SEMAPHORE | \
+ RTEMS_INHERIT_PRIORITY | RTEMS_NO_PRIORITY_CEILING | RTEMS_LOCAL)
+
+/**
+ * Symbol table cache size. They can be big so the cache needs space to work.
+ */
+#define RTEMS_RTL_ELF_SYMBOL_CACHE (2048)
+
+/**
+ * String table cache size.
+ */
+#define RTEMS_RTL_ELF_STRING_CACHE (2048)
+
+/**
+ * Relocations table cache size.
+ */
+#define RTEMS_RTL_ELF_RELOC_CACHE (2048)
+
+/**
+ * Decompression output buffer.
+ */
+#define RTEMS_RTL_COMP_OUTPUT (2048)
+
+/**
+ * Static RTL data is returned to the user when the linker is locked.
+ */
+static rtems_rtl_data_t* rtl;
+
+/**
+ * Define a default base global symbol loader function that is weak
+ * so a real table can be linked in when the user wants one.
+ */
+void rtems_rtl_base_global_syms_init (void) __attribute__ ((weak));
+void
+rtems_rtl_base_global_syms_init (void)
+{
+ /*
+ * Do nothing.
+ */
+}
+
+static bool
+rtems_rtl_data_init (void)
+{
+ /*
+ * Lock the RTL. We only create a lock if a call is made. First we test if a
+ * lock is present. If one is present we lock it. If not the libio lock is
+ * locked and we then test the lock again. If not present we create the lock
+ * then release libio lock.
+ */
+ if (!rtl)
+ {
+ rtems_libio_lock ();
+
+ if (!rtl)
+ {
+ rtems_status_code sc;
+ rtems_id lock;
+
+ /*
+ * Always in the heap.
+ */
+ rtl = malloc (sizeof (rtems_rtl_data_t));
+ if (!rtl)
+ {
+ errno = ENOMEM;
+ return false;
+ }
+
+ *rtl = (rtems_rtl_data_t) { 0 };
+
+ /*
+ * The initialise the allocator data.
+ */
+ rtems_rtl_alloc_initialise (&rtl->allocator);
+
+ /*
+ * Create the RTL lock.
+ */
+ sc = rtems_semaphore_create (rtems_build_name ('R', 'T', 'L', 'D'),
+ 1, RTEMS_MUTEX_ATTRIBS,
+ RTEMS_NO_PRIORITY, &lock);
+ if (sc != RTEMS_SUCCESSFUL)
+ {
+ free (rtl);
+ return false;
+ }
+
+ sc = rtems_semaphore_obtain (lock, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
+ if (sc != RTEMS_SUCCESSFUL)
+ {
+ rtems_semaphore_delete (lock);
+ free (rtl);
+ return false;
+ }
+
+ rtl->lock = lock;
+
+ /*
+ * Initialise the objects list and create any required services.
+ */
+ rtems_chain_initialize_empty (&rtl->objects);
+
+ if (!rtems_rtl_symbol_table_open (&rtl->globals,
+ RTEMS_RTL_SYMS_GLOBAL_BUCKETS))
+ {
+ rtems_semaphore_delete (lock);
+ free (rtl);
+ return false;
+ }
+
+ if (!rtems_rtl_unresolved_table_open (&rtl->unresolved,
+ RTEMS_RTL_UNRESOLVED_BLOCK_SIZE))
+ {
+ rtems_rtl_symbol_table_close (&rtl->globals);
+ rtems_semaphore_delete (lock);
+ free (rtl);
+ return false;
+ }
+
+ if (!rtems_rtl_obj_cache_open (&rtl->symbols,
+ RTEMS_RTL_ELF_SYMBOL_CACHE))
+ {
+ rtems_rtl_symbol_table_close (&rtl->globals);
+ rtems_rtl_unresolved_table_close (&rtl->unresolved);
+ rtems_semaphore_delete (lock);
+ free (rtl);
+ return false;
+ }
+
+ if (!rtems_rtl_obj_cache_open (&rtl->strings,
+ RTEMS_RTL_ELF_STRING_CACHE))
+ {
+ rtems_rtl_obj_cache_close (&rtl->symbols);
+ rtems_rtl_unresolved_table_close (&rtl->unresolved);
+ rtems_rtl_symbol_table_close (&rtl->globals);
+ rtems_semaphore_delete (lock);
+ free (rtl);
+ return false;
+ }
+
+ if (!rtems_rtl_obj_cache_open (&rtl->relocs,
+ RTEMS_RTL_ELF_RELOC_CACHE))
+ {
+ rtems_rtl_obj_cache_close (&rtl->strings);
+ rtems_rtl_obj_cache_close (&rtl->symbols);
+ rtems_rtl_unresolved_table_close (&rtl->unresolved);
+ rtems_rtl_symbol_table_close (&rtl->globals);
+ rtems_semaphore_delete (lock);
+ free (rtl);
+ return false;
+ }
+
+ if (!rtems_rtl_obj_comp_open (&rtl->decomp,
+ RTEMS_RTL_COMP_OUTPUT))
+ {
+ rtems_rtl_obj_cache_close (&rtl->relocs);
+ rtems_rtl_obj_cache_close (&rtl->strings);
+ rtems_rtl_obj_cache_close (&rtl->symbols);
+ rtems_rtl_unresolved_table_close (&rtl->unresolved);
+ rtems_rtl_symbol_table_close (&rtl->globals);
+ rtems_semaphore_delete (lock);
+ free (rtl);
+ return false;
+ }
+
+ rtl->base = rtems_rtl_obj_alloc ();
+ if (!rtl->base)
+ {
+ rtems_rtl_obj_comp_close (&rtl->decomp);
+ rtems_rtl_obj_cache_close (&rtl->relocs);
+ rtems_rtl_obj_cache_close (&rtl->strings);
+ rtems_rtl_obj_cache_close (&rtl->symbols);
+ rtems_rtl_unresolved_table_close (&rtl->unresolved);
+ rtems_rtl_symbol_table_close (&rtl->globals);
+ rtems_semaphore_delete (lock);
+ free (rtl);
+ return false;
+ }
+
+ /*
+ * Need to malloc the memory so the free does not complain.
+ */
+ rtl->base->oname = rtems_rtl_strdup ("rtems-kernel");
+
+ rtems_chain_append (&rtl->objects, &rtl->base->link);
+ }
+
+ rtems_libio_unlock ();
+
+ rtems_rtl_path_append (".");
+
+ rtems_rtl_base_global_syms_init ();
+
+ rtems_rtl_unlock ();
+ }
+ return true;
+}
+
+rtems_rtl_data_t*
+rtems_rtl_data (void)
+{
+ return rtl;
+}
+
+rtems_rtl_symbols_t*
+rtems_rtl_global_symbols (void)
+{
+ if (!rtl)
+ {
+ rtems_rtl_set_error (ENOENT, "no rtl");
+ return NULL;
+ }
+ return &rtl->globals;
+}
+
+rtems_rtl_unresolved_t*
+rtems_rtl_unresolved (void)
+{
+ if (!rtl)
+ {
+ rtems_rtl_set_error (ENOENT, "no rtl");
+ return NULL;
+ }
+ return &rtl->unresolved;
+}
+
+void
+rtems_rtl_obj_caches (rtems_rtl_obj_cache_t** symbols,
+ rtems_rtl_obj_cache_t** strings,
+ rtems_rtl_obj_cache_t** relocs)
+{
+ if (!rtl)
+ {
+ if (symbols)
+ *symbols = NULL;
+ if (strings)
+ *strings = NULL;
+ if (relocs)
+ *relocs = NULL;
+ }
+ else
+ {
+ if (symbols)
+ *symbols = &rtl->symbols;
+ if (strings)
+ *strings = &rtl->strings;
+ if (relocs)
+ *relocs = &rtl->relocs;
+ }
+}
+
+void
+rtems_rtl_obj_caches_flush ()
+{
+ if (rtl)
+ {
+ rtems_rtl_obj_cache_flush (&rtl->symbols);
+ rtems_rtl_obj_cache_flush (&rtl->strings);
+ rtems_rtl_obj_cache_flush (&rtl->relocs);
+ }
+}
+
+void
+rtems_rtl_obj_comp (rtems_rtl_obj_comp_t** decomp,
+ rtems_rtl_obj_cache_t* cache,
+ int fd,
+ int compression,
+ off_t offset)
+{
+ if (!rtl)
+ {
+ *decomp = NULL;
+ }
+ else
+ {
+ *decomp = &rtl->decomp;
+ rtems_rtl_obj_comp_set (*decomp, cache, fd, compression, offset);
+ }
+}
+
+rtems_rtl_data_t*
+rtems_rtl_lock (void)
+{
+ rtems_status_code sc;
+
+ if (!rtems_rtl_data_init ())
+ return NULL;
+
+ sc = rtems_semaphore_obtain (rtl->lock,
+ RTEMS_WAIT, RTEMS_NO_TIMEOUT);
+ if (sc != RTEMS_SUCCESSFUL)
+ {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ return rtl;
+}
+
+bool
+rtems_rtl_unlock (void)
+{
+ /*
+ * Not sure any error should be returned or an assert.
+ */
+ rtems_status_code sc;
+ sc = rtems_semaphore_release (rtl->lock);
+ if ((sc != RTEMS_SUCCESSFUL) && (errno == 0))
+ {
+ errno = EINVAL;
+ return false;
+ }
+ return true;
+}
+
+rtems_rtl_obj_t*
+rtems_rtl_check_handle (void* handle)
+{
+ rtems_rtl_obj_t* obj;
+ rtems_chain_node* node;
+
+ obj = handle;
+ node = rtems_chain_first (&rtl->objects);
+
+ while (!rtems_chain_is_tail (&rtl->objects, node))
+ {
+ rtems_rtl_obj_t* check = (rtems_rtl_obj_t*) node;
+ if (check == obj)
+ return obj;
+ node = rtems_chain_next (node);
+ }
+
+ return NULL;
+}
+
+rtems_rtl_obj_t*
+rtems_rtl_find_obj (const char* name)
+{
+ rtems_chain_node* node;
+ rtems_rtl_obj_t* found = NULL;
+ const char* aname = NULL;
+ const char* oname = NULL;
+ off_t ooffset;
+
+ if (!rtems_rtl_parse_name (name, &aname, &oname, &ooffset))
+ return NULL;
+
+ node = rtems_chain_first (&rtl->objects);
+
+ while (!rtems_chain_is_tail (&rtl->objects, node))
+ {
+ rtems_rtl_obj_t* obj = (rtems_rtl_obj_t*) node;
+ if ((aname == NULL && strcmp (obj->oname, oname) == 0) ||
+ (aname != NULL &&
+ strcmp (obj->aname, aname) == 0 && strcmp (obj->oname, oname) == 0))
+ {
+ found = obj;
+ break;
+ }
+ node = rtems_chain_next (node);
+ }
+
+ if (!aname)
+ rtems_rtl_alloc_del(RTEMS_RTL_ALLOC_OBJECT, (void*) aname);
+
+ if (!oname)
+ rtems_rtl_alloc_del(RTEMS_RTL_ALLOC_OBJECT, (void*) oname);
+
+ return found;
+}
+
+rtems_rtl_obj_t*
+rtems_rtl_load_object (const char* name, int mode)
+{
+ rtems_rtl_obj_t* obj;
+
+ if (rtems_rtl_trace (RTEMS_RTL_TRACE_LOAD))
+ printf ("rtl: loading '%s'\n", name);
+
+ /*
+ * See if the object module has already been loaded.
+ */
+ obj = rtems_rtl_find_obj (name);
+ if (!obj)
+ {
+ /*
+ * Allocate a new object file descriptor and attempt to load it.
+ */
+ obj = rtems_rtl_obj_alloc ();
+ if (obj == NULL)
+ {
+ rtems_rtl_set_error (ENOMEM, "no memory for object descriptor");
+ return NULL;
+ }
+
+ /*
+ * Find the file in the file system using the search path. The fname field
+ * will point to a valid file name if found.
+ */
+ if (!rtems_rtl_obj_find_file (obj, name))
+ {
+ rtems_rtl_obj_free (obj);
+ return NULL;
+ }
+
+ rtems_chain_append (&rtl->objects, &obj->link);
+
+ if (!rtems_rtl_obj_load (obj))
+ {
+ rtems_rtl_obj_free (obj);
+ return NULL;
+ }
+
+ rtems_rtl_unresolved_resolve ();
+ }
+
+ /*
+ * Increase the number of users.
+ */
+ ++obj->users;
+
+ /*
+ * FIXME: Resolving existing unresolved symbols could add more constructors
+ * lists that need to be called. Make a list in the obj load layer and
+ * invoke the list here.
+ */
+
+ /*
+ * Run any local constructors if this is the first user because the object
+ * file will have just been loaded. Unlock the linker to avoid any dead locks
+ * if the object file needs to load files or update the symbol table. We also
+ * do not want a constructor to unload this object file.
+ */
+ if (obj->users == 1)
+ {
+ obj->flags |= RTEMS_RTL_OBJ_LOCKED;
+ rtems_rtl_unlock ();
+ rtems_rtl_obj_run_ctors (obj);
+ rtems_rtl_lock ();
+ obj->flags &= ~RTEMS_RTL_OBJ_LOCKED;
+ }
+
+ return obj;
+}
+
+bool
+rtems_rtl_unload_object (rtems_rtl_obj_t* obj)
+{
+ bool ok = true;
+
+ if (rtems_rtl_trace (RTEMS_RTL_TRACE_UNLOAD))
+ printf ("rtl: unloading '%s'\n", rtems_rtl_obj_fname (obj));
+
+ /*
+ * If the object is locked it cannot be unloaded and the unload fails.
+ */
+ if ((obj->flags & RTEMS_RTL_OBJ_LOCKED) == RTEMS_RTL_OBJ_LOCKED)
+ {
+ rtems_rtl_set_error (EINVAL, "cannot unload when locked");
+ return false;
+ }
+
+ /*
+ * Check the number of users in a safe manner. If this is the last user unload the
+ * object file from memory.
+ */
+ if (obj->users > 0)
+ --obj->users;
+
+ if (obj->users == 0)
+ {
+ obj->flags |= RTEMS_RTL_OBJ_LOCKED;
+ rtems_rtl_unlock ();
+ rtems_rtl_obj_run_dtors (obj);
+ rtems_rtl_lock ();
+ obj->flags &= ~RTEMS_RTL_OBJ_LOCKED;
+
+ ok = rtems_rtl_obj_unload (obj);
+ }
+
+ return ok;
+}
+
+void
+rtems_rtl_run_ctors (rtems_rtl_obj_t* obj)
+{
+ rtems_rtl_obj_run_ctors (obj);
+}
+
+static bool
+rtems_rtl_path_update (bool prepend, const char* path)
+{
+ char* paths;
+ const char* src = NULL;
+ char* dst;
+ int len;
+
+ if (!rtems_rtl_lock ())
+ return false;
+
+ len = strlen (path);
+
+ if (rtl->paths)
+ len += strlen (rtl->paths) + 1;
+ else
+ prepend = true;
+
+ paths = rtems_rtl_alloc_new (RTEMS_RTL_ALLOC_OBJECT, len + 1, false);
+
+ if (!paths)
+ {
+ rtems_rtl_unlock ();
+ return false;
+ }
+
+ dst = paths;
+
+ if (prepend)
+ {
+ len = strlen (path);
+ src = path;
+ }
+ else if (rtl->paths)
+ {
+ len = strlen (rtl->paths);
+ src = rtl->paths;
+ }
+
+ memcpy (dst, src, len);
+
+ dst += len;
+
+ if (rtl->paths)
+ {
+ *dst = ':';
+ ++dst;
+ }
+
+ if (prepend)
+ {
+ src = rtl->paths;
+ if (src)
+ len = strlen (src);
+ }
+ else
+ {
+ len = strlen (path);
+ src = path;
+ }
+
+ if (src)
+ {
+ memcpy (dst, src, len);
+ dst += len;
+ }
+
+ *dst = '\0';
+
+ rtl->paths = paths;
+
+ rtems_rtl_unlock ();
+ return false;
+}
+
+bool
+rtems_rtl_path_append (const char* path)
+{
+ return rtems_rtl_path_update (false, path);
+}
+
+bool
+rtems_rtl_path_prepend (const char* path)
+{
+ return rtems_rtl_path_update (true, path);
+}
+
+void
+rtems_rtl_base_sym_global_add (const unsigned char* esyms,
+ unsigned int size)
+{
+ if (rtems_rtl_trace (RTEMS_RTL_TRACE_GLOBAL_SYM))
+ printf ("rtl: adding global symbols, table size %u\n", size);
+
+ if (!rtems_rtl_lock ())
+ {
+ rtems_rtl_set_error (EINVAL, "global add cannot lock rtl");
+ return;
+ }
+
+ rtems_rtl_symbol_global_add (rtl->base, esyms, size);
+
+ rtems_rtl_unlock ();
+}
+
+rtems_rtl_obj_t*
+rtems_rtl_baseimage (void)
+{
+ return NULL;
+}