summaryrefslogblamecommitdiffstats
path: root/cpukit/libdl/rtl-archive.c
blob: 4a6d2cbf0b984c8f7830371f973d79275641716c (plain) (tree)
1
2
3
4
5
6
7
8
9
10

                                           







                                       


                                                     



















                                                                              
   
 
                    






                    
                     











                                






















































































































































































                                                                                

                                                                              




























































                                                                               
                                                                          






                                      










                                                         
       

                                    
                                                                           
                       




































































































































                                                                              
                                                                         





























                                                                             
                   



































                                                      

                                       
     




                       


                                   
                                                            
         
                          

              
                   
                                                           
         
                          

              
                






                                                   

                                         
       







                                              





















                                                                          
                                               

















                                                                         
                                                                       




















































                                                                        

                                                                   




























                                                                            

                                                                         


                                                          








                                                                                     




                                                                   
                                      
         



                                                                          
       


                                                      
         


                                                     
         



                                                 







                                                                     
                                                                  
                                          












                                                               














































































































                                                                                

                                                                        


                                                     




                                                                            



































































































































































                                                                              


                                                                  




































































































































                                                                                 
                                















                                                                            
                                                 






















































































































                                                                                     
/* SPDX-License-Identifier: BSD-2-Clause */

/**
 * @file
 *
 * @ingroup rtl
 *
 * @brief RTEMS Run-Time Linker Archive
 */

/*
 *  COPYRIGHT (c) 2018 Chris Johns <chrisj@rtems.org>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <fnmatch.h>
#include <inttypes.h>
#include <stdio.h>
#include <string.h>

#include <rtems/libio_.h>

#include <rtems/rtl/rtl.h>
#include "rtl-chain-iterator.h"
#include <rtems/rtl/rtl-trace.h>
#include "rtl-string.h"
#include "rtl-error.h"

/**
 * Archive headers.
 */
#define RTEMS_RTL_AR_IDENT      "!<arch>\n"
#define RTEMS_RTL_AR_IDENT_SIZE (sizeof (RTEMS_RTL_AR_IDENT) - 1)
#define RTEMS_RTL_AR_FHDR_BASE  RTEMS_RTL_AR_IDENT_SIZE
#define RTEMS_RTL_AR_FNAME      (0)
#define RTEMS_RTL_AR_FNAME_SIZE (16)
#define RTEMS_RTL_AR_SIZE       (48)
#define RTEMS_RTL_AR_SIZE_SIZE  (10)
#define RTEMS_RTL_AR_MAGIC      (58)
#define RTEMS_RTL_AR_MAGIC_SIZE (2)
#define RTEMS_RTL_AR_FHDR_SIZE  (60)

/**
 * Read a 32bit value from the symbol table.
 */
static unsigned int
rtems_rtl_archive_read_32 (void* data)
{
  uint8_t*     b = (uint8_t*) data;
  unsigned int v = b[0];
  v = (v << 8) | b[1];
  v = (v << 8) | b[2];
  v = (v << 8) | b[3];
  return v;
}

static void
rtems_rtl_archive_set_error (int num, const char* text)
{
  if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
    printf ("rtl: archive: error: %3d:  %s\n", num, text);
}

static uint64_t
rtems_rtl_scan_decimal (const uint8_t* string, size_t len)
{
  uint64_t value = 0;

  while (len && (*string != ' '))
  {
    value *= 10;
    value += *string - '0';
    ++string;
    --len;
  }

  return value;
}

static bool
rtems_rtl_seek_read (int fd, off_t off, size_t len, uint8_t* buffer)
{
  if (lseek (fd, off, SEEK_SET) < 0)
    return false;
  if (read (fd, buffer, len) != len)
    return false;
  return true;
}

/**
 * Archive iterator.
 */
typedef bool (*rtems_rtl_archive_iterator) (rtems_rtl_archive* archive,
					    void*              data);

/**
 * Chain iterator data.
 */
typedef struct rtems_rtl_archive_chain_data
{
  void*                      data;      /**< User's data. */
  rtems_rtl_archive_iterator iterator;  /**< The actual iterator. */
} rtems_rtl_archive_chain_data;

static bool
rtems_rtl_archive_node_iterator (rtems_chain_node* node, void* data)
{
  rtems_rtl_archive*            archive;
  rtems_rtl_archive_chain_data* chain_data;
  archive    = (rtems_rtl_archive*) node;
  chain_data = (rtems_rtl_archive_chain_data*) data;
  return chain_data->iterator (archive, chain_data->data);
}

static void
rtems_rtl_archive_iterate_archives (rtems_rtl_archives*        archives,
                                    rtems_rtl_archive_iterator iterator,
                                    void*                      data)
{
  rtems_rtl_archive_chain_data chain_data = {
    .data = data,
    .iterator = iterator
  };
  rtems_rtl_chain_iterate (&archives->archives,
                           rtems_rtl_archive_node_iterator,
                           &chain_data);
}

static bool
rtems_rtl_rchive_name_end (const char c)
{
  return c == '\0' || c == '\n' || c == '/';
}

static const char*
rtems_rtl_archive_dup_name (const char* name)
{
  size_t len = 0;
  char*  dup;
  while (!rtems_rtl_rchive_name_end (name[len]))
    ++len;
  dup = rtems_rtl_alloc_new (RTEMS_RTL_ALLOC_OBJECT, len + 1, true);
  if (dup != NULL)
    memcpy (dup, name, len);
  return dup;
}

static bool
rtems_rtl_archive_match_name (const char* file_name, const char* name)
{
  if (name != NULL)
  {
    while (!rtems_rtl_rchive_name_end (*file_name) &&
           !rtems_rtl_rchive_name_end (*name) && *file_name == *name)
    {
      ++file_name;
      ++name;
    }
    if (((*file_name == '\0') || (*file_name == '\n') || (*file_name == '/')) &&
        ((*name == '\0') || (*name == '/')))
      return true;
  }
  return false;
}

static bool
rtems_rtl_archive_set_flags (rtems_rtl_archive* archive, void* data)
{
  uint32_t mask = *((uint32_t*) data);
  archive->flags |= mask;
  return true;
}

typedef struct rtems_rtl_archive_find_data
{
  rtems_rtl_archive* archive;
  const char*        path;
} rtems_rtl_archive_find_data;

static bool
rtems_rtl_archive_finder (rtems_rtl_archive* archive, void* data)
{
  rtems_rtl_archive_find_data* find;
  find = (rtems_rtl_archive_find_data*) data;
  if (strcmp (find->path, archive->name) == 0)
  {
    find->archive = archive;
    return false;
  }
  return true;
}

static rtems_rtl_archive*
rtems_rtl_archive_find (rtems_rtl_archives* archives,
                        const char*         path)
{
  rtems_rtl_archive_find_data find = {
    .archive = NULL,
    .path = path
  };
  rtems_rtl_archive_iterate_archives (archives,
                                      rtems_rtl_archive_finder,
                                      &find);
  return find.archive;
}

/*
 * Find an object file in archive that contains the symbol we are
 * searching for.
 *
 * The symbol search is performance sensitive. The archive's symbol table being
 * searched is the symbol table in the archive created by ranlib. This table is
 * not sorted so a sorted table of pointeres to the symbols is generated after
 * loading if there are enough symbols. For small symbol tables the search is
 * linear. The entire table is held in memory. At the time of writing this code
 * the symbol table for the SPARC architecture's libc is 16k.
 *
 * The ranlib table is:
 *
 *    [4]                - size of table in bytes
 *    [0..(entries x 4)] - 4 byte binary offsets into the archive
 *                         for each symbol
 *    [0..m]             - variable length table of strings, nul
 *                         separated and sorted
 *
 * Note: The loading of an object file from an archive uses an offset in the
 *       file name to speed the loading.
 */
typedef struct rtems_rtl_archive_obj_data
{
  const char*        symbol;   /**< The symbol to search for. */
  rtems_rtl_archive* archive;  /**< The archive the symbol is found
                                *   in. */
  off_t              offset;   /**< The offset in the archive if found
                                *   else 0 */
} rtems_rtl_archive_obj_data;

static int
rtems_rtl_archive_symbol_compare (const void* a, const void* b)
{
  const rtems_rtl_archive_symbol* sa;
  const rtems_rtl_archive_symbol* sb;
  sa = (const rtems_rtl_archive_symbol*) a;
  sb = (const rtems_rtl_archive_symbol*) b;
  return strcmp (sa->label, sb->label);
}

static bool
rtems_rtl_archive_obj_finder (rtems_rtl_archive* archive, void* data)
{
  const rtems_rtl_archive_symbols* symbols = &archive->symbols;

  if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
    printf ("rtl: archive: finder: %s: entries: %zu\n",
            archive->name, symbols->entries);

  /*
   * Make sure there is a valid symbol table.
   */
  if (symbols->base != NULL)
  {
    /*
     * Perform a linear search if there is no sorted symbol table.
     */
    rtems_rtl_archive_obj_data* search = (rtems_rtl_archive_obj_data*) data;
    if (symbols->symbols == NULL)
    {
      const char* symbol = symbols->names;
      size_t      entry;
      for (entry = 0; entry < symbols->entries; ++entry)
      {
        if (strcmp (search->symbol, symbol) == 0)
        {
          search->archive = archive;
          search->offset =
            rtems_rtl_archive_read_32 (symbols->base + ((entry + 1) * 4));
          return false;
        }
        symbol += strlen (symbol) + 1;
      }
    }
    else
    {
      rtems_rtl_archive_symbol*      match;
      const rtems_rtl_archive_symbol key = {
        .entry = -1,
        .label = search->symbol
      };
      match = bsearch (&key,
                       symbols->symbols,
                       symbols->entries,
                       sizeof (symbols->symbols[0]),
                       rtems_rtl_archive_symbol_compare);
      if (match != NULL)
      {
          search->archive = archive;
          search->offset =
            rtems_rtl_archive_read_32 (symbols->base + (match->entry * 4));
          return false;
      }
    }
  }

  /*
   * Next archive.
   */
  return true;
}

static rtems_rtl_archive*
rtems_rtl_archive_new (rtems_rtl_archives* archives,
                       const char*         path,
                       const char*         name)
{
  rtems_rtl_archive* archive;
  size_t             path_size;
  size_t             size;
  /*
   * Handle the case of the path being just '/', do not create '//'.
   */
  path_size = strlen (path);
  size = sizeof(rtems_rtl_archive) + path_size + strlen (name) + 1;
  if (path_size > 1)
    ++size;
  archive = rtems_rtl_alloc_new (RTEMS_RTL_ALLOC_OBJECT, size, true);
  if (archive == NULL)
  {
    if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
      printf ("rtl: archive: new: %s: no memory\n", name);
  }
  else
  {
    char* aname;
    archive->name = ((const char*) archive) + sizeof(rtems_rtl_archive);
    aname = (char*) archive->name;
    strcpy (aname, path);
    if (path_size > 1)
      strcat (aname, "/");
    strcat (aname, name);
    rtems_chain_set_off_chain (&archive->node);
    archive->flags |= RTEMS_RTL_ARCHIVE_LOAD;
  }
  return archive;
}

static void
rtems_rtl_archive_del (rtems_rtl_archive* archive)
{
  if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
    printf ("rtl: archive: del: %s\n",  archive->name);
  rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_SYMBOL, archive->symbols.base);
  rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_SYMBOL, archive->symbols.symbols);
  if (!rtems_chain_is_node_off_chain (&archive->node))
    rtems_chain_extract (&archive->node);
  rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_OBJECT, archive);
}

static rtems_rtl_archive*
rtems_rtl_archive_get (rtems_rtl_archives* archives,
                       const char*         path,
                       const char*         name)
{
  rtems_rtl_archive* archive;
  /*
   * Getting a new archive turns the path and name into a single path the stat
   * function can use. No matter how you try some memory is needed so it is
   * easier to get a new archive object and delete it if it exists.
   */
  archive = rtems_rtl_archive_new (archives, path, name);
  if (archive != NULL)
  {
    struct stat sb;
    if (stat (archive->name, &sb) == 0)
    {
      if (S_ISREG (sb.st_mode))
      {
        rtems_rtl_archive* find_archive;
        find_archive = rtems_rtl_archive_find (archives, archive->name);
        if (find_archive == NULL)
        {
          rtems_chain_append (&archives->archives, &archive->node);
        }
        else
        {
          rtems_rtl_archive_del (archive);
          archive = find_archive;
        }
        archive->flags &= ~RTEMS_RTL_ARCHIVE_REMOVE;
        if (archive->mtime != sb.st_mtime)
        {
          archive->flags |= RTEMS_RTL_ARCHIVE_LOAD;
          archive->size = sb.st_size;
          archive->mtime = sb.st_mtime;
        }
      }
    }
  }
  return archive;
}

static bool
rtems_rtl_archives_load_config (rtems_rtl_archives* archives)
{
  struct stat sb;

  if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
    printf ("rtl: archive: config load: %s\n", archives->config_name);

  if (archives->config_name == NULL)
    return false;

  if (stat (archives->config_name, &sb) < 0)
  {
    if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
      printf ("rtl: archive: no config: %s\n", archives->config_name);
    return false;
  }

  /*
   * If the configuration has change reload it.
   */
  if (sb.st_mtime != archives->config_mtime)
  {
    int     fd;
    ssize_t r;
    char*   s;
    bool    in_comment;

    archives->config_mtime = sb.st_mtime;
    rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_OBJECT, (void*) archives->config);
    archives->config_length = 0;
    archives->config =
      rtems_rtl_alloc_new (RTEMS_RTL_ALLOC_OBJECT, sb.st_size + 1, true);
    if (archives->config == NULL)
    {
      if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
        printf ("rtl: archive: no memory for config\n");
      return false;
    }

    if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
      printf ("rtl: archive: config load: read %s\n", archives->config_name);

    fd = open (archives->config_name, O_RDONLY);
    if (fd < 0)
    {
      rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_OBJECT, (void*) archives->config);
      archives->config = NULL;
      archives->config_length = 0;
      if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
        printf ("rtl: archive: config open error: %s\n", strerror (errno));
      return false;
    }

    r = read (fd, (void*) archives->config, sb.st_size);
    if (r < 0)
    {
      close (fd);
      rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_OBJECT, (void*) archives->config);
      archives->config = NULL;
      archives->config_length = 0;
      if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
        printf ("rtl: archive: config read error: %s\n", strerror (errno));
      return false;
    }

    close (fd);
    archives->config_length = r;

    /*
     * Remove comments.
     */
    s = (char*) archives->config;
    in_comment = false;
    for (r = 0; r < archives->config_length; ++r, ++s)
    {
      if (*s == '#')
        in_comment = true;
      if (in_comment)
      {
        if (*s == '\n')
          in_comment = false;
        *s = '\0';
      }
    }

    /*
     * Create lines by turning '\r' and '\n' to '\0'.
     */
    s = (char*) archives->config;
    for (r = 0; r < archives->config_length; ++r, ++s)
    {
      if (*s == '\r' || *s == '\n')
        *s = '\0';
    }

    /*
     * Remove leading and trailing white space.
     */
    s = (char*) archives->config;
    r = 0;
    while (r < archives->config_length)
    {
      if (s[r] == '\0')
      {
        ++r;
      }
      else
      {
        size_t ls = strlen (&s[r]);
        size_t b = 0;
        while (b < ls && isspace ((unsigned char) s[r + b]))
        {
          s[r + b] = '\0';
          ++b;
        }
        b = ls - 1;
        while (b > 0 && isspace ((unsigned char) s[r + b]))
        {
          s[r + b] = '\0';
          --b;
        }
        r += ls;
      }
    }

    if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
    {
      int line = 1;
      printf ("rtl: archive: config:\n");
      r = 0;
      while (r < archives->config_length)
      {
        const char* cs = &archives->config[r];
        size_t      len = strlen (cs);
        if (len > 0)
        {
          printf (" %3d: %s\n", line, cs);
          ++line;
        }
        r += len + 1;
      }
    }
  }

  return true;
}

void
rtems_rtl_archives_open (rtems_rtl_archives* archives, const char* config)
{
  if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
    printf ("rtl: archive: open: %s\n", config);
  memset (archives, 0, sizeof (rtems_rtl_archives));
  archives->config_name = rtems_rtl_strdup (config);
  rtems_chain_initialize_empty (&archives->archives);
}

void
rtems_rtl_archives_close (rtems_rtl_archives* archives)
{
  rtems_chain_node* node;
  if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
    printf ("rtl: archive: close: count=%zu\n",
            rtems_chain_node_count_unprotected (&archives->archives));
  node = rtems_chain_first (&archives->archives);
  while (!rtems_chain_is_tail (&archives->archives, node))
  {
    rtems_rtl_archive* archive = (rtems_rtl_archive*) node;
    rtems_chain_node*  next_node = rtems_chain_next (node);
    rtems_rtl_archive_del (archive);
    node = next_node;
  }
  rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_OBJECT, (void*) archives->config);
  rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_OBJECT, archives);
}

static void
rtems_rtl_archives_remove (rtems_rtl_archives* archives)
{
  rtems_chain_node* node = rtems_chain_first (&archives->archives);
  if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
    printf ("rtl: archive: refresh: remove: checking %zu archive(s)\n",
            rtems_chain_node_count_unprotected (&archives->archives));
  while (!rtems_chain_is_tail (&archives->archives, node))
  {
    rtems_rtl_archive* archive = (rtems_rtl_archive*) node;
    rtems_chain_node*  next_node = rtems_chain_next (node);
    if ((archive->flags & RTEMS_RTL_ARCHIVE_REMOVE) != 0)
    {
      archive->flags &= ~RTEMS_RTL_ARCHIVE_REMOVE;
      if ((archive->flags & RTEMS_RTL_ARCHIVE_USER_LOAD) == 0)
        rtems_rtl_archive_del (archive);
    }
    node = next_node;
  }
}

static bool
rtems_rtl_archive_loader (rtems_rtl_archive* archive, void* data)
{
  int* loaded = (int*) data;

  if ((archive->flags & RTEMS_RTL_ARCHIVE_LOAD) != 0)
  {
    int         fd;
    off_t       offset = 0;
    size_t      size = 0;
    const char* name = "/";

    if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
      printf ("rtl: archive: loader: %s\n", archive->name);

    fd = open (archive->name, O_RDONLY);
    if (fd < 0)
    {
      if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
        printf ("rtl: archive: loader: open error: %s: %s\n",
                archive->name, strerror (errno));
      rtems_rtl_archive_set_error (errno, "opening archive file");
      return true;
    }

    if (rtems_rtl_obj_archive_find_obj (fd,
                                        archive->size,
                                        &name,
                                        &offset,
                                        &size,
                                        &archive->enames,
                                        rtems_rtl_archive_set_error))
    {
      if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
        printf ("rtl: archive: loader: symbols: off=0x%08jx size=%zu\n",
                offset, size);

      /*
       * Reallocate the symbol table memory if it has changed size.
       * Note, an updated library may have the same symbol table.
       */
      if (archive->symbols.size != size)
      {
        rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_SYMBOL, archive->symbols.base);
        archive->symbols.base = rtems_rtl_alloc_new (RTEMS_RTL_ALLOC_SYMBOL,
                                                     size,
                                                     false);
        if (archive->symbols.base == NULL)
        {
          close (fd);
          memset (&archive->symbols, 0, sizeof (archive->symbols));
          rtems_rtl_archive_set_error (ENOMEM, "symbol table memory");
          return true;
        }
      }

      /*
       * Read the symbol table into memory and hold.
       */
      if (!rtems_rtl_seek_read (fd, offset, size, archive->symbols.base))
      {
        rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_SYMBOL, archive->symbols.base);
        close (fd);
        memset (&archive->symbols, 0, sizeof (archive->symbols));
        rtems_rtl_archive_set_error (errno, "reading symbols");
        return true;
      }

      /*
       * The first 4 byte value is the number of entries. Range check the
       * value so the alloc size does not overflow (Coverity 1442636).
       */
      archive->symbols.entries =
        rtems_rtl_archive_read_32 (archive->symbols.base);
      if (archive->symbols.entries >= (SIZE_MAX / sizeof (rtems_rtl_archive_symbol)))
      {
        rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_SYMBOL, archive->symbols.base);
        close (fd);
        memset (&archive->symbols, 0, sizeof (archive->symbols));
        rtems_rtl_archive_set_error (errno, "too many symbols");
        return true;
      }

      archive->symbols.size   = size;
      archive->symbols.names  = archive->symbols.base;
      archive->symbols.names += (archive->symbols.entries + 1) * 4;

      /*
       * Create a sorted symbol table.
       */
      size = archive->symbols.entries * sizeof (rtems_rtl_archive_symbol);
      archive->symbols.symbols =
        rtems_rtl_alloc_new (RTEMS_RTL_ALLOC_SYMBOL, size, true);
      if (archive->symbols.symbols != NULL)
      {
        const char* symbol = archive->symbols.names;
        size_t      e;
        for (e = 0; e < archive->symbols.entries; ++e)
        {
          archive->symbols.symbols[e].entry = e + 1;
          archive->symbols.symbols[e].label = symbol;
          symbol += strlen (symbol) + 1;
        }
        qsort (archive->symbols.symbols,
               archive->symbols.entries,
               sizeof (rtems_rtl_archive_symbol),
               rtems_rtl_archive_symbol_compare);
      }

      if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
        printf ("rtl: archive: loader: symbols: " \
                "base=%p entries=%zu names=%p (0x%08x) symbols=%p\n",
                archive->symbols.base,
                archive->symbols.entries,
                archive->symbols.names,
                (unsigned int) (archive->symbols.entries + 1) * 4,
                archive->symbols.symbols);

      if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVE_SYMS) &&
          archive->symbols.entries > 0)
      {
        size_t e;
        printf ("rtl: archive: symbols: %s\n", archive->name );
        for (e = 0; e < archive->symbols.entries; ++e)
        {
          printf(" %6zu: %6zu %s\n", e + 1,
                 archive->symbols.symbols[e].entry,
                 archive->symbols.symbols[e].label);
        }
      }
    }

    close (fd);

    archive->flags &= ~RTEMS_RTL_ARCHIVE_LOAD;

    ++(*loaded);
  }

  return true;
}

static bool
rtems_rtl_archives_load (rtems_rtl_archives* archives)
{
  int loaded = 0;
  if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
    printf ("rtl: archive: archive: load\n");
  rtems_rtl_archive_iterate_archives (archives,
                                      rtems_rtl_archive_loader,
                                      &loaded);
  return loaded > 0;
}

bool
rtems_rtl_archives_refresh (rtems_rtl_archives* archives)
{
  size_t   config_path = 0;
  uint32_t flags = RTEMS_RTL_ARCHIVE_REMOVE;

  /*
   * Reload the configuration file if it has not been loaded or has been
   * updated.
   */
  if (!rtems_rtl_archives_load_config (archives))
    return false;

  /*
   * Assume all existing archives are to be removed. If an existing archive
   * is ccnfigured and found teh remove flags is cleared. At the of the
   * refresh end remove any archives with this flag still set.
   */
  rtems_rtl_archive_iterate_archives (archives,
                                      rtems_rtl_archive_set_flags,
                                      &flags);

  while (config_path < archives->config_length)
  {
    const char* dirname = &archives->config[config_path];
    char*       basename;
    const char  root[2] = { '/', '\0' };
    DIR*        dir;

    if (*dirname == '\0')
    {
      ++config_path;
      continue;
    }

    if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
      printf ("rtl: archive: refresh: %s\n", dirname);

    config_path += strlen (dirname);

    /*
     * Relative paths do not work in the config. Must be absolute.
     */
    if (dirname[0] != '/')
    {
      if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
        printf ("rtl: archive: refresh: relative paths ignored: %s\n", dirname);
      continue;
    }

    /*
     * Scan the parent directory of the glob path matching file names.
     */
    basename = strrchr (dirname, '/');
    if (basename == NULL)
      continue;

    if (basename == dirname)
      dirname = &root[0];

    *basename = '\0';
    ++basename;

    dir = opendir (dirname);

    if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
      printf ("rtl: archive: refresh: %s %sfound\n",
              dirname, dir == NULL ? ": not " : "");

    if (dir != NULL)
    {
      while (true)
      {
        struct dirent  entry;
        struct dirent* result = NULL;

        if (readdir_r (dir, &entry, &result) != 0)
        {
          if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
            printf ("rtl: archive: refresh: readdir error\n");
          break;
        }

        if (result == NULL)
          break;

        if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
          printf ("rtl: archive: refresh: checking: %s (pattern: %s)\n",
                  entry.d_name, basename);

        if (fnmatch (basename, entry.d_name, 0) == 0)
        {
          rtems_rtl_archive* archive;
          archive = rtems_rtl_archive_get (archives, dirname, entry.d_name);
          if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
            printf ("rtl: archive: refresh: %s: %sfound\n",
                    entry.d_name, archive == NULL ? ": not " : "");
        }
      }
      closedir (dir);
    }

    --basename;
    *basename = '/';
  }

  /*
   * Remove all archives flagged to be removed.
   */
  rtems_rtl_archives_remove (archives);

  /*
   * Load any new archives. If any are loaded set the archive search flag in
   * any unresolved external symbols so the archives are searched. The archive
   * search flag avoids searching for symbols we know are not in the known
   * archives,
   */
  if (rtems_rtl_archives_load (archives))
    rtems_rtl_unresolved_set_archive_search ();

  return true;
}

bool
rtems_rtl_archive_load (rtems_rtl_archives* archives, const char* name)
{
  if (archives != NULL)
  {
    rtems_rtl_archive* archive;
    int                loaded = 0;

    archive = rtems_rtl_archive_get (archives, "", name);
    if (archive == NULL)
    {
      rtems_rtl_set_error (ENOENT, "archive not found");
      return false;
    }

    archive->flags |= RTEMS_RTL_ARCHIVE_USER_LOAD;

    rtems_rtl_archive_loader (archive, &loaded);
    if (loaded == 0)
    {
      rtems_rtl_archive_del (archive);
      rtems_rtl_set_error (ENOENT, "archive load falied");
    }

    return true;
  }
  return false;
}

rtems_rtl_archive_search
rtems_rtl_archive_obj_load (rtems_rtl_archives* archives,
                            const char*         symbol,
                            bool                load)
{
  rtems_rtl_obj*       obj;
  rtems_chain_control* pending;
  int                  fd;
  size_t               archive_count;

  rtems_rtl_archive_obj_data search = {
    .symbol  = symbol,
    .archive = NULL,
    .offset  = 0
  };

  archive_count = rtems_chain_node_count_unprotected (&archives->archives);

  if (archive_count == 0)
  {
    if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
      printf ("rtl: archive: load: no archives\n");
    return rtems_rtl_archive_search_no_config;
  }

  pending = rtems_rtl_pending_unprotected ();
  if (pending == NULL)
  {
    if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
      printf ("rtl: archive: load: no pending list\n");
    return rtems_rtl_archive_search_not_found;
  }

  if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
    printf ("rtl: archive: load: searching %zu archives\n", archive_count);

  rtems_rtl_archive_iterate_archives (archives,
                                      rtems_rtl_archive_obj_finder,
                                      &search);

  if (search.archive == NULL)
  {
    if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
      printf ("rtl: archive: load: not found: %s\n", symbol);
    return rtems_rtl_archive_search_not_found;
  }

  if (!load)
  {
    if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
      printf ("rtl: archive: load: found (no load): %s\n", symbol);
    return rtems_rtl_archive_search_found;
  }

  obj = rtems_rtl_obj_alloc ();
  if (obj == NULL)
  {
    if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
      printf ("rtl: archive: alloc: no memory: %s\n",
              search.archive->name);
    return rtems_rtl_archive_search_error;
  }

  obj->aname = rtems_rtl_strdup (search.archive->name);

  fd = open (obj->aname, O_RDONLY);
  if (fd < 0)
  {
    if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
      printf ("rtl: archive: load: open error: %s: %s\n",
              obj->aname, strerror (errno));
    rtems_rtl_obj_free (obj);
    return rtems_rtl_archive_search_error;
  }

  obj->oname = NULL;
  obj->ooffset = search.offset;

  if (!rtems_rtl_obj_archive_find_obj (fd,
                                       search.archive->size,
                                       &obj->oname,
                                       &obj->ooffset,
                                       &obj->fsize,
                                       &search.archive->enames,
                                       rtems_rtl_archive_set_error))
  {
    if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
      printf ("rtl: archive: load: load error: %s:%s\n",
              obj->aname, obj->oname);
    close (fd);
    rtems_rtl_obj_free (obj);
    return rtems_rtl_archive_search_error;
  }

  obj->fname = rtems_rtl_strdup (obj->aname);
  obj->ooffset -= RTEMS_RTL_AR_FHDR_SIZE;
  obj->fsize = search.archive->size;

  close (fd);

  if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
    printf ("rtl: archive: loading: %s:%s@0x%08jx size:%zu\n",
            obj->aname, obj->oname, obj->ooffset, obj->fsize);

  rtems_chain_append (pending, &obj->link);

  if (!rtems_rtl_obj_load (obj))
  {
    if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
      printf ("rtl: archive: loading: error: %s:%s@0x%08jx: %s\n",
              obj->aname, obj->oname, obj->ooffset,
              rtems_rtl_last_error_unprotected ());
    rtems_chain_extract (&obj->link);
    rtems_rtl_obj_free (obj);
    rtems_rtl_obj_caches_flush ();
    return rtems_rtl_archive_search_error;
  }

  rtems_rtl_obj_caches_flush ();

  if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
    printf ("rtl: archive: loading: loaded: %s:%s@0x%08jx\n",
            obj->aname, obj->oname, obj->ooffset);

  return rtems_rtl_archive_search_loaded;
}

bool
rtems_rtl_obj_archive_find_obj (int                     fd,
                                size_t                  fsize,
                                const char**            name,
                                off_t*                  ooffset,
                                size_t*                 osize,
                                off_t*                  extended_file_names,
                                rtems_rtl_archive_error error)
{
  uint8_t header[RTEMS_RTL_AR_FHDR_SIZE];
  bool    scanning;

  if (name == NULL)
  {
    error (errno, "invalid object name");
    *ooffset = 0;
    *osize = 0;
    return false;
  }

  if (rtems_rtl_trace (RTEMS_RTL_TRACE_ARCHIVES))
    printf ("rtl: archive: find obj: %s @ 0x%08jx\n", *name, *ooffset);

  if (read (fd, &header[0], RTEMS_RTL_AR_IDENT_SIZE) !=  RTEMS_RTL_AR_IDENT_SIZE)
  {
    error (errno, "reading archive identifer");
    *ooffset = 0;
    *osize = 0;
    return false;
  }

  if (memcmp (header, RTEMS_RTL_AR_IDENT, RTEMS_RTL_AR_IDENT_SIZE) != 0)
  {
    error (EINVAL, "invalid archive identifer");
    *ooffset = 0;
    *osize = 0;
    return false;
  }

  /*
   * Seek to the current offset in the archive and if we have a valid archive
   * file header present check the file name for a match with the oname field
   * of the object descriptor. If the archive header is not valid and it is the
   * first pass reset the offset and start the search again in case the offset
   * provided is not valid any more.
   *
   * The archive can have a symbol table at the start. Ignore it. A symbol
   * table has the file name '/'.
   *
   * The archive can also have the GNU extended file name table. This
   * complicates the processing. If the object's file name starts with '/' the
   * remainder of the file name is an offset into the extended file name
   * table. To find the extended file name table we need to scan from start of
   * the archive for a file name of '//'. Once found we remeber the table's
   * start and can direct seek to file name location. In other words the scan
   * only happens once.
   *
   * If the name had the offset encoded we go straight to that location.
   */

  if (*ooffset != 0)
    scanning = false;
  else
  {
    if (*name == NULL)
    {
      error (errno, "invalid object name and archive offset");
      *ooffset = 0;
      *osize = 0;
      return false;
    }
    scanning = true;
    *ooffset = RTEMS_RTL_AR_FHDR_BASE;
    *osize = 0;
  }

  while (*ooffset < fsize)
  {
    /*
     * Clean up any existing data.
     */
    memset (header, 0, sizeof (header));

    if (!rtems_rtl_seek_read (fd, *ooffset, RTEMS_RTL_AR_FHDR_SIZE, &header[0]))
    {
      error (errno, "seek/read archive file header");
      *ooffset = 0;
      *osize = 0;
      return false;
    }

    if ((header[RTEMS_RTL_AR_MAGIC] != 0x60) ||
        (header[RTEMS_RTL_AR_MAGIC + 1] != 0x0a))
    {
      if (scanning)
      {
        error (EINVAL, "invalid archive file header");
        *ooffset = 0;
        *osize = 0;
        return false;
      }

      scanning = true;
      *ooffset = RTEMS_RTL_AR_FHDR_BASE;
      continue;
    }

    /*
     * The archive header is always aligned to an even address.
     */
    *osize = (rtems_rtl_scan_decimal (&header[RTEMS_RTL_AR_SIZE],
                                      RTEMS_RTL_AR_SIZE_SIZE) + 1) & ~1;

    /*
     * Check for the GNU extensions.
     */
    if (header[0] == '/')
    {
      const char* name_ = *name;
      off_t extended_off;

      switch (header[1])
      {
        case ' ':
          /*
           * SVR4/GNU Symbols table. Nothing more to do.
           */
          *ooffset += RTEMS_RTL_AR_FHDR_SIZE;
          return true;
        case '/':
          /*
           * Extended file names table. Remember. If asked to find this file
           * return the result.
           */
          *extended_file_names = *ooffset + RTEMS_RTL_AR_FHDR_SIZE;
          if (name_[0] == '/' && name_[1] == '/')
          {
            *ooffset = *ooffset + RTEMS_RTL_AR_FHDR_SIZE;
            return true;
          }
          break;
        case '0':
        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
        case '8':
        case '9':
          /*
           * Offset into the extended file name table. If we do not have the
           * offset to the extended file name table find it.
           */
          extended_off =
            rtems_rtl_scan_decimal (&header[1], RTEMS_RTL_AR_FNAME_SIZE);

          if (*extended_file_names == 0)
          {
            off_t off = RTEMS_RTL_AR_IDENT_SIZE;
            while (*extended_file_names == 0)
            {
              off_t esize;

              if (!rtems_rtl_seek_read (fd, off,
                                        RTEMS_RTL_AR_FHDR_SIZE, &header[0]))
              {
                error (errno, "seeking/reading archive ext file name header");
                *ooffset = 0;
                *osize = 0;
                return false;
              }

              if ((header[RTEMS_RTL_AR_MAGIC] != 0x60) ||
                  (header[RTEMS_RTL_AR_MAGIC + 1] != 0x0a))
              {
                error (errno, "invalid archive file header");
                *ooffset = 0;
                *osize = 0;
                return false;
              }

              if ((header[0] == '/') && (header[1] == '/'))
              {
                *extended_file_names = off + RTEMS_RTL_AR_FHDR_SIZE;
                break;
              }

              esize =
                (rtems_rtl_scan_decimal (&header[RTEMS_RTL_AR_SIZE],
                                         RTEMS_RTL_AR_SIZE_SIZE) + 1) & ~1;
              off += esize + RTEMS_RTL_AR_FHDR_SIZE;
            }
          }

          if (*extended_file_names != 0)
          {
            /*
             * We know the offset in the archive to the extended file. Read the
             * name from the table and compare with the name we are after.
             */
            #define RTEMS_RTL_MAX_FILE_SIZE (256)
            char ename[RTEMS_RTL_MAX_FILE_SIZE];

            if (!rtems_rtl_seek_read (fd, *extended_file_names + extended_off,
                                      RTEMS_RTL_MAX_FILE_SIZE, (uint8_t*) &ename[0]))
            {
              error (errno, "invalid archive ext file seek/read");
              *ooffset = 0;
              *osize = 0;
              return false;
            }

            /*
             * If there is no name memory the user is asking us to return the
             * name in the archive at the offset.
             */
            if (*name == NULL)
              *name = rtems_rtl_archive_dup_name (ename);
            if (rtems_rtl_archive_match_name (*name, ename))
            {
              *ooffset += RTEMS_RTL_AR_FHDR_SIZE;
              return true;
            }
          }
          break;
        default:
          /*
           * Ignore the file because we do not know what it it.
           */
          break;
      }
    }
    else
    {
      const char* ename = (const char*) &header[RTEMS_RTL_AR_FNAME];
      if (*name == NULL)
        *name = rtems_rtl_archive_dup_name (ename);
      if (rtems_rtl_archive_match_name (*name, ename))
      {
        *ooffset += RTEMS_RTL_AR_FHDR_SIZE;
        return true;
      }
    }

    *ooffset += *osize + RTEMS_RTL_AR_FHDR_SIZE;
  }

  error (ENOENT, "object file not found");
  *ooffset = 0;
  *osize = 0;
  return false;

}