diff options
author | Chris Johns <chrisj@rtems.org> | 2014-10-26 18:09:41 -0700 |
---|---|---|
committer | Chris Johns <chrisj@rtems.org> | 2014-10-31 11:04:15 +1100 |
commit | ae5fe7e6bca2874c5f1ef077204bb63124fb3db3 (patch) | |
tree | 90a6e9e7b414ed3713011267b1fee404b5f6093f /cpukit/libdl/rap.c | |
parent | Added missing stm32f4xxxx_adc.h (diff) | |
download | rtems-ae5fe7e6bca2874c5f1ef077204bb63124fb3db3.tar.bz2 |
cpukit: Add libdl with the Runtime Loader (RTL) code.
This is a merge of the RTL project.
Diffstat (limited to 'cpukit/libdl/rap.c')
-rw-r--r-- | cpukit/libdl/rap.c | 484 |
1 files changed, 484 insertions, 0 deletions
diff --git a/cpukit/libdl/rap.c b/cpukit/libdl/rap.c new file mode 100644 index 0000000000..8dec41e416 --- /dev/null +++ b/cpukit/libdl/rap.c @@ -0,0 +1,484 @@ +/* + * COPYRIGHT (c) 2012 Chris Johns <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + */ +/** + * @file + * + * @ingroup rtems_rap + * + * @brief RTEMS Application Loader + * + * This is the RAP implementation. + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include <rtems/libio_.h> + +#include <dlfcn.h> +#include <rtems/rtl/rap.h> +#include <rtems/rtl/rtl.h> + +#include "rtl-find-file.h" + +/** + * The global RAP data. This structure is allocated on the heap when the first + * call to location an application and is never released. + */ +typedef struct rtems_rap_data_s +{ + rtems_id lock; /**< The RAP lock id */ + rtems_chain_control apps; /**< List if loaded application. */ + int last_errno; /**< Last error number. */ + char last_error[64]; /**< Last error string. */ +} rtems_rap_data_t; + +/** + * The RAP file data. This structure is allocated on the heap when a file is + * loaded. + */ +typedef struct rtems_rap_app_s +{ + rtems_chain_node node; /**< The node's link in the chain. */ + const char* name; /**< The file name */ + void* handle; /**< The dlopen handle. */ +} rtems_rap_app_t; + +/** + * Semaphore configuration to create a mutex. + */ +#define RTEMS_MUTEX_ATTRIBS \ + (RTEMS_PRIORITY | RTEMS_BINARY_SEMAPHORE | \ + RTEMS_INHERIT_PRIORITY | RTEMS_NO_PRIORITY_CEILING | RTEMS_LOCAL) + +/** + * RTL entry. + */ +#if (RTL_GLUE(__USER_LABEL_PREFIX__, 1) == RTL_GLUE(_, 1)) + #define RTL_ENTRY_POINT "_rtems" +#else + #define RTL_ENTRY_POINT "rtems" +#endif + +/** + * Static RAP data is returned to the user when the loader is locked. + */ +static rtems_rap_data_t rap_; + +/** + * Verbose level for the RAP loader. + */ +static bool rap_verbose; + +/** + * RAP entry call signature. + */ +typedef int (*rtems_rap_entry_t)(int argc, const char* argv[]); + +/** + * Forward decl. + */ +static bool rtems_rap_unlock (void); + +static bool +rtems_rap_data_init (void) +{ + /* + * Lock the RAP. We only create a lock if a call is made. First we test if a + * lock is present. If one is present we lock it. If not the libio lock is + * locked and we then test the lock again. If not present we create the lock + * then release libio lock. + */ + if (!rap_.lock) + { + rtems_libio_lock (); + + if (!rap_.lock) + { + rtems_status_code sc; + rtems_id lock; + + /* + * Create the RAP lock. + */ + sc = rtems_semaphore_create (rtems_build_name ('R', 'A', 'P', '_'), + 1, RTEMS_MUTEX_ATTRIBS, + RTEMS_NO_PRIORITY, &lock); + if (sc != RTEMS_SUCCESSFUL) + return false; + + sc = rtems_semaphore_obtain (lock, RTEMS_WAIT, RTEMS_NO_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + { + rtems_semaphore_delete (lock); + return false; + } + + rap_.lock = lock; + + /* + * Initialise the objects list and create any required services. + */ + rtems_chain_initialize_empty (&rap_.apps); + } + + rtems_libio_unlock (); + + rtems_rap_unlock (); + } + return true; +} + +static rtems_rap_data_t* +rtems_rap_lock (void) +{ + rtems_status_code sc; + + if (!rtems_rap_data_init ()) + return NULL; + + sc = rtems_semaphore_obtain (rap_.lock, + RTEMS_WAIT, RTEMS_NO_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + { + errno = EINVAL; + return NULL; + } + + return &rap_; +} + +static bool +rtems_rap_unlock (void) +{ + /* + * Not sure any error should be returned or an assert. + */ + rtems_status_code sc; + sc = rtems_semaphore_release (rap_.lock); + if ((sc != RTEMS_SUCCESSFUL) && (errno == 0)) + { + errno = EINVAL; + return false; + } + return true; +} + +static rtems_rap_app_t* +rtems_rap_check_handle (void* handle) +{ + rtems_rap_app_t* app; + rtems_chain_node* node; + + app = handle; + node = rtems_chain_first (&rap_.apps); + + while (!rtems_chain_is_tail (&rap_.apps, node)) + { + rtems_rap_app_t* check = (rtems_rap_app_t*) node; + if (check == app) + return app; + node = rtems_chain_next (node); + } + + return NULL; +} + +static rtems_rap_app_t* +rtems_rap_app_alloc (void) +{ + rtems_rap_app_t* app = malloc (sizeof (rtems_rap_app_t)); + memset (app, 0, sizeof (rtems_rap_app_t)); + rtems_chain_append (&rap_.apps, &app->node); + return app; +} + +static void +rtems_rap_app_free (rtems_rap_app_t* app) +{ + if (app->handle) + { + dlclose (app->handle); + app->handle = NULL; + } + + if (!rtems_chain_is_node_off_chain (&app->node)) + rtems_chain_extract (&app->node); +} + +static bool +rtems_rap_match_name (rtems_rap_app_t* app, const char* name) +{ + const char* a; + + /* + * Assume the app name is absolute, ie points to the file on disk. This means + * there is at least one delimiter in the name. + */ + + if (strncmp (app->name, name, strlen (name)) == 0) + return true; + + a = app->name + strlen (app->name) - 1; + + while (a >= app->name) + { + if (rtems_filesystem_is_delimiter (*a)) + { + const char* n = name; + + ++a; + + while (*a && *n) + { + if (*a == '.') + { + if (*n == '\0') + return true; + } + + ++a; + ++n; + } + + return false; + } + + --a; + } + + return false; +} + +static void +rtems_rap_get_rtl_error (void) +{ + rap_.last_errno = + rtems_rtl_get_error (rap_.last_error, sizeof (rap_.last_error)); +} + +static void +rtems_rap_set_error (int error, const char* format, ...) +{ + rtems_rap_data_t* rap = rtems_rap_lock (); + va_list ap; + va_start (ap, format); + rap->last_errno = error; + vsnprintf (rap->last_error, sizeof (rap->last_error), format, ap); + rtems_rap_unlock (); + va_end (ap); +} + +bool +rtems_rap_load (const char* name, int mode, int argc, const char* argv[]) +{ + rtems_rap_data_t* rap = rtems_rap_lock (); + + if (!rap) + return false; + + if (rap_verbose) + printf ("rap: loading '%s'\n", name); + + /* + * See if the app has already been loaded. + */ + if (!rtems_rap_find (name)) + { + rtems_rap_app_t* app; + rtems_rap_entry_t init; + rtems_rap_entry_t fini; + size_t size = 0; + int r; + + /* + * Allocate a new application descriptor and attempt to load it. + */ + app = rtems_rap_app_alloc (); + if (app == NULL) + { + rtems_rap_set_error (ENOMEM, "no memory for application"); + rtems_rap_unlock (); + return false; + } + + /* + * Find the file in the file system using the search path. + */ + if (!rtems_rtl_find_file (name, getenv ("PATH"), &app->name, &size)) + { + rtems_rap_set_error (ENOENT, "file not found"); + rtems_rap_app_free (app); + rtems_rap_unlock (); + return false; + } + + app->handle = dlopen (app->name, RTLD_NOW | mode); + if (!app->handle) + { + rtems_rap_get_rtl_error (); + rtems_rap_app_free (app); + rtems_rap_unlock (); + return false; + } + + init = dlsym (app->handle, RTL_ENTRY_POINT); + if (!init) + { + rtems_rap_get_rtl_error (); + rtems_rap_app_free (app); + rtems_rap_unlock (); + return false; + } + + fini = dlsym (app->handle, RTL_ENTRY_POINT); + if (!fini) + { + rtems_rap_get_rtl_error (); + rtems_rap_app_free (app); + rtems_rap_unlock (); + return false; + } + + r = init (argc, argv); + if (r != 0) + { + rtems_rap_set_error (r, "init call failure"); + rtems_rap_app_free (app); + rtems_rap_unlock (); + return false; + } + } + + rtems_rap_unlock (); + + return true; +} + +bool +rtems_rap_unload (const char* name) +{ + rtems_rap_app_t* app; + rtems_rap_entry_t fini; + int r; + + rtems_rap_lock (); + + app = rtems_rap_find (name); + + if (rap_verbose) + printf ("rap: unloading '%s'\n", name); + + if (!app) + { + rtems_rap_set_error (ENOENT, "invalid handle"); + rtems_rap_unlock (); + return false; + } + + fini = dlsym (app->handle, RTL_ENTRY_POINT); + if (!fini) + { + rtems_rap_get_rtl_error (); + rtems_rap_unlock (); + return false; + } + + r = fini (0, NULL); + if (r != 0) + { + rtems_rap_set_error (r, "fini failure"); + rtems_rap_unlock (); + return false; + } + + rtems_rap_app_free (app); + rtems_rap_unlock (); + + return true; +} + +void* +rtems_rap_find (const char* name) +{ + rtems_rap_data_t* rap = rtems_rap_lock (); + rtems_chain_node* node; + + node = rtems_chain_first (&rap->apps); + + while (!rtems_chain_is_tail (&rap->apps, node)) + { + rtems_rap_app_t* app = (rtems_rap_app_t*) node; + if (rtems_rap_match_name (app, name)) + { + rtems_rap_unlock (); + return app; + } + node = rtems_chain_next (node); + } + + rtems_rap_unlock (); + + return NULL; +} + +bool +rtems_rap_iterate (rtems_rap_iterator_t iterator) +{ + rtems_rap_data_t* rap = rtems_rap_lock (); + rtems_chain_node* node; + bool result = true; + + node = rtems_chain_first (&rap->apps); + + while (!rtems_chain_is_tail (&rap->apps, node)) + { + rtems_rap_app_t* app = (rtems_rap_app_t*) node; + result = iterator (app); + if (!result) + break; + node = rtems_chain_next (node); + } + + rtems_rap_unlock (); + + return result; +} + +const char* +rtems_rap_name (void* handle) +{ + rtems_rap_app_t* app = rtems_rap_check_handle (handle); + if (app) + return app->name; + return NULL; +} + +void* +rtems_rap_dl_handle (void* handle) +{ + rtems_rap_app_t* app = rtems_rap_check_handle (handle); + if (app) + return app->handle; + return NULL; +} + +int +rtems_rap_get_error (char* message, size_t max_message) +{ + rtems_rap_data_t* rap = rtems_rap_lock (); + int last_errno = rap->last_errno; + strncpy (message, rap->last_error, sizeof (rap->last_error)); + rtems_rap_unlock (); + return last_errno; +} |