From 89c59be38d9c83437abb9002e8fea5012652e5ff Mon Sep 17 00:00:00 2001 From: Chris Johns Date: Mon, 17 Dec 2018 16:36:48 +1100 Subject: libdl: Add symbol searching and loading from archives. - Load archive symbol tables to support searching of archives for symbols. - Search archive symbols and load the object file that contains the symbol. - Search the global and archives until all remaining unresolved symbols are not found. Group the loaded object files in the pending queue. - Run the object file and loaded dependents as a group before adding to the main object list. - Remove orphaned object files after references are removed. Updates #3686 --- cpukit/libdl/dlfcn.c | 4 +- cpukit/libdl/rtl-archive.c | 1301 +++++++++++++++++++++++++++++++++++++++++ cpukit/libdl/rtl-elf.c | 16 +- cpukit/libdl/rtl-obj.c | 473 ++++++--------- cpukit/libdl/rtl-shell.c | 15 +- cpukit/libdl/rtl-shell.h | 39 -- cpukit/libdl/rtl-string.h | 2 - cpukit/libdl/rtl-trace.c | 4 +- cpukit/libdl/rtl-unresolved.c | 160 ++++- cpukit/libdl/rtl.c | 277 +++++++-- 10 files changed, 1846 insertions(+), 445 deletions(-) create mode 100644 cpukit/libdl/rtl-archive.c delete mode 100644 cpukit/libdl/rtl-shell.h (limited to 'cpukit/libdl') diff --git a/cpukit/libdl/dlfcn.c b/cpukit/libdl/dlfcn.c index b97078cbaa..e90ed43668 100644 --- a/cpukit/libdl/dlfcn.c +++ b/cpukit/libdl/dlfcn.c @@ -52,7 +52,7 @@ dlopen (const char* name, int mode) _rtld_debug_state (); if (name) - obj = rtems_rtl_load_object (name, mode); + obj = rtems_rtl_load (name, mode); else obj = rtems_rtl_baseimage (); @@ -83,7 +83,7 @@ dlclose (void* handle) _rtld_debug.r_state = RT_DELETE; _rtld_debug_state (); - r = rtems_rtl_unload_object (obj) ? 0 : -1; + r = rtems_rtl_unload (obj) ? 0 : -1; _rtld_debug.r_state = RT_CONSISTENT; _rtld_debug_state (); diff --git a/cpukit/libdl/rtl-archive.c b/cpukit/libdl/rtl-archive.c new file mode 100644 index 0000000000..786c9c6f1d --- /dev/null +++ b/cpukit/libdl/rtl-archive.c @@ -0,0 +1,1301 @@ +/* + * COPYRIGHT (c) 2018 Chris Johns + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ +/** + * @file + * + * @ingroup rtl + * + * @brief RTEMS Run-Time Linker Archive + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include "rtl-chain-iterator.h" +#include +#include "rtl-string.h" +#include "rtl-error.h" + +/** + * The archive symbols threshold after which a sorted symbol table is + * created. + */ +#define RTEMS_RTL_ARCHIVE_SYMBOLS_SORT (8) + +/** + * Archive headers. + */ +#define RTEMS_RTL_AR_IDENT "!\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) + +/** + * Read a 32bit value from the symbol table. + */ +static unsigned int +rtems_rtl_archive_read_32 (void* data) +{ + uint8_t* b = (uint8_t*) data; + unsigned int v = b[0]; + v = (v << 8) | b[1]; + v = (v << 8) | b[2]; + v = (v << 8) | b[3]; + return v; +} + +static void +rtems_rtl_archive_set_error (int num, const char* text) +{ + if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES)) + printf ("rtl: archive: error: %3d: %s\n", num, text); +} + +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; +} + +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; +} + +/** + * Archive iterator. + */ +typedef bool (*rtems_rtl_archive_iterator) (rtems_rtl_archive* archive, + void* data); + +/** + * Chain iterator data. + */ +typedef struct rtems_rtl_archive_chain_data +{ + void* data; /**< User's data. */ + rtems_rtl_archive_iterator iterator; /**< The actual iterator. */ +} rtems_rtl_archive_chain_data; + +static bool +rtems_rtl_archive_node_iterator (rtems_chain_node* node, void* data) +{ + rtems_rtl_archive* archive; + rtems_rtl_archive_chain_data* chain_data; + archive = (rtems_rtl_archive*) node; + chain_data = (rtems_rtl_archive_chain_data*) data; + return chain_data->iterator (archive, chain_data->data); +} + +static void +rtems_rtl_archive_iterate_archives (rtems_rtl_archives* archives, + rtems_rtl_archive_iterator iterator, + void* data) +{ + rtems_rtl_archive_chain_data chain_data = { + .data = data, + .iterator = iterator + }; + rtems_rtl_chain_iterate (&archives->archives, + rtems_rtl_archive_node_iterator, + &chain_data); +} + +static bool +rtems_rtl_rchive_name_end (const char c) +{ + return c == '\0' || c == '\n' || c == '/'; +} + +static const char* +rtems_rtl_archive_dup_name (const char* name) +{ + size_t len = 0; + char* dup; + while (!rtems_rtl_rchive_name_end (name[len])) + ++len; + dup = rtems_rtl_alloc_new (RTEMS_RTL_ALLOC_OBJECT, len + 1, true); + if (dup != NULL) + memcpy (dup, name, len); + return dup; +} + +static bool +rtems_rtl_archive_match_name (const char* file_name, const char* name) +{ + if (name != NULL) + { + while (!rtems_rtl_rchive_name_end (*file_name) && + !rtems_rtl_rchive_name_end (*name) && *file_name == *name) + { + ++file_name; + ++name; + } + if (((*file_name == '\0') || (*file_name == '\n') || (*file_name == '/')) && + ((*name == '\0') || (*name == '/'))) + return true; + } + return false; +} + +static bool +rtems_rtl_archive_set_flags (rtems_rtl_archive* archive, void* data) +{ + uint32_t mask = *((uint32_t*) data); + archive->flags |= mask; + return true; +} + +typedef struct rtems_rtl_archive_find_data +{ + rtems_rtl_archive* archive; + const char* path; +} rtems_rtl_archive_find_data; + +static bool +rtems_rtl_archive_finder (rtems_rtl_archive* archive, void* data) +{ + rtems_rtl_archive_find_data* find; + find = (rtems_rtl_archive_find_data*) data; + if (strcmp (find->path, archive->name) == 0) + { + find->archive = archive; + return false; + } + return true; +} + +static rtems_rtl_archive* +rtems_rtl_archive_find (rtems_rtl_archives* archives, + const char* path) +{ + rtems_rtl_archive_find_data find = { + .archive = NULL, + .path = path + }; + rtems_rtl_archive_iterate_archives (archives, + rtems_rtl_archive_finder, + &find); + return find.archive; +} + +/* + * Find an object file in archive that contains the symbol we are + * searching for. + * + * The symbol search is performance sensitive. The archive's symbol table being + * searched is the symbol table in the archive created by ranlib. This table is + * not sorted so a sorted table of pointered to the symbols is generated after + * loading if there are enough symbols. For small symbol tables the searc is + * linear. The entire table is held in memory. At the time of writing this code + * the symbol table for the SPARC architecture's libc is 16k. + * + * The ranlib table is: + * + * [4] - size of table in bytes + * [0..(entries x 4)] - 4 byte binary offsets into the archive + * for each symbol + * [0..m] - variable length table of strings, nul + * separated and sorted + * + * Note: The loading of an object file from an archive uses an offset in the + * file name to speed the loading. + */ +typedef struct rtems_rtl_archive_obj_data +{ + const char* symbol; /**< The symbol to search for. */ + rtems_rtl_archive* archive; /**< The archive the symbol is found + * in. */ + off_t offset; /**< The offset in the archive if found + * else 0 */ +} rtems_rtl_archive_obj_data; + +static int +rtems_rtl_archive_symbol_compare (const void* a, const void* b) +{ + const rtems_rtl_archive_symbol* sa; + const rtems_rtl_archive_symbol* sb; + sa = (const rtems_rtl_archive_symbol*) a; + sb = (const rtems_rtl_archive_symbol*) b; + return strcmp (sa->label, sb->label); +} + +static bool +rtems_rtl_archive_obj_finder (rtems_rtl_archive* archive, void* data) +{ + const rtems_rtl_archive_symbols* symbols = &archive->symbols; + + if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES)) + printf ("rtl: archive: finder: %s: entries: %zu\n", + archive->name, symbols->entries); + + /* + * Make sure there is a valid symbol table. + */ + if (symbols->base != NULL) + { + /* + * Perform a linear search if there is no sorted symbol table. + */ + rtems_rtl_archive_obj_data* search = (rtems_rtl_archive_obj_data*) data; + if (symbols->symbols == NULL) + { + const char* symbol = symbols->names; + size_t entry; + for (entry = 0; entry < symbols->entries; ++entry) + { + if (strcmp (search->symbol, symbol) == 0) + { + search->archive = archive; + search->offset = + rtems_rtl_archive_read_32 (symbols->base + (entry * 4)); + return false; + } + symbol += strlen (symbol) + 1; + } + } + else + { + ssize_t entry = symbols->entries / 2; + ssize_t offset = entry; + ssize_t last_entry = -1; + while (entry >= 0 && + entry < symbols->entries && + entry != last_entry && + offset > 0) + { + int cmp = strcmp (search->symbol, symbols->symbols[entry].label); + if (cmp == 0) + { + entry = symbols->symbols[entry].entry; + search->archive = archive; + search->offset = + rtems_rtl_archive_read_32 (symbols->base + (entry * 4)); + return false; + } + last_entry = entry; + if (offset == 1) + offset = 0; + else + offset = ((offset - 1) / 2) + 1; + if (cmp < 0) + entry -= offset; + else + entry += offset; + } + } + } + + /* + * Next archive. + */ + return true; +} + +static rtems_rtl_archive* +rtems_rtl_archive_new (rtems_rtl_archives* archives, + const char* path, + const char* name) +{ + rtems_rtl_archive* archive; + size_t path_size; + size_t size; + /* + * Handle the case of the path being just '/', do not create '//'. + */ + path_size = strlen (path); + size = sizeof(rtems_rtl_archive) + path_size + strlen (name) + 1; + if (path_size > 1) + ++size; + archive = rtems_rtl_alloc_new (RTEMS_RTL_ALLOC_OBJECT, size, true); + if (archive == NULL) + { + if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES)) + printf ("rtl: archive: new: %s: no memory\n", name); + } + else + { + char* aname; + archive->name = ((const char*) archive) + sizeof(rtems_rtl_archive); + aname = (char*) archive->name; + strcpy (aname, path); + if (path_size > 1) + strcat (aname, "/"); + strcat (aname, name); + rtems_chain_set_off_chain (&archive->node); + archive->flags |= RTEMS_RTL_ARCHIVE_LOAD; + } + return archive; +} + +static void +rtems_rtl_archive_del (rtems_rtl_archive* archive) +{ + if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES)) + printf ("rtl: archive: del: %s\n", archive->name); + rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_SYMBOL, archive->symbols.base); + rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_SYMBOL, archive->symbols.symbols); + if (!rtems_chain_is_node_off_chain (&archive->node)) + rtems_chain_extract (&archive->node); + rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_OBJECT, archive); +} + +static rtems_rtl_archive* +rtems_rtl_archive_get (rtems_rtl_archives* archives, + const char* path, + const char* name) +{ + rtems_rtl_archive* archive; + /* + * Getting a new archive turns the path and name into a single path the stat + * function can use. No matter how you try some memory is needed so it is + * easier to get a new archive object and delete it if it exists. + */ + archive = rtems_rtl_archive_new (archives, path, name); + if (archive != NULL) + { + struct stat sb; + if (stat (archive->name, &sb) == 0) + { + if (S_ISREG (sb.st_mode)) + { + rtems_rtl_archive* find_archive; + find_archive = rtems_rtl_archive_find (archives, archive->name); + if (find_archive == NULL) + { + rtems_chain_append (&archives->archives, &archive->node); + } + else + { + rtems_rtl_archive_del (archive); + archive = find_archive; + } + archive->flags &= ~RTEMS_RTL_ARCHIVE_REMOVE; + if (archive->mtime != sb.st_mtime) + { + archive->flags |= RTEMS_RTL_ARCHIVE_LOAD; + archive->size = sb.st_size; + archive->mtime = sb.st_mtime; + } + } + } + } + return archive; +} + +static bool +rtems_rtl_archives_load_config (rtems_rtl_archives* archives) +{ + struct stat sb; + + if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES)) + printf ("rtl: archive: config load: %s\n", archives->config_name); + + if (archives->config_name == NULL) + return false; + + if (stat (archives->config_name, &sb) < 0) + { + if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES)) + printf ("rtl: archive: no config: %s\n", archives->config_name); + return false; + } + + /* + * If the configuration has change reload it. + */ + if (sb.st_mtime != archives->config_mtime) + { + int fd; + ssize_t r; + char* s; + bool in_comment; + + archives->config_mtime = sb.st_mtime; + rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_OBJECT, (void*) archives->config); + archives->config_length = 0; + archives->config = + rtems_rtl_alloc_new (RTEMS_RTL_ALLOC_OBJECT, sb.st_size, false); + if (archives->config == NULL) + { + if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES)) + printf ("rtl: archive: no memory for config\n"); + return false; + } + + if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES)) + printf ("rtl: archive: config load: read %s\n", archives->config_name); + + fd = open (archives->config_name, O_RDONLY); + if (fd < 0) + { + rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_OBJECT, (void*) archives->config); + archives->config = NULL; + archives->config_length = 0; + if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES)) + printf ("rtl: archive: config open error: %s\n", strerror (errno)); + return false; + } + + r = read (fd, (void*) archives->config, sb.st_size); + if (r < 0) + { + close (fd); + rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_OBJECT, (void*) archives->config); + archives->config = NULL; + archives->config_length = 0; + if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES)) + printf ("rtl: archive: config read error: %s\n", strerror (errno)); + } + + close (fd); + archives->config_length = r; + + /* + * Remove comments. + */ + s = (char*) archives->config; + in_comment = false; + for (r = 0; r < archives->config_length; ++r, ++s) + { + if (*s == '#') + in_comment = true; + if (in_comment) + { + if (*s == '\n') + in_comment = false; + *s = '\0'; + } + } + + /* + * Create lines by turning '\r' and '\n' to '\0'. + */ + s = (char*) archives->config; + for (r = 0; r < archives->config_length; ++r, ++s) + { + if (*s == '\r' || *s == '\n') + *s = '\0'; + } + + /* + * Remove leading and trailing white space. + */ + s = (char*) archives->config; + for (r = 0; r < archives->config_length; ++r) + { + if (s[r] != '\0') + { + size_t ls = strlen (&s[r]); + size_t b = 0; + while (b < ls && isspace (s[r + b])) + { + ++b; + } + if (b > 0) + memmove (&s[r], &s[r + b], ls - b); + b = ls - 1; + while (b > 0 && isspace (s[r + b])) + { + s[r + b] = '\0'; + --b; + } + } + } + + /* + * Compact the lines so there is only a single nul separator. + */ + s = (char*) archives->config; + for (r = 0; r < archives->config_length; ++r) + { + if (s[r] == '\0') + { + size_t e = r + 1; + while (e < archives->config_length) + { + if (s[e] != '\0') + { + if (archives->config_length - e - 1 > 0) + memmove (&s[r + 1], &s[e], archives->config_length - e - 1); + break; + } + } + } + } + + if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES)) + { + int line = 1; + printf ("rtl: archive: config:\n"); + s = (char*) archives->config; + for (r = 0; r < archives->config_length; ++r, ++line) + { + size_t len = strlen (s); + printf (" %3d: %s\n", line, s); + s += len + 2; + r += len; + } + } + } + + return true; +} + +void +rtems_rtl_archives_open (rtems_rtl_archives* archives, const char* config) +{ + if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES)) + printf ("rtl: archive: open: %s\n", config); + memset (archives, 0, sizeof (rtems_rtl_archives)); + archives->config_name = rtems_rtl_strdup (config); + rtems_chain_initialize_empty (&archives->archives); +} + +void +rtems_rtl_archives_close (rtems_rtl_archives* archives) +{ + rtems_chain_node* node; + if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES)) + printf ("rtl: archive: close: count=%ds\n", + rtems_chain_node_count_unprotected (&archives->archives)); + node = rtems_chain_first (&archives->archives); + while (!rtems_chain_is_tail (&archives->archives, node)) + { + rtems_rtl_archive* archive = (rtems_rtl_archive*) node; + rtems_chain_node* next_node = rtems_chain_next (node); + rtems_rtl_archive_del (archive); + node = next_node; + } + rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_OBJECT, (void*) archives->config); + rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_OBJECT, archives); +} + +static void +rtems_rtl_archives_remove (rtems_rtl_archives* archives) +{ + rtems_chain_node* node = rtems_chain_first (&archives->archives); + if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES)) + printf ("rtl: archive: refresh: remove: checking %d archive(s)\n", + rtems_chain_node_count_unprotected (&archives->archives)); + while (!rtems_chain_is_tail (&archives->archives, node)) + { + rtems_rtl_archive* archive = (rtems_rtl_archive*) node; + rtems_chain_node* next_node = rtems_chain_next (node); + if ((archive->flags & RTEMS_RTL_ARCHIVE_REMOVE) != 0) + { + archive->flags &= ~RTEMS_RTL_ARCHIVE_REMOVE; + if ((archive->flags & RTEMS_RTL_ARCHIVE_USER_LOAD) == 0) + rtems_rtl_archive_del (archive); + } + node = next_node; + } +} + +static bool +rtems_rtl_archive_loader (rtems_rtl_archive* archive, void* data) +{ + int* loaded = (int*) data; + + if ((archive->flags & RTEMS_RTL_ARCHIVE_LOAD) != 0) + { + int fd; + off_t offset = 0; + size_t size = 0; + const char* name = "/"; + + if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES)) + printf ("rtl: archive: loader: %s\n", archive->name); + + fd = open (archive->name, O_RDONLY); + if (fd < 0) + { + if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES)) + printf ("rtl: archive: loader: open error: %s: %s\n", + archive->name, strerror (errno)); + rtems_rtl_archive_set_error (errno, "opening archive file"); + return true; + } + + if (rtems_rtl_obj_archive_find_obj (fd, + archive->size, + &name, + &offset, + &size, + &archive->enames, + rtems_rtl_archive_set_error)) + { + if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES)) + printf ("rtl: archive: loader: symbols: off=0x%08jx size=%zu\n", + offset, size); + + /* + * Reallocation the symbol table memory if it has changed size. + * Note, an updated library may have te same symbol table. + */ + if (archive->symbols.size != size) + { + rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_SYMBOL, archive->symbols.base); + archive->symbols.base = rtems_rtl_alloc_new (RTEMS_RTL_ALLOC_SYMBOL, + size, + false); + if (archive->symbols.base == NULL) + { + close (fd); + memset (&archive->symbols, 0, sizeof (archive->symbols)); + rtems_rtl_archive_set_error (ENOMEM, "symbol table memory"); + return true; + } + } + + /* + * Read the symbol table into memory and hold. + */ + if (!rtems_rtl_seek_read (fd, offset, size, archive->symbols.base)) + { + rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_SYMBOL, archive->symbols.base); + close (fd); + memset (&archive->symbols, 0, sizeof (archive->symbols)); + rtems_rtl_archive_set_error (errno, "reading symbols"); + return true; + } + + /* + * The first 4 byte value is the number of entries. + */ + archive->symbols.entries = + rtems_rtl_archive_read_32 (archive->symbols.base); + archive->symbols.size = size; + archive->symbols.names = archive->symbols.base; + archive->symbols.names += (archive->symbols.entries + 1) * 4; + + /* + * Created a sorted symbol table if over the threshold number of symbols. + */ + if (archive->symbols.entries > RTEMS_RTL_ARCHIVE_SYMBOLS_SORT) + { + const size_t size = + archive->symbols.entries * sizeof (rtems_rtl_archive_symbol); + archive->symbols.symbols = + rtems_rtl_alloc_new (RTEMS_RTL_ALLOC_SYMBOL, size, true); + if (archive->symbols.symbols != NULL) + { + const char* symbol = archive->symbols.names; + size_t e; + for (e = 0; e < archive->symbols.entries; ++e) + { + archive->symbols.symbols[e].entry = e + 1; + archive->symbols.symbols[e].label = symbol; + symbol += strlen (symbol) + 1; + } + qsort (archive->symbols.symbols, + archive->symbols.entries, + sizeof (rtems_rtl_archive_symbol), + rtems_rtl_archive_symbol_compare); + } + } + + if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES)) + printf ("rtl: archive: loader: symbols: " \ + "base=%p entries=%zu names=%p (0x%08x) symbols=%p\n", + archive->symbols.base, + archive->symbols.entries, + archive->symbols.names, + (archive->symbols.entries + 1) * 4, + archive->symbols.symbols); + } + + close (fd); + + archive->flags &= ~RTEMS_RTL_ARCHIVE_LOAD; + + ++(*loaded); + } + + return true; +} + +static bool +rtems_rtl_archives_load (rtems_rtl_archives* archives) +{ + int loaded = 0; + if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES)) + printf ("rtl: archive: archive: load\n"); + rtems_rtl_archive_iterate_archives (archives, + rtems_rtl_archive_loader, + &loaded); + return loaded > 0; +} + +bool +rtems_rtl_archives_refresh (rtems_rtl_archives* archives) +{ + size_t config_path = 0; + uint32_t flags = RTEMS_RTL_ARCHIVE_REMOVE; + + /* + * Reload the configuration file if it has not been loaded or has been + * updated. + */ + if (!rtems_rtl_archives_load_config (archives)) + return false; + + /* + * Assume all existing archives are to be removed. If an existing archive + * is ccnfigured and found teh remove flags is cleared. At the of the + * refresh end remove any archives with this flag still set. + */ + rtems_rtl_archive_iterate_archives (archives, + rtems_rtl_archive_set_flags, + &flags); + + while (config_path < archives->config_length) + { + const char* dirname = &archives->config[config_path]; + char* basename; + const char root[2] = { '/', '\0' }; + DIR* dir; + + if (*dirname == '\0') + { + ++config_path; + continue; + } + + if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES)) + printf ("rtl: archive: refresh: %s\n", dirname); + + config_path += strlen (dirname); + + /* + * Relative paths do not work in the config. Must be absolute. + */ + if (dirname[0] != '/') + { + if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES)) + printf ("rtl: archive: refresh: relative paths ignored: %s\n", dirname); + continue; + } + + /* + * Scan the parent directory of the glob path matching file names. + */ + basename = strrchr (dirname, '/'); + if (basename == NULL) + continue; + + if (basename == dirname) + dirname = &root[0]; + + *basename = '\0'; + ++basename; + + dir = opendir (dirname); + + if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES)) + printf ("rtl: archive: refresh: %s %sfound\n", + dirname, dir == NULL ? ": not " : ""); + + if (dir != NULL) + { + while (true) + { + struct dirent entry; + struct dirent* result = NULL; + + if (readdir_r (dir, &entry, &result) != 0) + { + if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES)) + printf ("rtl: archive: refresh: readdir error\n"); + break; + } + + if (result == NULL) + break; + + if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES)) + printf ("rtl: archive: refresh: checking: %s\n", entry.d_name); + + if (fnmatch (basename, entry.d_name, 0) == 0) + { + struct stat sb; + if (stat (entry.d_name, &sb) == 0) + { + rtems_rtl_archive* archive; + archive = rtems_rtl_archive_get (archives, dirname, entry.d_name); + if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES)) + printf ("rtl: archive: refresh: %s: %sfound\n", + entry.d_name, archive == NULL ? ": not " : ""); + } + } + } + closedir (dir); + } + + --basename; + *basename = '/'; + } + + /* + * Remove all archives flagged to be removed. + */ + rtems_rtl_archives_remove (archives); + + /* + * Load any new archives. If any are loaded set the archive search flag in + * any unresolved external symbols so the archives are searched. The archive + * search flag avoids searching for symbols we know are not in the known + * archives, + */ + if (rtems_rtl_archives_load (archives)) + rtems_rtl_unresolved_set_archive_search (); + + return true; +} + +bool +rtems_rtl_archive_load (rtems_rtl_archives* archives, const char* name) +{ + if (archives != NULL) + { + rtems_rtl_archive* archive; + int loaded = 0; + + archive = rtems_rtl_archive_get (archives, "", name); + if (archive == NULL) + { + rtems_rtl_set_error (ENOENT, "archive not found"); + return false; + } + + archive->flags |= RTEMS_RTL_ARCHIVE_USER_LOAD; + + rtems_rtl_archive_loader (archive, &loaded); + if (loaded == 0) + { + rtems_rtl_archive_del (archive); + rtems_rtl_set_error (ENOENT, "archive load falied"); + } + + return true; + } + return false; +} + +rtems_rtl_archive_search +rtems_rtl_archive_obj_load (rtems_rtl_archives* archives, + const char* symbol, + bool load) +{ + rtems_rtl_obj* obj; + rtems_chain_control* pending; + int fd; + size_t archive_count; + + rtems_rtl_archive_obj_data search = { + .symbol = symbol, + .archive = NULL, + .offset = 0 + }; + + archive_count = rtems_chain_node_count_unprotected (&archives->archives); + + if (archive_count == 0) + { + if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES)) + printf ("rtl: archive: load: no archives\n"); + return rtems_rtl_archive_search_no_config; + } + + pending = rtems_rtl_pending_unprotected (); + if (pending == NULL) + { + if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES)) + printf ("rtl: archive: load: no pending list\n"); + return rtems_rtl_archive_search_not_found; + } + + if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES)) + printf ("rtl: archive: load: searching %zu archives\n", archive_count); + + rtems_rtl_archive_iterate_archives (archives, + rtems_rtl_archive_obj_finder, + &search); + + if (search.archive == NULL) + { + if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES)) + printf ("rtl: archive: load: not found: %s\n", symbol); + return rtems_rtl_archive_search_not_found; + } + + if (!load) + { + if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES)) + printf ("rtl: archive: load: found (no load): %s\n", symbol); + return rtems_rtl_archive_search_found; + } + + obj = rtems_rtl_obj_alloc (); + if (obj == NULL) + { + if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES)) + printf ("rtl: archive: alloc: no memory: %s\n", + search.archive->name); + return rtems_rtl_archive_search_error; + } + + obj->aname = rtems_rtl_strdup (search.archive->name); + + fd = open (obj->aname, O_RDONLY); + if (fd < 0) + { + if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES)) + printf ("rtl: archive: load: open error: %s: %s\n", + obj->aname, strerror (errno)); + rtems_rtl_obj_free (obj); + return rtems_rtl_archive_search_error; + } + + obj->oname = NULL; + obj->ooffset = search.offset; + + if (!rtems_rtl_obj_archive_find_obj (fd, + search.archive->size, + &obj->oname, + &obj->ooffset, + &obj->fsize, + &search.archive->enames, + rtems_rtl_archive_set_error)) + { + if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES)) + printf ("rtl: archive: load: load error: %s:%s\n", + obj->aname, obj->oname); + close (fd); + rtems_rtl_obj_free (obj); + return rtems_rtl_archive_search_error; + } + + obj->fname = rtems_rtl_strdup (obj->aname); + obj->ooffset -= RTEMS_RTL_AR_FHDR_SIZE; + obj->fsize = search.archive->size; + + close (fd); + + if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES)) + printf ("rtl: archive: loading: %s:%s@0x%08jx size:%zu\n", + obj->aname, obj->oname, obj->ooffset, obj->fsize); + + rtems_chain_append (pending, &obj->link); + + if (!rtems_rtl_obj_load (obj)) + { + if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES)) + printf ("rtl: archive: loading: error: %s:%s@0x%08jx\n", + obj->aname, obj->oname, obj->ooffset); + rtems_chain_extract (&obj->link); + rtems_rtl_obj_free (obj); + rtems_rtl_obj_caches_flush (); + return rtems_rtl_archive_search_error; + } + + rtems_rtl_obj_caches_flush (); + + if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES)) + printf ("rtl: archive: loading: loaded: %s:%s@0x%08jx\n", + obj->aname, obj->oname, obj->ooffset); + + return rtems_rtl_archive_search_loaded; +} + +bool +rtems_rtl_obj_archive_find_obj (int fd, + size_t fsize, + const char** name, + off_t* ooffset, + size_t* osize, + off_t* extended_file_names, + rtems_rtl_archive_error error) +{ + uint8_t header[RTEMS_RTL_AR_FHDR_SIZE]; + bool scanning; + + if (name == NULL) + { + error (errno, "invalid object name"); + *ooffset = 0; + *osize = 0; + return false; + } + + if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES)) + printf ("rtl: archive: find obj: %s @ 0x%08jx\n", *name, *ooffset); + + if (read (fd, &header[0], RTEMS_RTL_AR_IDENT_SIZE) != RTEMS_RTL_AR_IDENT_SIZE) + { + error (errno, "reading archive identifer"); + *ooffset = 0; + *osize = 0; + return false; + } + + if (memcmp (header, RTEMS_RTL_AR_IDENT, RTEMS_RTL_AR_IDENT_SIZE) != 0) + { + error (EINVAL, "invalid archive identifer"); + *ooffset = 0; + *osize = 0; + 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 (*ooffset != 0) + scanning = false; + else + { + if (*name == NULL) + { + error (errno, "invalid object name and archive offset"); + *ooffset = 0; + *osize = 0; + return false; + } + scanning = true; + *ooffset = RTEMS_RTL_AR_FHDR_BASE; + *osize = 0; + } + + while (*ooffset < fsize) + { + /* + * Clean up any existing data. + */ + memset (header, 0, sizeof (header)); + + if (!rtems_rtl_seek_read (fd, *ooffset, RTEMS_RTL_AR_FHDR_SIZE, &header[0])) + { + error (errno, "seek/read archive file header"); + *ooffset = 0; + *osize = 0; + return false; + } + + if ((header[RTEMS_RTL_AR_MAGIC] != 0x60) || + (header[RTEMS_RTL_AR_MAGIC + 1] != 0x0a)) + { + if (scanning) + { + error (EINVAL, "invalid archive file header"); + *ooffset = 0; + *osize = 0; + return false; + } + + scanning = true; + *ooffset = RTEMS_RTL_AR_FHDR_BASE; + continue; + } + + /* + * The archive header is always aligned to an even address. + */ + *osize = (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 ' ': + /* + * SVR4/GNU Symbols table. Nothing more to do. + */ + *ooffset += RTEMS_RTL_AR_FHDR_SIZE; + return true; + case '/': + /* + * Extended file names table. Remember. If asked to find this file + * return the result. + */ + *extended_file_names = *ooffset + RTEMS_RTL_AR_FHDR_SIZE; + if (*name[0] == '/' && *name[1] == '/') + { + *ooffset = *ooffset + RTEMS_RTL_AR_FHDR_SIZE; + return true; + } + 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 = RTEMS_RTL_AR_IDENT_SIZE; + while (*extended_file_names == 0) + { + off_t esize; + + if (!rtems_rtl_seek_read (fd, off, + RTEMS_RTL_AR_FHDR_SIZE, &header[0])) + { + error (errno, "seeking/reading archive ext file name header"); + *ooffset = 0; + *osize = 0; + return false; + } + + if ((header[RTEMS_RTL_AR_MAGIC] != 0x60) || + (header[RTEMS_RTL_AR_MAGIC + 1] != 0x0a)) + { + error (errno, "invalid archive file header"); + *ooffset = 0; + *osize = 0; + return false; + } + + if ((header[0] == '/') && (header[1] == '/')) + { + *extended_file_names = off + RTEMS_RTL_AR_FHDR_SIZE; + break; + } + + 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 (*extended_file_names != 0) + { + /* + * 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 ename[RTEMS_RTL_MAX_FILE_SIZE]; + + if (!rtems_rtl_seek_read (fd, *extended_file_names + extended_off, + RTEMS_RTL_MAX_FILE_SIZE, (uint8_t*) &ename[0])) + { + error (errno, "invalid archive ext file seek/read"); + *ooffset = 0; + *osize = 0; + return false; + } + + /* + * If there is no name memory the user is asking us to return the + * name in the archive at the offset. + */ + if (*name == NULL) + *name = rtems_rtl_archive_dup_name (ename); + if (rtems_rtl_archive_match_name (*name, ename)) + { + *ooffset += RTEMS_RTL_AR_FHDR_SIZE; + return true; + } + } + break; + default: + /* + * Ignore the file because we do not know what it it. + */ + break; + } + } + else + { + const char* ename = (const char*) &header[RTEMS_RTL_AR_FNAME]; + if (*name == NULL) + *name = rtems_rtl_archive_dup_name (ename); + if (rtems_rtl_archive_match_name (*name, ename)) + { + *ooffset += RTEMS_RTL_AR_FHDR_SIZE; + return true; + } + } + + *ooffset += *osize + RTEMS_RTL_AR_FHDR_SIZE; + } + + error (ENOENT, "object file not found"); + *ooffset = 0; + *osize = 0; + return false; + +} diff --git a/cpukit/libdl/rtl-elf.c b/cpukit/libdl/rtl-elf.c index 762130b9e7..caa37e6bab 100644 --- a/cpukit/libdl/rtl-elf.c +++ b/cpukit/libdl/rtl-elf.c @@ -248,6 +248,13 @@ rtems_rtl_elf_reloc_relocator (rtems_rtl_obj* obj, } sobj = rtems_rtl_find_obj_with_symbol (symbol); + + if (rtems_rtl_trace (RTEMS_RTL_TRACE_DEPENDENCY)) + printf ("rtl: depend: %s -> %s:%s\n", + obj->oname, + sobj == NULL ? "not-found" : sobj->oname, + symname); + if (sobj != NULL) { if (rtems_rtl_obj_add_dependent (obj, sobj)) @@ -471,6 +478,13 @@ rtems_rtl_obj_relocate_unresolved (rtems_rtl_unresolv_reloc* reloc, } sobj = rtems_rtl_find_obj_with_symbol (sym); + + if (rtems_rtl_trace (RTEMS_RTL_TRACE_DEPENDENCY)) + printf ("rtl: depend: %s -> %s:%s\n", + reloc->obj->oname, + sobj == NULL ? "not-found" : sobj->oname, + sym->name); + if (sobj != NULL) { if (rtems_rtl_obj_add_dependent (reloc->obj, sobj)) @@ -1274,8 +1288,6 @@ rtems_rtl_elf_file_load (rtems_rtl_obj* obj, int fd) if (!rtems_rtl_obj_relocate (obj, fd, rtems_rtl_elf_relocs_locator, &ehdr)) return false; - rtems_rtl_obj_synchronize_cache (obj); - rtems_rtl_symbol_obj_erase_local (obj); if (!rtems_rtl_elf_load_linkmap (obj)) diff --git a/cpukit/libdl/rtl-obj.c b/cpukit/libdl/rtl-obj.c index 28a5242ac3..858713dee1 100644 --- a/cpukit/libdl/rtl-obj.c +++ b/cpukit/libdl/rtl-obj.c @@ -106,7 +106,7 @@ rtems_rtl_obj_free_names (rtems_rtl_obj* obj) bool rtems_rtl_obj_free (rtems_rtl_obj* obj) { - if (obj->users || ((obj->flags & RTEMS_RTL_OBJ_LOCKED) != 0)) + if (obj->users > 0 || ((obj->flags & RTEMS_RTL_OBJ_LOCKED) != 0)) { rtems_rtl_set_error (EINVAL, "cannot free obj still in use"); return false; @@ -119,18 +119,63 @@ rtems_rtl_obj_free (rtems_rtl_obj* obj) rtems_rtl_obj_erase_dependents (obj); rtems_rtl_symbol_obj_erase (obj); rtems_rtl_obj_free_names (obj); - if (obj->sec_num) + if (obj->sec_num != NULL) free (obj->sec_num); - if (obj->linkmap) + if (obj->linkmap != NULL) rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_OBJECT, (void*) obj->linkmap); rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_OBJECT, obj); return true; } +typedef struct rtems_rtl_obj_unresolved_data +{ + bool has_unresolved; +} rtems_rtl_obj_unresolved_data; + +static bool +rtems_rtl_obj_unresolved_dependent (rtems_rtl_obj* obj, + rtems_rtl_obj* dependent, + void* data) +{ + rtems_rtl_obj_unresolved_data* ud; + ud = (rtems_rtl_obj_unresolved_data*) data; + if ((dependent->flags & RTEMS_RTL_OBJ_DEP_VISITED) == 0) + { + dependent->flags |= RTEMS_RTL_OBJ_DEP_VISITED; + if ((dependent->flags & RTEMS_RTL_OBJ_UNRESOLVED) != 0) + ud->has_unresolved = true; + else + { + rtems_rtl_obj_iterate_dependents (dependent, + rtems_rtl_obj_unresolved_dependent, + ud); + } + if (rtems_rtl_trace (RTEMS_RTL_TRACE_UNRESOLVED)) + printf ("rtl: obj: unresolved: dep: %s is %s\n", + dependent->oname, ud->has_unresolved ? "unresolved" : "resolved"); + } + return ud->has_unresolved; +} + bool rtems_rtl_obj_unresolved (rtems_rtl_obj* obj) { - return (obj->flags & RTEMS_RTL_OBJ_UNRESOLVED) != 0 ? true : false; + rtems_rtl_obj_unresolved_data ud = { + .has_unresolved = (obj->flags & RTEMS_RTL_OBJ_UNRESOLVED) != 0 + }; + if (rtems_rtl_trace (RTEMS_RTL_TRACE_UNRESOLVED)) + printf ("rtl: obj: unresolved: dep: %s is %s\n", + obj->oname, ud.has_unresolved ? "unresolved" : "resolved"); + if (!ud.has_unresolved) + { + rtems_rtl_obj_update_flags (RTEMS_RTL_OBJ_DEP_VISITED, 0); + obj->flags |= RTEMS_RTL_OBJ_DEP_VISITED; + rtems_rtl_obj_iterate_dependents (obj, + rtems_rtl_obj_unresolved_dependent, + &ud); + rtems_rtl_obj_update_flags (RTEMS_RTL_OBJ_DEP_VISITED, 0); + } + return ud.has_unresolved; } bool @@ -148,6 +193,9 @@ rtems_rtl_parse_name (const char* name, * 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. + * + * Note, if an archive the object file oofset may be know but the + * object file is not. Leave the object name as a NULL. */ end = name + strlen (name); colon = strrchr (name, ':'); @@ -155,7 +203,7 @@ rtems_rtl_parse_name (const char* name, colon = end; loname = rtems_rtl_alloc_new (RTEMS_RTL_ALLOC_OBJECT, colon - name + 1, true); - if (!oname) + if (!loname) { rtems_rtl_set_error (ENOMEM, "no memory for object file name"); return false; @@ -219,35 +267,6 @@ rtems_rtl_obj_parse_name (rtems_rtl_obj* 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; -} - /** * Section size summer iterator data. */ @@ -339,22 +358,6 @@ rtems_rtl_obj_section_handler (uint32_t mask, return true; } -bool -rtems_rtl_match_name (rtems_rtl_obj* 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* obj, const char* name) { @@ -596,9 +599,15 @@ rtems_rtl_obj_add_dependent (rtems_rtl_obj* obj, rtems_rtl_obj* dependent) { rtems_rtl_obj** free_slot; rtems_chain_node* node; + if (obj == dependent || dependent == rtems_rtl_baseimage ()) return false; + + if (rtems_rtl_trace (RTEMS_RTL_TRACE_DEPENDENCY)) + printf ("rtl: depend: add: %s -> %s\n", obj->oname, dependent->oname); + free_slot = NULL; + node = rtems_chain_first (&obj->dependents); while (!rtems_chain_is_tail (&obj->dependents, node)) { @@ -613,6 +622,7 @@ rtems_rtl_obj_add_dependent (rtems_rtl_obj* obj, rtems_rtl_obj* dependent) } node = rtems_chain_next (node); } + if (free_slot == NULL) { if (rtems_rtl_obj_alloc_dependents (obj, @@ -629,11 +639,47 @@ rtems_rtl_obj_add_dependent (rtems_rtl_obj* obj, rtems_rtl_obj* dependent) } } } + if (free_slot != NULL) *free_slot = dependent; + return free_slot != NULL; } + +bool +rtems_rtl_obj_remove_dependencies (rtems_rtl_obj* obj) +{ + /* + * If there are no references unload the object. + */ + if (obj->refs == 0) + { + /* + * Remove the refences from the object files this file depend on. The + * unload happens once the list of objects to be unloaded has been made and + * the destructors have been called for all those modules. + */ + rtems_chain_node* node = rtems_chain_first (&obj->dependents); + while (!rtems_chain_is_tail (&obj->dependents, node)) + { + rtems_rtl_obj_depends* depends = (rtems_rtl_obj_depends*) node; + size_t d; + for (d = 0; d < depends->dependents; ++d) + { + if (depends->depends[d] != NULL) + { + rtems_rtl_obj_dec_reference (depends->depends[d]); + depends->depends[d] = NULL; + } + } + node = rtems_chain_next (node); + } + return true; + } + return false; +} + bool rtems_rtl_obj_iterate_dependents (rtems_rtl_obj* obj, rtems_rtl_obj_depends_iterator iterator, @@ -1065,244 +1111,42 @@ rtems_rtl_obj_run_cdtors (rtems_rtl_obj* obj, uint32_t mask) } } -void -rtems_rtl_obj_run_ctors (rtems_rtl_obj* obj) +static bool +rtems_rtl_obj_cdtors_to_run (rtems_rtl_obj* obj, uint32_t mask) { - return rtems_rtl_obj_run_cdtors (obj, RTEMS_RTL_OBJ_SECT_CTOR); + rtems_chain_node* node = rtems_chain_first (&obj->sections); + while (!rtems_chain_is_tail (&obj->sections, node)) + { + rtems_rtl_obj_sect* sect = (rtems_rtl_obj_sect*) node; + if ((sect->flags & mask) == mask) + return true; + node = rtems_chain_next (node); + } + return false; } -void -rtems_rtl_obj_run_dtors (rtems_rtl_obj* obj) +bool +rtems_rtl_obj_ctors_to_run (rtems_rtl_obj* obj) { - return rtems_rtl_obj_run_cdtors (obj, RTEMS_RTL_OBJ_SECT_DTOR); + return rtems_rtl_obj_cdtors_to_run (obj, RTEMS_RTL_OBJ_SECT_CTOR); } -/** - * 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* obj, int fd) -{ -#define RTEMS_RTL_AR_IDENT "!\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; - } - } +void +rtems_rtl_obj_run_ctors (rtems_rtl_obj* obj) +{ + rtems_rtl_obj_run_cdtors (obj, RTEMS_RTL_OBJ_SECT_CTOR); +} - obj->ooffset += obj->fsize + RTEMS_RTL_AR_FHDR_SIZE; - } +bool +rtems_rtl_obj_dtors_to_run (rtems_rtl_obj* obj) +{ + return rtems_rtl_obj_cdtors_to_run (obj, RTEMS_RTL_OBJ_SECT_DTOR); +} - rtems_rtl_set_error (ENOENT, "object file not found"); - obj->ooffset = 0; - obj->fsize = 0; - return false; +void +rtems_rtl_obj_run_dtors (rtems_rtl_obj* obj) +{ + rtems_rtl_obj_run_cdtors (obj, RTEMS_RTL_OBJ_SECT_DTOR); } static bool @@ -1323,31 +1167,16 @@ rtems_rtl_obj_file_load (rtems_rtl_obj* obj, int fd) return false; } -static bool -rtems_rtl_obj_file_unload (rtems_rtl_obj* obj) +static void +rtems_rtl_obj_set_error (int num, const char* text) { - if (obj->format >= 0 && obj->format < RTEMS_RTL_LOADERS) - { - rtems_chain_node* node; - - if (!loaders[obj->format].unload (obj)) - return false; - - node = rtems_chain_first (&obj->dependents); - while (!rtems_chain_is_tail (&obj->dependents, node)) - { - rtems_rtl_obj_depends* depends = (rtems_rtl_obj_depends*) node; - size_t d; - for (d = 0; d < depends->dependents; ++d) - { - if (depends->depends[d] != NULL) - rtems_rtl_obj_dec_reference (depends->depends[d]); - } - node = rtems_chain_next (node); - } - } + rtems_rtl_set_error (num, text); +} - return false; +size_t +rtems_rtl_obj_get_reference (rtems_rtl_obj* obj) +{ + return obj->refs; } void @@ -1363,6 +1192,14 @@ rtems_rtl_obj_dec_reference (rtems_rtl_obj* obj) --obj->refs; } +bool +rtems_rtl_obj_orphaned (rtems_rtl_obj* obj) +{ + return ((obj->flags & RTEMS_RTL_OBJ_LOCKED) == 0 && + obj->users == 0 && + rtems_rtl_obj_get_reference (obj) == 0); +} + bool rtems_rtl_obj_load (rtems_rtl_obj* obj) { @@ -1377,7 +1214,7 @@ rtems_rtl_obj_load (rtems_rtl_obj* obj) fd = open (rtems_rtl_obj_fname (obj), O_RDONLY); if (fd < 0) { - rtems_rtl_set_error (ENOMEM, "opening for object file"); + rtems_rtl_set_error (errno, "opening for object file"); return false; } @@ -1387,7 +1224,14 @@ rtems_rtl_obj_load (rtems_rtl_obj* obj) */ if (rtems_rtl_obj_aname_valid (obj)) { - if (!rtems_rtl_obj_archive_find (obj, fd)) + off_t enames = 0; + if (!rtems_rtl_obj_archive_find_obj (fd, + obj->fsize, + &obj->oname, + &obj->ooffset, + &obj->fsize, + &enames, + rtems_rtl_obj_set_error)) { close (fd); return false; @@ -1403,7 +1247,10 @@ rtems_rtl_obj_load (rtems_rtl_obj* obj) return false; } - if (!_rtld_linkmap_add (obj)) /* For GDB */ + /* + * For GDB + */ + if (!_rtld_linkmap_add (obj)) { close (fd); return false; @@ -1417,7 +1264,15 @@ rtems_rtl_obj_load (rtems_rtl_obj* obj) bool rtems_rtl_obj_unload (rtems_rtl_obj* obj) { - _rtld_linkmap_delete(obj); - rtems_rtl_obj_file_unload (obj); - return true; + bool ok = false; + if (obj->format >= 0 && obj->format < RTEMS_RTL_LOADERS) + { + _rtld_linkmap_delete(obj); + ok = loaders[obj->format].unload (obj); + } + else + { + rtems_rtl_set_error (EINVAL, "invalid object loader format"); + } + return ok; } diff --git a/cpukit/libdl/rtl-shell.c b/cpukit/libdl/rtl-shell.c index f3471d8d8e..c2a1af5ddd 100644 --- a/cpukit/libdl/rtl-shell.c +++ b/cpukit/libdl/rtl-shell.c @@ -26,9 +26,9 @@ #include #include -#include "rtl-chain-iterator.h" -#include "rtl-shell.h" +#include #include +#include "rtl-chain-iterator.h" /** * The type of the shell handlers we have. @@ -119,6 +119,7 @@ typedef struct { rtems_rtl_data* rtl; /**< The RTL data. */ int indent; /**< Spaces to indent. */ + bool sep1; /**< Print a separator. */ bool oname; /**< Print object names. */ bool names; /**< Print details of all names. */ bool memory_map; /**< Print the memory map. */ @@ -206,6 +207,10 @@ rtems_rtl_obj_printer (rtems_rtl_obj_print* print, rtems_rtl_obj* obj) if (!print->base && (obj == print->rtl->base)) return true; + if (print->sep1) + { + printf ("%-*c--------------\n", print->indent, ' '); + } if (print->oname) { printf ("%-*cobject name : %s\n", @@ -279,7 +284,7 @@ rtems_rtl_unresolved_printer (rtems_rtl_unresolv_rec* rec, void* data) { rtems_rtl_obj_print* print = (rtems_rtl_obj_print*) data; - if (rec->type == rtems_rtl_unresolved_name) + if (rec->type == rtems_rtl_unresolved_symbol) printf ("%-*c%s\n", print->indent + 2, ' ', rec->rec.name.name); return false; } @@ -301,6 +306,7 @@ rtems_rtl_shell_list (rtems_rtl_data* rtl, int argc, char *argv[]) rtems_rtl_obj_print print; print.rtl = rtl; print.indent = 1; + print.sep1 = true; print.oname = true; print.names = true; print.memory_map = true; @@ -319,6 +325,7 @@ rtems_rtl_shell_sym (rtems_rtl_data* rtl, int argc, char *argv[]) rtems_rtl_obj_print print; print.rtl = rtl; print.indent = 1; + print.sep1 = true; print.oname = true; print.names = false; print.memory_map = false; @@ -329,7 +336,7 @@ rtems_rtl_shell_sym (rtems_rtl_data* rtl, int argc, char *argv[]) rtems_rtl_obj_print_iterator, &print); printf ("Unresolved:\n"); - rtems_rtl_unresolved_interate (rtems_rtl_unresolved_printer, &print); + rtems_rtl_unresolved_iterate (rtems_rtl_unresolved_printer, &print); return 0; } diff --git a/cpukit/libdl/rtl-shell.h b/cpukit/libdl/rtl-shell.h deleted file mode 100644 index 9df5ee203f..0000000000 --- a/cpukit/libdl/rtl-shell.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * COPYRIGHT (c) 2012 Chris Johns - * - * The license and distribution terms for this file may be - * found in the file LICENSE in this distribution or at - * http://www.rtems.org/license/LICENSE. - */ -/** - * @file - * - * @ingroup rtems_rtl - * - * @brief RTEMS Run-Time Linker ELF Shell Support. - */ - -#if !defined (_RTEMS_RTL_SHELL_H_) -#define _RTEMS_RTL_SHELL_H_ - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -#include - -/** - * The RTL single shell command contains sub-commands. - * - * @param argc The argument count. - * @param argv Array of argument strings. - * @retval 0 No error. - * @return int The exit code. - */ -int rtems_rtl_shell_command (int argc, char* argv[]); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif diff --git a/cpukit/libdl/rtl-string.h b/cpukit/libdl/rtl-string.h index bb61b83683..1aa2624fdf 100644 --- a/cpukit/libdl/rtl-string.h +++ b/cpukit/libdl/rtl-string.h @@ -16,8 +16,6 @@ #if !defined (_RTEMS_RTL_STRING_H_) #define _RTEMS_RTL_STRING_H_ -#include - #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ diff --git a/cpukit/libdl/rtl-trace.c b/cpukit/libdl/rtl-trace.c index d5e4b9e5ec..e09ec06758 100644 --- a/cpukit/libdl/rtl-trace.c +++ b/cpukit/libdl/rtl-trace.c @@ -69,7 +69,9 @@ rtems_rtl_trace_shell_command (int argc, char *argv[]) "load-sect", "allocator", "unresolved", - "cache" + "cache", + "archives", + "dependency" }; rtems_rtl_trace_mask set_value = 0; diff --git a/cpukit/libdl/rtl-unresolved.c b/cpukit/libdl/rtl-unresolved.c index f42fc7d03d..4e81c3c64e 100644 --- a/cpukit/libdl/rtl-unresolved.c +++ b/cpukit/libdl/rtl-unresolved.c @@ -51,11 +51,17 @@ rtems_rtl_unresolved_block_alloc (rtems_rtl_unresolved* unresolved) } static size_t -rtems_rtl_unresolved_name_recs (const char* name) +rtems_rtl_unresolved_symbol_rec_count (size_t length) { - size_t length = strlen (name); - return ((length + sizeof(rtems_rtl_unresolv_name) - 1) / - sizeof(rtems_rtl_unresolv_name)); + return ((length + sizeof(rtems_rtl_unresolv_symbol) - 1) / + sizeof(rtems_rtl_unresolv_symbol)); +} + + +static size_t +rtems_rtl_unresolved_symbol_recs (const char* name) +{ + return rtems_rtl_unresolved_symbol_rec_count (strlen (name)); } static int @@ -83,12 +89,11 @@ rtems_rtl_unresolved_rec_next (rtems_rtl_unresolv_rec* rec) rec = NULL; break; - case rtems_rtl_unresolved_name: + case rtems_rtl_unresolved_symbol: /* * Determine how many records the name occupies. Round up. */ - rec += ((rec->rec.name.length + sizeof(rtems_rtl_unresolv_name) - 1) / - sizeof(rtems_rtl_unresolv_name)); + rec += rtems_rtl_unresolved_symbol_rec_count (rec->rec.name.length); break; case rtems_rtl_unresolved_reloc: @@ -132,7 +137,7 @@ rtems_rtl_unresolved_find_name (rtems_rtl_unresolved* unresolved, while (!rtems_rtl_unresolved_rec_is_last (block, rec)) { - if (rec->type == rtems_rtl_unresolved_name) + if (rec->type == rtems_rtl_unresolved_symbol) { if ((rec->rec.name.length == length) && (strcmp (rec->rec.name.name, name) == 0)) @@ -168,7 +173,9 @@ rtems_rtl_unresolved_resolve_reloc (rtems_rtl_unresolv_rec* rec, { if (rec->type == rtems_rtl_unresolved_reloc) { + rtems_chain_control* pending; rtems_rtl_unresolved_reloc_data* rd; + rd = (rtems_rtl_unresolved_reloc_data*) data; if (rec->rec.reloc.name == rd->name && rec->rec.reloc.obj != NULL) @@ -179,7 +186,18 @@ rtems_rtl_unresolved_resolve_reloc (rtems_rtl_unresolv_rec* rec, rtems_rtl_obj_relocate_unresolved (&rec->rec.reloc, rd->sym); /* - * Set the object pointer to NULL to indicate the record is not used + * If all unresolved externals are resolved add the obj module + * to the pending queue. This will flush the object module's + * data from the cache and call it's constructors. + */ + if (rec->rec.reloc.obj->unresolved == 0) + { + pending = rtems_rtl_pending_unprotected (); + rtems_chain_append (pending, &rec->rec.reloc.obj->link); + } + + /* + * Check 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. @@ -196,7 +214,7 @@ static bool rtems_rtl_unresolved_resolve_iterator (rtems_rtl_unresolv_rec* rec, void* data) { - if (rec->type == rtems_rtl_unresolved_name) + if (rec->type == rtems_rtl_unresolved_symbol) { rtems_rtl_unresolved_reloc_data* rd; rd = (rtems_rtl_unresolved_reloc_data*) data; @@ -215,7 +233,7 @@ rtems_rtl_unresolved_resolve_iterator (rtems_rtl_unresolv_rec* rec, rd->name_rec = rec; - rtems_rtl_unresolved_interate (rtems_rtl_unresolved_resolve_reloc, rd); + rtems_rtl_unresolved_iterate (rtems_rtl_unresolved_resolve_reloc, rd); rd->name_rec = NULL; rd->sym = NULL; @@ -225,6 +243,57 @@ rtems_rtl_unresolved_resolve_iterator (rtems_rtl_unresolv_rec* rec, return false; } +/** + * Struct to pass archive relocation data in the iterator. + */ +typedef struct rtems_rtl_unresolved_archive_reloc_data +{ + uint16_t name; /**< Name index. */ + bool loaded; /**< Object file loaded. */ + rtems_rtl_archives* archives; /**< The archives to search. */ +} rtems_rtl_unresolved_archive_reloc_data; + +static bool +rtems_rtl_unresolved_archive_iterator (rtems_rtl_unresolv_rec* rec, + void* data) +{ + if (rec->type == rtems_rtl_unresolved_symbol) + { + rtems_rtl_unresolved_archive_reloc_data* ard; + ard = (rtems_rtl_unresolved_archive_reloc_data*) data; + + ++ard->name; + + if ((rec->rec.name.flags & RTEMS_RTL_UNRESOLV_SYM_SEARCH_ARCHIVE) != 0) + { + rtems_rtl_archive_search load; + + if (rtems_rtl_trace (RTEMS_RTL_TRACE_UNRESOLVED)) + printf ("rtl: unresolv: archive lookup: %d: %s\n", + ard->name, rec->rec.name.name); + + load = rtems_rtl_archive_obj_load (ard->archives, + rec->rec.name.name, true); + if (load == rtems_rtl_archive_search_loaded) + { + ard->loaded = true; + return true; + } + } + } + + return false; +} + +static bool +rtems_rtl_unresolved_archive_search_iterator (rtems_rtl_unresolv_rec* rec, + void* data) +{ + if (rec->type == rtems_rtl_unresolved_symbol) + rec->rec.name.flags = RTEMS_RTL_UNRESOLV_SYM_SEARCH_ARCHIVE; + return false; +} + static void rtems_rtl_unresolved_clean_block (rtems_rtl_unresolv_block* block, rtems_rtl_unresolv_rec* rec, @@ -248,8 +317,8 @@ rtems_rtl_unresolved_compact (void) if (unresolved) { /* - * Iterate over the blocks removing any empty strings. If a string is removed - * update the indexes of all strings above this level. + * Iterate over the blocks removing any empty strings. If a string is + * removed update the indexes of all strings above this level. */ rtems_chain_node* node = rtems_chain_first (&unresolved->blocks); uint16_t index = 0; @@ -261,7 +330,7 @@ rtems_rtl_unresolved_compact (void) while (!rtems_rtl_unresolved_rec_is_last (block, rec)) { bool next_rec = true; - if (rec->type == rtems_rtl_unresolved_name) + if (rec->type == rtems_rtl_unresolved_symbol) { ++index; if (rec->rec.name.refs == 0) @@ -305,7 +374,7 @@ rtems_rtl_unresolved_compact (void) /* * Compact the block removing the name record. */ - name_recs = rtems_rtl_unresolved_name_recs (rec->rec.name.name); + name_recs = rtems_rtl_unresolved_symbol_recs (rec->rec.name.name); rtems_rtl_unresolved_clean_block (block, rec, name_recs, unresolved->block_recs); --index; @@ -360,14 +429,14 @@ rtems_rtl_unresolved_table_close (rtems_rtl_unresolved* unresolved) while (!rtems_chain_is_tail (&unresolved->blocks, node)) { rtems_chain_node* next = rtems_chain_next (node); - free (node); + rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_EXTERNAL, node); node = next; } } bool -rtems_rtl_unresolved_interate (rtems_rtl_unresolved_iterator iterator, - void* data) +rtems_rtl_unresolved_iterate (rtems_rtl_unresolved_iterator iterator, + void* data) { rtems_rtl_unresolved* unresolved = rtems_rtl_unresolved_unprotected (); if (unresolved) @@ -438,7 +507,7 @@ rtems_rtl_unresolved_add (rtems_rtl_obj* obj, } name_index = rtems_rtl_unresolved_find_name (unresolved, name, true); - name_recs = rtems_rtl_unresolved_name_recs (name); + name_recs = rtems_rtl_unresolved_symbol_recs (name); /* * An index less than 0 means the name is present and "0 - index" is the next @@ -459,8 +528,9 @@ rtems_rtl_unresolved_add (rtems_rtl_obj* obj, } rec = rtems_rtl_unresolved_rec_first_free (name_block); - rec->type = rtems_rtl_unresolved_name; + rec->type = rtems_rtl_unresolved_symbol; rec->rec.name.refs = 1; + rec->rec.name.flags = RTEMS_RTL_UNRESOLV_SYM_SEARCH_ARCHIVE; rec->rec.name.length = strlen (name) + 1; memcpy ((void*) &rec->rec.name.name[0], name, rec->rec.name.length + 1); block->recs += name_recs; @@ -496,14 +566,41 @@ rtems_rtl_unresolved_add (rtems_rtl_obj* obj, void rtems_rtl_unresolved_resolve (void) { - rtems_rtl_unresolved_reloc_data rd; + bool resolving = true; + 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 (); + + /* + * The resolving process is two separate stages, The first stage is to + * iterate over the unresolved symbols search the global symbol table. If a + * symbol is found iterate over the unresolved relocation records for the + * symbol fixing up the relocations. The second stage is to search the + * archives for symbols we have not been search before and if a symbol if + * found in an archve loaded the object file. Loading an object file stops + * the search of the archives for symbols and stage one is performed + * again. The process repeats until no more symbols are resolved. + */ + while (resolving) + { + rtems_rtl_unresolved_reloc_data rd = { + .name = 0, + .name_rec = NULL, + .sym = NULL + }; + rtems_rtl_unresolved_archive_reloc_data ard = { + .name = 0, + .loaded = false, + .archives = rtems_rtl_archives_unprotected () + }; + + rtems_rtl_unresolved_iterate (rtems_rtl_unresolved_resolve_iterator, &rd); + rtems_rtl_unresolved_compact (); + rtems_rtl_unresolved_iterate (rtems_rtl_unresolved_archive_iterator, &ard); + + resolving = ard.loaded; + } + if (rtems_rtl_trace (RTEMS_RTL_TRACE_UNRESOLVED)) rtems_rtl_unresolved_dump (); } @@ -541,7 +638,7 @@ rtems_rtl_unresolved_dump_iterator (rtems_rtl_unresolv_rec* rec, case rtems_rtl_unresolved_empty: printf (" %03zu: 0: empty\n", dd->rec); break; - case rtems_rtl_unresolved_name: + case rtems_rtl_unresolved_symbol: ++dd->names; printf (" %3zu: 1: name: %3d refs: %2d: %2d: %s\n", dd->rec, dd->names, @@ -562,10 +659,17 @@ rtems_rtl_unresolved_dump_iterator (rtems_rtl_unresolv_rec* rec, return false; } +void +rtems_rtl_unresolved_set_archive_search (void) +{ + rtems_rtl_unresolved_iterate (rtems_rtl_unresolved_archive_search_iterator, + NULL); +} + void rtems_rtl_unresolved_dump (void) { rtems_rtl_unresolved_dump_data dd = { 0 }; printf ("RTL Unresolved data:\n"); - rtems_rtl_unresolved_interate (rtems_rtl_unresolved_dump_iterator, &dd); + rtems_rtl_unresolved_iterate (rtems_rtl_unresolved_dump_iterator, &dd); } diff --git a/cpukit/libdl/rtl.c b/cpukit/libdl/rtl.c index fdf4c1229f..f331cbbf02 100644 --- a/cpukit/libdl/rtl.c +++ b/cpukit/libdl/rtl.c @@ -27,9 +27,10 @@ #include #include +#include +#include "rtl-chain-iterator.h" #include "rtl-error.h" #include "rtl-string.h" -#include /** * Symbol table cache size. They can be big so the cache needs space to work. @@ -123,10 +124,14 @@ rtems_rtl_data_init (void) rtems_recursive_mutex_lock (&rtl->lock); /* - * Initialise the objects list and create any required services. + * 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)) { @@ -136,6 +141,14 @@ rtems_rtl_data_init (void) return false; } + /* + * Open the archives. + */ + rtems_rtl_archives_open (&rtl->archives, "/etc/rtl-libs.conf"); + + /* + * Open the unresolved table. + */ if (!rtems_rtl_unresolved_table_open (&rtl->unresolved, RTEMS_RTL_UNRESOLVED_BLOCK_SIZE)) { @@ -252,6 +265,28 @@ rtems_rtl_global_symbols (void) return &rtl->globals; } +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) { @@ -263,6 +298,17 @@ rtems_rtl_unresolved_unprotected (void) 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, @@ -317,19 +363,34 @@ rtems_rtl_obj_decompress (rtems_rtl_obj_comp** decomp, } } +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_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 (clear != 0) - obj->flags &= ~clear; - if (set != 0) - obj->flags |= set; - node = rtems_chain_next (node); - } + 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* @@ -390,16 +451,16 @@ rtems_rtl_find_obj (const char* name) (aname != NULL && strcmp (obj->aname, aname) == 0 && strcmp (obj->oname, oname) == 0)) { - found = obj; - break; + found = obj; + break; } node = rtems_chain_next (node); } - if (!aname) + if (aname != NULL) rtems_rtl_alloc_del(RTEMS_RTL_ALLOC_OBJECT, (void*) aname); - if (!oname) + if (oname != NULL) rtems_rtl_alloc_del(RTEMS_RTL_ALLOC_OBJECT, (void*) oname); return found; @@ -412,8 +473,15 @@ rtems_rtl_find_obj_with_symbol (const rtems_rtl_obj_sym* sym) while (!rtems_chain_is_tail (&rtl->objects, node)) { rtems_rtl_obj* obj = (rtems_rtl_obj*) node; - if (sym >= obj->global_table && - sym < (obj->global_table + obj->global_syms)) + 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); } @@ -455,18 +523,17 @@ rtems_rtl_load_object (const char* name, int mode) return NULL; } - rtems_chain_append (&rtl->objects, &obj->link); + rtems_chain_append (&rtl->pending, &obj->link); if (!rtems_rtl_obj_load (obj)) { + rtems_chain_extract (&obj->link); rtems_rtl_obj_free (obj); rtems_rtl_obj_caches_flush (); return NULL; } rtems_rtl_obj_caches_flush (); - - rtems_rtl_unresolved_resolve (); } /* @@ -474,25 +541,67 @@ rtems_rtl_load_object (const char* name, int mode) */ ++obj->users; + return obj; +} + +rtems_rtl_obj* +rtems_rtl_load (const char* name, int mode) +{ + rtems_rtl_obj* obj; + /* - * 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. + * Refesh the archives. */ + rtems_rtl_archives_refresh (&rtl->archives); /* - * 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. + * Collect the loaded object files. */ - if (obj->users == 1) + rtems_chain_initialize_empty (&rtl->pending); + + obj = rtems_rtl_load_object (name, mode); + if (obj != NULL) { - obj->flags |= RTEMS_RTL_OBJ_LOCKED; - rtems_rtl_unlock (); - rtems_rtl_obj_run_ctors (obj); - rtems_rtl_lock (); - obj->flags &= ~RTEMS_RTL_OBJ_LOCKED; + 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* obj = (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 (&obj->link); + rtems_chain_extract (&obj->link); + rtems_chain_append (&rtl->objects, &obj->link); + + /* + * Make sure the object file and cache is synchronized. + */ + rtems_rtl_obj_synchronize_cache (obj); + + /* + * 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; @@ -501,55 +610,107 @@ rtems_rtl_load_object (const char* name, int mode) bool rtems_rtl_unload_object (rtems_rtl_obj* obj) { - bool ok = true; - if (rtems_rtl_trace (RTEMS_RTL_TRACE_UNLOAD)) - printf ("rtl: unloading '%s'\n", rtems_rtl_obj_fname (obj)); + 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, "cannot unload when locked"); + rtems_rtl_set_error (EINVAL, "object file is locked"); return false; } /* - * Check the number of users in a safe manner. If this is the last user unload the - * object file from memory. + * Move the object file from the objects list to the pending list if unloaded. + */ + 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; - if (obj->users == 0) + return true; +} + +bool +rtems_rtl_unload (rtems_rtl_obj* obj) +{ + bool ok = rtems_rtl_unload_object (obj); + if (ok && obj->users == 0) { - if (obj->refs != 0) + rtems_chain_control unloading; + rtems_chain_node* node; + + /* + * Remove the orphaned object files from the objects list. This makes the + * list private and any changes in any desctructors will effect the run. + */ + rtems_chain_initialize_empty (&unloading); + + node = rtems_chain_first (&rtl->objects); + while (!rtems_chain_is_tail (&rtl->objects, node)) { - rtems_rtl_set_error (EBUSY, "object file referenced"); - return false; + 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: %s: %s\n", + rtems_rtl_obj_oname (obj), + rtems_rtl_obj_orphaned (uobj) ? "orphaned" : "inuse"); + if (rtems_rtl_obj_orphaned (uobj)) + { + 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; } - obj->flags |= RTEMS_RTL_OBJ_LOCKED; + + /* + * Call the desctructors unlocked. An RTL call will not deadlock. + */ rtems_rtl_unlock (); - rtems_rtl_obj_run_dtors (obj); - rtems_rtl_lock (); - obj->flags &= ~RTEMS_RTL_OBJ_LOCKED; - ok = rtems_rtl_obj_unload (obj); + node = rtems_chain_first (&unloading); + while (!rtems_chain_is_tail (&unloading, node)) + { + rtems_rtl_obj* uobj = (rtems_rtl_obj*) node; + rtems_rtl_obj_run_dtors (uobj); + node = rtems_chain_next (node); + } - rtems_rtl_obj_free (obj); - rtems_rtl_obj_caches_flush (); - } + 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; } -void -rtems_rtl_run_ctors (rtems_rtl_obj* obj) -{ - rtems_rtl_obj_run_ctors (obj); -} - static bool rtems_rtl_path_update (bool prepend, const char* path) { -- cgit v1.2.3