kulp / tenyr
1
// TODO make an mmap()-based version of this for systems that support it
2

3
// `context` parameters not often used in obj_op
4
#pragma GCC diagnostic ignored "-Wunused-parameter"
5

6
#include "obj.h"
7

8
#include <stdint.h>
9
#include <stdlib.h>
10
#include <string.h>
11
#include <errno.h>
12
#include <limits.h>
13
#include <assert.h>
14

15
#define MAGIC_BYTES "TOV"
16
#define OBJ_DEFAULT_VERSION 2
17

18
#define PUT(What,Where) put_sized(&(What), sizeof (What), 1, Where)
19
#define GET(What,Where) get_sized(&(What), sizeof (What), 1, Where)
20

21
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
22
#define get_sized get_sized_le
23
#define put_sized put_sized_le
24
#else
25
#define get_sized get_sized_be
26
#define put_sized put_sized_be
27
#endif
28

29
typedef int obj_op(struct obj *o, STREAM *out, void *context);
30

31
static obj_op
32
    put_recs     , get_recs     ,
33
    put_syms_v2  , get_syms_v2  ,
34
    put_relocs_v2, get_relocs_v2;
35

36
static const struct objops {
37
    obj_op *put_recs, *put_syms, *put_relocs,
38
           *get_recs, *get_syms, *get_relocs;
39
} objops = {
40
    .put_recs = put_recs, .put_syms = put_syms_v2, .put_relocs = put_relocs_v2,
41
    .get_recs = get_recs, .get_syms = get_syms_v2, .get_relocs = get_relocs_v2,
42
};
43

44 1
static inline void get_sized_le(void *what, size_t size, size_t count, STREAM *where)
45
{
46 1
    if (size * count == 0) {
47 1
        return;
48
    }
49 1
    if (where->op.fread(what, size, count, where) != count) {
50 1
        if (where->op.feof(where)) {
51 1
            fatal(0, "End of file unexpectedly reached while parsing object");
52
        } else {
53 0
            fatal(0, "Unknown error while parsing object");
54
        }
55
    }
56
}
57

58 1
static inline void put_sized_le(const void *what, size_t size, size_t count, STREAM *where)
59
{
60 1
    if (size * count == 0) {
61 1
        return;
62
    }
63 1
    if (where->op.fwrite(what, size, count, where) != count) {
64 1
        if (where->op.feof(where)) {
65 0
            fatal(0, "End of file unexpectedly reached while emitting object");
66
        } else {
67 0
            fatal(0, "Unknown error while emitting object");
68
        }
69
    }
70
}
71

72
static inline int32_t swapword(const int32_t in)
73
{
74
    return (((in >> 24) & 0xff) <<  0) |
75
           (((in >> 16) & 0xff) <<  8) |
76
           (((in >>  8) & 0xff) << 16) |
77
           (((in >>  0) & 0xff) << 24);
78
}
79

80
static inline void get_sized_be(void *what, size_t size, size_t count, STREAM *where)
81
{
82
    get_sized_le(what, size, count, where);
83
    // get_sized() isn't as general as it looks - it does bytes, UWords, and
84
    // strings.
85
    if (size == sizeof(SWord)) {
86
        SWord *dest = what;
87
        for (size_t i = 0; i < count; i++)
88
            dest[i] = swapword(dest[i]);
89
    }
90
}
91

92
static inline void put_sized_be(const void *what, size_t size, size_t count, STREAM *where)
93
{
94
    // put_sized() has an analagous caveat to get_sized()'s, but we only swap
95
    // one word at a time so we don't have to allocate arbitrarily-large
96
    // buffers.
97
    if (size == sizeof(SWord)) {
98
        const SWord *src = what;
99
        for (size_t i = 0; i < count; i++) {
100
            const SWord temp = swapword(src[i]);
101
            put_sized_le(&temp, sizeof(SWord), 1, where);
102
        }
103
    } else {
104
        put_sized_le(what, size, count, where);
105
    }
106
}
107

108 1
static int put_recs(struct obj *o, STREAM *out, void *context)
109
{
110 1
    PUT(o->rec_count, out);
111 1
    list_foreach(objrec, rec, o->records) {
112 1
        PUT(rec->addr, out);
113 1
        PUT(rec->size, out);
114 1
        put_sized(rec->data, sizeof *rec->data, (size_t)rec->size, out);
115
    }
116

117 1
    return 0;
118
}
119

120 1
static int put_syms_v2(struct obj *o, STREAM *out, void *context)
121
{
122 1
    PUT(o->sym_count, out);
123 1
    list_foreach(objsym, sym, o->symbols) {
124 1
        PUT(sym->flags, out);
125 1
        PUT(sym->name.len, out);
126 1
        put_sized(sym->name.str, round_up_to_word((size_t)sym->name.len), 1, out);
127 1
        PUT(sym->value, out);
128 1
        PUT(sym->size, out);
129
    }
130

131 1
    return 0;
132
}
133

134 1
static int put_relocs_v2(struct obj *o, STREAM *out, void *context)
135
{
136 1
    PUT(o->rlc_count, out);
137 1
    list_foreach(objrlc, rlc, o->relocs) {
138 1
        PUT(rlc->flags, out);
139 1
        PUT(rlc->name.len, out);
140 1
        put_sized(rlc->name.str, round_up_to_word((size_t)rlc->name.len), 1, out);
141 1
        PUT(rlc->addr, out);
142 1
        PUT(rlc->width, out);
143 1
        PUT(rlc->shift, out);
144
    }
145

146 1
    return 0;
147
}
148

149 1
static int obj_v2_write(struct obj *o, STREAM *out, const struct objops *ops)
150
{
151 1
    put_sized(MAGIC_BYTES, 3, 1, out);
152 1
    PUT(o->magic.parsed.version, out);
153 1
    PUT(o->flags, out);
154

155
    {
156 1
        int rc = 0;
157 1
        rc = ops->put_recs  (o, out, NULL); if (rc) return rc;
158 1
        rc = ops->put_syms  (o, out, NULL); if (rc) return rc;
159 1
        rc = ops->put_relocs(o, out, NULL); if (rc) return rc;
160
    }
161

162 1
    return 0;
163
}
164

165 1
int obj_write(struct obj *o, STREAM *out)
166
{
167 1
    int version = o->magic.parsed.version = OBJ_DEFAULT_VERSION;
168

169 1
    switch (version) {
170 1
        case 2:
171 1
            return obj_v2_write(o, out, &objops);
172 0
        default:
173 0
            fatal(0, "Unhandled version %d while emitting object", version);
174
    }
175
}
176

177
// CREATE_SCOPE uses a C99 for-loop to create a variable whose lifetime is only
178
// the statement following the CREATE_SCOPE (which may or may not be a compound
179
// statement)
180
#define CREATE_SCOPE(Type,Var,...) \
181
    for (   Type Var __VA_ARGS__, *Sentinel_ = (void*)&Var; \
182
            Sentinel_; \
183
            Sentinel_ = NULL) \
184
//
185

186
#define for_counted_get(Tag,Name,List,Count) \
187
    if (!(Count)) { /* avoid calloc when Count is zero */ } else \
188
    CREATE_SCOPE(struct Tag*,Name,=calloc((size_t)Count,sizeof *Name),**Prev_ = &List) \
189
    for (SWord i_ = Count; i_ > 0; *Prev_ = Name, Prev_ = &Name++->next, i_--) \
190
//
191

192 1
static int get_recs(struct obj *o, STREAM *in, void *context)
193
{
194 1
    long *filesize = context;
195

196 1
    GET(o->rec_count, in);
197 1
    if (o->rec_count < 0) {
198 1
        errno = EINVAL;
199 1
        return 1;
200
    }
201 1
    if (o->rec_count > OBJ_MAX_REC_CNT) {
202 1
        errno = EFBIG;
203 1
        return 1;
204
    }
205 1
    o->bloc.records = 1;
206 1
    for_counted_get(objrec, rec, o->records, o->rec_count) {
207 1
        GET(rec->addr, in);
208 1
        GET(rec->size, in);
209 1
        if (rec->size < 0) {
210 1
            errno = EINVAL;
211 1
            return 1;
212
        }
213 1
        long here = in->op.ftell(in);
214 1
        if (here < 0) {
215
            // not a seekable stream -- forge ahead recklessly
216 1
        } else if ((long)rec->size > *filesize - here) {
217 1
            errno = EFBIG;
218 1
            return 1;
219
        }
220 1
        rec->data = calloc((size_t)rec->size, sizeof *rec->data);
221 1
        assert(rec->data != NULL);
222 1
        get_sized(rec->data, sizeof *rec->data, (size_t)rec->size, in);
223
    }
224

225 1
    return 0;
226
}
227

228 1
static int get_syms_v2(struct obj *o, STREAM *in, void *context)
229
{
230 1
    GET(o->sym_count, in);
231 1
    if (o->sym_count < 0) {
232 1
        errno = EINVAL;
233 1
        return 1;
234
    }
235 1
    if (o->sym_count > OBJ_MAX_SYMBOLS) {
236 1
        errno = EFBIG;
237 1
        return 1;
238
    }
239 1
    o->bloc.symbols = 1;
240 1
    for_counted_get(objsym, sym, o->symbols, o->sym_count) {
241 1
        GET(sym->flags, in);
242 1
        GET(sym->name.len, in);
243 1
        if (sym->name.len > SYMBOL_LEN_V2) {
244 1
            errno = EFBIG;
245 1
            return 1;
246
        }
247 1
        size_t rounded_len = round_up_to_word((size_t)sym->name.len);
248 1
        sym->name.str = malloc(rounded_len + sizeof '\0');
249 1
        get_sized(sym->name.str, rounded_len, 1, in);
250 1
        sym->name.str[sym->name.len] = '\0';
251 1
        GET(sym->value, in);
252 1
        GET(sym->size, in);
253
    }
254

255 1
    return 0;
256
}
257

258 1
static int get_relocs_v2(struct obj *o, STREAM *in, void *context)
259
{
260 1
    GET(o->rlc_count, in);
261 1
    if (o->rlc_count < 0) {
262 1
        errno = EINVAL;
263 1
        return 1;
264
    }
265 1
    if (o->rlc_count > OBJ_MAX_RELOCS) {
266 1
        errno = EFBIG;
267 1
        return 1;
268
    }
269 1
    o->bloc.relocs = 1;
270 1
    for_counted_get(objrlc, rlc, o->relocs, o->rlc_count) {
271 1
        GET(rlc->flags, in);
272 1
        GET(rlc->name.len, in);
273 1
        if (rlc->name.len > SYMBOL_LEN_V2) {
274 1
            errno = EFBIG;
275 1
            return 1;
276
        }
277 1
        size_t rounded_len = round_up_to_word((size_t)rlc->name.len);
278 1
        rlc->name.str = malloc(rounded_len + sizeof '\0');
279 1
        get_sized(rlc->name.str, rounded_len, 1, in);
280 1
        rlc->name.str[rlc->name.len] = '\0';
281 1
        GET(rlc->addr, in);
282 1
        GET(rlc->width, in);
283 1
        GET(rlc->shift, in);
284
    }
285

286 1
    return 0;
287
}
288

289 1
static int obj_v2_read(struct obj *o, STREAM *in, const struct objops *ops)
290
{
291 1
    long where = in->op.ftell(in);
292 1
    long filesize = LONG_MAX;
293 1
    if (where >= 0 && !in->op.fseek(in, 0L, SEEK_END)) { // seekable stream
294 1
        filesize = in->op.ftell(in);
295 1
        if (in->op.fseek(in, where, SEEK_SET))
296 0
            fatal(PRINT_ERRNO, "Failed to seek input stream");
297
    }
298

299 1
    GET(o->flags, in);
300

301
    {
302 1
        int rc = 0;
303 1
        rc = ops->get_recs  (o, in, &filesize); if (rc) return rc;
304 1
        rc = ops->get_syms  (o, in,      NULL); if (rc) return rc;
305 1
        rc = ops->get_relocs(o, in,      NULL); if (rc) return rc;
306
    }
307

308 1
    return 0;
309
}
310

311 1
int obj_read(struct obj *o, STREAM *in)
312
{
313 1
    GET(o->magic.parsed.TOV, in);
314

315 1
    if (memcmp(o->magic.parsed.TOV, MAGIC_BYTES, sizeof o->magic.parsed.TOV))
316 1
        fatal(0, "Bad magic when loading object");
317

318 1
    GET(o->magic.parsed.version, in);
319

320 1
    int version = o->magic.parsed.version;
321 1
    switch (version) {
322 1
        case 2:
323 1
            return obj_v2_read(o, in, &objops);
324 1
        default:
325 1
            fatal(0, "Unhandled version number when loading object");
326
    }
327
}
328

329 1
static void obj_v2_free(struct obj *o)
330
{
331
#define obj_v2_free_helper(Tag, Field, Action)  \
332
    do {                                        \
333
        list_foreach(Tag, item, o->Field) {     \
334
            Action;                             \
335
            if (!o->bloc.Field)                 \
336
                free(item);                     \
337
        }                                       \
338
        if (o->bloc.Field)                      \
339
            free(o->Field);                     \
340
    } while (0)                                 \
341
    // end obj_v2_free_helper
342

343 1
    obj_v2_free_helper(objrlc, relocs , free(item->name.str));
344 1
    obj_v2_free_helper(objsym, symbols, free(item->name.str));
345 1
    obj_v2_free_helper(objrec, records, free(item->data    ));
346

347 1
    free(o);
348
}
349

350 1
void obj_free(struct obj *o)
351
{
352 1
    switch (o->magic.parsed.version) {
353 1
        case 2:
354 1
            obj_v2_free(o); break;
355 0
        default:
356 0
            fatal(0, "Unknown version number or corrupt memory while freeing object");
357
    }
358
}
359

360
/* vi: set ts=4 sw=4 et: */

Read our documentation on viewing source code .

Loading