forked from Imagelibrary/rtems
446 lines
9.3 KiB
C
446 lines
9.3 KiB
C
/* SPDX-License-Identifier: BSD-2-Clause */
|
|
|
|
/**
|
|
* @file
|
|
*
|
|
* @ingroup rtems_rap
|
|
*
|
|
* @brief RTEMS Application Loader
|
|
*
|
|
* This is the RAP implementation.
|
|
*/
|
|
|
|
/*
|
|
* COPYRIGHT (c) 2012, 2018 Chris Johns <chrisj@rtems.org>
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <pthread.h>
|
|
#include <stdarg.h>
|
|
#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
|
|
{
|
|
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;
|
|
}
|