diff options
author | Chris Johns <chrisj@rtems.org> | 2018-12-17 16:36:48 +1100 |
---|---|---|
committer | Chris Johns <chrisj@rtems.org> | 2019-02-09 10:06:34 +1100 |
commit | 89c59be38d9c83437abb9002e8fea5012652e5ff (patch) | |
tree | 954df408c26a58ae41851502f427f30771952bdf /cpukit/libdl/rtl-archive.c | |
parent | powerpc/psim: Increase the psim memory to 256M (diff) | |
download | rtems-89c59be38d9c83437abb9002e8fea5012652e5ff.tar.bz2 |
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
Diffstat (limited to 'cpukit/libdl/rtl-archive.c')
-rw-r--r-- | cpukit/libdl/rtl-archive.c | 1301 |
1 files changed, 1301 insertions, 0 deletions
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 <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ +/** + * @file + * + * @ingroup rtl + * + * @brief RTEMS Run-Time Linker Archive + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <ctype.h> +#include <dirent.h> +#include <errno.h> +#include <fnmatch.h> +#include <stdio.h> +#include <string.h> + +#include <rtems/libio_.h> + +#include <rtems/rtl/rtl.h> +#include "rtl-chain-iterator.h" +#include <rtems/rtl/rtl-trace.h> +#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 "!<arch>\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; + +} |