[ae5fe7e6] | 1 | /* |
---|
[f59d435d] | 2 | * COPYRIGHT (c) 2012, 2018 Chris Johns <chrisj@rtems.org> |
---|
[ae5fe7e6] | 3 | * |
---|
| 4 | * The license and distribution terms for this file may be |
---|
| 5 | * found in the file LICENSE in this distribution or at |
---|
[d4edbdbc] | 6 | * http://www.rtems.org/license/LICENSE. |
---|
[ae5fe7e6] | 7 | */ |
---|
| 8 | /** |
---|
| 9 | * @file |
---|
| 10 | * |
---|
| 11 | * @ingroup rtems_rap |
---|
| 12 | * |
---|
| 13 | * @brief RTEMS Application Loader |
---|
| 14 | * |
---|
| 15 | * This is the RAP implementation. |
---|
| 16 | */ |
---|
| 17 | |
---|
| 18 | #if HAVE_CONFIG_H |
---|
| 19 | #include "config.h" |
---|
| 20 | #endif |
---|
| 21 | |
---|
[87b7117f] | 22 | #include <pthread.h> |
---|
[506bfc8] | 23 | #include <stdarg.h> |
---|
[ae5fe7e6] | 24 | #include <stdlib.h> |
---|
| 25 | #include <stdio.h> |
---|
| 26 | #include <string.h> |
---|
| 27 | |
---|
| 28 | #include <rtems/libio_.h> |
---|
| 29 | |
---|
| 30 | #include <dlfcn.h> |
---|
| 31 | #include <rtems/rtl/rap.h> |
---|
| 32 | #include <rtems/rtl/rtl.h> |
---|
| 33 | |
---|
| 34 | #include "rtl-find-file.h" |
---|
| 35 | |
---|
| 36 | /** |
---|
| 37 | * The global RAP data. This structure is allocated on the heap when the first |
---|
| 38 | * call to location an application and is never released. |
---|
| 39 | */ |
---|
| 40 | typedef struct rtems_rap_data_s |
---|
| 41 | { |
---|
[87b7117f] | 42 | pthread_once_t once; |
---|
| 43 | rtems_mutex lock; /**< The RAP lock id */ |
---|
[ae5fe7e6] | 44 | rtems_chain_control apps; /**< List if loaded application. */ |
---|
| 45 | int last_errno; /**< Last error number. */ |
---|
| 46 | char last_error[64]; /**< Last error string. */ |
---|
[f59d435d] | 47 | } rtems_rap_data; |
---|
[ae5fe7e6] | 48 | |
---|
| 49 | /** |
---|
| 50 | * The RAP file data. This structure is allocated on the heap when a file is |
---|
| 51 | * loaded. |
---|
| 52 | */ |
---|
[f59d435d] | 53 | typedef struct rtems_rap_app |
---|
[ae5fe7e6] | 54 | { |
---|
| 55 | rtems_chain_node node; /**< The node's link in the chain. */ |
---|
| 56 | const char* name; /**< The file name */ |
---|
| 57 | void* handle; /**< The dlopen handle. */ |
---|
[f59d435d] | 58 | } rtems_rap_app; |
---|
[ae5fe7e6] | 59 | |
---|
| 60 | /** |
---|
| 61 | * RTL entry. |
---|
| 62 | */ |
---|
| 63 | #if (RTL_GLUE(__USER_LABEL_PREFIX__, 1) == RTL_GLUE(_, 1)) |
---|
| 64 | #define RTL_ENTRY_POINT "_rtems" |
---|
| 65 | #else |
---|
| 66 | #define RTL_ENTRY_POINT "rtems" |
---|
| 67 | #endif |
---|
| 68 | |
---|
| 69 | /** |
---|
| 70 | * Static RAP data is returned to the user when the loader is locked. |
---|
| 71 | */ |
---|
[f59d435d] | 72 | static rtems_rap_data rap_ = { .once = PTHREAD_ONCE_INIT }; |
---|
[ae5fe7e6] | 73 | |
---|
| 74 | /** |
---|
| 75 | * Verbose level for the RAP loader. |
---|
| 76 | */ |
---|
| 77 | static bool rap_verbose; |
---|
| 78 | |
---|
| 79 | /** |
---|
| 80 | * RAP entry call signature. |
---|
| 81 | */ |
---|
[f59d435d] | 82 | typedef int (*rtems_rap_entry)(int argc, const char* argv[]); |
---|
[ae5fe7e6] | 83 | |
---|
| 84 | /** |
---|
| 85 | * Forward decl. |
---|
| 86 | */ |
---|
[87b7117f] | 87 | static void rtems_rap_unlock (void); |
---|
[ae5fe7e6] | 88 | |
---|
[87b7117f] | 89 | static void |
---|
[ae5fe7e6] | 90 | rtems_rap_data_init (void) |
---|
| 91 | { |
---|
| 92 | /* |
---|
[87b7117f] | 93 | * Create the RAP lock. |
---|
[ae5fe7e6] | 94 | */ |
---|
[87b7117f] | 95 | rtems_mutex_init (&rap_.lock, "RAP"); |
---|
| 96 | |
---|
| 97 | /* |
---|
| 98 | * Initialise the objects list and create any required services. |
---|
| 99 | */ |
---|
| 100 | rtems_chain_initialize_empty (&rap_.apps); |
---|
[ae5fe7e6] | 101 | } |
---|
| 102 | |
---|
[f59d435d] | 103 | static rtems_rap_data* |
---|
[ae5fe7e6] | 104 | rtems_rap_lock (void) |
---|
| 105 | { |
---|
[87b7117f] | 106 | pthread_once (&rap_.once, rtems_rap_data_init); |
---|
| 107 | rtems_mutex_lock (&rap_.lock); |
---|
[ae5fe7e6] | 108 | |
---|
| 109 | return &rap_; |
---|
| 110 | } |
---|
| 111 | |
---|
[87b7117f] | 112 | static void |
---|
[ae5fe7e6] | 113 | rtems_rap_unlock (void) |
---|
| 114 | { |
---|
[87b7117f] | 115 | rtems_mutex_unlock (&rap_.lock); |
---|
[ae5fe7e6] | 116 | } |
---|
| 117 | |
---|
[f59d435d] | 118 | static rtems_rap_app* |
---|
[ae5fe7e6] | 119 | rtems_rap_check_handle (void* handle) |
---|
| 120 | { |
---|
[f59d435d] | 121 | rtems_rap_app* app; |
---|
[ae5fe7e6] | 122 | rtems_chain_node* node; |
---|
| 123 | |
---|
| 124 | app = handle; |
---|
| 125 | node = rtems_chain_first (&rap_.apps); |
---|
| 126 | |
---|
| 127 | while (!rtems_chain_is_tail (&rap_.apps, node)) |
---|
| 128 | { |
---|
[f59d435d] | 129 | rtems_rap_app* check = (rtems_rap_app*) node; |
---|
[ae5fe7e6] | 130 | if (check == app) |
---|
| 131 | return app; |
---|
| 132 | node = rtems_chain_next (node); |
---|
| 133 | } |
---|
| 134 | |
---|
| 135 | return NULL; |
---|
| 136 | } |
---|
| 137 | |
---|
[f59d435d] | 138 | static rtems_rap_app* |
---|
[ae5fe7e6] | 139 | rtems_rap_app_alloc (void) |
---|
| 140 | { |
---|
[f59d435d] | 141 | rtems_rap_app* app = malloc (sizeof (rtems_rap_app)); |
---|
| 142 | memset (app, 0, sizeof (rtems_rap_app)); |
---|
[ae5fe7e6] | 143 | rtems_chain_append (&rap_.apps, &app->node); |
---|
| 144 | return app; |
---|
| 145 | } |
---|
| 146 | |
---|
| 147 | static void |
---|
[f59d435d] | 148 | rtems_rap_app_free (rtems_rap_app* app) |
---|
[ae5fe7e6] | 149 | { |
---|
| 150 | if (app->handle) |
---|
| 151 | { |
---|
| 152 | dlclose (app->handle); |
---|
| 153 | app->handle = NULL; |
---|
| 154 | } |
---|
| 155 | |
---|
| 156 | if (!rtems_chain_is_node_off_chain (&app->node)) |
---|
| 157 | rtems_chain_extract (&app->node); |
---|
| 158 | } |
---|
| 159 | |
---|
| 160 | static bool |
---|
[f59d435d] | 161 | rtems_rap_match_name (rtems_rap_app* app, const char* name) |
---|
[ae5fe7e6] | 162 | { |
---|
| 163 | const char* a; |
---|
| 164 | |
---|
| 165 | /* |
---|
| 166 | * Assume the app name is absolute, ie points to the file on disk. This means |
---|
| 167 | * there is at least one delimiter in the name. |
---|
| 168 | */ |
---|
| 169 | |
---|
| 170 | if (strncmp (app->name, name, strlen (name)) == 0) |
---|
| 171 | return true; |
---|
| 172 | |
---|
| 173 | a = app->name + strlen (app->name) - 1; |
---|
| 174 | |
---|
| 175 | while (a >= app->name) |
---|
| 176 | { |
---|
| 177 | if (rtems_filesystem_is_delimiter (*a)) |
---|
| 178 | { |
---|
| 179 | const char* n = name; |
---|
| 180 | |
---|
| 181 | ++a; |
---|
| 182 | |
---|
| 183 | while (*a && *n) |
---|
| 184 | { |
---|
| 185 | if (*a == '.') |
---|
| 186 | { |
---|
| 187 | if (*n == '\0') |
---|
| 188 | return true; |
---|
| 189 | } |
---|
| 190 | |
---|
| 191 | ++a; |
---|
| 192 | ++n; |
---|
| 193 | } |
---|
| 194 | |
---|
| 195 | return false; |
---|
| 196 | } |
---|
| 197 | |
---|
| 198 | --a; |
---|
| 199 | } |
---|
| 200 | |
---|
| 201 | return false; |
---|
| 202 | } |
---|
| 203 | |
---|
| 204 | static void |
---|
| 205 | rtems_rap_get_rtl_error (void) |
---|
| 206 | { |
---|
| 207 | rap_.last_errno = |
---|
| 208 | rtems_rtl_get_error (rap_.last_error, sizeof (rap_.last_error)); |
---|
| 209 | } |
---|
| 210 | |
---|
| 211 | static void |
---|
| 212 | rtems_rap_set_error (int error, const char* format, ...) |
---|
| 213 | { |
---|
[f59d435d] | 214 | rtems_rap_data* rap = rtems_rap_lock (); |
---|
| 215 | va_list ap; |
---|
[ae5fe7e6] | 216 | va_start (ap, format); |
---|
| 217 | rap->last_errno = error; |
---|
| 218 | vsnprintf (rap->last_error, sizeof (rap->last_error), format, ap); |
---|
| 219 | rtems_rap_unlock (); |
---|
| 220 | va_end (ap); |
---|
| 221 | } |
---|
| 222 | |
---|
| 223 | bool |
---|
| 224 | rtems_rap_load (const char* name, int mode, int argc, const char* argv[]) |
---|
| 225 | { |
---|
[f59d435d] | 226 | rtems_rap_data* rap = rtems_rap_lock (); |
---|
[ae5fe7e6] | 227 | |
---|
| 228 | if (!rap) |
---|
| 229 | return false; |
---|
| 230 | |
---|
| 231 | if (rap_verbose) |
---|
| 232 | printf ("rap: loading '%s'\n", name); |
---|
| 233 | |
---|
| 234 | /* |
---|
| 235 | * See if the app has already been loaded. |
---|
| 236 | */ |
---|
| 237 | if (!rtems_rap_find (name)) |
---|
| 238 | { |
---|
[f59d435d] | 239 | rtems_rap_app* app; |
---|
| 240 | rtems_rap_entry init; |
---|
| 241 | rtems_rap_entry fini; |
---|
| 242 | size_t size = 0; |
---|
| 243 | int r; |
---|
[ae5fe7e6] | 244 | |
---|
| 245 | /* |
---|
| 246 | * Allocate a new application descriptor and attempt to load it. |
---|
| 247 | */ |
---|
| 248 | app = rtems_rap_app_alloc (); |
---|
| 249 | if (app == NULL) |
---|
| 250 | { |
---|
| 251 | rtems_rap_set_error (ENOMEM, "no memory for application"); |
---|
| 252 | rtems_rap_unlock (); |
---|
| 253 | return false; |
---|
| 254 | } |
---|
| 255 | |
---|
| 256 | /* |
---|
| 257 | * Find the file in the file system using the search path. |
---|
| 258 | */ |
---|
| 259 | if (!rtems_rtl_find_file (name, getenv ("PATH"), &app->name, &size)) |
---|
| 260 | { |
---|
| 261 | rtems_rap_set_error (ENOENT, "file not found"); |
---|
| 262 | rtems_rap_app_free (app); |
---|
| 263 | rtems_rap_unlock (); |
---|
| 264 | return false; |
---|
| 265 | } |
---|
| 266 | |
---|
| 267 | app->handle = dlopen (app->name, RTLD_NOW | mode); |
---|
| 268 | if (!app->handle) |
---|
| 269 | { |
---|
| 270 | rtems_rap_get_rtl_error (); |
---|
| 271 | rtems_rap_app_free (app); |
---|
| 272 | rtems_rap_unlock (); |
---|
| 273 | return false; |
---|
| 274 | } |
---|
| 275 | |
---|
| 276 | init = dlsym (app->handle, RTL_ENTRY_POINT); |
---|
| 277 | if (!init) |
---|
| 278 | { |
---|
| 279 | rtems_rap_get_rtl_error (); |
---|
| 280 | rtems_rap_app_free (app); |
---|
| 281 | rtems_rap_unlock (); |
---|
| 282 | return false; |
---|
| 283 | } |
---|
| 284 | |
---|
| 285 | fini = dlsym (app->handle, RTL_ENTRY_POINT); |
---|
| 286 | if (!fini) |
---|
| 287 | { |
---|
| 288 | rtems_rap_get_rtl_error (); |
---|
| 289 | rtems_rap_app_free (app); |
---|
| 290 | rtems_rap_unlock (); |
---|
| 291 | return false; |
---|
| 292 | } |
---|
| 293 | |
---|
| 294 | r = init (argc, argv); |
---|
| 295 | if (r != 0) |
---|
| 296 | { |
---|
| 297 | rtems_rap_set_error (r, "init call failure"); |
---|
| 298 | rtems_rap_app_free (app); |
---|
| 299 | rtems_rap_unlock (); |
---|
| 300 | return false; |
---|
| 301 | } |
---|
| 302 | } |
---|
| 303 | |
---|
| 304 | rtems_rap_unlock (); |
---|
| 305 | |
---|
| 306 | return true; |
---|
| 307 | } |
---|
| 308 | |
---|
| 309 | bool |
---|
| 310 | rtems_rap_unload (const char* name) |
---|
| 311 | { |
---|
[f59d435d] | 312 | rtems_rap_app* app; |
---|
| 313 | rtems_rap_entry fini; |
---|
| 314 | int r; |
---|
[ae5fe7e6] | 315 | |
---|
| 316 | rtems_rap_lock (); |
---|
| 317 | |
---|
| 318 | app = rtems_rap_find (name); |
---|
| 319 | |
---|
| 320 | if (rap_verbose) |
---|
| 321 | printf ("rap: unloading '%s'\n", name); |
---|
| 322 | |
---|
| 323 | if (!app) |
---|
| 324 | { |
---|
| 325 | rtems_rap_set_error (ENOENT, "invalid handle"); |
---|
| 326 | rtems_rap_unlock (); |
---|
| 327 | return false; |
---|
| 328 | } |
---|
| 329 | |
---|
| 330 | fini = dlsym (app->handle, RTL_ENTRY_POINT); |
---|
| 331 | if (!fini) |
---|
| 332 | { |
---|
| 333 | rtems_rap_get_rtl_error (); |
---|
| 334 | rtems_rap_unlock (); |
---|
| 335 | return false; |
---|
| 336 | } |
---|
| 337 | |
---|
| 338 | r = fini (0, NULL); |
---|
| 339 | if (r != 0) |
---|
| 340 | { |
---|
| 341 | rtems_rap_set_error (r, "fini failure"); |
---|
| 342 | rtems_rap_unlock (); |
---|
| 343 | return false; |
---|
| 344 | } |
---|
| 345 | |
---|
| 346 | rtems_rap_app_free (app); |
---|
| 347 | rtems_rap_unlock (); |
---|
| 348 | |
---|
| 349 | return true; |
---|
| 350 | } |
---|
| 351 | |
---|
| 352 | void* |
---|
| 353 | rtems_rap_find (const char* name) |
---|
| 354 | { |
---|
[f59d435d] | 355 | rtems_rap_data* rap = rtems_rap_lock (); |
---|
[ae5fe7e6] | 356 | rtems_chain_node* node; |
---|
| 357 | |
---|
| 358 | node = rtems_chain_first (&rap->apps); |
---|
| 359 | |
---|
| 360 | while (!rtems_chain_is_tail (&rap->apps, node)) |
---|
| 361 | { |
---|
[f59d435d] | 362 | rtems_rap_app* app = (rtems_rap_app*) node; |
---|
[ae5fe7e6] | 363 | if (rtems_rap_match_name (app, name)) |
---|
| 364 | { |
---|
| 365 | rtems_rap_unlock (); |
---|
| 366 | return app; |
---|
| 367 | } |
---|
| 368 | node = rtems_chain_next (node); |
---|
| 369 | } |
---|
| 370 | |
---|
| 371 | rtems_rap_unlock (); |
---|
| 372 | |
---|
| 373 | return NULL; |
---|
| 374 | } |
---|
| 375 | |
---|
| 376 | bool |
---|
[f59d435d] | 377 | rtems_rap_iterate (rtems_rap_iterator iterator) |
---|
[ae5fe7e6] | 378 | { |
---|
[f59d435d] | 379 | rtems_rap_data* rap = rtems_rap_lock (); |
---|
[ae5fe7e6] | 380 | rtems_chain_node* node; |
---|
| 381 | bool result = true; |
---|
| 382 | |
---|
| 383 | node = rtems_chain_first (&rap->apps); |
---|
| 384 | |
---|
| 385 | while (!rtems_chain_is_tail (&rap->apps, node)) |
---|
| 386 | { |
---|
[f59d435d] | 387 | rtems_rap_app* app = (rtems_rap_app*) node; |
---|
[ae5fe7e6] | 388 | result = iterator (app); |
---|
| 389 | if (!result) |
---|
| 390 | break; |
---|
| 391 | node = rtems_chain_next (node); |
---|
| 392 | } |
---|
| 393 | |
---|
| 394 | rtems_rap_unlock (); |
---|
| 395 | |
---|
| 396 | return result; |
---|
| 397 | } |
---|
| 398 | |
---|
| 399 | const char* |
---|
| 400 | rtems_rap_name (void* handle) |
---|
| 401 | { |
---|
[f59d435d] | 402 | rtems_rap_app* app = rtems_rap_check_handle (handle); |
---|
[ae5fe7e6] | 403 | if (app) |
---|
| 404 | return app->name; |
---|
| 405 | return NULL; |
---|
| 406 | } |
---|
| 407 | |
---|
| 408 | void* |
---|
| 409 | rtems_rap_dl_handle (void* handle) |
---|
| 410 | { |
---|
[f59d435d] | 411 | rtems_rap_app* app = rtems_rap_check_handle (handle); |
---|
[ae5fe7e6] | 412 | if (app) |
---|
| 413 | return app->handle; |
---|
| 414 | return NULL; |
---|
| 415 | } |
---|
| 416 | |
---|
| 417 | int |
---|
| 418 | rtems_rap_get_error (char* message, size_t max_message) |
---|
| 419 | { |
---|
[f59d435d] | 420 | rtems_rap_data* rap = rtems_rap_lock (); |
---|
| 421 | int last_errno = rap->last_errno; |
---|
[71a8446] | 422 | strlcpy (message, rap->last_error, max_message); |
---|
[ae5fe7e6] | 423 | rtems_rap_unlock (); |
---|
| 424 | return last_errno; |
---|
| 425 | } |
---|