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

5
Last change on this file since 4408603 was 89c59be, checked in by Chris Johns <chrisj@…>, on 12/17/18 at 05:36:48

libdl: Add symbol searching and loading from archives.

  • Load archive symbol tables to support searching of archives for symbols.
  • Search archive symbols and load the object file that contains the symbol.
  • Search the global and archives until all remaining unresolved symbols are not found. Group the loaded object files in the pending queue.
  • Run the object file and loaded dependents as a group before adding to the main object list.
  • Remove orphaned object files after references are removed.

Updates #3686

  • Property mode set to 100644
File size: 36.2 KB
Line 
1/*
2 *  COPYRIGHT (c) 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 rtl
12 *
13 * @brief RTEMS Run-Time Linker Archive
14 */
15
16#if HAVE_CONFIG_H
17#include "config.h"
18#endif
19
20#include <ctype.h>
21#include <dirent.h>
22#include <errno.h>
23#include <fnmatch.h>
24#include <stdio.h>
25#include <string.h>
26
27#include <rtems/libio_.h>
28
29#include <rtems/rtl/rtl.h>
30#include "rtl-chain-iterator.h"
31#include <rtems/rtl/rtl-trace.h>
32#include "rtl-string.h"
33#include "rtl-error.h"
34
35/**
36 * The archive symbols threshold after which a sorted symbol table is
37 * created.
38 */
39#define RTEMS_RTL_ARCHIVE_SYMBOLS_SORT (8)
40
41/**
42 * Archive headers.
43 */
44#define RTEMS_RTL_AR_IDENT      "!<arch>\n"
45#define RTEMS_RTL_AR_IDENT_SIZE (sizeof (RTEMS_RTL_AR_IDENT) - 1)
46#define RTEMS_RTL_AR_FHDR_BASE  RTEMS_RTL_AR_IDENT_SIZE
47#define RTEMS_RTL_AR_FNAME      (0)
48#define RTEMS_RTL_AR_FNAME_SIZE (16)
49#define RTEMS_RTL_AR_SIZE       (48)
50#define RTEMS_RTL_AR_SIZE_SIZE  (10)
51#define RTEMS_RTL_AR_MAGIC      (58)
52#define RTEMS_RTL_AR_MAGIC_SIZE (2)
53#define RTEMS_RTL_AR_FHDR_SIZE  (60)
54
55/**
56 * Read a 32bit value from the symbol table.
57 */
58static unsigned int
59rtems_rtl_archive_read_32 (void* data)
60{
61  uint8_t*     b = (uint8_t*) data;
62  unsigned int v = b[0];
63  v = (v << 8) | b[1];
64  v = (v << 8) | b[2];
65  v = (v << 8) | b[3];
66  return v;
67}
68
69static void
70rtems_rtl_archive_set_error (int num, const char* text)
71{
72  if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
73    printf ("rtl: archive: error: %3d:  %s\n", num, text);
74}
75
76static uint64_t
77rtems_rtl_scan_decimal (const uint8_t* string, size_t len)
78{
79  uint64_t value = 0;
80
81  while (len && (*string != ' '))
82  {
83    value *= 10;
84    value += *string - '0';
85    ++string;
86    --len;
87  }
88
89  return value;
90}
91
92static bool
93rtems_rtl_seek_read (int fd, off_t off, size_t len, uint8_t* buffer)
94{
95  if (lseek (fd, off, SEEK_SET) < 0)
96    return false;
97  if (read (fd, buffer, len) != len)
98    return false;
99  return true;
100}
101
102/**
103 * Archive iterator.
104 */
105typedef bool (*rtems_rtl_archive_iterator) (rtems_rtl_archive* archive,
106                                            void*              data);
107
108/**
109 * Chain iterator data.
110 */
111typedef struct rtems_rtl_archive_chain_data
112{
113  void*                      data;      /**< User's data. */
114  rtems_rtl_archive_iterator iterator;  /**< The actual iterator. */
115} rtems_rtl_archive_chain_data;
116
117static bool
118rtems_rtl_archive_node_iterator (rtems_chain_node* node, void* data)
119{
120  rtems_rtl_archive*            archive;
121  rtems_rtl_archive_chain_data* chain_data;
122  archive    = (rtems_rtl_archive*) node;
123  chain_data = (rtems_rtl_archive_chain_data*) data;
124  return chain_data->iterator (archive, chain_data->data);
125}
126
127static void
128rtems_rtl_archive_iterate_archives (rtems_rtl_archives*        archives,
129                                    rtems_rtl_archive_iterator iterator,
130                                    void*                      data)
131{
132  rtems_rtl_archive_chain_data chain_data = {
133    .data = data,
134    .iterator = iterator
135  };
136  rtems_rtl_chain_iterate (&archives->archives,
137                           rtems_rtl_archive_node_iterator,
138                           &chain_data);
139}
140
141static bool
142rtems_rtl_rchive_name_end (const char c)
143{
144  return c == '\0' || c == '\n' || c == '/';
145}
146
147static const char*
148rtems_rtl_archive_dup_name (const char* name)
149{
150  size_t len = 0;
151  char*  dup;
152  while (!rtems_rtl_rchive_name_end (name[len]))
153    ++len;
154  dup = rtems_rtl_alloc_new (RTEMS_RTL_ALLOC_OBJECT, len + 1, true);
155  if (dup != NULL)
156    memcpy (dup, name, len);
157  return dup;
158}
159
160static bool
161rtems_rtl_archive_match_name (const char* file_name, const char* name)
162{
163  if (name != NULL)
164  {
165    while (!rtems_rtl_rchive_name_end (*file_name) &&
166           !rtems_rtl_rchive_name_end (*name) && *file_name == *name)
167    {
168      ++file_name;
169      ++name;
170    }
171    if (((*file_name == '\0') || (*file_name == '\n') || (*file_name == '/')) &&
172        ((*name == '\0') || (*name == '/')))
173      return true;
174  }
175  return false;
176}
177
178static bool
179rtems_rtl_archive_set_flags (rtems_rtl_archive* archive, void* data)
180{
181  uint32_t mask = *((uint32_t*) data);
182  archive->flags |= mask;
183  return true;
184}
185
186typedef struct rtems_rtl_archive_find_data
187{
188  rtems_rtl_archive* archive;
189  const char*        path;
190} rtems_rtl_archive_find_data;
191
192static bool
193rtems_rtl_archive_finder (rtems_rtl_archive* archive, void* data)
194{
195  rtems_rtl_archive_find_data* find;
196  find = (rtems_rtl_archive_find_data*) data;
197  if (strcmp (find->path, archive->name) == 0)
198  {
199    find->archive = archive;
200    return false;
201  }
202  return true;
203}
204
205static rtems_rtl_archive*
206rtems_rtl_archive_find (rtems_rtl_archives* archives,
207                        const char*         path)
208{
209  rtems_rtl_archive_find_data find = {
210    .archive = NULL,
211    .path = path
212  };
213  rtems_rtl_archive_iterate_archives (archives,
214                                      rtems_rtl_archive_finder,
215                                      &find);
216  return find.archive;
217}
218
219/*
220 * Find an object file in archive that contains the symbol we are
221 * searching for.
222 *
223 * The symbol search is performance sensitive. The archive's symbol table being
224 * searched is the symbol table in the archive created by ranlib. This table is
225 * not sorted so a sorted table of pointered to the symbols is generated after
226 * loading if there are enough symbols. For small symbol tables the searc is
227 * linear. The entire table is held in memory. At the time of writing this code
228 * the symbol table for the SPARC architecture's libc is 16k.
229 *
230 * The ranlib table is:
231 *
232 *    [4]                - size of table in bytes
233 *    [0..(entries x 4)] - 4 byte binary offsets into the archive
234 *                         for each symbol
235 *    [0..m]             - variable length table of strings, nul
236 *                         separated and sorted
237 *
238 * Note: The loading of an object file from an archive uses an offset in the
239 *       file name to speed the loading.
240 */
241typedef struct rtems_rtl_archive_obj_data
242{
243  const char*        symbol;   /**< The symbol to search for. */
244  rtems_rtl_archive* archive;  /**< The archive the symbol is found
245                                *   in. */
246  off_t              offset;   /**< The offset in the archive if found
247                                *   else 0 */
248} rtems_rtl_archive_obj_data;
249
250static int
251rtems_rtl_archive_symbol_compare (const void* a, const void* b)
252{
253  const rtems_rtl_archive_symbol* sa;
254  const rtems_rtl_archive_symbol* sb;
255  sa = (const rtems_rtl_archive_symbol*) a;
256  sb = (const rtems_rtl_archive_symbol*) b;
257  return strcmp (sa->label, sb->label);
258}
259
260static bool
261rtems_rtl_archive_obj_finder (rtems_rtl_archive* archive, void* data)
262{
263  const rtems_rtl_archive_symbols* symbols = &archive->symbols;
264
265  if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
266    printf ("rtl: archive: finder: %s: entries: %zu\n",
267            archive->name, symbols->entries);
268
269  /*
270   * Make sure there is a valid symbol table.
271   */
272  if (symbols->base != NULL)
273  {
274    /*
275     * Perform a linear search if there is no sorted symbol table.
276     */
277    rtems_rtl_archive_obj_data* search = (rtems_rtl_archive_obj_data*) data;
278    if (symbols->symbols == NULL)
279    {
280      const char* symbol = symbols->names;
281      size_t      entry;
282      for (entry = 0; entry < symbols->entries; ++entry)
283      {
284        if (strcmp (search->symbol, symbol) == 0)
285        {
286          search->archive = archive;
287          search->offset =
288            rtems_rtl_archive_read_32 (symbols->base + (entry * 4));
289          return false;
290        }
291        symbol += strlen (symbol) + 1;
292      }
293    }
294    else
295    {
296      ssize_t entry = symbols->entries / 2;
297      ssize_t offset = entry;
298      ssize_t last_entry = -1;
299      while (entry >= 0 &&
300             entry < symbols->entries &&
301             entry != last_entry &&
302             offset > 0)
303      {
304        int cmp = strcmp (search->symbol, symbols->symbols[entry].label);
305        if (cmp == 0)
306        {
307          entry = symbols->symbols[entry].entry;
308          search->archive = archive;
309          search->offset =
310            rtems_rtl_archive_read_32 (symbols->base + (entry * 4));
311          return false;
312        }
313        last_entry = entry;
314        if (offset == 1)
315          offset = 0;
316        else
317          offset = ((offset - 1) / 2) + 1;
318        if (cmp < 0)
319          entry -= offset;
320        else
321          entry += offset;
322      }
323    }
324  }
325
326  /*
327   * Next archive.
328   */
329  return true;
330}
331
332static rtems_rtl_archive*
333rtems_rtl_archive_new (rtems_rtl_archives* archives,
334                       const char*         path,
335                       const char*         name)
336{
337  rtems_rtl_archive* archive;
338  size_t             path_size;
339  size_t             size;
340  /*
341   * Handle the case of the path being just '/', do not create '//'.
342   */
343  path_size = strlen (path);
344  size = sizeof(rtems_rtl_archive) + path_size + strlen (name) + 1;
345  if (path_size > 1)
346    ++size;
347  archive = rtems_rtl_alloc_new (RTEMS_RTL_ALLOC_OBJECT, size, true);
348  if (archive == NULL)
349  {
350    if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
351      printf ("rtl: archive: new: %s: no memory\n", name);
352  }
353  else
354  {
355    char* aname;
356    archive->name = ((const char*) archive) + sizeof(rtems_rtl_archive);
357    aname = (char*) archive->name;
358    strcpy (aname, path);
359    if (path_size > 1)
360      strcat (aname, "/");
361    strcat (aname, name);
362    rtems_chain_set_off_chain (&archive->node);
363    archive->flags |= RTEMS_RTL_ARCHIVE_LOAD;
364  }
365  return archive;
366}
367
368static void
369rtems_rtl_archive_del (rtems_rtl_archive* archive)
370{
371  if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
372    printf ("rtl: archive: del: %s\n",  archive->name);
373  rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_SYMBOL, archive->symbols.base);
374  rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_SYMBOL, archive->symbols.symbols);
375  if (!rtems_chain_is_node_off_chain (&archive->node))
376    rtems_chain_extract (&archive->node);
377  rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_OBJECT, archive);
378}
379
380static rtems_rtl_archive*
381rtems_rtl_archive_get (rtems_rtl_archives* archives,
382                       const char*         path,
383                       const char*         name)
384{
385  rtems_rtl_archive* archive;
386  /*
387   * Getting a new archive turns the path and name into a single path the stat
388   * function can use. No matter how you try some memory is needed so it is
389   * easier to get a new archive object and delete it if it exists.
390   */
391  archive = rtems_rtl_archive_new (archives, path, name);
392  if (archive != NULL)
393  {
394    struct stat sb;
395    if (stat (archive->name, &sb) == 0)
396    {
397      if (S_ISREG (sb.st_mode))
398      {
399        rtems_rtl_archive* find_archive;
400        find_archive = rtems_rtl_archive_find (archives, archive->name);
401        if (find_archive == NULL)
402        {
403          rtems_chain_append (&archives->archives, &archive->node);
404        }
405        else
406        {
407          rtems_rtl_archive_del (archive);
408          archive = find_archive;
409        }
410        archive->flags &= ~RTEMS_RTL_ARCHIVE_REMOVE;
411        if (archive->mtime != sb.st_mtime)
412        {
413          archive->flags |= RTEMS_RTL_ARCHIVE_LOAD;
414          archive->size = sb.st_size;
415          archive->mtime = sb.st_mtime;
416        }
417      }
418    }
419  }
420  return archive;
421}
422
423static bool
424rtems_rtl_archives_load_config (rtems_rtl_archives* archives)
425{
426  struct stat sb;
427
428  if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
429    printf ("rtl: archive: config load: %s\n", archives->config_name);
430
431  if (archives->config_name == NULL)
432    return false;
433
434  if (stat (archives->config_name, &sb) < 0)
435  {
436    if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
437      printf ("rtl: archive: no config: %s\n", archives->config_name);
438    return false;
439  }
440
441  /*
442   * If the configuration has change reload it.
443   */
444  if (sb.st_mtime != archives->config_mtime)
445  {
446    int     fd;
447    ssize_t r;
448    char*   s;
449    bool    in_comment;
450
451    archives->config_mtime = sb.st_mtime;
452    rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_OBJECT, (void*) archives->config);
453    archives->config_length = 0;
454    archives->config =
455      rtems_rtl_alloc_new (RTEMS_RTL_ALLOC_OBJECT, sb.st_size, false);
456    if (archives->config == NULL)
457    {
458      if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
459        printf ("rtl: archive: no memory for config\n");
460      return false;
461    }
462
463    if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
464      printf ("rtl: archive: config load: read %s\n", archives->config_name);
465
466    fd = open (archives->config_name, O_RDONLY);
467    if (fd < 0)
468    {
469      rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_OBJECT, (void*) archives->config);
470      archives->config = NULL;
471      archives->config_length = 0;
472      if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
473        printf ("rtl: archive: config open error: %s\n", strerror (errno));
474      return false;
475    }
476
477    r = read (fd, (void*) archives->config, sb.st_size);
478    if (r < 0)
479    {
480      close (fd);
481      rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_OBJECT, (void*) archives->config);
482      archives->config = NULL;
483      archives->config_length = 0;
484      if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
485        printf ("rtl: archive: config read error: %s\n", strerror (errno));
486    }
487
488    close (fd);
489    archives->config_length = r;
490
491    /*
492     * Remove comments.
493     */
494    s = (char*) archives->config;
495    in_comment = false;
496    for (r = 0; r < archives->config_length; ++r, ++s)
497    {
498      if (*s == '#')
499        in_comment = true;
500      if (in_comment)
501      {
502        if (*s == '\n')
503          in_comment = false;
504        *s = '\0';
505      }
506    }
507
508    /*
509     * Create lines by turning '\r' and '\n' to '\0'.
510     */
511    s = (char*) archives->config;
512    for (r = 0; r < archives->config_length; ++r, ++s)
513    {
514      if (*s == '\r' || *s == '\n')
515        *s = '\0';
516    }
517
518    /*
519     * Remove leading and trailing white space.
520     */
521    s = (char*) archives->config;
522    for (r = 0; r < archives->config_length; ++r)
523    {
524      if (s[r] != '\0')
525      {
526        size_t ls = strlen (&s[r]);
527        size_t b = 0;
528        while (b < ls && isspace (s[r + b]))
529        {
530          ++b;
531        }
532        if (b > 0)
533          memmove (&s[r], &s[r + b], ls - b);
534        b = ls - 1;
535        while (b > 0 && isspace (s[r + b]))
536        {
537          s[r + b] = '\0';
538          --b;
539        }
540      }
541    }
542
543    /*
544     * Compact the lines so there is only a single nul separator.
545     */
546    s = (char*) archives->config;
547    for (r = 0; r < archives->config_length; ++r)
548    {
549      if (s[r] == '\0')
550      {
551        size_t e = r + 1;
552        while (e < archives->config_length)
553        {
554          if (s[e] != '\0')
555          {
556            if (archives->config_length - e - 1 > 0)
557              memmove (&s[r + 1], &s[e], archives->config_length - e - 1);
558            break;
559          }
560        }
561      }
562    }
563
564    if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
565    {
566      int line = 1;
567      printf ("rtl: archive: config:\n");
568      s = (char*) archives->config;
569      for (r = 0; r < archives->config_length; ++r, ++line)
570      {
571        size_t len = strlen (s);
572        printf (" %3d: %s\n", line, s);
573        s += len + 2;
574        r += len;
575      }
576    }
577  }
578
579  return true;
580}
581
582void
583rtems_rtl_archives_open (rtems_rtl_archives* archives, const char* config)
584{
585  if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
586    printf ("rtl: archive: open: %s\n", config);
587  memset (archives, 0, sizeof (rtems_rtl_archives));
588  archives->config_name = rtems_rtl_strdup (config);
589  rtems_chain_initialize_empty (&archives->archives);
590}
591
592void
593rtems_rtl_archives_close (rtems_rtl_archives* archives)
594{
595  rtems_chain_node* node;
596  if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
597    printf ("rtl: archive: close: count=%ds\n",
598            rtems_chain_node_count_unprotected (&archives->archives));
599  node = rtems_chain_first (&archives->archives);
600  while (!rtems_chain_is_tail (&archives->archives, node))
601  {
602    rtems_rtl_archive* archive = (rtems_rtl_archive*) node;
603    rtems_chain_node*  next_node = rtems_chain_next (node);
604    rtems_rtl_archive_del (archive);
605    node = next_node;
606  }
607  rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_OBJECT, (void*) archives->config);
608  rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_OBJECT, archives);
609}
610
611static void
612rtems_rtl_archives_remove (rtems_rtl_archives* archives)
613{
614  rtems_chain_node* node = rtems_chain_first (&archives->archives);
615  if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
616    printf ("rtl: archive: refresh: remove: checking %d archive(s)\n",
617            rtems_chain_node_count_unprotected (&archives->archives));
618  while (!rtems_chain_is_tail (&archives->archives, node))
619  {
620    rtems_rtl_archive* archive = (rtems_rtl_archive*) node;
621    rtems_chain_node*  next_node = rtems_chain_next (node);
622    if ((archive->flags & RTEMS_RTL_ARCHIVE_REMOVE) != 0)
623    {
624      archive->flags &= ~RTEMS_RTL_ARCHIVE_REMOVE;
625      if ((archive->flags & RTEMS_RTL_ARCHIVE_USER_LOAD) == 0)
626        rtems_rtl_archive_del (archive);
627    }
628    node = next_node;
629  }
630}
631
632static bool
633rtems_rtl_archive_loader (rtems_rtl_archive* archive, void* data)
634{
635  int* loaded = (int*) data;
636
637  if ((archive->flags & RTEMS_RTL_ARCHIVE_LOAD) != 0)
638  {
639    int         fd;
640    off_t       offset = 0;
641    size_t      size = 0;
642    const char* name = "/";
643
644    if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
645      printf ("rtl: archive: loader: %s\n", archive->name);
646
647    fd = open (archive->name, O_RDONLY);
648    if (fd < 0)
649    {
650      if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
651        printf ("rtl: archive: loader: open error: %s: %s\n",
652                archive->name, strerror (errno));
653      rtems_rtl_archive_set_error (errno, "opening archive file");
654      return true;
655    }
656
657    if (rtems_rtl_obj_archive_find_obj (fd,
658                                        archive->size,
659                                        &name,
660                                        &offset,
661                                        &size,
662                                        &archive->enames,
663                                        rtems_rtl_archive_set_error))
664    {
665      if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
666        printf ("rtl: archive: loader: symbols: off=0x%08jx size=%zu\n",
667                offset, size);
668
669      /*
670       * Reallocation the symbol table memory if it has changed size.
671       * Note, an updated library may have te same symbol table.
672       */
673      if (archive->symbols.size != size)
674      {
675        rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_SYMBOL, archive->symbols.base);
676        archive->symbols.base = rtems_rtl_alloc_new (RTEMS_RTL_ALLOC_SYMBOL,
677                                                     size,
678                                                     false);
679        if (archive->symbols.base == NULL)
680        {
681          close (fd);
682          memset (&archive->symbols, 0, sizeof (archive->symbols));
683          rtems_rtl_archive_set_error (ENOMEM, "symbol table memory");
684          return true;
685        }
686      }
687
688      /*
689       * Read the symbol table into memory and hold.
690       */
691      if (!rtems_rtl_seek_read (fd, offset, size, archive->symbols.base))
692      {
693        rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_SYMBOL, archive->symbols.base);
694        close (fd);
695        memset (&archive->symbols, 0, sizeof (archive->symbols));
696        rtems_rtl_archive_set_error (errno, "reading symbols");
697        return true;
698      }
699
700      /*
701       * The first 4 byte value is the number of entries.
702       */
703      archive->symbols.entries =
704        rtems_rtl_archive_read_32 (archive->symbols.base);
705      archive->symbols.size   = size;
706      archive->symbols.names  = archive->symbols.base;
707      archive->symbols.names += (archive->symbols.entries + 1) * 4;
708
709      /*
710       * Created a sorted symbol table if over the threshold number of symbols.
711       */
712      if (archive->symbols.entries > RTEMS_RTL_ARCHIVE_SYMBOLS_SORT)
713      {
714        const size_t size =
715          archive->symbols.entries * sizeof (rtems_rtl_archive_symbol);
716        archive->symbols.symbols =
717          rtems_rtl_alloc_new (RTEMS_RTL_ALLOC_SYMBOL, size, true);
718        if (archive->symbols.symbols != NULL)
719        {
720          const char* symbol = archive->symbols.names;
721          size_t      e;
722          for (e = 0; e < archive->symbols.entries; ++e)
723          {
724            archive->symbols.symbols[e].entry = e + 1;
725            archive->symbols.symbols[e].label = symbol;
726            symbol += strlen (symbol) + 1;
727          }
728          qsort (archive->symbols.symbols,
729                 archive->symbols.entries,
730                 sizeof (rtems_rtl_archive_symbol),
731                 rtems_rtl_archive_symbol_compare);
732        }
733      }
734
735      if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
736        printf ("rtl: archive: loader: symbols: " \
737                "base=%p entries=%zu names=%p (0x%08x) symbols=%p\n",
738                archive->symbols.base,
739                archive->symbols.entries,
740                archive->symbols.names,
741                (archive->symbols.entries + 1) * 4,
742                archive->symbols.symbols);
743    }
744
745    close (fd);
746
747    archive->flags &= ~RTEMS_RTL_ARCHIVE_LOAD;
748
749    ++(*loaded);
750  }
751
752  return true;
753}
754
755static bool
756rtems_rtl_archives_load (rtems_rtl_archives* archives)
757{
758  int loaded = 0;
759  if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
760    printf ("rtl: archive: archive: load\n");
761  rtems_rtl_archive_iterate_archives (archives,
762                                      rtems_rtl_archive_loader,
763                                      &loaded);
764  return loaded > 0;
765}
766
767bool
768rtems_rtl_archives_refresh (rtems_rtl_archives* archives)
769{
770  size_t   config_path = 0;
771  uint32_t flags = RTEMS_RTL_ARCHIVE_REMOVE;
772
773  /*
774   * Reload the configuration file if it has not been loaded or has been
775   * updated.
776   */
777  if (!rtems_rtl_archives_load_config (archives))
778    return false;
779
780  /*
781   * Assume all existing archives are to be removed. If an existing archive
782   * is ccnfigured and found teh remove flags is cleared. At the of the
783   * refresh end remove any archives with this flag still set.
784   */
785  rtems_rtl_archive_iterate_archives (archives,
786                                      rtems_rtl_archive_set_flags,
787                                      &flags);
788
789  while (config_path < archives->config_length)
790  {
791    const char* dirname = &archives->config[config_path];
792    char*       basename;
793    const char  root[2] = { '/', '\0' };
794    DIR*        dir;
795
796    if (*dirname == '\0')
797    {
798      ++config_path;
799      continue;
800    }
801
802    if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
803      printf ("rtl: archive: refresh: %s\n", dirname);
804
805    config_path += strlen (dirname);
806
807    /*
808     * Relative paths do not work in the config. Must be absolute.
809     */
810    if (dirname[0] != '/')
811    {
812      if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
813        printf ("rtl: archive: refresh: relative paths ignored: %s\n", dirname);
814      continue;
815    }
816
817    /*
818     * Scan the parent directory of the glob path matching file names.
819     */
820    basename = strrchr (dirname, '/');
821    if (basename == NULL)
822      continue;
823
824    if (basename == dirname)
825      dirname = &root[0];
826
827    *basename = '\0';
828    ++basename;
829
830    dir = opendir (dirname);
831
832    if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
833      printf ("rtl: archive: refresh: %s %sfound\n",
834              dirname, dir == NULL ? ": not " : "");
835
836    if (dir != NULL)
837    {
838      while (true)
839      {
840        struct dirent  entry;
841        struct dirent* result = NULL;
842
843        if (readdir_r (dir, &entry, &result) != 0)
844        {
845          if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
846            printf ("rtl: archive: refresh: readdir error\n");
847          break;
848        }
849
850        if (result == NULL)
851          break;
852
853        if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
854          printf ("rtl: archive: refresh: checking: %s\n", entry.d_name);
855
856        if (fnmatch (basename, entry.d_name, 0) == 0)
857        {
858          struct stat sb;
859          if (stat (entry.d_name, &sb) == 0)
860          {
861            rtems_rtl_archive* archive;
862            archive = rtems_rtl_archive_get (archives, dirname, entry.d_name);
863            if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
864              printf ("rtl: archive: refresh: %s: %sfound\n",
865                      entry.d_name, archive == NULL ? ": not " : "");
866          }
867        }
868      }
869      closedir (dir);
870    }
871
872    --basename;
873    *basename = '/';
874  }
875
876  /*
877   * Remove all archives flagged to be removed.
878   */
879  rtems_rtl_archives_remove (archives);
880
881  /*
882   * Load any new archives. If any are loaded set the archive search flag in
883   * any unresolved external symbols so the archives are searched. The archive
884   * search flag avoids searching for symbols we know are not in the known
885   * archives,
886   */
887  if (rtems_rtl_archives_load (archives))
888    rtems_rtl_unresolved_set_archive_search ();
889
890  return true;
891}
892
893bool
894rtems_rtl_archive_load (rtems_rtl_archives* archives, const char* name)
895{
896  if (archives != NULL)
897  {
898    rtems_rtl_archive* archive;
899    int                loaded = 0;
900
901    archive = rtems_rtl_archive_get (archives, "", name);
902    if (archive == NULL)
903    {
904      rtems_rtl_set_error (ENOENT, "archive not found");
905      return false;
906    }
907
908    archive->flags |= RTEMS_RTL_ARCHIVE_USER_LOAD;
909
910    rtems_rtl_archive_loader (archive, &loaded);
911    if (loaded == 0)
912    {
913      rtems_rtl_archive_del (archive);
914      rtems_rtl_set_error (ENOENT, "archive load falied");
915    }
916
917    return true;
918  }
919  return false;
920}
921
922rtems_rtl_archive_search
923rtems_rtl_archive_obj_load (rtems_rtl_archives* archives,
924                            const char*         symbol,
925                            bool                load)
926{
927  rtems_rtl_obj*       obj;
928  rtems_chain_control* pending;
929  int                  fd;
930  size_t               archive_count;
931
932  rtems_rtl_archive_obj_data search = {
933    .symbol  = symbol,
934    .archive = NULL,
935    .offset  = 0
936  };
937
938  archive_count = rtems_chain_node_count_unprotected (&archives->archives);
939
940  if (archive_count == 0)
941  {
942    if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
943      printf ("rtl: archive: load: no archives\n");
944    return rtems_rtl_archive_search_no_config;
945  }
946
947  pending = rtems_rtl_pending_unprotected ();
948  if (pending == NULL)
949  {
950    if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
951      printf ("rtl: archive: load: no pending list\n");
952    return rtems_rtl_archive_search_not_found;
953  }
954
955  if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
956    printf ("rtl: archive: load: searching %zu archives\n", archive_count);
957
958  rtems_rtl_archive_iterate_archives (archives,
959                                      rtems_rtl_archive_obj_finder,
960                                      &search);
961
962  if (search.archive == NULL)
963  {
964    if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
965      printf ("rtl: archive: load: not found: %s\n", symbol);
966    return rtems_rtl_archive_search_not_found;
967  }
968
969  if (!load)
970  {
971    if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
972      printf ("rtl: archive: load: found (no load): %s\n", symbol);
973    return rtems_rtl_archive_search_found;
974  }
975
976  obj = rtems_rtl_obj_alloc ();
977  if (obj == NULL)
978  {
979    if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
980      printf ("rtl: archive: alloc: no memory: %s\n",
981              search.archive->name);
982    return rtems_rtl_archive_search_error;
983  }
984
985  obj->aname = rtems_rtl_strdup (search.archive->name);
986
987  fd = open (obj->aname, O_RDONLY);
988  if (fd < 0)
989  {
990    if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
991      printf ("rtl: archive: load: open error: %s: %s\n",
992              obj->aname, strerror (errno));
993    rtems_rtl_obj_free (obj);
994    return rtems_rtl_archive_search_error;
995  }
996
997  obj->oname = NULL;
998  obj->ooffset = search.offset;
999
1000  if (!rtems_rtl_obj_archive_find_obj (fd,
1001                                       search.archive->size,
1002                                       &obj->oname,
1003                                       &obj->ooffset,
1004                                       &obj->fsize,
1005                                       &search.archive->enames,
1006                                       rtems_rtl_archive_set_error))
1007  {
1008    if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
1009      printf ("rtl: archive: load: load error: %s:%s\n",
1010              obj->aname, obj->oname);
1011    close (fd);
1012    rtems_rtl_obj_free (obj);
1013    return rtems_rtl_archive_search_error;
1014  }
1015
1016  obj->fname = rtems_rtl_strdup (obj->aname);
1017  obj->ooffset -= RTEMS_RTL_AR_FHDR_SIZE;
1018  obj->fsize = search.archive->size;
1019
1020  close (fd);
1021
1022  if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
1023    printf ("rtl: archive: loading: %s:%s@0x%08jx size:%zu\n",
1024            obj->aname, obj->oname, obj->ooffset, obj->fsize);
1025
1026  rtems_chain_append (pending, &obj->link);
1027
1028  if (!rtems_rtl_obj_load (obj))
1029  {
1030    if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
1031      printf ("rtl: archive: loading: error: %s:%s@0x%08jx\n",
1032              obj->aname, obj->oname, obj->ooffset);
1033    rtems_chain_extract (&obj->link);
1034    rtems_rtl_obj_free (obj);
1035    rtems_rtl_obj_caches_flush ();
1036    return rtems_rtl_archive_search_error;
1037  }
1038
1039  rtems_rtl_obj_caches_flush ();
1040
1041  if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
1042    printf ("rtl: archive: loading: loaded: %s:%s@0x%08jx\n",
1043            obj->aname, obj->oname, obj->ooffset);
1044
1045  return rtems_rtl_archive_search_loaded;
1046}
1047
1048bool
1049rtems_rtl_obj_archive_find_obj (int                     fd,
1050                                size_t                  fsize,
1051                                const char**            name,
1052                                off_t*                  ooffset,
1053                                size_t*                 osize,
1054                                off_t*                  extended_file_names,
1055                                rtems_rtl_archive_error error)
1056{
1057  uint8_t header[RTEMS_RTL_AR_FHDR_SIZE];
1058  bool    scanning;
1059
1060  if (name == NULL)
1061  {
1062    error (errno, "invalid object name");
1063    *ooffset = 0;
1064    *osize = 0;
1065    return false;
1066  }
1067
1068  if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
1069    printf ("rtl: archive: find obj: %s @ 0x%08jx\n", *name, *ooffset);
1070
1071  if (read (fd, &header[0], RTEMS_RTL_AR_IDENT_SIZE) !=  RTEMS_RTL_AR_IDENT_SIZE)
1072  {
1073    error (errno, "reading archive identifer");
1074    *ooffset = 0;
1075    *osize = 0;
1076    return false;
1077  }
1078
1079  if (memcmp (header, RTEMS_RTL_AR_IDENT, RTEMS_RTL_AR_IDENT_SIZE) != 0)
1080  {
1081    error (EINVAL, "invalid archive identifer");
1082    *ooffset = 0;
1083    *osize = 0;
1084    return false;
1085  }
1086
1087  /*
1088   * Seek to the current offset in the archive and if we have a valid archive
1089   * file header present check the file name for a match with the oname field
1090   * of the object descriptor. If the archive header is not valid and it is the
1091   * first pass reset the offset and start the search again in case the offset
1092   * provided is not valid any more.
1093   *
1094   * The archive can have a symbol table at the start. Ignore it. A symbol
1095   * table has the file name '/'.
1096   *
1097   * The archive can also have the GNU extended file name table. This
1098   * complicates the processing. If the object's file name starts with '/' the
1099   * remainder of the file name is an offset into the extended file name
1100   * table. To find the extended file name table we need to scan from start of
1101   * the archive for a file name of '//'. Once found we remeber the table's
1102   * start and can direct seek to file name location. In other words the scan
1103   * only happens once.
1104   *
1105   * If the name had the offset encoded we go straight to that location.
1106   */
1107
1108  if (*ooffset != 0)
1109    scanning = false;
1110  else
1111  {
1112    if (*name == NULL)
1113    {
1114      error (errno, "invalid object name and archive offset");
1115      *ooffset = 0;
1116      *osize = 0;
1117      return false;
1118    }
1119    scanning = true;
1120    *ooffset = RTEMS_RTL_AR_FHDR_BASE;
1121    *osize = 0;
1122  }
1123
1124  while (*ooffset < fsize)
1125  {
1126    /*
1127     * Clean up any existing data.
1128     */
1129    memset (header, 0, sizeof (header));
1130
1131    if (!rtems_rtl_seek_read (fd, *ooffset, RTEMS_RTL_AR_FHDR_SIZE, &header[0]))
1132    {
1133      error (errno, "seek/read archive file header");
1134      *ooffset = 0;
1135      *osize = 0;
1136      return false;
1137    }
1138
1139    if ((header[RTEMS_RTL_AR_MAGIC] != 0x60) ||
1140        (header[RTEMS_RTL_AR_MAGIC + 1] != 0x0a))
1141    {
1142      if (scanning)
1143      {
1144        error (EINVAL, "invalid archive file header");
1145        *ooffset = 0;
1146        *osize = 0;
1147        return false;
1148      }
1149
1150      scanning = true;
1151      *ooffset = RTEMS_RTL_AR_FHDR_BASE;
1152      continue;
1153    }
1154
1155    /*
1156     * The archive header is always aligned to an even address.
1157     */
1158    *osize = (rtems_rtl_scan_decimal (&header[RTEMS_RTL_AR_SIZE],
1159                                      RTEMS_RTL_AR_SIZE_SIZE) + 1) & ~1;
1160
1161    /*
1162     * Check for the GNU extensions.
1163     */
1164    if (header[0] == '/')
1165    {
1166      off_t extended_off;
1167
1168      switch (header[1])
1169      {
1170        case ' ':
1171          /*
1172           * SVR4/GNU Symbols table. Nothing more to do.
1173           */
1174          *ooffset += RTEMS_RTL_AR_FHDR_SIZE;
1175          return true;
1176        case '/':
1177          /*
1178           * Extended file names table. Remember. If asked to find this file
1179           * return the result.
1180           */
1181          *extended_file_names = *ooffset + RTEMS_RTL_AR_FHDR_SIZE;
1182          if (*name[0] == '/' && *name[1] == '/')
1183          {
1184            *ooffset = *ooffset + RTEMS_RTL_AR_FHDR_SIZE;
1185            return true;
1186          }
1187          break;
1188        case '0':
1189        case '1':
1190        case '2':
1191        case '3':
1192        case '4':
1193        case '5':
1194        case '6':
1195        case '7':
1196        case '8':
1197        case '9':
1198          /*
1199           * Offset into the extended file name table. If we do not have the
1200           * offset to the extended file name table find it.
1201           */
1202          extended_off =
1203            rtems_rtl_scan_decimal (&header[1], RTEMS_RTL_AR_FNAME_SIZE);
1204
1205          if (*extended_file_names == 0)
1206          {
1207            off_t off = RTEMS_RTL_AR_IDENT_SIZE;
1208            while (*extended_file_names == 0)
1209            {
1210              off_t esize;
1211
1212              if (!rtems_rtl_seek_read (fd, off,
1213                                        RTEMS_RTL_AR_FHDR_SIZE, &header[0]))
1214              {
1215                error (errno, "seeking/reading archive ext file name header");
1216                *ooffset = 0;
1217                *osize = 0;
1218                return false;
1219              }
1220
1221              if ((header[RTEMS_RTL_AR_MAGIC] != 0x60) ||
1222                  (header[RTEMS_RTL_AR_MAGIC + 1] != 0x0a))
1223              {
1224                error (errno, "invalid archive file header");
1225                *ooffset = 0;
1226                *osize = 0;
1227                return false;
1228              }
1229
1230              if ((header[0] == '/') && (header[1] == '/'))
1231              {
1232                *extended_file_names = off + RTEMS_RTL_AR_FHDR_SIZE;
1233                break;
1234              }
1235
1236              esize =
1237                (rtems_rtl_scan_decimal (&header[RTEMS_RTL_AR_SIZE],
1238                                         RTEMS_RTL_AR_SIZE_SIZE) + 1) & ~1;
1239              off += esize + RTEMS_RTL_AR_FHDR_SIZE;
1240            }
1241          }
1242
1243          if (*extended_file_names != 0)
1244          {
1245            /*
1246             * We know the offset in the archive to the extended file. Read the
1247             * name from the table and compare with the name we are after.
1248             */
1249            #define RTEMS_RTL_MAX_FILE_SIZE (256)
1250            char ename[RTEMS_RTL_MAX_FILE_SIZE];
1251
1252            if (!rtems_rtl_seek_read (fd, *extended_file_names + extended_off,
1253                                      RTEMS_RTL_MAX_FILE_SIZE, (uint8_t*) &ename[0]))
1254            {
1255              error (errno, "invalid archive ext file seek/read");
1256              *ooffset = 0;
1257              *osize = 0;
1258              return false;
1259            }
1260
1261            /*
1262             * If there is no name memory the user is asking us to return the
1263             * name in the archive at the offset.
1264             */
1265            if (*name == NULL)
1266              *name = rtems_rtl_archive_dup_name (ename);
1267            if (rtems_rtl_archive_match_name (*name, ename))
1268            {
1269              *ooffset += RTEMS_RTL_AR_FHDR_SIZE;
1270              return true;
1271            }
1272          }
1273          break;
1274        default:
1275          /*
1276           * Ignore the file because we do not know what it it.
1277           */
1278          break;
1279      }
1280    }
1281    else
1282    {
1283      const char* ename = (const char*) &header[RTEMS_RTL_AR_FNAME];
1284      if (*name == NULL)
1285        *name = rtems_rtl_archive_dup_name (ename);
1286      if (rtems_rtl_archive_match_name (*name, ename))
1287      {
1288        *ooffset += RTEMS_RTL_AR_FHDR_SIZE;
1289        return true;
1290      }
1291    }
1292
1293    *ooffset += *osize + RTEMS_RTL_AR_FHDR_SIZE;
1294  }
1295
1296  error (ENOENT, "object file not found");
1297  *ooffset = 0;
1298  *osize = 0;
1299  return false;
1300
1301}
Note: See TracBrowser for help on using the repository browser.