1 // Inferno utils/5l/obj.c
2 // http://code.google.com/p/inferno-os/source/browse/utils/5l/obj.c
3 //
4 // Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
5 // Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
6 // Portions Copyright © 1997-1999 Vita Nuova Limited
7 // Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
8 // Portions Copyright © 2004,2006 Bruce Ellis
9 // Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
10 // Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
11 // Portions Copyright © 2009 The Go Authors. All rights reserved.
12 //
13 // Permission is hereby granted, free of charge, to any person obtaining a copy
14 // of this software and associated documentation files (the "Software"), to deal
15 // in the Software without restriction, including without limitation the rights
16 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17 // copies of the Software, and to permit persons to whom the Software is
18 // furnished to do so, subject to the following conditions:
19 //
20 // The above copyright notice and this permission notice shall be included in
21 // all copies or substantial portions of the Software.
22 //
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
29 // THE SOFTWARE.
30
31 // Reading object files.
32
33 #define EXTERN
34 #include "l.h"
35 #include "../ld/lib.h"
36 #include "../ld/elf.h"
37 #include <ar.h>
38
39 #ifndef DEFAULT
40 #define DEFAULT '9'
41 #endif
42
43 char *noname = "<none>";
44 char *thestring = "arm";
45
46 Header headers[] = {
47 "noheader", Hnoheader,
48 "risc", Hrisc,
49 "plan9", Hplan9x32,
50 "netbsd", Hnetbsd,
51 "ixp1200", Hixp1200,
52 "ipaq", Hipaq,
53 "linux", Hlinux,
54 0, 0
55 };
56
57 /*
58 * -Hrisc -T0x10005000 -R4 is aif for risc os
59 * -Hplan9 -T4128 -R4096 is plan9 format
60 * -Hnetbsd -T0xF0000020 -R4 is NetBSD format
61 * -Hixp1200 is IXP1200 (raw)
62 * -Hipaq -T0xC0008010 -R1024 is ipaq
63 * -Hlinux -Tx -Rx is linux elf
64 */
65
66 static char*
67 linkername[] =
68 {
69 "runtime.softfloat",
70 "math.sqrtGoC",
71 };
72
73 void
74 usage(void)
75 {
76 fprint(2, "usage: 5l [-E entry] [-H head] [-I interpreter] [-L dir] [-T text] [-D data] [-R rnd] [-r path] [-o out] main.5\n");
77 errorexit();
78 }
79
80 void
81 main(int argc, char *argv[])
82 {
83 int c, i;
84 char *p;
85
86 Binit(&bso, 1, OWRITE);
87 listinit();
88 nerrors = 0;
89 outfile = "5.out";
90 HEADTYPE = -1;
91 INITTEXT = -1;
92 INITDAT = -1;
93 INITRND = -1;
94 INITENTRY = 0;
95
96 p = getenv("GOARM");
97 if(p != nil && strcmp(p, "5") == 0)
98 debug['F'] = 1;
99
100 ARGBEGIN {
101 default:
102 c = ARGC();
103 if(c == 'l')
104 usage();
105 if(c >= 0 && c < sizeof(debug))
106 debug[c]++;
107 break;
108 case 'o':
109 outfile = EARGF(usage());
110 break;
111 case 'E':
112 INITENTRY = EARGF(usage());
113 break;
114 case 'I':
115 interpreter = EARGF(usage());
116 break;
117 case 'L':
118 Lflag(EARGF(usage()));
119 break;
120 case 'T':
121 INITTEXT = atolwhex(EARGF(usage()));
122 break;
123 case 'D':
124 INITDAT = atolwhex(EARGF(usage()));
125 break;
126 case 'R':
127 INITRND = atolwhex(EARGF(usage()));
128 break;
129 case 'r':
130 rpath = EARGF(usage());
131 break;
132 case 'H':
133 HEADTYPE = headtype(EARGF(usage()));
134 /* do something about setting INITTEXT */
135 break;
136 case 'V':
137 print("%cl version %s\n", thechar, getgoversion());
138 errorexit();
139 } ARGEND
140
141 USED(argc);
142
143 if(argc != 1)
144 usage();
145
146 libinit();
147
148 if(HEADTYPE == -1)
149 HEADTYPE = Hlinux;
150 switch(HEADTYPE) {
151 default:
152 diag("unknown -H option");
153 errorexit();
154 case Hnoheader: /* no header */
155 HEADR = 0L;
156 if(INITTEXT == -1)
157 INITTEXT = 0;
158 if(INITDAT == -1)
159 INITDAT = 0;
160 if(INITRND == -1)
161 INITRND = 4;
162 break;
163 case Hrisc: /* aif for risc os */
164 HEADR = 128L;
165 if(INITTEXT == -1)
166 INITTEXT = 0x10005000 + HEADR;
167 if(INITDAT == -1)
168 INITDAT = 0;
169 if(INITRND == -1)
170 INITRND = 4;
171 break;
172 case Hplan9x32: /* plan 9 */
173 HEADR = 32L;
174 if(INITTEXT == -1)
175 INITTEXT = 4128;
176 if(INITDAT == -1)
177 INITDAT = 0;
178 if(INITRND == -1)
179 INITRND = 4096;
180 break;
181 case Hnetbsd: /* boot for NetBSD */
182 HEADR = 32L;
183 if(INITTEXT == -1)
184 INITTEXT = 0xF0000020L;
185 if(INITDAT == -1)
186 INITDAT = 0;
187 if(INITRND == -1)
188 INITRND = 4096;
189 break;
190 case Hixp1200: /* boot for IXP1200 */
191 HEADR = 0L;
192 if(INITTEXT == -1)
193 INITTEXT = 0x0;
194 if(INITDAT == -1)
195 INITDAT = 0;
196 if(INITRND == -1)
197 INITRND = 4;
198 break;
199 case Hipaq: /* boot for ipaq */
200 HEADR = 16L;
201 if(INITTEXT == -1)
202 INITTEXT = 0xC0008010;
203 if(INITDAT == -1)
204 INITDAT = 0;
205 if(INITRND == -1)
206 INITRND = 1024;
207 break;
208 case Hlinux: /* arm elf */
209 debug['d'] = 1; // no dynamic linking
210 elfinit();
211 HEADR = ELFRESERVE;
212 if(INITTEXT == -1)
213 INITTEXT = 0x10000 + HEADR;
214 if(INITDAT == -1)
215 INITDAT = 0;
216 if(INITRND == -1)
217 INITRND = 4096;
218 break;
219 }
220 if(INITDAT != 0 && INITRND != 0)
221 print("warning: -D0x%ux is ignored because of -R0x%ux\n",
222 INITDAT, INITRND);
223 if(debug['v'])
224 Bprint(&bso, "HEADER = -H0x%d -T0x%ux -D0x%ux -R0x%ux\n",
225 HEADTYPE, INITTEXT, INITDAT, INITRND);
226 Bflush(&bso);
227 zprg.as = AGOK;
228 zprg.scond = 14;
229 zprg.reg = NREG;
230 zprg.from.name = D_NONE;
231 zprg.from.type = D_NONE;
232 zprg.from.reg = NREG;
233 zprg.to = zprg.from;
234 buildop();
235 histgen = 0;
236 pc = 0;
237 dtype = 4;
238 nuxiinit();
239
240 version = 0;
241 cbp = buf.cbuf;
242 cbc = sizeof(buf.cbuf);
243
244 addlibpath("command line", "command line", argv[0], "main");
245 loadlib();
246
247 // mark some functions that are only referenced after linker code editing
248 // TODO(kaib): this doesn't work, the prog can't be found in runtime
249 for(i=0; i<nelem(linkername); i++)
250 mark(lookup(linkername[i], 0));
251 deadcode();
252 if(textp == nil) {
253 diag("no code");
254 errorexit();
255 }
256
257 patch();
258 if(debug['p'])
259 if(debug['1'])
260 doprof1();
261 else
262 doprof2();
263 doelf();
264 follow();
265 softfloat();
266 noops();
267 dostkcheck();
268 span();
269 pclntab();
270 symtab();
271 dodata();
272 address();
273 doweak();
274 reloc();
275 asmb();
276 undef();
277
278 if(debug['c'])
279 print("ARM size = %d\n", armsize);
280 if(debug['v']) {
281 Bprint(&bso, "%5.2f cpu time\n", cputime());
282 Bprint(&bso, "%d sizeof adr\n", sizeof(Adr));
283 Bprint(&bso, "%d sizeof prog\n", sizeof(Prog));
284 }
285 Bflush(&bso);
286 errorexit();
287 }
288
289 static void
290 zaddr(Biobuf *f, Adr *a, Sym *h[])
291 {
292 int i, c;
293 int32 l;
294 Sym *s;
295 Auto *u;
296
297 a->type = Bgetc(f);
298 a->reg = Bgetc(f);
299 c = Bgetc(f);
300 if(c < 0 || c > NSYM){
301 print("sym out of range: %d\n", c);
302 Bputc(f, ALAST+1);
303 return;
304 }
305 a->sym = h[c];
306 a->name = Bgetc(f);
307
308 if((schar)a->reg < 0 || a->reg > NREG) {
309 print("register out of range %d\n", a->reg);
310 Bputc(f, ALAST+1);
311 return; /* force real diagnostic */
312 }
313
314 if(a->type == D_CONST || a->type == D_OCONST) {
315 if(a->name == D_EXTERN || a->name == D_STATIC) {
316 s = a->sym;
317 if(s != S && (s->type == STEXT || s->type == SCONST || s->type == SXREF)) {
318 if(0 && !s->fnptr && s->name[0] != '.')
319 print("%s used as function pointer\n", s->name);
320 s->fnptr = 1; // over the top cos of SXREF
321 }
322 }
323 }
324
325 switch(a->type) {
326 default:
327 print("unknown type %d\n", a->type);
328 Bputc(f, ALAST+1);
329 return; /* force real diagnostic */
330
331 case D_NONE:
332 case D_REG:
333 case D_FREG:
334 case D_PSR:
335 case D_FPCR:
336 break;
337
338 case D_REGREG:
339 a->offset = Bgetc(f);
340 break;
341
342 case D_CONST2:
343 a->offset2 = Bget4(f); // fall through
344 case D_BRANCH:
345 case D_OREG:
346 case D_CONST:
347 case D_OCONST:
348 case D_SHIFT:
349 a->offset = Bget4(f);
350 break;
351
352 case D_SCONST:
353 a->sval = mal(NSNAME);
354 Bread(f, a->sval, NSNAME);
355 break;
356
357 case D_FCONST:
358 a->ieee.l = Bget4(f);
359 a->ieee.h = Bget4(f);
360 break;
361 }
362 s = a->sym;
363 if(s == S)
364 return;
365 i = a->name;
366 if(i != D_AUTO && i != D_PARAM)
367 return;
368
369 l = a->offset;
370 for(u=curauto; u; u=u->link)
371 if(u->asym == s)
372 if(u->type == i) {
373 if(u->aoffset > l)
374 u->aoffset = l;
375 return;
376 }
377
378 u = mal(sizeof(Auto));
379 u->link = curauto;
380 curauto = u;
381 u->asym = s;
382 u->aoffset = l;
383 u->type = i;
384 }
385
386 void
387 nopout(Prog *p)
388 {
389 p->as = ANOP;
390 p->from.type = D_NONE;
391 p->to.type = D_NONE;
392 }
393
394 void
395 ldobj1(Biobuf *f, char *pkg, int64 len, char *pn)
396 {
397 int32 ipc;
398 Prog *p;
399 Sym *h[NSYM], *s;
400 int v, o, r, skip;
401 uint32 sig;
402 char *name;
403 int ntext;
404 int32 eof;
405 char src[1024], *x;
406 Prog *lastp;
407
408 lastp = nil;
409 ntext = 0;
410 eof = Boffset(f) + len;
411 src[0] = 0;
412
413 newloop:
414 memset(h, 0, sizeof(h));
415 version++;
416 histfrogp = 0;
417 ipc = pc;
418 skip = 0;
419
420 loop:
421 if(f->state == Bracteof || Boffset(f) >= eof)
422 goto eof;
423 o = Bgetc(f);
424 if(o == Beof)
425 goto eof;
426
427 if(o <= AXXX || o >= ALAST) {
428 diag("%s:#%lld: opcode out of range: %#ux", pn, Boffset(f), o);
429 print(" probably not a .5 file\n");
430 errorexit();
431 }
432 if(o == ANAME || o == ASIGNAME) {
433 sig = 0;
434 if(o == ASIGNAME)
435 sig = Bget4(f);
436 v = Bgetc(f); /* type */
437 o = Bgetc(f); /* sym */
438 r = 0;
439 if(v == D_STATIC)
440 r = version;
441 name = Brdline(f, '\0');
442 if(name == nil) {
443 if(Blinelen(f) > 0) {
444 fprint(2, "%s: name too long\n", pn);
445 errorexit();
446 }
447 goto eof;
448 }
449 x = expandpkg(name, pkg);
450 s = lookup(x, r);
451 if(x != name)
452 free(x);
453
454 if(sig != 0){
455 if(s->sig != 0 && s->sig != sig)
456 diag("incompatible type signatures %ux(%s) and %ux(%s) for %s", s->sig, s->file, sig, pn, s->name);
457 s->sig = sig;
458 s->file = pn;
459 }
460
461 if(debug['W'])
462 print(" ANAME %s\n", s->name);
463 if(o < 0 || o >= nelem(h)) {
464 fprint(2, "%s: mangled input file\n", pn);
465 errorexit();
466 }
467 h[o] = s;
468 if((v == D_EXTERN || v == D_STATIC) && s->type == 0)
469 s->type = SXREF;
470 if(v == D_FILE) {
471 if(s->type != SFILE) {
472 histgen++;
473 s->type = SFILE;
474 s->value = histgen;
475 }
476 if(histfrogp < MAXHIST) {
477 histfrog[histfrogp] = s;
478 histfrogp++;
479 } else
480 collapsefrog(s);
481 }
482 goto loop;
483 }
484
485 p = mal(sizeof(Prog));
486 p->as = o;
487 p->scond = Bgetc(f);
488 p->reg = Bgetc(f);
489 p->line = Bget4(f);
490
491 zaddr(f, &p->from, h);
492 zaddr(f, &p->to, h);
493
494 if(p->as != ATEXT && p->as != AGLOBL && p->reg > NREG)
495 diag("register out of range %A %d", p->as, p->reg);
496
497 p->link = P;
498 p->cond = P;
499
500 if(debug['W'])
501 print("%P\n", p);
502
503 switch(o) {
504 case AHISTORY:
505 if(p->to.offset == -1) {
506 addlib(src, pn);
507 histfrogp = 0;
508 goto loop;
509 }
510 if(src[0] == '\0')
511 copyhistfrog(src, sizeof src);
512 addhist(p->line, D_FILE); /* 'z' */
513 if(p->to.offset)
514 addhist(p->to.offset, D_FILE1); /* 'Z' */
515 histfrogp = 0;
516 goto loop;
517
518 case AEND:
519 histtoauto();
520 if(cursym != nil && cursym->text)
521 cursym->autom = curauto;
522 curauto = 0;
523 cursym = nil;
524 if(Boffset(f) == eof)
525 return;
526 goto newloop;
527
528 case AGLOBL:
529 s = p->from.sym;
530 if(s == S) {
531 diag("GLOBL must have a name\n%P", p);
532 errorexit();
533 }
534 if(s->type == 0 || s->type == SXREF) {
535 s->type = SBSS;
536 s->value = 0;
537 }
538 if(s->type != SBSS) {
539 diag("redefinition: %s\n%P", s->name, p);
540 s->type = SBSS;
541 s->value = 0;
542 }
543 if(p->to.offset > s->size)
544 s->size = p->to.offset;
545 if(p->reg & DUPOK)
546 s->dupok = 1;
547 break;
548
549 case ADATA:
550 // Assume that AGLOBL comes after ADATA.
551 // If we've seen an AGLOBL that said this sym was DUPOK,
552 // ignore any more ADATA we see, which must be
553 // redefinitions.
554 s = p->from.sym;
555 if(s->dupok) {
556 if(debug['v'])
557 Bprint(&bso, "skipping %s in %s: dupok\n", s->name, pn);
558 goto loop;
559 }
560 if(s->file == nil)
561 s->file = pn;
562 else if(s->file != pn) {
563 diag("multiple initialization for %s: in both %s and %s", s->name, s->file, pn);
564 errorexit();
565 }
566 savedata(s, p, pn);
567 unmal(p, sizeof *p);
568 break;
569
570 case AGOK:
571 diag("unknown opcode\n%P", p);
572 p->pc = pc;
573 pc++;
574 break;
575
576 case ATEXT:
577 if(cursym != nil && cursym->text) {
578 histtoauto();
579 cursym->autom = curauto;
580 curauto = 0;
581 }
582 s = p->from.sym;
583 if(s == S) {
584 diag("TEXT must have a name\n%P", p);
585 errorexit();
586 }
587 cursym = s;
588 if(ntext++ == 0 && s->type != 0 && s->type != SXREF) {
589 /* redefinition, so file has probably been seen before */
590 if(debug['v'])
591 Bprint(&bso, "skipping: %s: redefinition: %s", pn, s->name);
592 return;
593 }
594 skip = 0;
595 if(s->type != 0 && s->type != SXREF) {
596 if(p->reg & DUPOK) {
597 skip = 1;
598 goto casedef;
599 }
600 diag("redefinition: %s\n%P", s->name, p);
601 }
602 if(etextp)
603 etextp->next = s;
604 else
605 textp = s;
606 etextp = s;
607 p->align = 4;
608 autosize = (p->to.offset+3L) & ~3L;
609 p->to.offset = autosize;
610 autosize += 4;
611 s->type = STEXT;
612 s->text = p;
613 s->value = pc;
614 lastp = p;
615 p->pc = pc;
616 pc++;
617 break;
618
619 case ASUB:
620 if(p->from.type == D_CONST)
621 if(p->from.name == D_NONE)
622 if(p->from.offset < 0) {
623 p->from.offset = -p->from.offset;
624 p->as = AADD;
625 }
626 goto casedef;
627
628 case AADD:
629 if(p->from.type == D_CONST)
630 if(p->from.name == D_NONE)
631 if(p->from.offset < 0) {
632 p->from.offset = -p->from.offset;
633 p->as = ASUB;
634 }
635 goto casedef;
636
637 case AMOVWD:
638 case AMOVWF:
639 case AMOVDW:
640 case AMOVFW:
641 case AMOVFD:
642 case AMOVDF:
643 // case AMOVF:
644 // case AMOVD:
645 case ACMPF:
646 case ACMPD:
647 case AADDF:
648 case AADDD:
649 case ASUBF:
650 case ASUBD:
651 case AMULF:
652 case AMULD:
653 case ADIVF:
654 case ADIVD:
655 goto casedef;
656
657 case AMOVF:
658 if(skip)
659 goto casedef;
660
661 if(p->from.type == D_FCONST && chipfloat(&p->from.ieee) < 0 &&
662 (chipzero(&p->from.ieee) < 0 || (p->scond & C_SCOND) != C_SCOND_NONE)) {
663 /* size sb 9 max */
664 sprint(literal, "$%ux", ieeedtof(&p->from.ieee));
665 s = lookup(literal, 0);
666 if(s->type == 0) {
667 s->type = SBSS;
668 adduint32(s, ieeedtof(&p->from.ieee));
669 s->reachable = 0;
670 }
671 p->from.type = D_OREG;
672 p->from.sym = s;
673 p->from.name = D_EXTERN;
674 p->from.offset = 0;
675 }
676 goto casedef;
677
678 case AMOVD:
679 if(skip)
680 goto casedef;
681
682 if(p->from.type == D_FCONST && chipfloat(&p->from.ieee) < 0 &&
683 (chipzero(&p->from.ieee) < 0 || (p->scond & C_SCOND) != C_SCOND_NONE)) {
684 /* size sb 18 max */
685 sprint(literal, "$%ux.%ux",
686 p->from.ieee.l, p->from.ieee.h);
687 s = lookup(literal, 0);
688 if(s->type == 0) {
689 s->type = SBSS;
690 adduint32(s, p->from.ieee.l);
691 adduint32(s, p->from.ieee.h);
692 s->reachable = 0;
693 }
694 p->from.type = D_OREG;
695 p->from.sym = s;
696 p->from.name = D_EXTERN;
697 p->from.offset = 0;
698 }
699 goto casedef;
700
701 default:
702 casedef:
703 if(skip)
704 nopout(p);
705 p->pc = pc;
706 pc++;
707 if(p->to.type == D_BRANCH)
708 p->to.offset += ipc;
709 if(lastp == nil) {
710 if(p->as != ANOP)
711 diag("unexpected instruction: %P", p);
712 break;
713 }
714 lastp->link = p;
715 lastp = p;
716 break;
717 }
718 goto loop;
719
720 eof:
721 diag("truncated object file: %s", pn);
722 }
723
724 Prog*
725 prg(void)
726 {
727 Prog *p;
728
729 p = mal(sizeof(Prog));
730 *p = zprg;
731 return p;
732 }
733
734 Prog*
735 appendp(Prog *q)
736 {
737 Prog *p;
738
739 p = prg();
740 p->link = q->link;
741 q->link = p;
742 p->line = q->line;
743 return p;
744 }