kulp / tenyr
1
#define _XOPEN_SOURCE (700) /* for strdup */
2

3
#include "obj.h"
4
// for RAM_BASE
5
#include "devices/ram.h"
6
#include "common.h"
7
#include "os_common.h"
8
#include "param.h"
9

10
#include <stdlib.h>
11
#include <stdio.h>
12
#include <getopt.h>
13
#include <search.h>
14
#include <string.h>
15
#include <strings.h>
16
#include <errno.h>
17
#include <assert.h>
18

19
struct link_state {
20
    SWord addr;     ///< current address
21
    int obj_count;
22
    struct obj_list {
23
        struct obj *obj;
24
        int i;
25
        struct obj_list *next;
26
    } *objs, **next_obj;
27
    struct obj *relocated;
28

29
    long insns, syms, rlcs, words;
30
};
31

32
struct defn {
33
    char *name;
34
    struct obj *obj;
35
    SWord reladdr;
36
    SWord flags;
37

38
    struct link_state *state;   ///< state reference used for twalk() support
39
};
40

41
struct objmeta {
42
    struct obj *obj;
43
    int size;
44
    int offset;
45

46
    struct link_state *state;   ///< state reference used for twalk() support
47
};
48

49
static const char shortopts[] = "o:p:hV";
50

51
static const struct option longopts[] = {
52
    { "output"      , required_argument, NULL, 'o' },
53
    { "param"       , required_argument, NULL, 'p' },
54

55
    { "help"        ,       no_argument, NULL, 'h' },
56
    { "version"     ,       no_argument, NULL, 'V' },
57

58
    { NULL, 0, NULL, 0 },
59
};
60

61 1
static const char *version(void)
62
{
63 1
    return "tld version " STR(BUILD_NAME) " built " __DATE__;
64
}
65

66

67 1
static void usage(const char *me)
68
{
69 1
    printf("Usage: %s [ OPTIONS ] image-file [ image-file ... ] \n"
70
           "Options:\n"
71
           "  -o, --output=X        write output to filename X\n"
72
           "  -p, --param=X=Y       set parameter X to value Y\n"
73
           "  -h, --help            display this message\n"
74
           "  -V, --version         print the string `%s'\n"
75
           , me, version());
76
}
77

78 1
static int do_load(struct link_state *s, STREAM *in)
79
{
80 1
    int rc = 0;
81 1
    struct obj_list *node = calloc(1, sizeof *node);
82

83 1
    struct obj *o = calloc(1, sizeof *o);
84 1
    rc = obj_read(o, in);
85 1
    node->obj = o;
86 1
    node->i = s->obj_count++;
87
    // put the objects on the list in order
88 1
    node->next = NULL;
89 1
    *s->next_obj = node;
90 1
    s->next_obj = &node->next;
91

92 1
    return rc;
93
}
94

95 1
static void do_unload(struct link_state *s)
96
{
97 1
    list_foreach(obj_list,ol,s->objs) {
98 1
        obj_free(ol->obj);
99 1
        free(ol);
100
    }
101
}
102

103 1
static int ptrcmp(const void *a, const void *b)
104
{
105 1
    return (int)(*(const char* const*)a - *(const char* const*)b);
106
}
107

108 1
static int def_str_cmp(const void *a, const void *b)
109
{
110 1
    const struct defn *aa = a, *bb = b;
111 1
    return strcmp(aa->name, bb->name);
112
}
113

114 1
static void do_link_build_state(struct link_state *s, void **objtree, void **defns)
115
{
116
    // running offset, tracking where to pack objects tightly one after another
117 1
    SWord running = 0;
118

119
    // read in all symbols
120 1
    list_foreach(obj_list, Node, s->objs) {
121 1
        struct obj *i = Node->obj;
122

123
        // Loading an object has already validated the object count.
124
        assert(i->rec_count >= 0);
125
        assert(i->rec_count < OBJ_MAX_REC_CNT);
126

127 1
        if (i->rec_count == 0) {
128 1
            debug(0, "Object has no records, skipping");
129 1
            continue;
130
        }
131

132
        // TODO support more than one record per object
133 1
        if (i->rec_count > 1)
134 1
            debug(0, "Object has more than one record, only using first");
135

136 1
        struct objmeta *meta = calloc(1, sizeof *meta);
137 1
        meta->state = s;
138 1
        meta->obj = i;
139 1
        meta->size = i->records[0].size;
140 1
        meta->offset = running;
141 1
        debug(1, "Object %p adds record size %d @ %#x", i, meta->size, meta->offset);
142 1
        running += i->records[0].size;
143

144 1
        tsearch(meta, objtree, ptrcmp);
145

146 1
        list_foreach(objsym, sym, i->symbols) {
147 1
            struct defn *def = calloc(1, sizeof *def);
148 1
            def->state = s;
149 1
            def->name = strdup(sym->name.str);
150 1
            def->obj = i;
151 1
            def->reladdr = sym->value;
152 1
            def->flags = sym->flags;
153 1
            debug(2, "Object %p adds symbol `%s` @ %#x", i, def->name, def->reladdr);
154

155 1
            struct defn **look = tsearch(def, defns, def_str_cmp);
156 1
            if (*look != def)
157 1
                fatal(0, "Duplicate definition for symbol `%s'", def->name);
158
        }
159
    }
160
}
161

162 1
static void do_link_relocate_obj_reloc(struct obj *i, struct objrlc *rlc,
163
                                       void **objtree, void **defns)
164
{
165 1
    SWord reladdr = 0;
166

167
    // Objects with record counts other than 1 have already been pruned in
168
    // do_link_build_state.
169
    // TODO support more than one record per object
170
    assert(i->rec_count == 1);
171

172 1
    struct objrec *r = &i->records[0];
173 1
    if (rlc->addr < r->addr ||
174 1
        rlc->addr - r->addr > r->size)
175
    {
176 1
        fatal(0, "Invalid relocation @ 0x%08x outside record @ 0x%08x size %d",
177
              rlc->addr, r->addr, r->size);
178
        return;
179
    }
180

181 1
    struct objmeta **me = tfind(&i, objtree, ptrcmp);
182 1
    if (rlc->name.len) { // TODO use length
183 1
        debug(1, "Object %p relocating name `%s` from %#x to %#x", i, rlc->name.str, rlc->addr, reladdr);
184
        struct defn def;
185 1
        def.name = rlc->name.str; // safe as long as we use tfind() but not tsearch()
186 1
        struct defn **look = tfind(&def, defns, def_str_cmp);
187 1
        if (!look)
188 1
            fatal(0, "Missing definition for symbol `%s'", rlc->name.str);
189 1
        reladdr = (*look)->reladdr;
190

191 1
        if (((*look)->flags & RLC_ABSOLUTE) == 0) {
192 1
            struct objmeta **it = tfind(&(*look)->obj, objtree, ptrcmp);
193 1
            reladdr += (*it)->offset;
194
        }
195
    } else {
196 1
        debug(1, "Object %p relocating . @ %#x", i, rlc->addr);
197
        // this is a null relocation ; it just wants us to update the offset
198 1
        reladdr = (*me)->offset;
199
    }
200
    // here we actually add the found-symbol's value to the relocation slot,
201
    // being careful to trim to the right width
202 1
    SWord mult = (rlc->flags & RLC_NEGATE) ? -1 : +1;
203 1
    SWord *dest = &r->data[rlc->addr - r->addr];
204 1
    SWord mask = ((SWord)((1u << (rlc->width - 1)) << 1) - 1);
205 1
    SWord updated = (*dest + mult * (reladdr >> rlc->shift)) & mask;
206 1
    *dest = (*dest & ~mask) | updated;
207
}
208

209 1
static void do_link_relocate_obj(struct obj *i, void **objtree, void **defns)
210
{
211 1
    list_foreach(objrlc, rlc, i->relocs)
212 1
        do_link_relocate_obj_reloc(i, rlc, objtree, defns);
213
}
214

215 1
static void do_link_relocate(struct obj_list *ol, void **objtree, void **defns)
216
{
217 1
    list_foreach(obj_list, Node, ol) {
218 1
        if (!Node->obj->rec_count) {
219 1
            debug(0, "Object has no records, skipping");
220 1
            continue;
221
        }
222

223 1
        do_link_relocate_obj(Node->obj, objtree, defns);
224
    }
225
}
226

227 1
static void do_link_process(struct link_state *s)
228
{
229 1
    void *objtree = NULL;   ///< tsearch-tree of `struct objmeta'
230 1
    void *defns   = NULL;   ///< tsearch tree of `struct defns'
231

232 1
    do_link_build_state(s, &objtree, &defns);
233 1
    do_link_relocate(s->objs, &objtree, &defns);
234

235 1
    while (objtree) {
236 1
        void *node = *(void**)objtree;
237 1
        tdelete(node, &objtree, ptrcmp);
238 1
        free(node);
239
    }
240

241 1
    while (defns) {
242 1
        struct defn *node = *(void**)defns;
243 1
        tdelete(node, &defns, def_str_cmp);
244 1
        free(node->name);
245 1
        free(node);
246
    }
247
}
248

249 1
static void do_link_emit(struct link_state *s, struct obj *o)
250
{
251 1
    long rec_count = 0;
252
    // copy records
253 1
    struct objrec **ptr_objrec = &o->records, *front = NULL;
254 1
    int32_t addr = 0;
255 1
    list_foreach(obj_list, Node, s->objs) {
256 1
        struct obj *i = Node->obj;
257

258 1
        list_foreach(objrec, rec, i->records) {
259 1
            struct objrec *n = calloc(1, sizeof *n);
260

261 1
            n->addr = addr;
262 1
            n->size = rec->size;
263 1
            n->data = malloc((size_t)rec->size * sizeof *n->data);
264 1
            n->next = NULL;
265 1
            memcpy(n->data, rec->data, (size_t)rec->size * sizeof *n->data);
266

267 1
            if (*ptr_objrec) (*ptr_objrec)->next = n;
268 1
            if (!front) front = n;
269 1
            *ptr_objrec = n;
270 1
            ptr_objrec = &n->next;
271

272 1
            addr += rec->size;
273 1
            rec_count++;
274
        }
275
    }
276

277 1
    o->records = front;
278

279 1
    o->rec_count = (SWord)rec_count;
280 1
    o->sym_count = (SWord)s->syms;
281 1
    o->rlc_count = (SWord)s->rlcs;
282
}
283

284 1
static void do_link(struct link_state *s)
285
{
286 1
    struct obj *o = s->relocated = calloc(1, sizeof *o);
287

288 1
    do_link_process(s);
289 1
    do_link_emit(s, o);
290
}
291

292 1
static void do_emit(struct link_state *s, STREAM *out)
293
{
294 1
    obj_write(s->relocated, out);
295
}
296

297 1
static int do_load_all(struct link_state *s, int count, char **names)
298
{
299 1
    int rc = 0;
300

301 1
    for (int i = 0; i < count; i++) {
302 1
        FILE *infile = NULL;
303

304 1
        if (!strcmp(names[i], "-")) {
305 1
            infile = stdin;
306
        } else {
307 1
            infile = os_fopen(names[i], "rb");
308 1
            if (!infile)
309 1
                fatal(PRINT_ERRNO, "Failed to open input file `%s'", names[i]);
310
        }
311

312
        // Explicitly clear errors and EOF in case we run main() twice
313
        // (emscripten)
314 1
        clearerr(infile);
315

316 1
        const struct stream in_ = stream_make_from_file(infile), *in = &in_;
317

318 1
        rc = do_load(s, in);
319 1
        int saved = errno;
320

321 1
        fclose(infile);
322

323 1
        errno = saved;
324 1
        if (rc)
325 1
            return rc;
326
    }
327

328 1
    return 0;
329
}
330

331 1
int main(int argc, char *argv[])
332
{
333 1
    int rc = 0;
334

335 1
    struct link_state _s = {
336
        .addr = 0,
337
        .next_obj = &_s.objs,
338 1
    }, *s = &_s;
339

340 1
    char * volatile outfname = NULL;
341 1
    FILE * volatile outfile = stdout;
342

343 1
    struct param_state * volatile params = NULL;
344 1
    param_init((struct param_state **)&params);
345

346 1
    if ((rc = setjmp(errbuf))) {
347 1
        if (rc == DISPLAY_USAGE)
348 1
            usage(argv[0]);
349
        // We may have created an output file already, but we do not try to
350
        // remove it, because doing so by filename would be a race condition.
351
        // The most important reason to remove the output file is to avoid
352
        // tricking a build system into thinking that a failed build created a
353
        // good output file; with GNU Make this can be avoided by using
354
        // .DELETE_ON_ERROR, and other build systems have similar features.
355 1
        rc = EXIT_FAILURE;
356 1
        goto cleanup;
357
    }
358

359
    // Explicitly reset optind for cases where main() is called more than once
360
    // (emscripten)
361 1
    optind = 0;
362

363
    int ch;
364 1
    while ((ch = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) {
365 1
        switch (ch) {
366 1
            case 'o': outfname = optarg; break;
367 1
            case 'p': param_add(params, optarg); break;
368

369 1
            case 'V':
370 1
                puts(version());
371 1
                rc = EXIT_SUCCESS;
372 1
                goto cleanup;
373 1
            case 'h':
374 1
                usage(argv[0]);
375 1
                rc = EXIT_SUCCESS;
376 1
                goto cleanup;
377 1
            default:
378 1
                usage(argv[0]);
379 1
                rc = EXIT_FAILURE;
380 1
                goto cleanup;
381
        }
382
    }
383

384 1
    if (optind >= argc)
385 1
        fatal(DISPLAY_USAGE, "No input files specified on the command line");
386

387 1
    os_preamble();
388

389 1
    if (outfname)
390 1
        outfile = os_fopen(outfname, "wb");
391 1
    if (!outfile)
392 1
        fatal(PRINT_ERRNO, "Failed to open output file `%s'", outfname ? outfname : "<stdout>");
393

394 1
    const struct stream out_ = stream_make_from_file(outfile), *out = &out_;
395

396 1
    rc = do_load_all(s, argc - optind, &argv[optind]);
397 1
    if (rc)
398 1
        fatal(PRINT_ERRNO, "Failed to load objects");
399 1
    do_link(s);
400 1
    do_emit(s, out);
401 1
    do_unload(s);
402 1
    list_foreach(objrec, rec, s->relocated->records) {
403 1
        free(rec->data);
404 1
        free(rec);
405
    }
406 1
    free(s->relocated);
407

408 1
    fclose(outfile);
409 1
    out = NULL;
410

411 1
cleanup:
412 1
    param_destroy(params);
413

414 1
    return rc;
415
}
416

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

Read our documentation on viewing source code .

Loading