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

5
Last change on this file since dad6fd43 was dad6fd43, checked in by Chris Johns <chrisj@…>, on 03/09/19 at 18:04:42

libdl: Add an archive command

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