-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathld.c
1681 lines (1582 loc) · 40.9 KB
/
ld.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* Ld symbol loading support. For the moment we don't do anything clever
* to avoid memory wastage.
*
* Theory of operation
*
* We scan the header and load the symbols (non debug) for each object
* We then compute the total size of each segment
* We calculate the base address of each object file code/data/bss
* We write a dummy executable header
* We relocate each object code and write all the code to the file
* We do the same with the data
* We set up the header and bss sizes
* We write out symbols (optional)
* We write the header back
*
* The relocation can be one of three forms eventually
* ld -r:
* We write the entire object out as one .o file with all the
* internal references resolved and all the symbols adjusted
* versus that. Undefined symbols are allowed and carried over
* a.out (or similar format)
* We resolve the entire object as above but write out with a
* binary header. No undefined symbols are allowed
* bin:
* We resolve the entire object and perform all relocations
* to generate a binary with a fixed load address. No undefined
* symbols or relocations are left
*
* There are a few things not yet addressed
* 1. For speed libraries can start with an _RANLIB ar file node which
* is an index of all the symbols by library module for speed.
* 2. Banked binaries (segments 5-7 ?).
* 3. Use typedefs and the like to support 32bit as well as 16bit
* addresses when built on bigger machines..
* 4. For word addressing
* - we need to deal with bytepointers (ptr scaling encoding needs
* adding)
* - we need to scale o_base when adjusting symbols as o_base
* is in bytes.
* - we need to deal with overflows resulting from word to byte
* conversion on symbols.
* - we need to deal with the fact some stuff is tracked at
* byte level.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <getopt.h>
#include <ctype.h>
#include <sys/stat.h>
#include "obj.h"
#include "ld.h"
#include "ar.h" /* Pick up our ar.h just in case the
compiling OS has a weird ar.h */
#ifndef ENABLE_RESCAN
#define ENABLE_RESCAN 0
#endif
#ifdef ARCH32
#define io_readaddr() io_read32()
#define MAXSIZE 4
typedef int32_t addrdiff_t;
#else
#define io_readaddr() io_read16()
#define MAXSIZE 2
typedef int16_t addrdiff_t;
#endif
static char *arg0; /* Command name */
static struct object *processing; /* Object being processed */
static const char *libentry; /* Library entry name if relevant */
static struct object *objects, *otail; /* List of objects */
static struct symbol *symhash[NHASH]; /* Symbol has tables */
static addr_t base[OSEG]; /* Base of each segment */
static addr_t size[OSEG]; /* Size of each segment */
static addr_t align = 1; /* Alignment */
static addr_t baseset[OSEG]; /* Did the user force this one */
#define LD_RELOC 0 /* Output a relocatable binary stream */
#define LD_RFLAG 1 /* Output an object module */
#define LD_ABSOLUTE 2 /* Output a linked binary */
#define LD_FUZIX 3 /* Output a Fuzix binary */
static uint_fast8_t ldmode = LD_FUZIX; /* Operating mode */
static uint_fast8_t rawstream; /* Outputting raw or quoted ? */
static uint_fast8_t split_id; /* True if code and data both zero based */
static unsigned arch; /* Architecture */
static uint16_t arch_flags; /* Architecture specific flags */
static uint_fast8_t verbose; /* Verbose reporting */
static int err; /* Error tracking */
/*static int dbgsyms = 1;*/ /* Set to dumb debug symbols */
static int strip = 0; /* Set to strip symbols */
static int obj_flags = -1; /* Object module flags for compat */
static const char *mapname; /* Name of map file to write */
static const char *outname; /* Name of output file */
static addr_t dot; /* Working address as we link */
static unsigned progress; /* Did we make forward progress ?
Used while library linking */
static const char *segmentorder = "CLDBX"; /* Segment default order */
static int_fast8_t rel_shift; /* Relocation scaling */
static addr_t rel_mask; /* Relocation mask */
static uint_fast8_t rel_check; /* Check fits mask */
static FILE *relocf;
/*
* Report an error, and if possible give the object or library that
* we were processing.
*/
void warning(const char *p)
{
if (processing)
fprintf(stderr, "While processing: %s", processing->path);
if (libentry)
fprintf(stderr, "(%.16s)", libentry);
fputc('\n', stderr);
fputs(p, stderr);
fputc('\n', stderr);
err |= 2;
}
void error(const char *p)
{
warning(p);
exit(err);
}
/*
* Standard routines wrapped with error exit tests
*/
static void *xmalloc(size_t s)
{
register void *p = malloc(s);
if (p == NULL)
error("out of memory");
return p;
}
/*
* Optimized disk I/O for library scanning
*/
static uint8_t iobuf[512];
static uint8_t *ioptr;
static unsigned ioblock;
static unsigned iopos;
static unsigned iolen;
static int iofd;
static void io_close(void)
{
close(iofd);
iofd = -1;
}
static unsigned io_get(unsigned block)
{
if (block != ioblock + 1 && lseek(iofd, ((off_t)block) << 9, 0L) < 0) {
perror("lseek");
exit(err | 1);
}
/* printf("io_get: seek to %lx\n", (off_t)(block << 9)); */
ioblock = block;
iolen = read(iofd, iobuf, 512);
if (iolen == -1) {
perror("read");
exit(err | 1);
}
iopos = 0;
ioptr = iobuf;
/* printf("io_get read block %d iolen now %d\n", block, iolen); */
return iolen;
}
static int io_lseek(off_t pos)
{
unsigned block = pos >> 9;
if (block != ioblock)
io_get(block);
iopos = pos & 511;
ioptr = iobuf + iopos;
if (iopos > iolen)
return -1;
/* printf("io_lseek %lx, block %d pos %d len %d\n",
pos, ioblock, iopos, iolen); */
return 0;
}
#if 0
static off_t io_getpos(void)
{
return (((off_t)ioblock) << 9) | iopos;
}
#endif
static int io_read(void *bufp, unsigned len)
{
register unsigned left = iolen - iopos;
register uint8_t *buf = bufp;
register unsigned n;
unsigned bytes = 0;
while(len) {
/* printf("len %d, left %d, ptr %p, iopos %d iolen %d\n",
len, left, ioptr, iopos, iolen);
if (ioptr != iobuf + iopos) {
fprintf(stderr, "Botch %p %p\n",
ioptr, iobuf + iopos);
exit(1);
} */
n = len;
if (n > left)
n = left;
if (n) {
memcpy(buf, ioptr, n);
ioptr += n;
iopos += len;
len -= n;
bytes += n;
buf += n;
}
if (len) {
if (io_get(ioblock + 1) == 0)
break;
left = iolen;
}
}
/* printf("io_read %d\n", bytes); */
return bytes;
}
static unsigned io_read16(void)
{
uint8_t p[2];
io_read(p, 2);
return (p[1] << 8) | p[0];
}
#ifdef ARCH32
static uint32_t io_read32(void)
{
uint8_t p[4];
io_read(p, 4);
return (p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0];
}
#endif
/* Our embedded relocs make this a hot path so optimize it. We may
want a helper that hands back blocks until the reloc marker ? */
unsigned io_readb(uint_fast8_t *ch)
{
if (iopos < iolen) {
iopos++;
*ch = *ioptr++;
return 1;
} else
return io_read(ch, 1);
}
static int io_open(const char *path)
{
iofd = open(path, O_RDONLY);
ioblock = 0xFFFF; /* Force a re-read */
if (iofd == -1 || io_lseek(0)) {
perror(path);
exit(err | 1);
}
/* printf("opened %s\n", path); */
return iofd;
}
static FILE *xfopen(const char *path, const char *mode)
{
FILE *fp = fopen(path, mode);
if (fp == NULL) {
perror(path);
exit(err | 1);
}
return fp;
}
static void xfclose(FILE *fp)
{
if (fclose(fp)) {
perror("fclose");
exit(err | 1);
}
}
static void xfseek(FILE *fp, off_t pos)
{
if (fseek(fp, pos, SEEK_SET) < 0) {
perror("fseek");
exit(err | 1);
}
}
static addr_t xstrtoul(const char *p)
{
char *r;
unsigned long x = strtoul(p, &r, 0);
if (r == p) {
fprintf(stderr, "'%s' is not a valid numeric constant.\n", p);
exit(1);
}
if (x > 65535) {
fprintf(stderr, "'%s' is not in the range 0-65535.\n", p);
exit(1);
}
return x;
}
/*
* Target specific behaviour
*/
static unsigned target_has_regzp(void)
{
/* Processors which have has ZP as register space don't write out
the ZP segment as it's not part of the actual real memory map */
if (arch == OA_Z8)
return 1;
return 0;
}
/*
* Manage the linked list of object files and object modules within
* libraries that we have seen.
*/
static struct object *new_object(void)
{
register struct object *o = xmalloc(sizeof(struct object));
o->next = NULL;
o->syment = NULL;
return o;
}
static void insert_object(register struct object *o)
{
if (otail)
otail->next = o;
else
objects = o;
otail = o;
}
static void free_object(register struct object *o)
{
if (o->syment)
free(o->syment);
if (o->oh)
free(o->oh);
free(o);
}
/*
* Add a symbol to our symbol tables as we discover it. Log the
* fact if tracing.
*/
struct symbol *new_symbol(const char *name, int hash)
{
register struct symbol *s = xmalloc(sizeof(struct symbol));
strncpy(s->name, name, NAMELEN);
s->next = symhash[hash];
symhash[hash] = s;
if (verbose)
printf("+%.*s\n", NAMELEN, name);
return s;
}
/*
* Find a symbol in a given has table
*/
struct symbol *find_symbol(register const char *name, int hash)
{
register struct symbol *s = symhash[hash];
while (s) {
if (strncmp(s->name, name, NAMELEN) == 0)
return s;
s = s->next;
}
return NULL;
}
/*
* A simple but adequate hashing algorithm. A better one would
* be worth it for performance.
*/
static uint8_t hash_symbol(register const char *name)
{
register int hash = 0;
register uint_fast8_t n = 0;
while(*name && n++ < NAMELEN)
hash += *name++;
return (hash&(NHASH-1));
}
/*
* Check if a symbol name is known but undefined. We use this to decide
* whether to incorporate a library module
*/
static int is_undefined(const char *name)
{
int hash = hash_symbol(name);
struct symbol *s = find_symbol(name, hash);
if (s == NULL || !(s->type & S_UNKNOWN))
return 0;
/* This is a symbol we need */
progress++;
return 1;
}
/*
* Check that two versions of a symbol are compatible.
*/
static void segment_mismatch(register struct symbol *s, uint_fast8_t type2)
{
register uint_fast8_t seg1 = s->type & S_SEGMENT;
register uint_fast8_t seg2 = type2 & S_SEGMENT;
/* Matching */
if (seg1 == seg2)
return;
/* Existing entry was 'anything'. Co-erce to definition */
if (seg1 == S_ANY) {
s->type &= ~S_SEGMENT;
s->type |= seg2;
return;
}
/* Regardless of the claimed type, an absolute definition fulfills
any need. */
if (seg2 == ABSOLUTE || seg2 == S_ANY)
return;
fprintf(stderr, "Segment mismatch for symbol '%.*s'.\n", NAMELEN, s->name);
fprintf(stderr, "Want segment %d but constrained to %d.\n",
seg2, seg1);
err |= 2;
}
/*
* We have learned about a new symbol. Find the symbol if it exists, or
* create it if not. Do the necessary work to promote unknown symbols
* to known and also to ensure we don't have multiple incompatible
* definitions or have incompatible definition and requirement.
*/
static struct symbol *find_alloc_symbol(struct object *o, uint_fast8_t type, const char *id, addr_t value)
{
uint8_t hash = hash_symbol(id);
register struct symbol *s = find_symbol(id, hash);
if (s == NULL) {
s = new_symbol(id, hash);
s->type = type;
/*FIXME strlcpy(s->name, id, NAMELEN); */
strncpy(s->name, id, NAMELEN);
s->value = value;
if (!(type & S_UNKNOWN))
s->definedby = o;
else
s->definedby = NULL;
return s;
}
/* Already exists. See what is going on */
if (type & S_UNKNOWN) {
/* We are an external reference to a symbol. No work needed */
segment_mismatch(s, type);
return s;
}
if (s->type & S_UNKNOWN) {
/* We are referencing a symbol that was previously unknown but which
we define. Fill in the details */
segment_mismatch(s, type);
s->type &= ~S_UNKNOWN;
s->value = value;
s->definedby = o;
return s;
}
/* Two definitions.. usually bad but allow duplicate absolutes */
if (((s->type | type) & S_SEGMENT) != ABSOLUTE || s->value != value) {
/* FIXME: expand to report files somehow ? */
fprintf(stderr, "%.*s: multiply defined.\n", NAMELEN, id);
}
/* Duplicate absolutes - just keep current entry */
return s;
}
/*
* Add the internal symbols indicating where the segments start and
* end.
*/
static void insert_internal_symbol(const char *name, int seg, addr_t val)
{
if (seg == -1)
find_alloc_symbol(NULL, S_ANY | S_UNKNOWN, name, 0);
else
find_alloc_symbol(NULL, seg | S_PUBLIC, name, val);
}
/*
* Number the symbols that we will write out in the order they will
* appear in the output file. We don't care about the mode too much.
* In valid reloc mode we won't have any S_UNKNOWN symbols anyway
*/
static void renumber_symbols(void)
{
static int sym = 0;
register struct symbol *s;
register int i;
for (i = 0; i < NHASH; i++)
for (s = symhash[i]; s != NULL; s=s->next)
if (s->type & (S_PUBLIC|S_UNKNOWN))
s->number = sym++;
}
/* Write the symbols to the output file */
static void write_symbols(FILE *fp)
{
register struct symbol *s;
register int i;
for (i = 0; i < NHASH; i++) {
for (s = symhash[i]; s != NULL; s=s->next) {
fputc(s->type, fp);
fwrite(s->name, NAMELEN, 1, fp);
fputc(s->value, fp);
fputc(s->value >> 8, fp);
#ifdef ARCH32
fputc(s->value >> 16, fp);
fputc(s->value >> 24, fp);
#endif
}
}
}
/*
* TODO: Fold all these symbol table walks into a helper
*/
/*
* Print a symbol for the map file
*/
static void print_symbol(register struct symbol *s, FILE *fp)
{
char c;
if (s->type & S_UNKNOWN)
c = 'U';
else {
c = "ACDBZXSLsb"[s->type & S_SEGMENT];
if (s->type & S_PUBLIC)
c = toupper(c);
}
#ifdef ARCH32
fprintf(fp, "%08X %c %.*s\n", s->value, c, NAMELEN, s->name);
#else
fprintf(fp, "%04X %c %.*s\n", s->value, c, NAMELEN, s->name);
#endif
}
/*
* Walk the symbol table generating a map file as we go
*/
static void write_map_file(FILE *fp)
{
register struct symbol *s;
register int i;
for (i = 0; i < NHASH; i++) {
for (s = symhash[i]; s != NULL; s=s->next)
print_symbol(s, fp);
}
}
/*
* Check that the newly discovered object file is the same format
* as the existing one. Also check for big endian as we don't yet
* support that (although we are close).
*/
static void compatible_obj(register struct objhdr *oh)
{
if (obj_flags != -1 && oh->o_flags != obj_flags) {
fprintf(stderr, "Mixed object types not supported.\n");
exit(1);
}
obj_flags = oh->o_flags;
}
/*
* See if we already merged an object module. With a library we
* scan mutiple times but we don't import the same module twice
*/
static int have_object(off_t pos, const char *name)
{
register struct object *o = objects;
while(o) {
if (o->off == pos && strcmp(name, o->path) == 0)
return 1;
o = o->next;
}
return 0;
}
static unsigned get_object(register struct object *o)
{
o->oh = xmalloc(sizeof(struct objhdr));
io_lseek(o->off);
return io_read(o->oh, sizeof(struct objhdr));
}
static void put_object(register struct object *o)
{
if (o->oh)
free(o->oh);
o->oh = NULL;
}
/*
* Open an object file and seek to the right location in case it is
* a library module.
*/
static void openobject(struct object *o)
{
io_open(o->path);
get_object(o);
}
/*
* Load a new object file. The off argument allows us to load an
* object module out of a library by giving the library file handle
* and the byte offset into it.
*
* Do all the error reporting and processing needed to incorporate
* the module, and load and add all the symbols.
*/
static struct object *load_object(off_t off, int lib, const char *path)
{
register int i;
uint_fast8_t type;
char name[NAMELEN + 1];
register struct object *o = new_object();
struct symbol **sp;
int nsym;
addr_t value;
o->path = path;
o->off = off;
processing = o; /* For error reporting */
if (get_object(o) != sizeof(struct objhdr) || o->oh->o_magic != MAGIC_OBJ || o->oh->o_symbase == 0) {
/* A library may contain other things, just ignore them */
if (lib) {
free_object(o);
processing = NULL;
return NULL;
}
else /* But an object file must be valid */
error("bad object file");
}
compatible_obj(o->oh);
/* Load up the symbols */
nsym = (o->oh->o_dbgbase - o->oh->o_symbase) / S_ENTRYSIZE;
if (nsym < 0||nsym > 65535)
error("bad object file");
/* Allocate the symbol entries */
o->syment = (struct symbol **) xmalloc(sizeof(struct symbol *) * nsym);
o->nsym = nsym;
restart:
io_lseek(off + o->oh->o_symbase);
sp = o->syment;
for (i = 0; i < nsym; i++) {
io_readb(&type);
io_read(name, NAMELEN);
name[NAMELEN] = 0;
value = io_readaddr(); /* Little endian */
if (!(type & S_UNKNOWN) && (type & S_SEGMENT) >= OSEG) {
fprintf(stderr, "Symbol %s\n", name);
if ((type & S_SEGMENT) == UNKNOWN)
error("exported but undefined");
else
error("bad symbol");
}
/* In library mode we look for a symbol that means we will load
this object - and then restart wih lib = 0 */
if (lib) {
if (!(type & S_UNKNOWN) && is_undefined(name)) {
if (verbose)
printf("importing for '%s'\n", name);
lib = 0;
goto restart;
}
} else
*sp++ = find_alloc_symbol(o, type, name, value);
}
/* If we get here with lib set then this was a library module we didn't
in fact require */
if (lib) {
free_object(o);
processing = NULL;
return NULL;
}
insert_object(o);
/* Make sure all the files are the same architeture */
if (arch) {
if (o->oh->o_arch != arch)
error("wrong architecture");
} else
arch = o->oh->o_arch;
/* The CPU features required is the sum of all the flags in the objects */
arch_flags |= o->oh->o_cpuflags;
processing = NULL;
put_object(o);
return o;
}
/*
* Helper for layout computation. Add one segment after another
* ane ensure it fits. If a segment base is hand set don't touch it
*/
static void append_segment(int a, int b)
{
if (baseset[a])
return;
base[a] = ((base[b] + size[b] + align - 1)/align) * align;
if (base[a] < base[b])
error("image too large");
}
static char segnames[] = "CDBZXSLsb";
static void order_segments(void)
{
register const char *s = segmentorder;
register unsigned last = 0xFF;
register unsigned n;
while(*s) {
char *p = strchr(segnames, *s);
if (p == NULL) {
fprintf(stderr, "Unknown segment '%c'.\n", *s);
error("invalid segment order");
}
n = p - segnames + 1;
if (!baseset[n]) {
if (last != 0xFF)
append_segment(n, last);
}
last = n;
s++;
}
}
/*
* Once all the objects are loaded this function walks the list and
* assigns each object file a base address for each segment. We do
* this by walking the list once to find the total size of code/data/bss
* and then a second time to set the offsets.
*/
static void set_segment_bases(void)
{
register struct object *o;
addr_t pos[OSEG];
register int i;
/* We are doing a simple model here without split I/D for now */
for (i = 1; i < OSEG; i++)
size[i] = 0;
/* Now run through once computing the basic size of each segment */
for (o = objects; o != NULL; o = o->next) {
openobject(o);
if (verbose)
printf("%s:\n", o->path);
for (i = 1; i < OSEG; i++) {
size[i] += o->oh->o_size[i];
if (verbose)
printf("\t%c : %04X %04X\n",
"ACDBZXSLsb??????"[i], o->oh->o_size[i],
size[i]);
if (size[i] < o->oh->o_size[i])
error("segment too large");
}
put_object(o);
io_close();
}
if (verbose) {
for (i = 1; i < 7; i++)
printf("Segment %c Size %04X\n", "ACDBZXc"[i], size[i]);
}
/* We now know where to put the binary */
if (ldmode == LD_RELOC) {
/* Creating a binary - put the segments together */
if (split_id && !baseset[2]) {
base[2] = 0;
baseset[2] = 1;
}
}
order_segments();
if (ldmode != LD_RFLAG) {
/* ZP if any is assumed to be set on input */
/* FIXME: check the literals fit .. make this a more sensible
overlap check loop ? */
if (base[3] < base[2] || base[3] + size[3] < base[3])
error("image too large");
/* Whoopee it fits */
/* Insert the linker symbols */
/* FIXME: symbols for all OSEG segments */
insert_internal_symbol("__code", CODE, 0);
insert_internal_symbol("__data", DATA, 0);
insert_internal_symbol("__bss", BSS, 0);
insert_internal_symbol("__literal", LITERAL, 0);
insert_internal_symbol("__end", BSS, size[3]);
insert_internal_symbol("__zp", ZP, 0);
insert_internal_symbol("__discard", DISCARD, 0);
insert_internal_symbol("__common", COMMON, 0);
insert_internal_symbol("__buffers", BUFFERS, 0);
insert_internal_symbol("__commondata", COMMONDATA, 0);
insert_internal_symbol("__code_size", ABSOLUTE, size[CODE]);
insert_internal_symbol("__data_size", ABSOLUTE, size[DATA]);
insert_internal_symbol("__bss_size", ABSOLUTE, size[BSS]);
insert_internal_symbol("__literal_size", ABSOLUTE, size[LITERAL]);
insert_internal_symbol("__zp_size", ABSOLUTE, size[ZP]);
insert_internal_symbol("__discard_size", ABSOLUTE, size[DISCARD]);
insert_internal_symbol("__common_size", ABSOLUTE, size[COMMON]);
insert_internal_symbol("__buffers_size", ABSOLUTE, size[BUFFERS]);
insert_internal_symbol("__commondata_size", ABSOLUTE, size[COMMONDATA]);
}
/* Now set the base of each object appropriately */
memcpy(&pos, &base, sizeof(pos));
for (o = objects; o != NULL; o = o->next) {
openobject(o);
o->base[0] = 0;
for (i = 1; i < OSEG; i++) {
o->base[i] = pos[i];
pos[i] += o->oh->o_size[i];
}
put_object(o);
io_close();
}
/* At this point we have correctly relocated the base for each object. What
we have yet to do is to relocate the symbols. Internal symbols are always
created as absolute with no definedby */
for (i = 0; i < NHASH; i++) {
struct symbol *s = symhash[i];
while (s != NULL) {
uint_fast8_t seg = s->type & S_SEGMENT;
/* base will be 0 for absolute */
if (s->definedby)
s->value += s->definedby->base[seg];
else
s->value += base[seg];
/* FIXME: check overflow */
s = s->next;
}
}
/* We now know all the base addresses and all the symbol values are
corrected. Everything needed for relocation is present */
}
/*
* Write a target byte with correct quoting if needed
*
* We quote if we are outputing a new link binary (ld -r), or a
* relocatable.
*/
static void target_pquoteb(uint8_t v, FILE *op)
{
if (v == REL_ESC && !rawstream) {
fputc(v, op);
fputc(REL_REL, op);
} else
fputc(v, op);
}
/*
* Write a word to the target in the correct endianness
*/
static void target_put(struct object *o, addr_t value, uint16_t size, FILE *op)
{
#ifdef ARCH32
if (o->oh->o_flags & OF_BIGENDIAN) {
unsigned rs = (size - 1) * 8;
while(size--) {
target_pquoteb(value >> rs, op);
value <<= 8;
}
} else {
while(size--) {
target_pquoteb(value, op);
value >>= 8;
}
}
#else
/* Tighter short paths for 16bit so it can run nicely on small
boxes */
if (size == 1)
target_pquoteb(value, op);
else {
if (o->oh->o_flags&OF_BIGENDIAN) {
target_pquoteb(value >> 8, op);
target_pquoteb(value, op);
} else {
target_pquoteb(value, op);
target_pquoteb(value >> 8, op);
}
}
#endif
}
static uint_fast8_t target_pgetb(void)
{
uint_fast8_t c;
if (io_readb(&c) == 0)
error("unexpected EOF");
return c;
}
/*
* Read a work from the target in the correct endianness. For
* better or worse all our relocation streams are always little endian
* while the instruction stream being relocated is of necessity native
* endian.
*/
static addr_t target_get(struct object *o, uint16_t size)
{
#ifdef ARCH32
addr_t v = 0;
if (o->oh->o_flags & OF_BIGENDIAN) {
while(size--) {
v <<= 8;
v |= target_pgetb();
}
} else {
unsigned s = 0;
while(size--) {
v |= target_pgetb() << s;
s += 8;
}
}
return v;
#else
/* Tighter hardcoded for speed on 8bit boxes */
if (size == 1)
return target_pgetb();
else {
if (o->oh->o_flags & OF_BIGENDIAN)
return (target_pgetb() << 8) + target_pgetb();
else
return target_pgetb() + (target_pgetb() << 8);
}
#endif
}
static void record_reloc(struct object *o, unsigned high, unsigned size, unsigned seg, addr_t addr)
{
if (!relocf)
return;
/* Absolutes are .. absolute */
if (seg == ABSOLUTE)
return;
#ifndef ARCH32
if (size == 2 && !(o->oh->o_flags & OF_BIGENDIAN))
addr++;
#endif
if (seg == ZP) {
fputc(0, relocf);
fputc(0, relocf);
fputc((addr & 0xFF), relocf);
return;
}
if (size == 1 && !high)
return;
/* Record the address of the high byte */
#ifdef ARCH32
/* 32bit relocs work differently, record the actual addr */
fputc(2, relocf);
fputc((addr >> 24), relocf);
fputc((addr >> 16), relocf);
fputc((addr >> 8), relocf);
fputc(addr, relocf);
#else
fputc(1, relocf);
fputc((addr >> 8), relocf);
fputc(addr, relocf);
#endif
}