source: rtems/cpukit/libdl/rtl-unresolved.c @ 4408603

Last change on this file since 4408603 was 4408603, checked in by Chris Johns <chrisj@…>, on Jan 9, 2019 at 11:14:11 AM

libdl: Fix the support for constructors and desctructors.

  • Fix the handling of pending objects.
  • Add a constructor flags in objects to track then being called.

Closes #2921

  • Property mode set to 100644
File size: 19.6 KB
Line 
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_rtl
12 *
13 * @brief RTEMS Run-Time Linker Object File Unresolved Relocations Table.
14 */
15
16#if HAVE_CONFIG_H
17#include "config.h"
18#endif
19
20#include <errno.h>
21#include <inttypes.h>
22#include <stdlib.h>
23#include <stdio.h>
24#include <string.h>
25
26#include <rtems/rtl/rtl.h>
27#include "rtl-error.h"
28#include <rtems/rtl/rtl-unresolved.h>
29#include <rtems/rtl/rtl-trace.h>
30
31static rtems_rtl_unresolv_block*
32rtems_rtl_unresolved_block_alloc (rtems_rtl_unresolved* unresolved)
33{
34  /*
35   * The block header contains a record.
36   */
37  size_t size =
38    (sizeof(rtems_rtl_unresolv_block) +
39     (sizeof(rtems_rtl_unresolv_rec) * (unresolved->block_recs - 1)));
40  rtems_rtl_unresolv_block* block =
41    rtems_rtl_alloc_new (RTEMS_RTL_ALLOC_EXTERNAL, size, true);
42  if (block)
43  {
44    if (rtems_rtl_trace (RTEMS_RTL_TRACE_UNRESOLVED))
45      printf ("rtl: unresolv: block-alloc %p\n", block);
46    rtems_chain_append (&unresolved->blocks, &block->link);
47  }
48  else
49    rtems_rtl_set_error (ENOMEM, "no memory for unresolved block");
50  return block;
51}
52
53static size_t
54rtems_rtl_unresolved_symbol_rec_count (size_t length)
55{
56  return ((length + sizeof(rtems_rtl_unresolv_symbol) - 1) /
57          sizeof(rtems_rtl_unresolv_symbol));
58}
59
60
61static size_t
62rtems_rtl_unresolved_symbol_recs (const char* name)
63{
64  return rtems_rtl_unresolved_symbol_rec_count (strlen (name));
65}
66
67static int
68rtems_rtl_unresolved_rec_index (rtems_rtl_unresolv_block* block,
69                                rtems_rtl_unresolv_rec* rec)
70{
71  return (rec - &block->rec) / sizeof (rtems_rtl_unresolv_rec);
72}
73
74static rtems_rtl_unresolv_rec*
75rtems_rtl_unresolved_rec_first (rtems_rtl_unresolv_block* block)
76{
77  return &block->rec;
78}
79
80static rtems_rtl_unresolv_rec*
81rtems_rtl_unresolved_rec_next (rtems_rtl_unresolv_rec* rec)
82{
83  switch (rec->type)
84  {
85    case rtems_rtl_unresolved_empty:
86      /*
87       * Empty returns NULL. The end of the records in the block.
88       */
89      rec = NULL;
90      break;
91
92    case rtems_rtl_unresolved_symbol:
93      /*
94       * Determine how many records the name occupies. Round up.
95       */
96      rec += rtems_rtl_unresolved_symbol_rec_count (rec->rec.name.length);
97      break;
98
99    case rtems_rtl_unresolved_reloc:
100      ++rec;
101      break;
102
103    default:
104      break;
105  }
106
107  return rec;
108}
109
110static bool
111rtems_rtl_unresolved_rec_is_last (rtems_rtl_unresolv_block* block,
112                                  rtems_rtl_unresolv_rec*   rec)
113{
114  int index = (rec - &block->rec) / sizeof (rec);
115  return !rec || (index >= block->recs) || (rec->type == rtems_rtl_unresolved_empty);
116}
117
118static rtems_rtl_unresolv_rec*
119rtems_rtl_unresolved_rec_first_free (rtems_rtl_unresolv_block* block)
120{
121  return &block->rec + block->recs;
122}
123
124static int
125rtems_rtl_unresolved_find_name (rtems_rtl_unresolved* unresolved,
126                                const char*           name,
127                                bool                  update_refcount)
128{
129  size_t length = strlen (name) + 1;
130  int    index = 1;
131
132  rtems_chain_node* node = rtems_chain_first (&unresolved->blocks);
133  while (!rtems_chain_is_tail (&unresolved->blocks, node))
134  {
135    rtems_rtl_unresolv_block* block = (rtems_rtl_unresolv_block*) node;
136    rtems_rtl_unresolv_rec* rec = rtems_rtl_unresolved_rec_first (block);
137
138    while (!rtems_rtl_unresolved_rec_is_last (block, rec))
139    {
140      if (rec->type == rtems_rtl_unresolved_symbol)
141      {
142        if ((rec->rec.name.length == length)
143            && (strcmp (rec->rec.name.name, name) == 0))
144        {
145          if (update_refcount)
146            ++rec->rec.name.refs;
147          return index;
148        }
149        ++index;
150      }
151      rec = rtems_rtl_unresolved_rec_next (rec);
152    }
153
154    node = rtems_chain_next (node);
155  }
156
157  return 0 - index;
158}
159
160/**
161 * Struct to pass relocation data in the iterator.
162 */
163typedef struct rtems_rtl_unresolved_reloc_data
164{
165  uint16_t                name;     /**< Name index. */
166  rtems_rtl_unresolv_rec* name_rec; /**< Name record. */
167  rtems_rtl_obj_sym*      sym;      /**< The symbol record. */
168} rtems_rtl_unresolved_reloc_data;
169
170static bool
171rtems_rtl_unresolved_resolve_reloc (rtems_rtl_unresolv_rec* rec,
172                                    void*                   data)
173{
174  if (rec->type == rtems_rtl_unresolved_reloc)
175  {
176    rtems_chain_control*             pending;
177    rtems_rtl_unresolved_reloc_data* rd;
178
179    rd = (rtems_rtl_unresolved_reloc_data*) data;
180
181    if (rec->rec.reloc.name == rd->name && rec->rec.reloc.obj != NULL)
182    {
183      if (rtems_rtl_trace (RTEMS_RTL_TRACE_UNRESOLVED))
184        printf ("rtl: unresolv: resolve reloc: %s\n",
185                rd->name_rec->rec.name.name);
186
187      rtems_rtl_obj_relocate_unresolved (&rec->rec.reloc, rd->sym);
188
189      /*
190       * If all unresolved externals are resolved add the obj module
191       * to the pending queue. This will flush the object module's
192       * data from the cache and call it's constructors.
193       */
194      if (rec->rec.reloc.obj->unresolved == 0)
195      {
196        pending = rtems_rtl_pending_unprotected ();
197        rtems_chain_extract (&rec->rec.reloc.obj->link);
198        rtems_chain_append (pending, &rec->rec.reloc.obj->link);
199      }
200
201      /*
202       * Check Set the object pointer to NULL to indicate the record is not used
203       * anymore. Update the reference count of the name. The sweep after
204       * relocating will remove the reloc records with obj set to NULL and
205       * names with a reference count of 0.
206       */
207      rec->rec.reloc.obj = NULL;
208      if (rd->name_rec != NULL && rd->name_rec->rec.name.refs > 0)
209        --rd->name_rec->rec.name.refs;
210    }
211  }
212  return false;
213}
214
215static bool
216rtems_rtl_unresolved_resolve_iterator (rtems_rtl_unresolv_rec* rec,
217                                       void*                   data)
218{
219  if (rec->type == rtems_rtl_unresolved_symbol)
220  {
221    rtems_rtl_unresolved_reloc_data* rd;
222    rd = (rtems_rtl_unresolved_reloc_data*) data;
223
224    ++rd->name;
225
226    if (rtems_rtl_trace (RTEMS_RTL_TRACE_UNRESOLVED))
227      printf ("rtl: unresolv: lookup: %d: %s\n", rd->name, rec->rec.name.name);
228
229    rd->sym = rtems_rtl_symbol_global_find (rec->rec.name.name);
230
231    if (rd->sym)
232    {
233      if (rtems_rtl_trace (RTEMS_RTL_TRACE_UNRESOLVED))
234        printf ("rtl: unresolv: found: %s\n", rec->rec.name.name);
235
236      rd->name_rec = rec;
237
238      rtems_rtl_unresolved_iterate (rtems_rtl_unresolved_resolve_reloc, rd);
239
240      rd->name_rec = NULL;
241      rd->sym = NULL;
242    }
243  }
244
245  return false;
246}
247
248/**
249 * Struct to pass archive relocation data in the iterator.
250 */
251typedef struct rtems_rtl_unresolved_archive_reloc_data
252{
253  uint16_t            name;     /**< Name index. */
254  bool                loaded;   /**< Object file loaded. */
255  rtems_rtl_archives* archives; /**< The archives to search. */
256} rtems_rtl_unresolved_archive_reloc_data;
257
258static bool
259rtems_rtl_unresolved_archive_iterator (rtems_rtl_unresolv_rec* rec,
260                                       void*                   data)
261{
262  if (rec->type == rtems_rtl_unresolved_symbol)
263  {
264    rtems_rtl_unresolved_archive_reloc_data* ard;
265    ard = (rtems_rtl_unresolved_archive_reloc_data*) data;
266
267    ++ard->name;
268
269    if ((rec->rec.name.flags & RTEMS_RTL_UNRESOLV_SYM_SEARCH_ARCHIVE) != 0)
270    {
271      rtems_rtl_archive_search load;
272
273      if (rtems_rtl_trace (RTEMS_RTL_TRACE_UNRESOLVED))
274        printf ("rtl: unresolv: archive lookup: %d: %s\n",
275                ard->name, rec->rec.name.name);
276
277      load = rtems_rtl_archive_obj_load (ard->archives,
278                                         rec->rec.name.name, true);
279      if (load == rtems_rtl_archive_search_loaded)
280      {
281        ard->loaded = true;
282        return true;
283      }
284    }
285  }
286
287  return false;
288}
289
290static bool
291rtems_rtl_unresolved_archive_search_iterator (rtems_rtl_unresolv_rec* rec,
292                                              void*                   data)
293{
294  if (rec->type == rtems_rtl_unresolved_symbol)
295    rec->rec.name.flags = RTEMS_RTL_UNRESOLV_SYM_SEARCH_ARCHIVE;
296  return false;
297}
298
299static void
300rtems_rtl_unresolved_clean_block (rtems_rtl_unresolv_block* block,
301                                  rtems_rtl_unresolv_rec*   rec,
302                                  size_t                    count,
303                                  size_t                    recs_per_block)
304{
305  size_t index = rtems_rtl_unresolved_rec_index (block, rec);
306  size_t bytes =
307    (block->recs - index - count) * sizeof (rtems_rtl_unresolv_rec);
308  if (bytes)
309    memmove (rec, rec + count, bytes);
310  block->recs -= count;
311  bytes = count * sizeof (rtems_rtl_unresolv_rec);
312  memset (&block->rec + block->recs, 0, bytes);
313}
314
315static void
316rtems_rtl_unresolved_compact (void)
317{
318  rtems_rtl_unresolved* unresolved = rtems_rtl_unresolved_unprotected ();
319  if (unresolved)
320  {
321    /*
322     * Iterate over the blocks removing any empty strings. If a string is
323     * removed update the indexes of all strings above this level.
324     */
325    rtems_chain_node* node = rtems_chain_first (&unresolved->blocks);
326    uint16_t          index = 0;
327    while (!rtems_chain_is_tail (&unresolved->blocks, node))
328    {
329      rtems_rtl_unresolv_block* block = (rtems_rtl_unresolv_block*) node;
330      rtems_rtl_unresolv_rec*   rec = rtems_rtl_unresolved_rec_first (block);
331
332      while (!rtems_rtl_unresolved_rec_is_last (block, rec))
333      {
334        bool next_rec = true;
335        if (rec->type == rtems_rtl_unresolved_symbol)
336        {
337          ++index;
338          if (rec->rec.name.refs == 0)
339          {
340            /*
341             * Iterate over the remaining reloc records and update the index.
342             */
343            rtems_chain_node*       reindex_node;
344            rtems_rtl_unresolv_rec* reindex_first;
345            size_t                  name_recs;
346            if (rtems_rtl_trace (RTEMS_RTL_TRACE_UNRESOLVED))
347              printf ("rtl: unresolv: remove name: %s\n", rec->rec.name.name);
348            reindex_node = node;
349            reindex_first = rtems_rtl_unresolved_rec_next (rec);
350            while (!rtems_chain_is_tail (&unresolved->blocks, reindex_node))
351            {
352              rtems_rtl_unresolv_rec*   reindex_rec;
353              rtems_rtl_unresolv_block* reindex_block;
354              reindex_block = (rtems_rtl_unresolv_block*) reindex_node;
355              if (reindex_first != NULL)
356              {
357                reindex_rec = reindex_first;
358                reindex_first = NULL;
359              }
360              else
361              {
362                reindex_rec = rtems_rtl_unresolved_rec_first (reindex_block);
363              }
364              while (!rtems_rtl_unresolved_rec_is_last (reindex_block,
365                                                        reindex_rec))
366              {
367                if (reindex_rec->type == rtems_rtl_unresolved_reloc)
368                {
369                  if (reindex_rec->rec.reloc.name >= index)
370                    --reindex_rec->rec.reloc.name;
371                }
372                reindex_rec = rtems_rtl_unresolved_rec_next (reindex_rec);
373              }
374              reindex_node = rtems_chain_next (reindex_node);
375            }
376            /*
377             * Compact the block removing the name record.
378             */
379            name_recs = rtems_rtl_unresolved_symbol_recs (rec->rec.name.name);
380            rtems_rtl_unresolved_clean_block (block, rec, name_recs,
381                                              unresolved->block_recs);
382            --index;
383            next_rec = false;
384          }
385        }
386        else if (rec->type == rtems_rtl_unresolved_reloc)
387        {
388          if (rec->rec.reloc.obj == NULL)
389          {
390            rtems_rtl_unresolved_clean_block (block, rec, 1,
391                                              unresolved->block_recs);
392            next_rec = false;
393          }
394        }
395
396        if (next_rec)
397          rec = rtems_rtl_unresolved_rec_next (rec);
398      }
399
400      if (block->recs == 0)
401      {
402        rtems_chain_node* next_node = rtems_chain_next (node);
403        if (rtems_rtl_trace (RTEMS_RTL_TRACE_UNRESOLVED))
404          printf ("rtl: unresolv: block-del %p\n", block);
405        rtems_chain_extract (node);
406        rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_EXTERNAL, block);
407        node = next_node;
408      }
409      else
410      {
411        node = rtems_chain_next (node);
412      }
413    }
414  }
415}
416
417bool
418rtems_rtl_unresolved_table_open (rtems_rtl_unresolved* unresolved,
419                                 size_t                block_recs)
420{
421  unresolved->marker = 0xdeadf00d;
422  unresolved->block_recs = block_recs;
423  rtems_chain_initialize_empty (&unresolved->blocks);
424  return true;
425}
426
427void
428rtems_rtl_unresolved_table_close (rtems_rtl_unresolved* unresolved)
429{
430  rtems_chain_node* node = rtems_chain_first (&unresolved->blocks);
431  while (!rtems_chain_is_tail (&unresolved->blocks, node))
432  {
433    rtems_chain_node* next = rtems_chain_next (node);
434    rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_EXTERNAL, node);
435    node = next;
436  }
437}
438
439bool
440rtems_rtl_unresolved_iterate (rtems_rtl_unresolved_iterator iterator,
441                              void*                         data)
442{
443  rtems_rtl_unresolved* unresolved = rtems_rtl_unresolved_unprotected ();
444  if (unresolved)
445  {
446    rtems_chain_node* node = rtems_chain_first (&unresolved->blocks);
447    while (!rtems_chain_is_tail (&unresolved->blocks, node))
448    {
449      rtems_rtl_unresolv_block* block = (rtems_rtl_unresolv_block*) node;
450      rtems_rtl_unresolv_rec*   rec = rtems_rtl_unresolved_rec_first (block);
451
452      while (!rtems_rtl_unresolved_rec_is_last (block, rec))
453      {
454        if (iterator (rec, data))
455          return true;
456        rec = rtems_rtl_unresolved_rec_next (rec);
457      }
458
459      node = rtems_chain_next (node);
460    }
461  }
462  return false;
463}
464
465bool
466rtems_rtl_unresolved_add (rtems_rtl_obj*        obj,
467                          const uint16_t        flags,
468                          const char*           name,
469                          const uint16_t        sect,
470                          const rtems_rtl_word* rel)
471{
472  rtems_rtl_unresolved*     unresolved;
473  rtems_chain_node*         node;
474  rtems_rtl_unresolv_block* block;
475  rtems_rtl_unresolv_rec*   rec;
476  int                       name_index;
477  size_t                    name_recs;
478
479  if (rtems_rtl_trace (RTEMS_RTL_TRACE_UNRESOLVED))
480    printf ("rtl: unresolv: add: %s(s:%d) -> %s\n",
481            rtems_rtl_obj_oname (obj), sect, name);
482
483  unresolved = rtems_rtl_unresolved_unprotected ();
484  if (!unresolved)
485    return false;
486
487  /*
488   * Find the first block with a spare record.
489   */
490  node = rtems_chain_first (&unresolved->blocks);
491  block = NULL;
492  while (!rtems_chain_is_tail (&unresolved->blocks, node))
493  {
494    block = (rtems_rtl_unresolv_block*) node;
495    if (block->recs < unresolved->block_recs)
496      break;
497    block = NULL;
498    node = rtems_chain_next (node);
499  }
500
501  /*
502   * No blocks with any spare records, allocate a new block.
503   */
504  if (!block)
505  {
506    block = rtems_rtl_unresolved_block_alloc (unresolved);
507    if (!block)
508      return false;
509  }
510
511  name_index = rtems_rtl_unresolved_find_name (unresolved, name, true);
512  name_recs = rtems_rtl_unresolved_symbol_recs (name);
513
514  /*
515   * An index less than 0 means the name is present and "0 - index" is the next
516   * index to use.
517   */
518  if (name_index < 0)
519  {
520    rtems_rtl_unresolv_block* name_block = block;
521
522    /*
523     * Is there enough room to fit the name ? It not add a new block.
524     */
525    if (name_recs > (unresolved->block_recs - block->recs))
526    {
527      name_block = rtems_rtl_unresolved_block_alloc (unresolved);
528      if (!name_block)
529        return false;
530    }
531
532    rec = rtems_rtl_unresolved_rec_first_free (name_block);
533    rec->type = rtems_rtl_unresolved_symbol;
534    rec->rec.name.refs = 1;
535    rec->rec.name.flags = RTEMS_RTL_UNRESOLV_SYM_SEARCH_ARCHIVE;
536    rec->rec.name.length = strlen (name) + 1;
537    memcpy ((void*) &rec->rec.name.name[0], name, rec->rec.name.length + 1);
538    block->recs += name_recs;
539    name_index = 0 - name_index;
540
541    /*
542     * If the name block is the reloc block and it is full allocate a new
543     * block for the relocation record.
544     */
545    if ((block == name_block) && (block->recs >= unresolved->block_recs))
546    {
547      block = rtems_rtl_unresolved_block_alloc (unresolved);
548      if (!block)
549        return false;
550    }
551  }
552
553  rec = rtems_rtl_unresolved_rec_first_free (block);
554  rec->type = rtems_rtl_unresolved_reloc;
555  rec->rec.reloc.obj = obj;
556  rec->rec.reloc.flags = flags;
557  rec->rec.reloc.name = name_index;
558  rec->rec.reloc.sect = sect;
559  rec->rec.reloc.rel[0] = rel[0];
560  rec->rec.reloc.rel[1] = rel[1];
561  rec->rec.reloc.rel[2] = rel[2];
562
563  ++block->recs;
564
565  return true;
566}
567
568void
569rtems_rtl_unresolved_resolve (void)
570{
571  bool resolving = true;
572
573  if (rtems_rtl_trace (RTEMS_RTL_TRACE_UNRESOLVED))
574    printf ("rtl: unresolv: global resolve\n");
575
576  /*
577   * The resolving process is two separate stages, The first stage is to
578   * iterate over the unresolved symbols search the global symbol table. If a
579   * symbol is found iterate over the unresolved relocation records for the
580   * symbol fixing up the relocations. The second stage is to search the
581   * archives for symbols we have not been search before and if a symbol if
582   * found in an archve loaded the object file. Loading an object file stops
583   * the search of the archives for symbols and stage one is performed
584   * again. The process repeats until no more symbols are resolved.
585   */
586  while (resolving)
587  {
588    rtems_rtl_unresolved_reloc_data rd = {
589      .name = 0,
590      .name_rec = NULL,
591      .sym = NULL
592    };
593    rtems_rtl_unresolved_archive_reloc_data ard = {
594      .name = 0,
595      .loaded = false,
596      .archives = rtems_rtl_archives_unprotected ()
597    };
598
599    rtems_rtl_unresolved_iterate (rtems_rtl_unresolved_resolve_iterator, &rd);
600    rtems_rtl_unresolved_compact ();
601    rtems_rtl_unresolved_iterate (rtems_rtl_unresolved_archive_iterator, &ard);
602
603    resolving = ard.loaded;
604  }
605
606  if (rtems_rtl_trace (RTEMS_RTL_TRACE_UNRESOLVED))
607    rtems_rtl_unresolved_dump ();
608}
609
610bool
611rtems_rtl_unresolved_remove (rtems_rtl_obj*        obj,
612                             const char*           name,
613                             const uint16_t        sect,
614                             const rtems_rtl_word* rel)
615{
616  rtems_rtl_unresolved* unresolved;
617  unresolved = rtems_rtl_unresolved_unprotected ();
618  if (!unresolved)
619    return false;
620  return false;
621}
622
623/**
624 * Struct to pass relocation data in the iterator.
625 */
626typedef struct rtems_rtl_unresolved_dump_data
627{
628  size_t rec;
629  size_t names;
630} rtems_rtl_unresolved_dump_data;
631
632static bool
633rtems_rtl_unresolved_dump_iterator (rtems_rtl_unresolv_rec* rec,
634                                    void*                   data)
635{
636  rtems_rtl_unresolved_dump_data* dd;
637  dd = (rtems_rtl_unresolved_dump_data*) data;
638  switch (rec->type)
639  {
640  case rtems_rtl_unresolved_empty:
641    printf (" %03zu: 0: empty\n", dd->rec);
642    break;
643  case rtems_rtl_unresolved_symbol:
644    ++dd->names;
645    printf (" %3zu: 1:  name: %3d refs: %2d: %2d: %s\n",
646            dd->rec, dd->names,
647            rec->rec.name.refs, rec->rec.name.length, rec->rec.name.name);
648    break;
649  case rtems_rtl_unresolved_reloc:
650    printf (" %3zu: 2: reloc: obj:%s name:%2d: sect:%d\n",
651            dd->rec,
652            rec->rec.reloc.obj->oname,
653            rec->rec.reloc.name,
654            rec->rec.reloc.sect);
655    break;
656  default:
657    printf (" %03zu: %d: unknown\n", dd->rec, rec->type);
658    break;
659  }
660  ++dd->rec;
661  return false;
662}
663
664void
665rtems_rtl_unresolved_set_archive_search (void)
666{
667  rtems_rtl_unresolved_iterate (rtems_rtl_unresolved_archive_search_iterator,
668                                NULL);
669}
670
671void
672rtems_rtl_unresolved_dump (void)
673{
674  rtems_rtl_unresolved_dump_data dd = { 0 };
675  printf ("RTL Unresolved data:\n");
676  rtems_rtl_unresolved_iterate (rtems_rtl_unresolved_dump_iterator, &dd);
677}
Note: See TracBrowser for help on using the repository browser.