[PATCH 04/11] libdl: Add symbol searching and loading from archives.
chrisj at rtems.org
chrisj at rtems.org
Tue Feb 5 01:42:57 UTC 2019
From: Chris Johns <chrisj at rtems.org>
- 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/Makefile.am | 1 +
cpukit/include/rtems/rtl/rtl-archive.h | 196 +++
cpukit/include/rtems/rtl/rtl-obj.h | 66 +-
.../{libdl => include/rtems/rtl}/rtl-shell.h | 2 -
cpukit/include/rtems/rtl/rtl-trace.h | 2 +
cpukit/include/rtems/rtl/rtl-unresolved.h | 37 +-
cpukit/include/rtems/rtl/rtl.h | 56 +-
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-string.h | 2 -
cpukit/libdl/rtl-trace.c | 4 +-
cpukit/libdl/rtl-unresolved.c | 160 +-
cpukit/libdl/rtl.c | 277 +++-
16 files changed, 2175 insertions(+), 437 deletions(-)
create mode 100644 cpukit/include/rtems/rtl/rtl-archive.h
rename cpukit/{libdl => include/rtems/rtl}/rtl-shell.h (97%)
create mode 100644 cpukit/libdl/rtl-archive.c
diff --git a/cpukit/Makefile.am b/cpukit/Makefile.am
index 7a138c0c6e..8ea7cbbfb0 100644
--- a/cpukit/Makefile.am
+++ b/cpukit/Makefile.am
@@ -1140,6 +1140,7 @@ librtemscpu_a_SOURCES += libdl/rap.c
librtemscpu_a_SOURCES += libdl/rap-shell.c
librtemscpu_a_SOURCES += libdl/rtl-allocator.c
librtemscpu_a_SOURCES += libdl/rtl-alloc-heap.c
+librtemscpu_a_SOURCES += libdl/rtl-archive.c
librtemscpu_a_SOURCES += libdl/rtl.c
librtemscpu_a_SOURCES += libdl/rtl-chain-iterator.c
librtemscpu_a_SOURCES += libdl/rtl-debugger.c
diff --git a/cpukit/include/rtems/rtl/rtl-archive.h b/cpukit/include/rtems/rtl/rtl-archive.h
new file mode 100644
index 0000000000..1fe3aae385
--- /dev/null
+++ b/cpukit/include/rtems/rtl/rtl-archive.h
@@ -0,0 +1,196 @@
+/*
+ * COPYRIGHT (c) 2018 Chris Johns <chrisj at 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 rtems_rtl
+ *
+ * @brief RTEMS Run-Time Linker Archive
+ *
+ * The RTL Archive module manages dependent loading of object files from
+ * archives. The archives need to have a `ranlib` generated symbol table.
+ *
+ * This module reads a configuration file called `rtl-libs.conf` from a default
+ * directory of `/etc`. The file is a line per glob'ed path to archives to
+ * search for symbols.
+ *
+ * The archive symbols are held in a per archive cache for searching.
+ *
+ * @note Errors in the reading of a config file, locating archives, reading
+ * symbol tables and loading object files are not considered RTL error
+ * reported to a user. The user error is undefined symbols.
+ */
+
+#if !defined (_RTEMS_RTL_ARCHIVE_H_)
+#define _RTEMS_RTL_ARCHIVE_H_
+
+#include <rtems.h>
+#include <rtems/chain.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/**
+ * Flags for archives.
+ */
+#define RTEMS_RTL_ARCHIVE_USER_LOAD (1 << 0) /**< User forced load. */
+#define RTEMS_RTL_ARCHIVE_REMOVE (1 << 1) /**< The achive is not found. */
+#define RTEMS_RTL_ARCHIVE_LOAD (1 << 2) /**< Load the achive. */
+
+/**
+ * Symbol search and loading results.
+ */
+typedef enum rtems_rtl_archive_search
+{
+ rtems_rtl_archive_search_not_found = 0, /**< The search failed to find the
+ symbol. */
+ rtems_rtl_archive_search_found = 1, /**< The symbols was found. */
+ rtems_rtl_archive_search_loaded = 2, /**< The symbol was found and the
+ object file has been loaded */
+ rtems_rtl_archive_search_error = 3, /**< There was an error searching or
+ loading. */
+ rtems_rtl_archive_search_no_config = 4 /**< There is no config or it is
+ * invalid. */
+} rtems_rtl_archive_search;
+
+/**
+ * RTL Archive symbols.
+ */
+typedef struct rtems_rtl_archive_symbol
+{
+ size_t entry; /**< Index in the symbol offset table. */
+ const char* label; /**< The symbol's label. */
+} rtems_rtl_archive_symbol;
+
+/**
+ * RTL Archive symbols.
+ */
+typedef struct rtems_rtl_archive_symbols
+{
+ void* base; /**< Base of the symbol table. */
+ size_t size; /**< Size of the symbol table. */
+ size_t entries; /**< Entries in the symbol table. */
+ const char* names; /**< Start of the symbol names. */
+ rtems_rtl_archive_symbol* symbols; /**< Sorted symbol table. */
+} rtems_rtl_archive_symbols;
+
+/**
+ * RTL Archive data.
+ */
+typedef struct rtems_rtl_archive
+{
+ rtems_chain_node node; /**< Chain link. */
+ const char* name; /**< Archive absolute path. */
+ size_t size; /**< Size of the archive. */
+ time_t mtime; /**< Archive's last modified time. */
+ off_t enames; /**< Extended file name offset, lazy load. */
+ rtems_rtl_archive_symbols symbols; /**< Ranlib symbol table. */
+ size_t refs; /**< Loaded object modules. */
+ uint32_t flags; /**< Some flags. */
+} rtems_rtl_archive;
+
+/**
+ * RTL Archive data.
+ */
+typedef struct rtems_rtl_archives
+{
+ const char* config_name; /**< Config file name. */
+ time_t config_mtime; /**< Config last modified time. */
+ size_t config_length; /**< Length the config data. */
+ const char* config; /**< Config file contents. */
+ rtems_chain_control archives; /**< The located archives. */
+} rtems_rtl_archives;
+
+/**
+ * Error handler call when finding an archive.
+ */
+typedef void (*rtems_rtl_archive_error)(int num, const char* text);
+
+/**
+ * Open the RTL archives support with the specified configration file.
+ *
+ * @param archives The archives data to open.
+ * @param config The path to the configuration file.
+ * @return bool Returns @true is the archives are open.
+ */
+void rtems_rtl_archives_open (rtems_rtl_archives* archives, const char* config);
+
+/**
+ * Close the RTL archives support.
+ *
+ * @param archives The archives data to close.
+ */
+void rtems_rtl_archives_close (rtems_rtl_archives* archives);
+
+/**
+ * Refresh the archives data. Check if the configuration has changes and if it
+ * has reload it. Check each path in the configuration and creates archive
+ * instances for new archives and remove archives not present any more.
+ *
+ * Refreshing is a development aid so reboots can be avoided as users trial
+ * configurations that work.
+ *
+ * @param archives The archives data to refresh.
+ * @retval false The refresh failed, an error will have been set.
+ */
+bool rtems_rtl_archives_refresh (rtems_rtl_archives* archives);
+
+/**
+ * Load an archive.
+ *
+ * @param archives The archives data to search.
+ * @param name The archive to load.
+ * @retval true The archive is loaded.
+ */
+bool rtems_rtl_archive_load (rtems_rtl_archives* archives, const char* name);
+
+/**
+ * Search for a symbol and load the first object file that has the symbol.
+ *
+ * @param archives The archives data to search.
+ * @param symbol The symbol name to search for.
+ * @param load If @true load the object file the symbol is found in
+ * else return the found not found status.
+ */
+rtems_rtl_archive_search rtems_rtl_archive_obj_load (rtems_rtl_archives* archives,
+ const char* symbol,
+ bool load);
+
+/**
+ * Find a module in an archive returning the offset in the archive and
+ * the size. If the name field is pointing to a string pointer and
+ * that poniter is NULL and the offset is valid the name is extracted
+ * from the archive and filled in. This is used when loading a file
+ * from the archive after a symbol is found. The file name is not know
+ * and could be extended which requires searching the extended string
+ * table in the archive.
+ *
+ * @param fd Open file handle for the archive.
+ * @param fsize Size of the archive.
+ * @paarm name Pointer to the name string.
+ * @param offset The offset of the file in the archive.
+ * @param size The size of the file in the archive.
+ * @param extended_names The offset in the archive for the extended names.
+ * @param error The error handler called on an error.
+ * @retval true The file was found in the archive.
+ * @retval false The file was not found.
+ */
+bool rtems_rtl_obj_archive_find_obj (int fd,
+ size_t fsize,
+ const char** name,
+ off_t* offset,
+ size_t* size,
+ off_t* extended_names,
+ rtems_rtl_archive_error error);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif
diff --git a/cpukit/include/rtems/rtl/rtl-obj.h b/cpukit/include/rtems/rtl/rtl-obj.h
index 976d6c293f..bc503887e2 100644
--- a/cpukit/include/rtems/rtl/rtl-obj.h
+++ b/cpukit/include/rtems/rtl/rtl-obj.h
@@ -92,9 +92,8 @@ typedef struct rtems_rtl_loader_table
#define RTEMS_RTL_OBJ_SECT_DATA (1 << 2) /**< Section holds program data. */
#define RTEMS_RTL_OBJ_SECT_BSS (1 << 3) /**< Section holds program bss. */
#define RTEMS_RTL_OBJ_SECT_EH (1 << 4) /**< Section holds exception data. */
-#define RTEMS_RTL_OBJ_SECT_REL (1 << 5) /**< Section holds relocation records. */
-#define RTEMS_RTL_OBJ_SECT_RELA (1 << 6) /**< Section holds relocation addend
- * records. */
+#define RTEMS_RTL_OBJ_SECT_REL (1 << 5) /**< Section holds relocation recs. */
+#define RTEMS_RTL_OBJ_SECT_RELA (1 << 6) /**< Section holds reloc addend recs. */
#define RTEMS_RTL_OBJ_SECT_SYM (1 << 7) /**< Section holds symbols. */
#define RTEMS_RTL_OBJ_SECT_STR (1 << 8) /**< Section holds strings. */
#define RTEMS_RTL_OBJ_SECT_ALLOC (1 << 9) /**< Section allocates runtime memory. */
@@ -168,6 +167,7 @@ typedef bool (*rtems_rtl_obj_depends_iterator) (rtems_rtl_obj* obj,
#define RTEMS_RTL_OBJ_BASE (1 << 2) /**< The base image. */
#define RTEMS_RTL_OBJ_RELOC_TAG (1 << 3) /**< Tag the object as visited when reloc
* parsing. */
+#define RTEMS_RTL_OBJ_DEP_VISITED (1 << 4) /**< Dependency loop detection. */
/**
* RTL Object. There is one for each object module loaded plus one for the base
@@ -337,6 +337,20 @@ static inline size_t rtems_rtl_obj_align (size_t offset,
return offset;
}
+/**
+ * Is the symbol in this object's files globa symbol table?
+ *
+ * @param obj The object file's descriptor to search.
+ * @param sym The symbol to check.
+ * @retval bool Returns @true if present else @false is returned.
+ */
+static inline bool rtems_rtl_obj_has_symbol (const rtems_rtl_obj* obj,
+ const rtems_rtl_obj_sym* sym)
+{
+ return (sym >= obj->global_table &&
+ sym < (obj->global_table + obj->global_syms));
+}
+
/**
* Allocate an object structure on the heap.
*
@@ -379,14 +393,6 @@ bool rtems_rtl_parse_name (const char* name,
const char** oname,
off_t* ooffset);
-/**
- * Check of the name matches the object file's object name.
- *
- * @param obj The object file's descriptor.
- * @param name The name to match.
- */
-bool rtems_rtl_match_name (rtems_rtl_obj* obj, const char* name);
-
/**
* Find an object file on disk that matches the name. The object descriptor is
* fill in with the various parts of a name. A name can have archive, object
@@ -496,6 +502,15 @@ void rtems_rtl_obj_erase_dependents (rtems_rtl_obj* obj);
*/
bool rtems_rtl_obj_add_dependent (rtems_rtl_obj* obj, rtems_rtl_obj* dependent);
+/**
+ * Remove dependencies. This decrements the dependent object file references.
+ *
+ * @param obj The object file's descriptor.
+ * @retval true The dependencies have been removed.
+ * @retval false There is no space in the table.
+ */
+bool rtems_rtl_obj_remove_dependencies (rtems_rtl_obj* obj);
+
/**
* Iterate over the module dependenices.
*
@@ -689,6 +704,13 @@ bool rtems_rtl_obj_load_sections (rtems_rtl_obj* obj,
rtems_rtl_obj_sect_handler handler,
void* data);
+/**
+ * Does the object have constructors to run?
+ *
+ * @return bool True if there are constructors to run.
+ */
+bool rtems_rtl_obj_ctors_to_run (rtems_rtl_obj* obj);
+
/**
* Invoke the constructors the object has. Constructors are a table of pointers
* to "void (*)(void);" where NULL pointers are skipped. The table's size is
@@ -699,6 +721,13 @@ bool rtems_rtl_obj_load_sections (rtems_rtl_obj* obj,
*/
void rtems_rtl_obj_run_ctors (rtems_rtl_obj* obj);
+/**
+ * Does the object have destructors to run?
+ *
+ * @return bool True if there are destructors to run.
+ */
+bool rtems_rtl_obj_dtors_to_run (rtems_rtl_obj* obj);
+
/**
* Invoke the destructors the object has. Destructors are a table of pointers
* to "void (*)(void);" where NULL pointers are skipped. The table's size is
@@ -709,6 +738,13 @@ void rtems_rtl_obj_run_ctors (rtems_rtl_obj* obj);
*/
void rtems_rtl_obj_run_dtors (rtems_rtl_obj* obj);
+/**
+ * Get the object file reference count.
+ *
+ * @retval int The object file's reference count.
+ */
+size_t rtems_rtl_obj_get_reference (rtems_rtl_obj* obj);
+
/**
* Increment the object file reference count.
*
@@ -723,6 +759,14 @@ void rtems_rtl_obj_inc_reference (rtems_rtl_obj* obj);
*/
void rtems_rtl_obj_dec_reference (rtems_rtl_obj* obj);
+/**
+ * Is the object file orphaned? An orphaned object file is not locked, has no
+ * users and it not being referenced.
+ *
+ * @param obj The object file's descriptor.
+ */
+bool rtems_rtl_obj_orphaned (rtems_rtl_obj* obj);
+
/**
* Load the object file, reading all sections into memory, symbols and
* performing any relocation fixups.
diff --git a/cpukit/libdl/rtl-shell.h b/cpukit/include/rtems/rtl/rtl-shell.h
similarity index 97%
rename from cpukit/libdl/rtl-shell.h
rename to cpukit/include/rtems/rtl/rtl-shell.h
index 9df5ee203f..b0a6e8d8d4 100644
--- a/cpukit/libdl/rtl-shell.h
+++ b/cpukit/include/rtems/rtl/rtl-shell.h
@@ -20,8 +20,6 @@
extern "C" {
#endif /* __cplusplus */
-#include <stdint.h>
-
/**
* The RTL single shell command contains sub-commands.
*
diff --git a/cpukit/include/rtems/rtl/rtl-trace.h b/cpukit/include/rtems/rtl/rtl-trace.h
index 4b93c8c91c..84e81d5344 100644
--- a/cpukit/include/rtems/rtl/rtl-trace.h
+++ b/cpukit/include/rtems/rtl/rtl-trace.h
@@ -48,6 +48,8 @@ typedef uint32_t rtems_rtl_trace_mask;
#define RTEMS_RTL_TRACE_ALLOCATOR (1UL << 9)
#define RTEMS_RTL_TRACE_UNRESOLVED (1UL << 10)
#define RTEMS_RTL_TRACE_CACHE (1UL << 11)
+#define RTEMS_RTL_TRACE_ARCHIVES (1UL << 12)
+#define RTEMS_RTL_TRACE_DEPENDENCY (1UL << 13)
#define RTEMS_RTL_TRACE_ALL (0xffffffffUL & ~(RTEMS_RTL_TRACE_CACHE))
/**
diff --git a/cpukit/include/rtems/rtl/rtl-unresolved.h b/cpukit/include/rtems/rtl/rtl-unresolved.h
index a69b51ac7f..a425384370 100644
--- a/cpukit/include/rtems/rtl/rtl-unresolved.h
+++ b/cpukit/include/rtems/rtl/rtl-unresolved.h
@@ -71,21 +71,30 @@ typedef uint32_t rtems_rtl_word;
typedef enum rtems_rtl_unresolved_rtype
{
rtems_rtl_unresolved_empty = 0, /**< The records is empty. Must always be 0 */
- rtems_rtl_unresolved_name = 1, /**< The record is a name. */
+ rtems_rtl_unresolved_symbol = 1, /**< The record is a symbol. */
rtems_rtl_unresolved_reloc = 2 /**< The record is a relocation record. */
} rtems_rtl_unresolved_rtype;
/**
- * Unresolved externals symbol names. The names are reference counted and
- * separate from the relocation records because a number of records could
- * reference the same symbol name.
+ * Unresolved external symbol flags.
*/
-typedef struct rtems_rtl_unresolv_name
+#define RTEMS_RTL_UNRESOLV_SYM_SEARCH_ARCHIVE (1 << 0) /**< Search the archive. */
+
+/**
+ * Unresolved externals symbols. The symbols are reference counted and separate
+ * from the relocation records because a number of records could reference the
+ * same symbol.
+ *
+ * The name is extended in the allocator of the record in the unresolved data
+ * block. The 10 is a minimum that is added to by joining more than one record.
+ */
+typedef struct rtems_rtl_unresolv_symbol
{
uint16_t refs; /**< The number of references to this name. */
+ uint16_t flags; /**< Flags to manage the symbol. */
uint16_t length; /**< The length of this name. */
- const char name[12]; /**< The symbol name. */
-} rtems_rtl_unresolv_name;
+ const char name[10]; /**< The symbol name. */
+} rtems_rtl_unresolv_symbol;
/**
* Unresolved externals symbols require the relocation records to be held
@@ -108,8 +117,8 @@ typedef struct rtems_rtl_unresolv_rec
rtems_rtl_unresolved_rtype type;
union
{
- rtems_rtl_unresolv_name name; /**< The name, or */
- rtems_rtl_unresolv_reloc reloc; /**< the relocation record. */
+ rtems_rtl_unresolv_symbol name; /**< The symnbol, or */
+ rtems_rtl_unresolv_reloc reloc; /**< the relocation record. */
} rec;
} rtems_rtl_unresolv_rec;
@@ -166,8 +175,8 @@ void rtems_rtl_unresolved_table_close (rtems_rtl_unresolved* unresolved);
/**
* Iterate over the table of unresolved entries.
*/
-bool rtems_rtl_unresolved_interate (rtems_rtl_unresolved_iterator iterator,
- void* data);
+bool rtems_rtl_unresolved_iterate (rtems_rtl_unresolved_iterator iterator,
+ void* data);
/**
* Add a relocation to the list of unresolved relocations.
@@ -205,6 +214,12 @@ bool rtems_rtl_unresolved_remove (rtems_rtl_obj* obj,
const uint16_t sect,
const rtems_rtl_word* rel);
+/**
+ * Set all symbols to be archive searchable. This is done when the available
+ * archives have been refreshed and there are new archives to search for.
+ */
+void rtems_rtl_unresolved_set_archive_search (void);
+
/**
* Dump the RTL unresolved data.
*/
diff --git a/cpukit/include/rtems/rtl/rtl.h b/cpukit/include/rtems/rtl/rtl.h
index 41cb010ef4..debf17cd44 100644
--- a/cpukit/include/rtems/rtl/rtl.h
+++ b/cpukit/include/rtems/rtl/rtl.h
@@ -24,6 +24,7 @@
#include <rtems/thread.h>
#include <rtems/rtl/rtl-allocator.h>
+#include <rtems/rtl/rtl-archive.h>
#include <rtems/rtl/rtl-fwd.h>
#include <rtems/rtl/rtl-obj.h>
#include <rtems/rtl/rtl-obj-cache.h>
@@ -105,8 +106,10 @@ struct rtems_rtl_data
rtems_recursive_mutex lock; /**< The RTL lock */
rtems_rtl_alloc_data allocator; /**< The allocator data. */
rtems_chain_control objects; /**< List if loaded object files. */
+ rtems_chain_control pending; /**< Listof object files needing work. */
const char* paths; /**< Search paths for archives. */
rtems_rtl_symbols globals; /**< Global symbol table. */
+ rtems_rtl_archives archives; /**< Archive search and loader. */
rtems_rtl_unresolved unresolved; /**< Unresolved symbols. */
rtems_rtl_obj* base; /**< Base object file. */
rtems_rtl_obj_cache symbols; /**< Symbols object file cache. */
@@ -126,8 +129,8 @@ struct rtems_rtl_data
rtems_rtl_data* rtems_rtl_data_unprotected (void);
/**
- * Get the RTL global symbol table with out locking. This call assmes the RTL
- * is locked.
+ * Get the RTL global symbol table with out locking. This call assumes
+ * the RTL is locked.
*
* @return rtems_rtl_symbols* The RTL global symbols after being locked.
* @retval NULL The RTL data is not initialised.
@@ -135,7 +138,24 @@ rtems_rtl_data* rtems_rtl_data_unprotected (void);
rtems_rtl_symbols* rtems_rtl_global_symbols (void);
/**
- * Get the RTL resolved table with out locking. This call assmes the RTL
+ * Get the RTL objects table with out locking. This call assumes the RTL
+ * is locked.
+ *
+ * @return rtems_chain_control* The RTL objects chain.
+ * @retval NULL The RTL data is not initialised.
+ */
+rtems_chain_control* rtems_rtl_objects_unprotected (void);
+
+/**
+ * Get the RTL pending with out locking. This call assumes the RTL is locked.
+ *
+ * @return rtems_chain_control* The RTL pending list control.
+ * @retval NULL The RTL data is not initialised.
+ */
+rtems_chain_control* rtems_rtl_pending_unprotected (void);
+
+/**
+ * Get the RTL unresolved table with out locking. This call assumes the RTL
* is locked.
*
* @return rtems_rtl_unresolv* The RTL unresolved symbols and reloc records.
@@ -143,6 +163,14 @@ rtems_rtl_symbols* rtems_rtl_global_symbols (void);
*/
rtems_rtl_unresolved* rtems_rtl_unresolved_unprotected (void);
+/**
+ * Get the RTL archives with out locking. This call assumes the RTL is locked.
+ *
+ * @return rtems_rtl_archives* The RTL acrhives.
+ * @retval NULL The RTL data is not initialised.
+ */
+rtems_rtl_archives* rtems_rtl_archives_unprotected (void);
+
/**
* Get the RTL symbols, strings, or relocations object file caches. This call
* assmes the RTL is locked.
@@ -276,12 +304,26 @@ rtems_rtl_obj* rtems_rtl_load_object (const char* name, int mode);
bool rtems_rtl_unload_object (rtems_rtl_obj* obj);
/**
- * Run any constructor functions the object file may contain. This call
- * assumes the linker is unlocked.
+ * Load an object file. This is the user accessable interface to unloading an
+ * object file. See @rtems_rtl_load_object.
+ *
+ * @param name The name of the object file.
+ * @param mode The mode of the load as defined by the dlopen call.
+ * @return rtl_obj* The object file descriptor. NULL is returned if the load fails.
+ */
+rtems_rtl_obj* rtems_rtl_load (const char* name, int mode);
+
+/**
+ * Unload an object file. This is the user accessable interface to unloading an
+ * object file. See @rtems_rtl_unload_object.
+ *
+ * Assumes the RTL has been locked.
*
- * @param obj The object file.
+ * @param obj The object file descriptor.
+ * @retval true The object file has been unloaded.
+ * @retval false The object file could not be unloaded.
*/
-void rtems_rtl_run_ctors (rtems_rtl_obj* obj);
+bool rtems_rtl_unload (rtems_rtl_obj* obj);
/**
* Get the last error message clearing it. This operation locks the run time
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 <chrisj at 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 at 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 at 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 at 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 "!<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)
-
- 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 <string.h>
#include <rtems/rtl/rtl.h>
-#include "rtl-chain-iterator.h"
-#include "rtl-shell.h"
+#include <rtems/rtl/rtl-shell.h>
#include <rtems/rtl/rtl-trace.h>
+#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-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 <rtems/rtl/rtl-indirect-ptr.h>
-
#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 <rtems/rtl/rtl.h>
#include <rtems/rtl/rtl-allocator.h>
+#include <rtems/rtl/rtl-trace.h>
+#include "rtl-chain-iterator.h"
#include "rtl-error.h"
#include "rtl-string.h"
-#include <rtems/rtl/rtl-trace.h>
/**
* 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)
{
--
2.19.1
More information about the devel
mailing list