/* SPDX-License-Identifier: BSD-2-Clause */ /** * @file * * @ingroup rtl * * @brief RTEMS Run-Time Linker Archive */ /* * COPYRIGHT (c) 2018 Chris Johns * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include #include "rtl-chain-iterator.h" #include #include "rtl-string.h" #include "rtl-error.h" /** * 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 pointeres to the symbols is generated after * loading if there are enough symbols. For small symbol tables the search 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 + 1) * 4)); return false; } symbol += strlen (symbol) + 1; } } else { rtems_rtl_archive_symbol* match; const rtems_rtl_archive_symbol key = { .entry = -1, .label = search->symbol }; match = bsearch (&key, symbols->symbols, symbols->entries, sizeof (symbols->symbols[0]), rtems_rtl_archive_symbol_compare); if (match != NULL) { search->archive = archive; search->offset = rtems_rtl_archive_read_32 (symbols->base + (match->entry * 4)); return false; } } } /* * 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 + 1, true); 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)); return false; } 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; r = 0; while (r < archives->config_length) { if (s[r] == '\0') { ++r; } else { size_t ls = strlen (&s[r]); size_t b = 0; while (b < ls && isspace ((unsigned char) s[r + b])) { s[r + b] = '\0'; ++b; } b = ls - 1; while (b > 0 && isspace ((unsigned char) s[r + b])) { s[r + b] = '\0'; --b; } r += ls; } } if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES)) { int line = 1; printf ("rtl: archive: config:\n"); r = 0; while (r < archives->config_length) { const char* cs = &archives->config[r]; size_t len = strlen (cs); if (len > 0) { printf (" %3d: %s\n", line, cs); ++line; } r += len + 1; } } } 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=%zu\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 %zu 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); /* * Reallocate the symbol table memory if it has changed size. * Note, an updated library may have the 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. Range check the * value so the alloc size does not overflow (Coverity 1442636). */ archive->symbols.entries = rtems_rtl_archive_read_32 (archive->symbols.base); if (archive->symbols.entries >= (SIZE_MAX / sizeof (rtems_rtl_archive_symbol))) { 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, "too many symbols"); return true; } archive->symbols.size = size; archive->symbols.names = archive->symbols.base; archive->symbols.names += (archive->symbols.entries + 1) * 4; /* * Create a sorted symbol table. */ 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, (unsigned int) (archive->symbols.entries + 1) * 4, archive->symbols.symbols); if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVE_SYMS) && archive->symbols.entries > 0) { size_t e; printf ("rtl: archive: symbols: %s\n", archive->name ); for (e = 0; e < archive->symbols.entries; ++e) { printf(" %6zu: %6zu %s\n", e + 1, archive->symbols.symbols[e].entry, archive->symbols.symbols[e].label); } } } 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 (pattern: %s)\n", entry.d_name, basename); if (fnmatch (basename, entry.d_name, 0) == 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: %s\n", obj->aname, obj->oname, obj->ooffset, rtems_rtl_last_error_unprotected ()); 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] == '/') { const char* name_ = *name; 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; }