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

5
Last change on this file since d9800ac was d9800ac, checked in by Sebastian Huber <sebastian.huber@…>, on Jan 3, 2018 at 5:36:10 AM

libdl: Use self-contained recursive mutex

Update #2843.

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