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