source: rtems/cpukit/libdl/rtl.c @ ae5fe7e6

4.115
Last change on this file since ae5fe7e6 was ae5fe7e6, checked in by Chris Johns <chrisj@…>, on 10/27/14 at 01:09:41

cpukit: Add libdl with the Runtime Loader (RTL) code.

This is a merge of the RTL project.

  • Property mode set to 100644
File size: 13.4 KB
Line 
1/*
2 *  COPYRIGHT (c) 2012 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.com/license/LICENSE.
7 */
8/**
9 * @file
10 *
11 * @ingroup rtems_rtld
12 *
13 * @brief RTEMS Run-Time Link Editor
14 *
15 * This is the RTL implementation.
16 */
17
18#if HAVE_CONFIG_H
19#include "config.h"
20#endif
21
22#include <stdlib.h>
23#include <stdio.h>
24#include <string.h>
25
26#include <rtems/libio_.h>
27
28#include <rtems/rtl/rtl.h>
29#include "rtl-allocator.h"
30#include "rtl-error.h"
31#include "rtl-string.h"
32#include "rtl-trace.h"
33
34/**
35 * Semaphore configuration to create a mutex.
36 */
37#define RTEMS_MUTEX_ATTRIBS \
38  (RTEMS_PRIORITY | RTEMS_BINARY_SEMAPHORE | \
39   RTEMS_INHERIT_PRIORITY | RTEMS_NO_PRIORITY_CEILING | RTEMS_LOCAL)
40
41/**
42 * Symbol table cache size. They can be big so the cache needs space to work.
43 */
44#define RTEMS_RTL_ELF_SYMBOL_CACHE (2048)
45
46/**
47 * String table cache size.
48 */
49#define RTEMS_RTL_ELF_STRING_CACHE (2048)
50
51/**
52 * Relocations table cache size.
53 */
54#define RTEMS_RTL_ELF_RELOC_CACHE (2048)
55
56/**
57 * Decompression output buffer.
58 */
59#define RTEMS_RTL_COMP_OUTPUT (2048)
60
61/**
62 * Static RTL data is returned to the user when the linker is locked.
63 */
64static rtems_rtl_data_t* rtl;
65
66/**
67 * Define a default base global symbol loader function that is weak
68 * so a real table can be linked in when the user wants one.
69 */
70void rtems_rtl_base_global_syms_init (void) __attribute__ ((weak));
71void
72rtems_rtl_base_global_syms_init (void)
73{
74  /*
75   * Do nothing.
76   */
77}
78
79static bool
80rtems_rtl_data_init (void)
81{
82  /*
83   * Lock the RTL. We only create a lock if a call is made. First we test if a
84   * lock is present. If one is present we lock it. If not the libio lock is
85   * locked and we then test the lock again. If not present we create the lock
86   * then release libio lock.
87   */
88  if (!rtl)
89  {
90    rtems_libio_lock ();
91
92    if (!rtl)
93    {
94      rtems_status_code sc;
95      rtems_id          lock;
96
97      /*
98       * Always in the heap.
99       */
100      rtl = malloc (sizeof (rtems_rtl_data_t));
101      if (!rtl)
102      {
103        errno = ENOMEM;
104        return false;
105      }
106
107      *rtl = (rtems_rtl_data_t) { 0 };
108
109      /*
110       * The initialise the allocator data.
111       */
112      rtems_rtl_alloc_initialise (&rtl->allocator);
113
114      /*
115       * Create the RTL lock.
116       */
117      sc = rtems_semaphore_create (rtems_build_name ('R', 'T', 'L', 'D'),
118                                   1, RTEMS_MUTEX_ATTRIBS,
119                                   RTEMS_NO_PRIORITY, &lock);
120      if (sc != RTEMS_SUCCESSFUL)
121      {
122        free (rtl);
123        return false;
124      }
125
126      sc = rtems_semaphore_obtain (lock, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
127      if (sc != RTEMS_SUCCESSFUL)
128      {
129        rtems_semaphore_delete (lock);
130        free (rtl);
131        return false;
132      }
133
134      rtl->lock = lock;
135
136      /*
137       * Initialise the objects list and create any required services.
138       */
139      rtems_chain_initialize_empty (&rtl->objects);
140
141      if (!rtems_rtl_symbol_table_open (&rtl->globals,
142                                        RTEMS_RTL_SYMS_GLOBAL_BUCKETS))
143      {
144        rtems_semaphore_delete (lock);
145        free (rtl);
146        return false;
147      }
148
149      if (!rtems_rtl_unresolved_table_open (&rtl->unresolved,
150                                            RTEMS_RTL_UNRESOLVED_BLOCK_SIZE))
151      {
152        rtems_rtl_symbol_table_close (&rtl->globals);
153        rtems_semaphore_delete (lock);
154        free (rtl);
155        return false;
156      }
157
158      if (!rtems_rtl_obj_cache_open (&rtl->symbols,
159                                     RTEMS_RTL_ELF_SYMBOL_CACHE))
160      {
161        rtems_rtl_symbol_table_close (&rtl->globals);
162        rtems_rtl_unresolved_table_close (&rtl->unresolved);
163        rtems_semaphore_delete (lock);
164        free (rtl);
165        return false;
166      }
167
168      if (!rtems_rtl_obj_cache_open (&rtl->strings,
169                                     RTEMS_RTL_ELF_STRING_CACHE))
170      {
171        rtems_rtl_obj_cache_close (&rtl->symbols);
172        rtems_rtl_unresolved_table_close (&rtl->unresolved);
173        rtems_rtl_symbol_table_close (&rtl->globals);
174        rtems_semaphore_delete (lock);
175        free (rtl);
176        return false;
177      }
178
179      if (!rtems_rtl_obj_cache_open (&rtl->relocs,
180                                     RTEMS_RTL_ELF_RELOC_CACHE))
181      {
182        rtems_rtl_obj_cache_close (&rtl->strings);
183        rtems_rtl_obj_cache_close (&rtl->symbols);
184        rtems_rtl_unresolved_table_close (&rtl->unresolved);
185        rtems_rtl_symbol_table_close (&rtl->globals);
186        rtems_semaphore_delete (lock);
187        free (rtl);
188        return false;
189      }
190
191      if (!rtems_rtl_obj_comp_open (&rtl->decomp,
192                                    RTEMS_RTL_COMP_OUTPUT))
193      {
194        rtems_rtl_obj_cache_close (&rtl->relocs);
195        rtems_rtl_obj_cache_close (&rtl->strings);
196        rtems_rtl_obj_cache_close (&rtl->symbols);
197        rtems_rtl_unresolved_table_close (&rtl->unresolved);
198        rtems_rtl_symbol_table_close (&rtl->globals);
199        rtems_semaphore_delete (lock);
200        free (rtl);
201        return false;
202      }
203
204      rtl->base = rtems_rtl_obj_alloc ();
205      if (!rtl->base)
206      {
207        rtems_rtl_obj_comp_close (&rtl->decomp);
208        rtems_rtl_obj_cache_close (&rtl->relocs);
209        rtems_rtl_obj_cache_close (&rtl->strings);
210        rtems_rtl_obj_cache_close (&rtl->symbols);
211        rtems_rtl_unresolved_table_close (&rtl->unresolved);
212        rtems_rtl_symbol_table_close (&rtl->globals);
213        rtems_semaphore_delete (lock);
214        free (rtl);
215        return false;
216      }
217
218      /*
219       * Need to malloc the memory so the free does not complain.
220       */
221      rtl->base->oname = rtems_rtl_strdup ("rtems-kernel");
222
223      rtems_chain_append (&rtl->objects, &rtl->base->link);
224    }
225
226    rtems_libio_unlock ();
227
228    rtems_rtl_path_append (".");
229
230    rtems_rtl_base_global_syms_init ();
231
232    rtems_rtl_unlock ();
233  }
234  return true;
235}
236
237rtems_rtl_data_t*
238rtems_rtl_data (void)
239{
240  return rtl;
241}
242
243rtems_rtl_symbols_t*
244rtems_rtl_global_symbols (void)
245{
246  if (!rtl)
247  {
248    rtems_rtl_set_error (ENOENT, "no rtl");
249    return NULL;
250  }
251  return &rtl->globals;
252}
253
254rtems_rtl_unresolved_t*
255rtems_rtl_unresolved (void)
256{
257  if (!rtl)
258  {
259    rtems_rtl_set_error (ENOENT, "no rtl");
260    return NULL;
261  }
262  return &rtl->unresolved;
263}
264
265void
266rtems_rtl_obj_caches (rtems_rtl_obj_cache_t** symbols,
267                      rtems_rtl_obj_cache_t** strings,
268                      rtems_rtl_obj_cache_t** relocs)
269{
270  if (!rtl)
271  {
272    if (symbols)
273       *symbols = NULL;
274    if (strings)
275      *strings = NULL;
276    if (relocs)
277      *relocs = NULL;
278  }
279  else
280  {
281    if (symbols)
282      *symbols = &rtl->symbols;
283    if (strings)
284      *strings = &rtl->strings;
285    if (relocs)
286      *relocs = &rtl->relocs;
287  }
288}
289
290void
291rtems_rtl_obj_caches_flush ()
292{
293  if (rtl)
294  {
295    rtems_rtl_obj_cache_flush (&rtl->symbols);
296    rtems_rtl_obj_cache_flush (&rtl->strings);
297    rtems_rtl_obj_cache_flush (&rtl->relocs);
298  }
299}
300
301void
302rtems_rtl_obj_comp (rtems_rtl_obj_comp_t** decomp,
303                    rtems_rtl_obj_cache_t* cache,
304                    int                    fd,
305                    int                    compression,
306                    off_t                  offset)
307{
308  if (!rtl)
309  {
310    *decomp = NULL;
311  }
312  else
313  {
314    *decomp = &rtl->decomp;
315    rtems_rtl_obj_comp_set (*decomp, cache, fd, compression, offset);
316  }
317}
318
319rtems_rtl_data_t*
320rtems_rtl_lock (void)
321{
322  rtems_status_code sc;
323
324  if (!rtems_rtl_data_init ())
325    return NULL;
326
327  sc = rtems_semaphore_obtain (rtl->lock,
328                               RTEMS_WAIT, RTEMS_NO_TIMEOUT);
329  if (sc != RTEMS_SUCCESSFUL)
330  {
331    errno = EINVAL;
332    return NULL;
333  }
334
335  return rtl;
336}
337
338bool
339rtems_rtl_unlock (void)
340{
341  /*
342   * Not sure any error should be returned or an assert.
343   */
344  rtems_status_code sc;
345  sc = rtems_semaphore_release (rtl->lock);
346  if ((sc != RTEMS_SUCCESSFUL) && (errno == 0))
347  {
348    errno = EINVAL;
349    return false;
350  }
351  return true;
352}
353
354rtems_rtl_obj_t*
355rtems_rtl_check_handle (void* handle)
356{
357  rtems_rtl_obj_t*    obj;
358  rtems_chain_node* node;
359
360  obj = handle;
361  node = rtems_chain_first (&rtl->objects);
362
363  while (!rtems_chain_is_tail (&rtl->objects, node))
364  {
365    rtems_rtl_obj_t* check = (rtems_rtl_obj_t*) node;
366    if (check == obj)
367      return obj;
368    node = rtems_chain_next (node);
369  }
370
371  return NULL;
372}
373
374rtems_rtl_obj_t*
375rtems_rtl_find_obj (const char* name)
376{
377  rtems_chain_node* node;
378  rtems_rtl_obj_t*  found = NULL;
379  const char*       aname = NULL;
380  const char*       oname = NULL;
381  off_t             ooffset;
382
383  if (!rtems_rtl_parse_name (name, &aname, &oname, &ooffset))
384    return NULL;
385
386  node = rtems_chain_first (&rtl->objects);
387
388  while (!rtems_chain_is_tail (&rtl->objects, node))
389  {
390    rtems_rtl_obj_t* obj = (rtems_rtl_obj_t*) node;
391    if ((aname == NULL && strcmp (obj->oname, oname) == 0) ||
392        (aname != NULL &&
393         strcmp (obj->aname, aname) == 0 && strcmp (obj->oname, oname) == 0))
394    {
395        found = obj;
396        break;
397    }
398    node = rtems_chain_next (node);
399  }
400
401  if (!aname)
402    rtems_rtl_alloc_del(RTEMS_RTL_ALLOC_OBJECT, (void*) aname);
403
404  if (!oname)
405    rtems_rtl_alloc_del(RTEMS_RTL_ALLOC_OBJECT, (void*) oname);
406
407  return found;
408}
409
410rtems_rtl_obj_t*
411rtems_rtl_load_object (const char* name, int mode)
412{
413  rtems_rtl_obj_t* obj;
414
415  if (rtems_rtl_trace (RTEMS_RTL_TRACE_LOAD))
416    printf ("rtl: loading '%s'\n", name);
417
418  /*
419   * See if the object module has already been loaded.
420   */
421  obj = rtems_rtl_find_obj (name);
422  if (!obj)
423  {
424    /*
425     * Allocate a new object file descriptor and attempt to load it.
426     */
427    obj = rtems_rtl_obj_alloc ();
428    if (obj == NULL)
429    {
430      rtems_rtl_set_error (ENOMEM, "no memory for object descriptor");
431      return NULL;
432    }
433
434    /*
435     * Find the file in the file system using the search path. The fname field
436     * will point to a valid file name if found.
437     */
438    if (!rtems_rtl_obj_find_file (obj, name))
439    {
440      rtems_rtl_obj_free (obj);
441      return NULL;
442    }
443
444    rtems_chain_append (&rtl->objects, &obj->link);
445
446    if (!rtems_rtl_obj_load (obj))
447    {
448      rtems_rtl_obj_free (obj);
449      return NULL;
450    }
451
452    rtems_rtl_unresolved_resolve ();
453  }
454
455  /*
456   * Increase the number of users.
457   */
458  ++obj->users;
459
460  /*
461   * FIXME: Resolving existing unresolved symbols could add more constructors
462   *        lists that need to be called. Make a list in the obj load layer and
463   *        invoke the list here.
464   */
465
466  /*
467   * Run any local constructors if this is the first user because the object
468   * file will have just been loaded. Unlock the linker to avoid any dead locks
469   * if the object file needs to load files or update the symbol table. We also
470   * do not want a constructor to unload this object file.
471   */
472  if (obj->users == 1)
473  {
474    obj->flags |= RTEMS_RTL_OBJ_LOCKED;
475    rtems_rtl_unlock ();
476    rtems_rtl_obj_run_ctors (obj);
477    rtems_rtl_lock ();
478    obj->flags &= ~RTEMS_RTL_OBJ_LOCKED;
479  }
480
481  return obj;
482}
483
484bool
485rtems_rtl_unload_object (rtems_rtl_obj_t* obj)
486{
487  bool ok = true;
488
489  if (rtems_rtl_trace (RTEMS_RTL_TRACE_UNLOAD))
490    printf ("rtl: unloading '%s'\n", rtems_rtl_obj_fname (obj));
491
492  /*
493   * If the object is locked it cannot be unloaded and the unload fails.
494   */
495  if ((obj->flags & RTEMS_RTL_OBJ_LOCKED) == RTEMS_RTL_OBJ_LOCKED)
496  {
497    rtems_rtl_set_error (EINVAL, "cannot unload when locked");
498    return false;
499  }
500
501  /*
502   * Check the number of users in a safe manner. If this is the last user unload the
503   * object file from memory.
504   */
505  if (obj->users > 0)
506    --obj->users;
507
508  if (obj->users == 0)
509  {
510    obj->flags |= RTEMS_RTL_OBJ_LOCKED;
511    rtems_rtl_unlock ();
512    rtems_rtl_obj_run_dtors (obj);
513    rtems_rtl_lock ();
514    obj->flags &= ~RTEMS_RTL_OBJ_LOCKED;
515
516    ok = rtems_rtl_obj_unload (obj);
517  }
518
519  return ok;
520}
521
522void
523rtems_rtl_run_ctors (rtems_rtl_obj_t* obj)
524{
525  rtems_rtl_obj_run_ctors (obj);
526}
527
528static bool
529rtems_rtl_path_update (bool prepend, const char* path)
530{
531  char*       paths;
532  const char* src = NULL;
533  char*       dst;
534  int         len;
535
536  if (!rtems_rtl_lock ())
537    return false;
538
539  len = strlen (path);
540
541  if (rtl->paths)
542    len += strlen (rtl->paths) + 1;
543  else
544    prepend = true;
545
546  paths = rtems_rtl_alloc_new (RTEMS_RTL_ALLOC_OBJECT, len + 1, false);
547
548  if (!paths)
549  {
550    rtems_rtl_unlock ();
551    return false;
552  }
553
554  dst = paths;
555
556  if (prepend)
557  {
558    len = strlen (path);
559    src = path;
560  }
561  else if (rtl->paths)
562  {
563    len = strlen (rtl->paths);
564    src = rtl->paths;
565  }
566
567  memcpy (dst, src, len);
568
569  dst += len;
570
571  if (rtl->paths)
572  {
573    *dst = ':';
574    ++dst;
575  }
576
577  if (prepend)
578  {
579    src = rtl->paths;
580    if (src)
581      len = strlen (src);
582  }
583  else
584  {
585    len = strlen (path);
586    src = path;
587  }
588
589  if (src)
590  {
591    memcpy (dst, src, len);
592    dst += len;
593  }
594
595  *dst = '\0';
596
597  rtl->paths = paths;
598
599  rtems_rtl_unlock ();
600  return false;
601}
602
603bool
604rtems_rtl_path_append (const char* path)
605{
606  return rtems_rtl_path_update (false, path);
607}
608
609bool
610rtems_rtl_path_prepend (const char* path)
611{
612  return rtems_rtl_path_update (true, path);
613}
614
615void
616rtems_rtl_base_sym_global_add (const unsigned char* esyms,
617                               unsigned int         size)
618{
619  if (rtems_rtl_trace (RTEMS_RTL_TRACE_GLOBAL_SYM))
620    printf ("rtl: adding global symbols, table size %u\n", size);
621
622  if (!rtems_rtl_lock ())
623  {
624    rtems_rtl_set_error (EINVAL, "global add cannot lock rtl");
625    return;
626  }
627
628  rtems_rtl_symbol_global_add (rtl->base, esyms, size);
629
630  rtems_rtl_unlock ();
631}
632
633rtems_rtl_obj_t*
634rtems_rtl_baseimage (void)
635{
636  return NULL;
637}
Note: See TracBrowser for help on using the repository browser.