forked from Imagelibrary/rtems
890 lines
21 KiB
C
890 lines
21 KiB
C
/* SPDX-License-Identifier: BSD-2-Clause */
|
|
|
|
/**
|
|
* @file
|
|
*
|
|
* @ingroup rtems_rtld
|
|
*
|
|
* @brief RTEMS Run-Time Link Editor
|
|
*
|
|
* This is the RTL implementation.
|
|
*/
|
|
|
|
/*
|
|
* COPYRIGHT (c) 2012, 2018 Chris Johns <chrisj@rtems.org>
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#ifdef 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 <rtems/rtl/rtl-allocator.h>
|
|
#include <rtems/rtl/rtl-trace.h>
|
|
#include "rtl-chain-iterator.h"
|
|
#include "rtl-error.h"
|
|
#include "rtl-string.h"
|
|
|
|
/**
|
|
* 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* rtl;
|
|
static bool rtl_data_init;
|
|
|
|
/**
|
|
* 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)
|
|
{
|
|
/*
|
|
* We cannot set an error in this code because there is no RTL data to
|
|
* hold it.
|
|
*/
|
|
|
|
if (rtl_data_init)
|
|
{
|
|
rtems_libio_unlock ();
|
|
return false;
|
|
}
|
|
|
|
rtl_data_init = true;
|
|
|
|
/*
|
|
* Always in the heap.
|
|
*/
|
|
rtl = malloc (sizeof (rtems_rtl_data));
|
|
if (!rtl)
|
|
{
|
|
rtems_libio_unlock ();
|
|
errno = ENOMEM;
|
|
return false;
|
|
}
|
|
|
|
*rtl = (rtems_rtl_data) { 0 };
|
|
|
|
/*
|
|
* The initialise the allocator data.
|
|
*/
|
|
rtems_rtl_alloc_initialise (&rtl->allocator);
|
|
|
|
/*
|
|
* Create the RTL lock.
|
|
*/
|
|
rtems_recursive_mutex_init (&rtl->lock, "Run-Time Linker");
|
|
rtems_recursive_mutex_lock (&rtl->lock);
|
|
|
|
/*
|
|
* Initialise the objects and pending list.
|
|
*/
|
|
rtems_chain_initialize_empty (&rtl->objects);
|
|
rtems_chain_initialize_empty (&rtl->pending);
|
|
|
|
/*
|
|
* Open the global symbol table.
|
|
*/
|
|
if (!rtems_rtl_symbol_table_open (&rtl->globals,
|
|
RTEMS_RTL_SYMS_GLOBAL_BUCKETS))
|
|
{
|
|
rtems_recursive_mutex_destroy (&rtl->lock);
|
|
free (rtl);
|
|
rtems_libio_unlock ();
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* Open the archives.
|
|
*/
|
|
rtems_rtl_archives_open (&rtl->archives, "/etc/libdl.conf");
|
|
|
|
/*
|
|
* Open the unresolved table.
|
|
*/
|
|
if (!rtems_rtl_unresolved_table_open (&rtl->unresolved,
|
|
RTEMS_RTL_UNRESOLVED_BLOCK_SIZE))
|
|
{
|
|
rtems_rtl_symbol_table_close (&rtl->globals);
|
|
rtems_recursive_mutex_destroy (&rtl->lock);
|
|
free (rtl);
|
|
rtems_libio_unlock ();
|
|
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_recursive_mutex_destroy (&rtl->lock);
|
|
free (rtl);
|
|
rtems_libio_unlock ();
|
|
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_recursive_mutex_destroy (&rtl->lock);
|
|
free (rtl);
|
|
rtems_libio_unlock ();
|
|
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_recursive_mutex_destroy (&rtl->lock);
|
|
free (rtl);
|
|
rtems_libio_unlock ();
|
|
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_recursive_mutex_destroy (&rtl->lock);
|
|
free (rtl);
|
|
rtems_libio_unlock ();
|
|
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_recursive_mutex_destroy (&rtl->lock);
|
|
free (rtl);
|
|
rtems_libio_unlock ();
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* Need to malloc the memory so the free does not complain.
|
|
*/
|
|
rtl->base->oname = rtems_rtl_strdup ("rtems-kernel");
|
|
|
|
/*
|
|
* Lock the base image and flag it as the base image.
|
|
*/
|
|
rtl->base->flags |= RTEMS_RTL_OBJ_LOCKED | RTEMS_RTL_OBJ_BASE;
|
|
|
|
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*
|
|
rtems_rtl_data_unprotected (void)
|
|
{
|
|
return rtl;
|
|
}
|
|
|
|
rtems_rtl_symbols*
|
|
rtems_rtl_global_symbols (void)
|
|
{
|
|
if (!rtl)
|
|
{
|
|
rtems_rtl_set_error (ENOENT, "no rtl");
|
|
return NULL;
|
|
}
|
|
return &rtl->globals;
|
|
}
|
|
|
|
const char*
|
|
rtems_rtl_last_error_unprotected (void)
|
|
{
|
|
if (!rtl)
|
|
return NULL;
|
|
return rtl->last_error;
|
|
}
|
|
|
|
rtems_chain_control*
|
|
rtems_rtl_objects_unprotected (void)
|
|
{
|
|
if (!rtl)
|
|
{
|
|
rtems_rtl_set_error (ENOENT, "no rtl");
|
|
return NULL;
|
|
}
|
|
return &rtl->objects;
|
|
}
|
|
|
|
rtems_chain_control*
|
|
rtems_rtl_pending_unprotected (void)
|
|
{
|
|
if (!rtl)
|
|
{
|
|
rtems_rtl_set_error (ENOENT, "no rtl");
|
|
return NULL;
|
|
}
|
|
return &rtl->pending;
|
|
}
|
|
|
|
rtems_rtl_unresolved*
|
|
rtems_rtl_unresolved_unprotected (void)
|
|
{
|
|
if (!rtl)
|
|
{
|
|
rtems_rtl_set_error (ENOENT, "no rtl");
|
|
return NULL;
|
|
}
|
|
return &rtl->unresolved;
|
|
}
|
|
|
|
rtems_rtl_archives*
|
|
rtems_rtl_archives_unprotected (void)
|
|
{
|
|
if (!rtl)
|
|
{
|
|
rtems_rtl_set_error (ENOENT, "no rtl");
|
|
return NULL;
|
|
}
|
|
return &rtl->archives;
|
|
}
|
|
|
|
void
|
|
rtems_rtl_obj_caches (rtems_rtl_obj_cache** symbols,
|
|
rtems_rtl_obj_cache** strings,
|
|
rtems_rtl_obj_cache** 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 (void)
|
|
{
|
|
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_decompress (rtems_rtl_obj_comp** decomp,
|
|
rtems_rtl_obj_cache* 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);
|
|
}
|
|
}
|
|
|
|
typedef struct rtems_rtl_obj_flags_data
|
|
{
|
|
uint32_t clear; /**< Flags to clear, do not invert. */
|
|
uint32_t set; /**< Flags to set, applied after clearing. */
|
|
} rtems_rtl_obj_flags_data;
|
|
|
|
static bool
|
|
rtems_rtl_obj_flags_iterator (rtems_chain_node* node, void* data)
|
|
{
|
|
rtems_rtl_obj* obj = (rtems_rtl_obj*) node;
|
|
rtems_rtl_obj_flags_data* flags = (rtems_rtl_obj_flags_data*) data;
|
|
if (flags->clear != 0)
|
|
obj->flags &= ~flags->clear;
|
|
if (flags->set != 0)
|
|
obj->flags |= flags->set;
|
|
return true;
|
|
}
|
|
|
|
void
|
|
rtems_rtl_obj_update_flags (uint32_t clear, uint32_t set)
|
|
{
|
|
rtems_rtl_obj_flags_data flags = {
|
|
.clear = clear,
|
|
.set = set
|
|
};
|
|
rtems_rtl_chain_iterate (&rtl->objects,
|
|
rtems_rtl_obj_flags_iterator,
|
|
&flags);
|
|
}
|
|
|
|
rtems_rtl_data*
|
|
rtems_rtl_lock (void)
|
|
{
|
|
if (!rtems_rtl_data_init ())
|
|
return NULL;
|
|
|
|
rtems_recursive_mutex_lock (&rtl->lock);
|
|
|
|
return rtl;
|
|
}
|
|
|
|
void
|
|
rtems_rtl_unlock (void)
|
|
{
|
|
rtems_recursive_mutex_unlock (&rtl->lock);
|
|
}
|
|
|
|
rtems_rtl_obj*
|
|
rtems_rtl_check_handle (void* handle)
|
|
{
|
|
rtems_rtl_obj* obj;
|
|
rtems_chain_node* node;
|
|
|
|
obj = handle;
|
|
node = rtems_chain_first (&rtl->objects);
|
|
|
|
while (!rtems_chain_is_tail (&rtl->objects, node))
|
|
{
|
|
rtems_rtl_obj* check = (rtems_rtl_obj*) node;
|
|
if (check == obj)
|
|
return obj;
|
|
node = rtems_chain_next (node);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
rtems_rtl_obj*
|
|
rtems_rtl_find_obj (const char* name)
|
|
{
|
|
rtems_chain_node* node;
|
|
rtems_rtl_obj* 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* obj = (rtems_rtl_obj*) 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 != NULL)
|
|
rtems_rtl_alloc_del(RTEMS_RTL_ALLOC_OBJECT, (void*) aname);
|
|
|
|
if (oname != NULL)
|
|
rtems_rtl_alloc_del(RTEMS_RTL_ALLOC_OBJECT, (void*) oname);
|
|
|
|
return found;
|
|
}
|
|
|
|
rtems_rtl_obj*
|
|
rtems_rtl_find_obj_with_symbol (const rtems_rtl_obj_sym* sym)
|
|
{
|
|
if (sym != NULL)
|
|
{
|
|
rtems_chain_node* node = rtems_chain_first (&rtl->objects);
|
|
while (!rtems_chain_is_tail (&rtl->objects, node))
|
|
{
|
|
rtems_rtl_obj* obj = (rtems_rtl_obj*) node;
|
|
if (rtems_rtl_obj_has_symbol (obj, sym))
|
|
return obj;
|
|
node = rtems_chain_next (node);
|
|
}
|
|
node = rtems_chain_first (&rtl->pending);
|
|
while (!rtems_chain_is_tail (&rtl->pending, node))
|
|
{
|
|
rtems_rtl_obj* obj = (rtems_rtl_obj*) node;
|
|
if (rtems_rtl_obj_has_symbol (obj, sym))
|
|
return obj;
|
|
node = rtems_chain_next (node);
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
rtems_rtl_obj*
|
|
rtems_rtl_load_object (const char* name, int mode)
|
|
{
|
|
rtems_rtl_obj* 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;
|
|
}
|
|
|
|
rtems_chain_append (&rtl->pending, &obj->link);
|
|
|
|
/*
|
|
* 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);
|
|
rtems_rtl_obj_caches_flush ();
|
|
return NULL;
|
|
}
|
|
|
|
if (!rtems_rtl_obj_load (obj))
|
|
{
|
|
rtems_chain_extract (&obj->link);
|
|
rtems_rtl_obj_free (obj);
|
|
rtems_rtl_obj_caches_flush ();
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* If there are unresolved externals remove from the pending queue and place
|
|
* on the objects list until the symbols are resolved.
|
|
*/
|
|
if (obj->unresolved != 0)
|
|
{
|
|
rtems_chain_extract (&obj->link);
|
|
rtems_chain_append (&rtl->objects, &obj->link);
|
|
}
|
|
|
|
rtems_rtl_obj_caches_flush ();
|
|
}
|
|
|
|
/*
|
|
* Increase the number of users.
|
|
*/
|
|
++obj->users;
|
|
|
|
return obj;
|
|
}
|
|
|
|
rtems_rtl_obj*
|
|
rtems_rtl_load (const char* name, int mode)
|
|
{
|
|
rtems_rtl_obj* obj;
|
|
|
|
/*
|
|
* Refesh the archives.
|
|
*/
|
|
rtems_rtl_archives_refresh (&rtl->archives);
|
|
|
|
/*
|
|
* Collect the loaded object files.
|
|
*/
|
|
rtems_chain_initialize_empty (&rtl->pending);
|
|
|
|
obj = rtems_rtl_load_object (name, mode);
|
|
if (obj != NULL)
|
|
{
|
|
rtems_chain_node* node;
|
|
|
|
rtems_rtl_unresolved_resolve ();
|
|
|
|
/*
|
|
* Iterator over the pending list of object files that have been loaded.
|
|
*/
|
|
node = rtems_chain_first (&rtl->pending);
|
|
while (!rtems_chain_is_tail (&rtl->pending, node))
|
|
{
|
|
rtems_rtl_obj* pobj = (rtems_rtl_obj*) node;
|
|
|
|
/*
|
|
* Move to the next pending object file and place this object file on the
|
|
* RTL's objects list.
|
|
*/
|
|
node = rtems_chain_next (&pobj->link);
|
|
rtems_chain_extract (&pobj->link);
|
|
rtems_chain_append (&rtl->objects, &pobj->link);
|
|
|
|
/*
|
|
* Make sure the object file and cache is synchronized.
|
|
*/
|
|
rtems_rtl_obj_synchronize_cache (pobj);
|
|
|
|
/*
|
|
* Run any local constructors if they have not been run. 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 ((pobj->flags & RTEMS_RTL_OBJ_CTOR_RUN) == 0)
|
|
{
|
|
pobj->flags |= RTEMS_RTL_OBJ_LOCKED | RTEMS_RTL_OBJ_CTOR_RUN;
|
|
rtems_rtl_unlock ();
|
|
rtems_rtl_obj_run_ctors (pobj);
|
|
rtems_rtl_lock ();
|
|
pobj->flags &= ~RTEMS_RTL_OBJ_LOCKED;
|
|
}
|
|
}
|
|
}
|
|
|
|
return obj;
|
|
}
|
|
|
|
bool
|
|
rtems_rtl_unload_object (rtems_rtl_obj* obj)
|
|
{
|
|
if (rtems_rtl_trace (RTEMS_RTL_TRACE_UNLOAD))
|
|
printf ("rtl: unload object '%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, "object file is locked");
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* The object file cannot be unloaded if it is referenced.
|
|
*/
|
|
if (rtems_rtl_obj_get_reference (obj) > 0)
|
|
{
|
|
rtems_rtl_set_error (EINVAL, "object file has references to it");
|
|
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;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
rtems_rtl_unload (rtems_rtl_obj* obj)
|
|
{
|
|
bool ok = rtems_rtl_unload_object (obj);
|
|
if (ok && obj->users == 0)
|
|
{
|
|
rtems_chain_control unloading;
|
|
rtems_chain_node* node;
|
|
bool orphaned_found = true;
|
|
int loop = 0;
|
|
|
|
/*
|
|
* Remove the orphaned object files from the objects list. The unloading is
|
|
* private so any changes in any desctructors will not effect the list as it
|
|
* is being iterated over.
|
|
*
|
|
* To avoid maintaining a complex tree loop while oprhans are still be found.
|
|
*/
|
|
|
|
rtems_chain_initialize_empty (&unloading);
|
|
|
|
while (orphaned_found)
|
|
{
|
|
orphaned_found = false;
|
|
++loop;
|
|
node = rtems_chain_first (&rtl->objects);
|
|
while (!rtems_chain_is_tail (&rtl->objects, node))
|
|
{
|
|
rtems_chain_node* next_node = rtems_chain_next (node);
|
|
rtems_rtl_obj* uobj = (rtems_rtl_obj*) node;
|
|
if (rtems_rtl_trace (RTEMS_RTL_TRACE_UNLOAD))
|
|
printf ("rtl: unload object: %3i: %9s: %s\n",
|
|
loop,
|
|
rtems_rtl_obj_orphaned (uobj) ? "orphaned" : "inuse",
|
|
rtems_rtl_obj_oname (uobj));
|
|
if (rtems_rtl_obj_orphaned (uobj))
|
|
{
|
|
orphaned_found = true;
|
|
rtems_rtl_obj_remove_dependencies (uobj);
|
|
rtems_chain_extract (&uobj->link);
|
|
rtems_chain_append (&unloading, &uobj->link);
|
|
uobj->flags |= RTEMS_RTL_OBJ_LOCKED;
|
|
}
|
|
node = next_node;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Call the desctructors unlocked. An RTL call will not deadlock.
|
|
*/
|
|
rtems_rtl_unlock ();
|
|
|
|
node = rtems_chain_first (&unloading);
|
|
while (!rtems_chain_is_tail (&unloading, node))
|
|
{
|
|
rtems_rtl_obj* uobj = (rtems_rtl_obj*) node;
|
|
if ((uobj->flags & RTEMS_RTL_OBJ_CTOR_RUN) != 0)
|
|
{
|
|
rtems_rtl_obj_run_dtors (uobj);
|
|
uobj->flags &= ~RTEMS_RTL_OBJ_CTOR_RUN;
|
|
}
|
|
node = rtems_chain_next (node);
|
|
}
|
|
|
|
rtems_rtl_lock ();
|
|
|
|
/*
|
|
* Unload the object files.
|
|
*/
|
|
node = rtems_chain_first (&unloading);
|
|
while (!rtems_chain_is_tail (&unloading, node))
|
|
{
|
|
rtems_chain_node* next_node = rtems_chain_next (node);
|
|
rtems_rtl_obj* uobj = (rtems_rtl_obj*) node;
|
|
if (rtems_rtl_trace (RTEMS_RTL_TRACE_UNLOAD))
|
|
printf ("rtl: unloading '%s'\n", rtems_rtl_obj_oname (uobj));
|
|
uobj->flags &= ~RTEMS_RTL_OBJ_LOCKED;
|
|
if (!rtems_rtl_obj_unload (uobj))
|
|
ok = false;
|
|
rtems_rtl_obj_free (uobj);
|
|
rtems_rtl_obj_caches_flush ();
|
|
node = next_node;
|
|
}
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
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,
|
|
const rtems_rtl_tls_offset* tls_offsets,
|
|
unsigned int tls_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, tls_offsets, tls_size);
|
|
|
|
rtems_rtl_unlock ();
|
|
}
|
|
|
|
rtems_rtl_obj*
|
|
rtems_rtl_baseimage (void)
|
|
{
|
|
rtems_rtl_obj* base = NULL;
|
|
if (rtems_rtl_lock ())
|
|
{
|
|
base = rtl->base;
|
|
rtems_rtl_unlock ();
|
|
}
|
|
return base;
|
|
}
|