1 | /* |
---|
2 | * COPYRIGHT (c) 2012, 2018 Chris Johns <chrisj@rtems.org> |
---|
3 | * |
---|
4 | * The license and distribution terms for this file may be |
---|
5 | * found in the file LICENSE in this distribution or at |
---|
6 | * http://www.rtems.org/license/LICENSE. |
---|
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 | |
---|
22 | #include <pthread.h> |
---|
23 | #include <stdarg.h> |
---|
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 | { |
---|
42 | pthread_once_t once; |
---|
43 | rtems_mutex lock; /**< The RAP lock id */ |
---|
44 | rtems_chain_control apps; /**< List if loaded application. */ |
---|
45 | int last_errno; /**< Last error number. */ |
---|
46 | char last_error[64]; /**< Last error string. */ |
---|
47 | } rtems_rap_data; |
---|
48 | |
---|
49 | /** |
---|
50 | * The RAP file data. This structure is allocated on the heap when a file is |
---|
51 | * loaded. |
---|
52 | */ |
---|
53 | typedef struct rtems_rap_app |
---|
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. */ |
---|
58 | } rtems_rap_app; |
---|
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 | */ |
---|
72 | static rtems_rap_data rap_ = { .once = PTHREAD_ONCE_INIT }; |
---|
73 | |
---|
74 | /** |
---|
75 | * Verbose level for the RAP loader. |
---|
76 | */ |
---|
77 | static bool rap_verbose; |
---|
78 | |
---|
79 | /** |
---|
80 | * RAP entry call signature. |
---|
81 | */ |
---|
82 | typedef int (*rtems_rap_entry)(int argc, const char* argv[]); |
---|
83 | |
---|
84 | /** |
---|
85 | * Forward decl. |
---|
86 | */ |
---|
87 | static void rtems_rap_unlock (void); |
---|
88 | |
---|
89 | static void |
---|
90 | rtems_rap_data_init (void) |
---|
91 | { |
---|
92 | /* |
---|
93 | * Create the RAP lock. |
---|
94 | */ |
---|
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); |
---|
101 | } |
---|
102 | |
---|
103 | static rtems_rap_data* |
---|
104 | rtems_rap_lock (void) |
---|
105 | { |
---|
106 | pthread_once (&rap_.once, rtems_rap_data_init); |
---|
107 | rtems_mutex_lock (&rap_.lock); |
---|
108 | |
---|
109 | return &rap_; |
---|
110 | } |
---|
111 | |
---|
112 | static void |
---|
113 | rtems_rap_unlock (void) |
---|
114 | { |
---|
115 | rtems_mutex_unlock (&rap_.lock); |
---|
116 | } |
---|
117 | |
---|
118 | static rtems_rap_app* |
---|
119 | rtems_rap_check_handle (void* handle) |
---|
120 | { |
---|
121 | rtems_rap_app* app; |
---|
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 | { |
---|
129 | rtems_rap_app* check = (rtems_rap_app*) node; |
---|
130 | if (check == app) |
---|
131 | return app; |
---|
132 | node = rtems_chain_next (node); |
---|
133 | } |
---|
134 | |
---|
135 | return NULL; |
---|
136 | } |
---|
137 | |
---|
138 | static rtems_rap_app* |
---|
139 | rtems_rap_app_alloc (void) |
---|
140 | { |
---|
141 | rtems_rap_app* app = malloc (sizeof (rtems_rap_app)); |
---|
142 | memset (app, 0, sizeof (rtems_rap_app)); |
---|
143 | rtems_chain_append (&rap_.apps, &app->node); |
---|
144 | return app; |
---|
145 | } |
---|
146 | |
---|
147 | static void |
---|
148 | rtems_rap_app_free (rtems_rap_app* app) |
---|
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 |
---|
161 | rtems_rap_match_name (rtems_rap_app* app, const char* name) |
---|
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 | { |
---|
214 | rtems_rap_data* rap = rtems_rap_lock (); |
---|
215 | va_list ap; |
---|
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 | { |
---|
226 | rtems_rap_data* rap = rtems_rap_lock (); |
---|
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 | { |
---|
239 | rtems_rap_app* app; |
---|
240 | rtems_rap_entry init; |
---|
241 | rtems_rap_entry fini; |
---|
242 | size_t size = 0; |
---|
243 | int r; |
---|
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 | { |
---|
312 | rtems_rap_app* app; |
---|
313 | rtems_rap_entry fini; |
---|
314 | int r; |
---|
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 | { |
---|
355 | rtems_rap_data* rap = rtems_rap_lock (); |
---|
356 | rtems_chain_node* node; |
---|
357 | |
---|
358 | node = rtems_chain_first (&rap->apps); |
---|
359 | |
---|
360 | while (!rtems_chain_is_tail (&rap->apps, node)) |
---|
361 | { |
---|
362 | rtems_rap_app* app = (rtems_rap_app*) node; |
---|
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 |
---|
377 | rtems_rap_iterate (rtems_rap_iterator iterator) |
---|
378 | { |
---|
379 | rtems_rap_data* rap = rtems_rap_lock (); |
---|
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 | { |
---|
387 | rtems_rap_app* app = (rtems_rap_app*) node; |
---|
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 | { |
---|
402 | rtems_rap_app* app = rtems_rap_check_handle (handle); |
---|
403 | if (app) |
---|
404 | return app->name; |
---|
405 | return NULL; |
---|
406 | } |
---|
407 | |
---|
408 | void* |
---|
409 | rtems_rap_dl_handle (void* handle) |
---|
410 | { |
---|
411 | rtems_rap_app* app = rtems_rap_check_handle (handle); |
---|
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 | { |
---|
420 | rtems_rap_data* rap = rtems_rap_lock (); |
---|
421 | int last_errno = rap->last_errno; |
---|
422 | strlcpy (message, rap->last_error, max_message); |
---|
423 | rtems_rap_unlock (); |
---|
424 | return last_errno; |
---|
425 | } |
---|