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}