master xplshn/aruu / shared / libtcutil / ar / archive.c
  1#include "archive.h"
  2
  3#include <ar.h>
  4#include <assert.h>
  5#include <stdlib.h> /* strtoul */
  6#include <string.h>
  7
  8#include "../tcutil.h"
  9
 10/* read 4-byte big-endian uint32 */
 11static uint32_t
 12read4be(FILE *fp)
 13{
 14  unsigned char buf[4];
 15  read_or_die(fp, buf, -1, sizeof(buf), "read4be");
 16  return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
 17}
 18
 19static uint32_t *
 20read_file_offsets(FILE *fp, uint32_t symbol_count)
 21{
 22  uint32_t      *file_offsets;
 23  uint32_t      *p;
 24  uint32_t       i;
 25  unsigned char *q;
 26  uint32_t       offset;
 27
 28  file_offsets = malloc_or_die(sizeof(*file_offsets) * symbol_count);
 29  read_or_die(fp, file_offsets, -1, sizeof(*file_offsets) * symbol_count, "Offsets");
 30  /* convert big endian to machine endian */
 31  p = file_offsets;
 32  for (i = 0; i < symbol_count; ++i) {
 33    q      = (unsigned char *)p;
 34    offset = (q[0] << 24) | (q[1] << 16) | (q[2] << 8) | q[3];
 35    *p++   = offset;
 36  }
 37  return file_offsets;
 38}
 39
 40static int
 41compare_uint32(const void *a, const void *b)
 42{
 43  uint32_t          x       = *(const uint32_t *)a;
 44  struct ArContent *content = (struct ArContent *)b;
 45  uint32_t          y       = content->file_offset;
 46  return x < y ? -1 : x > y ? 1 : 0;
 47}
 48
 49/* build unique sorted array of arcontent from raw file offsets */
 50static struct ArContent *
 51allocate_contents_buffer(const uint32_t *file_offsets, uint32_t symbol_count, size_t *plen)
 52{
 53  uint32_t         *offsets;
 54  ssize_t           len, capa;
 55  uint32_t          i;
 56  struct ArContent *contents;
 57  ssize_t           j;
 58
 59  offsets = NULL;
 60  len     = 0;
 61  capa    = 0;
 62
 63  for (i = 0; i < symbol_count; ++i) {
 64    uint32_t value = file_offsets[i];
 65    ssize_t  lo = -1, hi = len;
 66    ssize_t  m;
 67
 68    /* binary search for insertion point */
 69    while (hi - lo > 1) {
 70      m = lo + ((hi - lo) >> 1);
 71      if (offsets[m] < value)
 72        lo = m;
 73      else
 74        hi = m;
 75    }
 76
 77    if (hi >= len || offsets[hi] != value) {
 78      if (capa <= len) {
 79        capa <<= 1;
 80        if (capa <= 0)
 81          capa = 8;
 82        offsets = realloc_or_die(offsets, sizeof(*offsets) * capa);
 83      }
 84      memmove(&offsets[hi + 1], &offsets[hi], (len - hi) * sizeof(*offsets));
 85      offsets[hi] = value;
 86      ++len;
 87    }
 88  }
 89
 90  contents = calloc_or_die(sizeof(*contents) * len);
 91  for (j = 0; j < len; ++j) {
 92    contents[j].obj         = NULL;
 93    contents[j].file_offset = offsets[j];
 94  }
 95  free(offsets);
 96
 97  *plen = len;
 98  return contents;
 99}
100
101struct Archive *
102load_archive(const char *filename)
103{
104  FILE             *fp;
105  struct Archive   *ar;
106  char              mag[SARMAG];
107  struct ar_hdr     ghdr;
108  uint32_t          symbol_count;
109  uint32_t         *file_offsets;
110  size_t            content_count;
111  struct ArContent *contents;
112  struct ArSymbol  *symbols;
113  uint32_t          i;
114  size_t            pos;
115  size_t            strtablen;
116  char             *strtab;
117  char             *p;
118
119  if (!is_file(filename) || (fp = fopen(filename, "rb")) == NULL)
120    return NULL;
121
122  ar               = calloc_or_die(sizeof(*ar));
123  ar->fp           = fp;
124  ar->symbol_count = 0;
125  ar->symbols      = NULL;
126  table_init(&ar->symbol_table);
127  ar->contents = new_vector();
128
129  read_or_die(fp, mag, -1, sizeof(mag), "Magic");
130  if (memcmp(mag, ARMAG, sizeof(mag)) != 0)
131    error("Magic expected");
132
133  read_or_die(fp, &ghdr, -1, sizeof(ghdr), "Global header");
134  if (memcmp(ghdr.ar_fmag, ARFMAG, sizeof(ghdr.ar_fmag)) != 0)
135    error("FMagic expected");
136
137  symbol_count     = read4be(fp);
138  ar->symbol_count = symbol_count;
139  if (symbol_count > 0) {
140    file_offsets = read_file_offsets(fp, symbol_count);
141    contents     = allocate_contents_buffer(file_offsets, symbol_count, &content_count);
142
143    symbols     = malloc_or_die(sizeof(*symbols) * symbol_count);
144    ar->symbols = symbols;
145    for (i = 0; i < symbol_count; ++i) {
146      uint32_t          value = file_offsets[i];
147      struct ArContent *result;
148      uint32_t          index;
149
150      result = bsearch(&value, contents, content_count, sizeof(*contents), compare_uint32);
151      assert(result != NULL);
152      index              = result - contents;
153      symbols[i].content = &contents[index];
154    }
155
156    pos = ftell(fp);
157    assert(pos < contents[0].file_offset);
158    strtablen = contents[0].file_offset - pos;
159    strtab    = malloc_or_die(strtablen); /* freed locally, not stored */
160    read_or_die(fp, strtab, -1, strtablen, "struct Strtab");
161    p = strtab;
162    for (i = 0; i < symbol_count; ++i) {
163      char              *q;
164      struct ArSymbol   *symbol;
165      const struct Name *name;
166
167      q = memchr(p, '\0', &strtab[strtablen] - p);
168      if (q == NULL)
169        error("Illegal strtab");
170
171      symbol = &symbols[i];
172      name   = alloc_name(p, q, 0);
173      table_put(&ar->symbol_table, name, symbol);
174
175      p = q + 1;
176    }
177
178    free(file_offsets);
179  }
180  return ar;
181}
182
183void *
184load_archive_content(
185    struct Archive *ar, struct ArSymbol *symbol, void *(*load)(FILE *, const char *, size_t)
186)
187{
188  struct ArContent *content;
189  struct ar_hdr     hdr;
190  char             *p;
191  char              sizestr[sizeof(hdr.ar_size) + 1];
192  void             *obj;
193
194  content = symbol->content;
195  if (content->obj != NULL)
196    return content->obj;
197
198  fseek(ar->fp, content->file_offset, SEEK_SET);
199
200  read_or_die(ar->fp, &hdr, -1, sizeof(hdr), "hdr");
201  if (memcmp(hdr.ar_fmag, ARFMAG, sizeof(hdr.ar_fmag)) != 0)
202    error("Malformed archive");
203
204  memcpy(content->name, hdr.ar_name, sizeof(hdr.ar_name));
205  p = memchr(content->name, '/', sizeof(hdr.ar_name));
206  if (p != NULL)
207    *p = '\0';
208
209  memcpy(sizestr, hdr.ar_size, sizeof(hdr.ar_size));
210  sizestr[sizeof(hdr.ar_size)] = '\0';
211  content->size                = strtoul(sizestr, NULL, 10);
212
213  obj = (*load)(ar->fp, content->name, content->size);
214  if (obj == NULL)
215    error("Failed to extract .o: %.*s", (int)sizeof(content->name), content->name);
216  content->obj = obj;
217  vec_push(ar->contents, content);
218
219  return obj;
220}