forked from Imagelibrary/rtems
638 lines
13 KiB
C
638 lines
13 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 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;
|
|
}
|