summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris Johns <chrisj@rtems.org>2013-01-15 09:50:29 +1100
committerChris Johns <chrisj@rtems.org>2013-01-15 09:50:29 +1100
commit298b8932c3ced96746e30b13f9d067a27e62fe94 (patch)
tree358f8840a8e417b81f9fd685a9995d52e8175ab7
parentfd1a5952e2cdc392ee966199ca09468fc7ee11cc (diff)
Add support for RAP module loading/unload plus a shell command.
Add a RAP module loading and unloading. This support wraps the dlopen API plus manages calling the entry point. The modules are also held in a list so users can find them, iterator over them and unload them. This API provides a system level way to manage application modules. The find file user needs to manage errors. Load the rap module by default and list the loaded modules. Add the RAP shell command to the shell in main.
-rw-r--r--main.c3
-rw-r--r--rap-shell.c106
-rw-r--r--rap-shell.h14
-rw-r--r--rap.c473
-rw-r--r--rap.h115
-rw-r--r--rtl-find-file.c7
-rw-r--r--rtl-obj.c4
-rw-r--r--shell-init3
-rw-r--r--wscript2
9 files changed, 720 insertions, 7 deletions
diff --git a/main.c b/main.c
index 9586502..a8f8640 100644
--- a/main.c
+++ b/main.c
@@ -47,6 +47,7 @@
int remote_debug;
#endif
+#include <rap-shell.h>
#include <rtl-shell.h>
#include <rtl-trace.h>
@@ -429,6 +430,8 @@ main (int argc, char* argv[])
"fderase driver", shell_flash_erase);
rtems_shell_add_cmd ("rtl", "misc",
"Runtime Linker", rtems_rtl_shell_command);
+ rtems_shell_add_cmd ("rap", "misc",
+ "Application loader", shell_rap);
rtems_shell_add_cmd ("dlo", "misc",
"load object file", shell_dlopen);
rtems_shell_add_cmd ("dlc", "misc",
diff --git a/rap-shell.c b/rap-shell.c
new file mode 100644
index 0000000..3d5cf2c
--- /dev/null
+++ b/rap-shell.c
@@ -0,0 +1,106 @@
+/*
+ * COPYRIGHT (c) 2013 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_rtld
+ *
+ * @brief RTEMS Application Loader.
+ *
+ * Shell command wrappers for the RTEMS Application loader.
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <rap.h>
+#include <rap-shell.h>
+
+static void
+shell_rap_command_help (void)
+{
+ printf ("usage: rap [cmd] [arg]\n" \
+ "Commands and options:\n" \
+ "ls: List the loaded applications (also list)\n" \
+ "ld: Load an application (also load)\n" \
+ "un: Unload an application (also unload)\n");
+}
+
+static void
+shell_rap_get_error (const char* what)
+{
+ char message[64];
+ int error;
+ error = rtems_rap_get_error (message, sizeof (message));
+ printf ("error: %s: (%d) %s\n", what, error, message);
+}
+
+static bool
+shell_rap_list_handler (void* handle)
+{
+ printf (" %-10p %-10p %-s\n",
+ handle, rtems_rap_dl_handle (handle), rtems_rap_name (handle));
+ return true;
+}
+
+static int
+shell_rap_list (int argc, char* argv[])
+{
+ printf (" App DL Handle Name\n");
+ return rtems_rap_iterate (shell_rap_list_handler) ? 0 : 1;
+}
+
+static int
+shell_rap_load (int argc, char* argv[])
+{
+ int r = 0;
+ if (argc == 0)
+ {
+ printf ("error: no application name\n");
+ return 0;
+ }
+ if (rtems_rap_load (argv[0], 0, argc - 1, (const char**) (argv + 1)))
+ printf ("%s loaded\n", argv[0]);
+ else
+ {
+ r = 1;
+ shell_rap_get_error ("loading");
+ }
+ return r;
+}
+
+int
+shell_rap (int argc, char* argv[])
+{
+ if (argc == 1)
+ {
+ shell_rap_command_help ();
+ return 0;
+ }
+
+ if ((strcmp (argv[1], "ls") == 0) ||
+ (strcmp (argv[1], "list") == 0))
+ {
+ return shell_rap_list (argc - 2, argv + 2);
+ }
+ else if ((strcmp (argv[1], "ld") == 0) ||
+ (strcmp (argv[1], "load") == 0))
+ {
+ return shell_rap_load (argc - 2, argv + 2);
+ }
+
+ printf ("error: invalid command: %s\n", argv[1]);
+ return 0;
+}
+
diff --git a/rap-shell.h b/rap-shell.h
new file mode 100644
index 0000000..c32529d
--- /dev/null
+++ b/rap-shell.h
@@ -0,0 +1,14 @@
+/*
+ * COPYRIGHT (c) 2013 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.
+ */
+
+#if !defined(_RAP_SHELL_H_)
+#define _RAP_SHELL_H_
+
+int shell_rap (int argc, char* argv[]);
+
+#endif
diff --git a/rap.c b/rap.c
new file mode 100644
index 0000000..850d84a
--- /dev/null
+++ b/rap.c
@@ -0,0 +1,473 @@
+/*
+ * 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 <rap.h>
+#include <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)
+
+/**
+ * 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;
+ uint32_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, "rtems");
+ if (!init)
+ {
+ rtems_rap_get_rtl_error ();
+ rtems_rap_app_free (app);
+ rtems_rap_unlock ();
+ return false;
+ }
+
+ fini = dlsym (app->handle, "rtems");
+ 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;
+ }
+ }
+
+ 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, "rtems");
+ 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;
+}
diff --git a/rap.h b/rap.h
new file mode 100644
index 0000000..e53699f
--- /dev/null
+++ b/rap.h
@@ -0,0 +1,115 @@
+/*
+ * COPYRIGHT (c) 2013 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 RTEMS Application loader for files in the RAP format.
+ */
+
+#if !defined (_RAP_H_)
+#define _RAP_H_
+
+#include <rtems.h>
+#include <rtems/chain.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/**
+ * @defgroup rtems_rap RTEMS Application Loader
+ *
+ * The module implements an application loader for files in the RAP format. The
+ * RAP format is:
+ *
+ * <header>
+ * <compressed container>
+ *
+ * The compressed container is a stream of ELF relocatable object files.
+ *
+ * TBD.
+ */
+
+/**
+ * The module iterator handle.
+ */
+typedef bool (*rtems_rap_iterator_t) (void* handle);
+
+/**
+ * Load an application.
+ *
+ * @param name The name of the application file.
+ * @return bool True if the module loads else an error.
+ */
+bool rtems_rap_load (const char* name, int mode, int argc, const char* argv[]);
+
+/**
+ * Unload an application.
+ *
+ * @param obj The application descriptor.
+ * @retval true The application file has been unloaded.
+ * @retval false The application could not be unloaded.
+ */
+bool rtems_rap_unload (const char* name);
+
+/**
+ * Find the application handle given a file name.
+ *
+ * @param name The name of the application file. It can be absolute or
+ * relative. Relative names can the basename with an extension.
+ * @retval NULL No application file with that name found.
+ * @return void* The application descriptor.
+ */
+void* rtems_rap_find (const char* name);
+
+/**
+ * Run an iterator over the modules calling the iterator function.
+ *
+ * @param iterator The iterator function.
+ * @retval true The iterator function returned did not return false.
+ * @retval false The iterator function returned false..
+ */
+bool rtems_rap_iterate (rtems_rap_iterator_t iterator);
+
+/**
+ * Return the name of the module given a handle.
+ *
+ * @param handle The module handle.
+ * @return const char* The name of the module if the handle is valid else it
+ * is NULL.
+ */
+const char* rtems_rap_name (void* handle);
+
+/**
+ * Return the DL handle used to load the module given the RAP handle.
+ *
+ * @param handle The module handle.
+ * @return void* The DL handle returned by the dlopen call.
+ */
+void* rtems_rap_dl_handle (void* handle);
+
+/**
+ * Get the last error message clearing it. This call is not thread safe is
+ * multiple threads are loading object files at the same time. This call
+ * follows the model provided by the dlopen family of calls.
+ *
+ * @param message Pointer to a buffer to copy the message into.
+ * @param max_message The maximum message that can be copied.
+ * @return int The last error number.
+ */
+int rtems_rap_get_error (char* message, size_t max_message);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif
diff --git a/rtl-find-file.c b/rtl-find-file.c
index 87a7501..15d749e 100644
--- a/rtl-find-file.c
+++ b/rtl-find-file.c
@@ -47,12 +47,12 @@ rtems_rtl_find_file (const char* name,
*file_name = NULL;
*size = 0;
- if (rtems_filesystem_is_delimiter (name[0]))
+ if (rtems_filesystem_is_delimiter (name[0]) || (name[0] == '.'))
{
if (stat (name, &sb) == 0)
*file_name = rtems_rtl_strdup (name);
}
- else
+ else if (paths)
{
const char* start;
const char* end;
@@ -102,10 +102,7 @@ rtems_rtl_find_file (const char* name,
}
if (!*file_name)
- {
- rtems_rtl_set_error (ENOMEM, "file not found");
return false;
- }
*size = sb.st_size;
diff --git a/rtl-obj.c b/rtl-obj.c
index df7ec8b..ddf4a0b 100644
--- a/rtl-obj.c
+++ b/rtl-obj.c
@@ -367,7 +367,11 @@ rtems_rtl_obj_find_file (rtems_rtl_obj_t* obj, const char* name)
rtl = rtems_rtl_lock ();
if (!rtems_rtl_find_file (pname, rtl->paths, &obj->fname, &obj->fsize))
+ {
+ rtems_rtl_set_error (ENOENT, "file not found");
+ rtems_rtl_unlock ();
return false;
+ }
rtems_rtl_unlock ();
diff --git a/shell-init b/shell-init
index 97033d9..0155377 100644
--- a/shell-init
+++ b/shell-init
@@ -7,5 +7,4 @@
#mkdir c
#mount -t dosfs /dev/hda1 /c
#dlo c/bsdport.rap
-
-dlo bsdport.rap
+#dlo bsdport.rap
diff --git a/wscript b/wscript
index 2faba1b..9f9417a 100644
--- a/wscript
+++ b/wscript
@@ -85,6 +85,8 @@ def build(bld):
source = ['dlfcn.c',
'dlfcn-shell.c',
'fastlz.c',
+ 'rap.c',
+ 'rap-shell.c',
'rtl.c',
'rtl-alloc-heap.c',
'rtl-allocator.c',