/* * COPYRIGHT (c) 2012, 2018 Chris Johns * * The license and distribution terms for this file may be * found in the file LICENSE in this distribution or at * http://www.rtems.org/license/LICENSE. */ /** * @file * * @ingroup rtems_rap * * @brief RTEMS Application Loader * * This is the RAP implementation. */ #if HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include #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 { pthread_once_t once; rtems_mutex 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; /** * The RAP file data. This structure is allocated on the heap when a file is * loaded. */ typedef struct rtems_rap_app { 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; /** * 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 rap_ = { .once = PTHREAD_ONCE_INIT }; /** * Verbose level for the RAP loader. */ static bool rap_verbose; /** * RAP entry call signature. */ typedef int (*rtems_rap_entry)(int argc, const char* argv[]); /** * Forward decl. */ static void rtems_rap_unlock (void); static void rtems_rap_data_init (void) { /* * Create the RAP lock. */ rtems_mutex_init (&rap_.lock, "RAP"); /* * Initialise the objects list and create any required services. */ rtems_chain_initialize_empty (&rap_.apps); } static rtems_rap_data* rtems_rap_lock (void) { pthread_once (&rap_.once, rtems_rap_data_init); rtems_mutex_lock (&rap_.lock); return &rap_; } static void rtems_rap_unlock (void) { rtems_mutex_unlock (&rap_.lock); } static rtems_rap_app* rtems_rap_check_handle (void* handle) { rtems_rap_app* app; rtems_chain_node* node; app = handle; node = rtems_chain_first (&rap_.apps); while (!rtems_chain_is_tail (&rap_.apps, node)) { rtems_rap_app* check = (rtems_rap_app*) node; if (check == app) return app; node = rtems_chain_next (node); } return NULL; } static rtems_rap_app* rtems_rap_app_alloc (void) { rtems_rap_app* app = malloc (sizeof (rtems_rap_app)); memset (app, 0, sizeof (rtems_rap_app)); rtems_chain_append (&rap_.apps, &app->node); return app; } static void rtems_rap_app_free (rtems_rap_app* 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* 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* 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* 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* app; rtems_rap_entry init; rtems_rap_entry 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* app; rtems_rap_entry 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* 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* app = (rtems_rap_app*) 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 iterator) { rtems_rap_data* 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* app = (rtems_rap_app*) 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* app = rtems_rap_check_handle (handle); if (app) return app->name; return NULL; } void* rtems_rap_dl_handle (void* handle) { rtems_rap_app* 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* rap = rtems_rap_lock (); int last_errno = rap->last_errno; strlcpy (message, rap->last_error, max_message); rtems_rap_unlock (); return last_errno; }