1 // Inferno utils/6l/asm.c
2 // http://code.google.com/p/inferno-os/source/browse/utils/6l/asm.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 // Writing object files.
32
33 #include "l.h"
34 #include "../ld/lib.h"
35 #include "../ld/elf.h"
36 #include "../ld/dwarf.h"
37 #include "../ld/macho.h"
38 #include "../ld/pe.h"
39
40 #define Dbufslop 100
41
42 #define PADDR(a) ((uint32)(a) & ~0x80000000)
43
44 char linuxdynld[] = "/lib64/ld-linux-x86-64.so.2";
45 char freebsddynld[] = "/libexec/ld-elf.so.1";
46 char openbsddynld[] = "/usr/libexec/ld.so";
47
48 char zeroes[32];
49
50 vlong
51 entryvalue(void)
52 {
53 char *a;
54 Sym *s;
55
56 a = INITENTRY;
57 if(*a >= '0' && *a <= '9')
58 return atolwhex(a);
59 s = lookup(a, 0);
60 if(s->type == 0)
61 return INITTEXT;
62 if(s->type != STEXT)
63 diag("entry not text: %s", s->name);
64 return s->value;
65 }
66
67 vlong
68 datoff(vlong addr)
69 {
70 if(addr >= segdata.vaddr)
71 return addr - segdata.vaddr + segdata.fileoff;
72 if(addr >= segtext.vaddr)
73 return addr - segtext.vaddr + segtext.fileoff;
74 diag("datoff %#llx", addr);
75 return 0;
76 }
77
78 enum {
79 ElfStrEmpty,
80 ElfStrInterp,
81 ElfStrHash,
82 ElfStrGot,
83 ElfStrGotPlt,
84 ElfStrDynamic,
85 ElfStrDynsym,
86 ElfStrDynstr,
87 ElfStrRela,
88 ElfStrText,
89 ElfStrData,
90 ElfStrBss,
91 ElfStrShstrtab,
92 ElfStrSymtab,
93 ElfStrStrtab,
94 ElfStrRelaPlt,
95 ElfStrPlt,
96 ElfStrGnuVersion,
97 ElfStrGnuVersionR,
98 NElfStr
99 };
100
101 vlong elfstr[NElfStr];
102
103 static int
104 needlib(char *name)
105 {
106 char *p;
107 Sym *s;
108
109 if(*name == '\0')
110 return 0;
111
112 /* reuse hash code in symbol table */
113 p = smprint(".elfload.%s", name);
114 s = lookup(p, 0);
115 if(s->type == 0) {
116 s->type = 100; // avoid SDATA, etc.
117 return 1;
118 }
119 return 0;
120 }
121
122 int nelfsym = 1;
123
124 static void addpltsym(Sym*);
125 static void addgotsym(Sym*);
126
127 void
128 adddynrel(Sym *s, Reloc *r)
129 {
130 Sym *targ, *rela, *got;
131
132 targ = r->sym;
133 cursym = s;
134
135 switch(r->type) {
136 default:
137 if(r->type >= 256) {
138 diag("unexpected relocation type %d", r->type);
139 return;
140 }
141 break;
142
143 // Handle relocations found in ELF object files.
144 case 256 + R_X86_64_PC32:
145 if(targ->dynimpname != nil && !targ->dynexport)
146 diag("unexpected R_X86_64_PC32 relocation for dynamic symbol %s", targ->name);
147 if(targ->type == 0 || targ->type == SXREF)
148 diag("unknown symbol %s in pcrel", targ->name);
149 r->type = D_PCREL;
150 r->add += 4;
151 return;
152
153 case 256 + R_X86_64_PLT32:
154 r->type = D_PCREL;
155 r->add += 4;
156 if(targ->dynimpname != nil && !targ->dynexport) {
157 addpltsym(targ);
158 r->sym = lookup(".plt", 0);
159 r->add += targ->plt;
160 }
161 return;
162
163 case 256 + R_X86_64_GOTPCREL:
164 if(targ->dynimpname == nil || targ->dynexport) {
165 // have symbol
166 if(r->off >= 2 && s->p[r->off-2] == 0x8b) {
167 // turn MOVQ of GOT entry into LEAQ of symbol itself
168 s->p[r->off-2] = 0x8d;
169 r->type = D_PCREL;
170 r->add += 4;
171 return;
172 }
173 // fall back to using GOT and hope for the best (CMOV*)
174 // TODO: just needs relocation, no need to put in .dynsym
175 targ->dynimpname = targ->name;
176 }
177 addgotsym(targ);
178 r->type = D_PCREL;
179 r->sym = lookup(".got", 0);
180 r->add += 4;
181 r->add += targ->got;
182 return;
183
184 case 256 + R_X86_64_64:
185 if(targ->dynimpname != nil && !targ->dynexport)
186 diag("unexpected R_X86_64_64 relocation for dynamic symbol %s", targ->name);
187 r->type = D_ADDR;
188 return;
189
190 // Handle relocations found in Mach-O object files.
191 case 512 + MACHO_X86_64_RELOC_UNSIGNED*2 + 0:
192 case 512 + MACHO_X86_64_RELOC_SIGNED*2 + 0:
193 case 512 + MACHO_X86_64_RELOC_BRANCH*2 + 0:
194 // TODO: What is the difference between all these?
195 r->type = D_ADDR;
196 if(targ->dynimpname != nil && !targ->dynexport)
197 diag("unexpected reloc for dynamic symbol %s", targ->name);
198 return;
199
200 case 512 + MACHO_X86_64_RELOC_BRANCH*2 + 1:
201 if(targ->dynimpname != nil && !targ->dynexport) {
202 addpltsym(targ);
203 r->sym = lookup(".plt", 0);
204 r->add = targ->plt;
205 r->type = D_PCREL;
206 return;
207 }
208 // fall through
209 case 512 + MACHO_X86_64_RELOC_UNSIGNED*2 + 1:
210 case 512 + MACHO_X86_64_RELOC_SIGNED*2 + 1:
211 case 512 + MACHO_X86_64_RELOC_SIGNED_1*2 + 1:
212 case 512 + MACHO_X86_64_RELOC_SIGNED_2*2 + 1:
213 case 512 + MACHO_X86_64_RELOC_SIGNED_4*2 + 1:
214 r->type = D_PCREL;
215 if(targ->dynimpname != nil && !targ->dynexport)
216 diag("unexpected pc-relative reloc for dynamic symbol %s", targ->name);
217 return;
218
219 case 512 + MACHO_X86_64_RELOC_GOT_LOAD*2 + 1:
220 if(targ->dynimpname == nil || targ->dynexport) {
221 // have symbol
222 // turn MOVQ of GOT entry into LEAQ of symbol itself
223 if(r->off < 2 || s->p[r->off-2] != 0x8b) {
224 diag("unexpected GOT_LOAD reloc for non-dynamic symbol %s", targ->name);
225 return;
226 }
227 s->p[r->off-2] = 0x8d;
228 r->type = D_PCREL;
229 return;
230 }
231 // fall through
232 case 512 + MACHO_X86_64_RELOC_GOT*2 + 1:
233 if(targ->dynimpname == nil || targ->dynexport)
234 diag("unexpected GOT reloc for non-dynamic symbol %s", targ->name);
235 addgotsym(targ);
236 r->type = D_PCREL;
237 r->sym = lookup(".got", 0);
238 r->add += targ->got;
239 return;
240 }
241
242 // Handle references to ELF symbols from our own object files.
243 if(targ->dynimpname == nil || targ->dynexport)
244 return;
245
246 switch(r->type) {
247 case D_PCREL:
248 addpltsym(targ);
249 r->sym = lookup(".plt", 0);
250 r->add = targ->plt;
251 return;
252
253 case D_ADDR:
254 if(s->type != SDATA)
255 break;
256 if(iself) {
257 adddynsym(targ);
258 rela = lookup(".rela", 0);
259 addaddrplus(rela, s, r->off);
260 if(r->siz == 8)
261 adduint64(rela, ELF64_R_INFO(targ->dynid, R_X86_64_64));
262 else
263 adduint64(rela, ELF64_R_INFO(targ->dynid, R_X86_64_32));
264 adduint64(rela, r->add);
265 r->type = 256; // ignore during relocsym
266 return;
267 }
268 if(HEADTYPE == Hdarwin && s->size == PtrSize && r->off == 0) {
269 // Mach-O relocations are a royal pain to lay out.
270 // They use a compact stateful bytecode representation
271 // that is too much bother to deal with.
272 // Instead, interpret the C declaration
273 // void *_Cvar_stderr = &stderr;
274 // as making _Cvar_stderr the name of a GOT entry
275 // for stderr. This is separate from the usual GOT entry,
276 // just in case the C code assigns to the variable,
277 // and of course it only works for single pointers,
278 // but we only need to support cgo and that's all it needs.
279 adddynsym(targ);
280 got = lookup(".got", 0);
281 s->type = got->type | SSUB;
282 s->outer = got;
283 s->sub = got->sub;
284 got->sub = s;
285 s->value = got->size;
286 adduint64(got, 0);
287 adduint32(lookup(".linkedit.got", 0), targ->dynid);
288 r->type = 256; // ignore during relocsym
289 return;
290 }
291 break;
292 }
293
294 cursym = s;
295 diag("unsupported relocation for dynamic symbol %s (type=%d stype=%d)", targ->name, r->type, targ->type);
296 }
297
298 int
299 archreloc(Reloc *r, Sym *s, vlong *val)
300 {
301 USED(r);
302 USED(s);
303 USED(val);
304 return -1;
305 }
306
307 static void
308 elfsetupplt(void)
309 {
310 Sym *plt, *got;
311
312 plt = lookup(".plt", 0);
313 got = lookup(".got.plt", 0);
314 if(plt->size == 0) {
315 // pushq got+8(IP)
316 adduint8(plt, 0xff);
317 adduint8(plt, 0x35);
318 addpcrelplus(plt, got, 8);
319
320 // jmpq got+16(IP)
321 adduint8(plt, 0xff);
322 adduint8(plt, 0x25);
323 addpcrelplus(plt, got, 16);
324
325 // nopl 0(AX)
326 adduint32(plt, 0x00401f0f);
327
328 // assume got->size == 0 too
329 addaddrplus(got, lookup(".dynamic", 0), 0);
330 adduint64(got, 0);
331 adduint64(got, 0);
332 }
333 }
334
335 static void
336 addpltsym(Sym *s)
337 {
338 if(s->plt >= 0)
339 return;
340
341 adddynsym(s);
342
343 if(iself) {
344 Sym *plt, *got, *rela;
345
346 plt = lookup(".plt", 0);
347 got = lookup(".got.plt", 0);
348 rela = lookup(".rela.plt", 0);
349 if(plt->size == 0)
350 elfsetupplt();
351
352 // jmpq *got+size(IP)
353 adduint8(plt, 0xff);
354 adduint8(plt, 0x25);
355 addpcrelplus(plt, got, got->size);
356
357 // add to got: pointer to current pos in plt
358 addaddrplus(got, plt, plt->size);
359
360 // pushq $x
361 adduint8(plt, 0x68);
362 adduint32(plt, (got->size-24-8)/8);
363
364 // jmpq .plt
365 adduint8(plt, 0xe9);
366 adduint32(plt, -(plt->size+4));
367
368 // rela
369 addaddrplus(rela, got, got->size-8);
370 adduint64(rela, ELF64_R_INFO(s->dynid, R_X86_64_JMP_SLOT));
371 adduint64(rela, 0);
372
373 s->plt = plt->size - 16;
374 } else if(HEADTYPE == Hdarwin) {
375 // To do lazy symbol lookup right, we're supposed
376 // to tell the dynamic loader which library each
377 // symbol comes from and format the link info
378 // section just so. I'm too lazy (ha!) to do that
379 // so for now we'll just use non-lazy pointers,
380 // which don't need to be told which library to use.
381 //
382 // http://networkpx.blogspot.com/2009/09/about-lcdyldinfoonly-command.html
383 // has details about what we're avoiding.
384
385 Sym *plt;
386
387 addgotsym(s);
388 plt = lookup(".plt", 0);
389
390 adduint32(lookup(".linkedit.plt", 0), s->dynid);
391
392 // jmpq *got+size(IP)
393 s->plt = plt->size;
394
395 adduint8(plt, 0xff);
396 adduint8(plt, 0x25);
397 addpcrelplus(plt, lookup(".got", 0), s->got);
398 } else {
399 diag("addpltsym: unsupported binary format");
400 }
401 }
402
403 static void
404 addgotsym(Sym *s)
405 {
406 Sym *got, *rela;
407
408 if(s->got >= 0)
409 return;
410
411 adddynsym(s);
412 got = lookup(".got", 0);
413 s->got = got->size;
414 adduint64(got, 0);
415
416 if(iself) {
417 rela = lookup(".rela", 0);
418 addaddrplus(rela, got, s->got);
419 adduint64(rela, ELF64_R_INFO(s->dynid, R_X86_64_GLOB_DAT));
420 adduint64(rela, 0);
421 } else if(HEADTYPE == Hdarwin) {
422 adduint32(lookup(".linkedit.got", 0), s->dynid);
423 } else {
424 diag("addgotsym: unsupported binary format");
425 }
426 }
427
428 void
429 adddynsym(Sym *s)
430 {
431 Sym *d, *str;
432 int t;
433 char *name;
434
435 if(s->dynid >= 0)
436 return;
437
438 if(s->dynimpname == nil)
439 diag("adddynsym: no dynamic name for %s", s->name);
440
441 if(iself) {
442 s->dynid = nelfsym++;
443
444 d = lookup(".dynsym", 0);
445
446 name = s->dynimpname;
447 if(name == nil)
448 name = s->name;
449 adduint32(d, addstring(lookup(".dynstr", 0), name));
450 /* type */
451 t = STB_GLOBAL << 4;
452 if(s->dynexport && s->type == STEXT)
453 t |= STT_FUNC;
454 else
455 t |= STT_OBJECT;
456 adduint8(d, t);
457
458 /* reserved */
459 adduint8(d, 0);
460
461 /* section where symbol is defined */
462 if(!s->dynexport && s->dynimpname != nil)
463 adduint16(d, SHN_UNDEF);
464 else {
465 switch(s->type) {
466 default:
467 case STEXT:
468 t = 11;
469 break;
470 case SRODATA:
471 t = 12;
472 break;
473 case SDATA:
474 t = 13;
475 break;
476 case SBSS:
477 t = 14;
478 break;
479 }
480 adduint16(d, t);
481 }
482
483 /* value */
484 if(s->type == SDYNIMPORT)
485 adduint64(d, 0);
486 else
487 addaddr(d, s);
488
489 /* size of object */
490 adduint64(d, 0);
491
492 if(!s->dynexport && s->dynimplib && needlib(s->dynimplib)) {
493 elfwritedynent(lookup(".dynamic", 0), DT_NEEDED,
494 addstring(lookup(".dynstr", 0), s->dynimplib));
495 }
496 } else if(HEADTYPE == Hdarwin) {
497 // Mach-o symbol nlist64
498 d = lookup(".dynsym", 0);
499 name = s->dynimpname;
500 if(name == nil)
501 name = s->name;
502 s->dynid = d->size/16;
503 // darwin still puts _ prefixes on all C symbols
504 str = lookup(".dynstr", 0);
505 adduint32(d, str->size);
506 adduint8(str, '_');
507 addstring(str, name);
508 if(s->type == SDYNIMPORT) {
509 adduint8(d, 0x01); // type - N_EXT - external symbol
510 adduint8(d, 0); // section
511 } else {
512 adduint8(d, 0x0f);
513 switch(s->type) {
514 default:
515 case STEXT:
516 adduint8(d, 1);
517 break;
518 case SDATA:
519 adduint8(d, 2);
520 break;
521 case SBSS:
522 adduint8(d, 4);
523 break;
524 }
525 }
526 adduint16(d, 0); // desc
527 if(s->type == SDYNIMPORT)
528 adduint64(d, 0); // value
529 else
530 addaddr(d, s);
531 } else if(HEADTYPE != Hwindows) {
532 diag("adddynsym: unsupported binary format");
533 }
534 }
535
536 void
537 adddynlib(char *lib)
538 {
539 Sym *s;
540
541 if(!needlib(lib))
542 return;
543
544 if(iself) {
545 s = lookup(".dynstr", 0);
546 if(s->size == 0)
547 addstring(s, "");
548 elfwritedynent(lookup(".dynamic", 0), DT_NEEDED, addstring(s, lib));
549 } else if(HEADTYPE == Hdarwin) {
550 machoadddynlib(lib);
551 } else {
552 diag("adddynlib: unsupported binary format");
553 }
554 }
555
556 void
557 doelf(void)
558 {
559 Sym *s, *shstrtab, *dynstr;
560
561 if(HEADTYPE != Hlinux && HEADTYPE != Hfreebsd && HEADTYPE != Hopenbsd)
562 return;
563
564 /* predefine strings we need for section headers */
565 shstrtab = lookup(".shstrtab", 0);
566 shstrtab->type = SELFROSECT;
567 shstrtab->reachable = 1;
568
569 elfstr[ElfStrEmpty] = addstring(shstrtab, "");
570 elfstr[ElfStrText] = addstring(shstrtab, ".text");
571 elfstr[ElfStrData] = addstring(shstrtab, ".data");
572 elfstr[ElfStrBss] = addstring(shstrtab, ".bss");
573 addstring(shstrtab, ".elfdata");
574 addstring(shstrtab, ".rodata");
575 addstring(shstrtab, ".gosymtab");
576 addstring(shstrtab, ".gopclntab");
577 if(!debug['s']) {
578 elfstr[ElfStrSymtab] = addstring(shstrtab, ".symtab");
579 elfstr[ElfStrStrtab] = addstring(shstrtab, ".strtab");
580 dwarfaddshstrings(shstrtab);
581 }
582 elfstr[ElfStrShstrtab] = addstring(shstrtab, ".shstrtab");
583
584 if(!debug['d']) { /* -d suppresses dynamic loader format */
585 elfstr[ElfStrInterp] = addstring(shstrtab, ".interp");
586 elfstr[ElfStrHash] = addstring(shstrtab, ".hash");
587 elfstr[ElfStrGot] = addstring(shstrtab, ".got");
588 elfstr[ElfStrGotPlt] = addstring(shstrtab, ".got.plt");
589 elfstr[ElfStrDynamic] = addstring(shstrtab, ".dynamic");
590 elfstr[ElfStrDynsym] = addstring(shstrtab, ".dynsym");
591 elfstr[ElfStrDynstr] = addstring(shstrtab, ".dynstr");
592 elfstr[ElfStrRela] = addstring(shstrtab, ".rela");
593 elfstr[ElfStrRelaPlt] = addstring(shstrtab, ".rela.plt");
594 elfstr[ElfStrPlt] = addstring(shstrtab, ".plt");
595 elfstr[ElfStrGnuVersion] = addstring(shstrtab, ".gnu.version");
596 elfstr[ElfStrGnuVersionR] = addstring(shstrtab, ".gnu.version_r");
597
598 /* dynamic symbol table - first entry all zeros */
599 s = lookup(".dynsym", 0);
600 s->type = SELFROSECT;
601 s->reachable = 1;
602 s->size += ELF64SYMSIZE;
603
604 /* dynamic string table */
605 s = lookup(".dynstr", 0);
606 s->type = SELFROSECT;
607 s->reachable = 1;
608 if(s->size == 0)
609 addstring(s, "");
610 dynstr = s;
611
612 /* relocation table */
613 s = lookup(".rela", 0);
614 s->reachable = 1;
615 s->type = SELFROSECT;
616
617 /* global offset table */
618 s = lookup(".got", 0);
619 s->reachable = 1;
620 s->type = SELFSECT; // writable
621
622 /* hash */
623 s = lookup(".hash", 0);
624 s->reachable = 1;
625 s->type = SELFROSECT;
626
627 s = lookup(".got.plt", 0);
628 s->reachable = 1;
629 s->type = SELFSECT; // writable
630
631 s = lookup(".plt", 0);
632 s->reachable = 1;
633 s->type = SELFROSECT;
634
635 elfsetupplt();
636
637 s = lookup(".rela.plt", 0);
638 s->reachable = 1;
639 s->type = SELFROSECT;
640
641 s = lookup(".gnu.version", 0);
642 s->reachable = 1;
643 s->type = SELFROSECT;
644
645 s = lookup(".gnu.version_r", 0);
646 s->reachable = 1;
647 s->type = SELFROSECT;
648
649 /* define dynamic elf table */
650 s = lookup(".dynamic", 0);
651 s->reachable = 1;
652 s->type = SELFROSECT;
653
654 /*
655 * .dynamic table
656 */
657 elfwritedynentsym(s, DT_HASH, lookup(".hash", 0));
658 elfwritedynentsym(s, DT_SYMTAB, lookup(".dynsym", 0));
659 elfwritedynent(s, DT_SYMENT, ELF64SYMSIZE);
660 elfwritedynentsym(s, DT_STRTAB, lookup(".dynstr", 0));
661 elfwritedynentsymsize(s, DT_STRSZ, lookup(".dynstr", 0));
662 elfwritedynentsym(s, DT_RELA, lookup(".rela", 0));
663 elfwritedynentsymsize(s, DT_RELASZ, lookup(".rela", 0));
664 elfwritedynent(s, DT_RELAENT, ELF64RELASIZE);
665 if(rpath)
666 elfwritedynent(s, DT_RUNPATH, addstring(dynstr, rpath));
667
668 elfwritedynentsym(s, DT_PLTGOT, lookup(".got.plt", 0));
669 elfwritedynent(s, DT_PLTREL, DT_RELA);
670 elfwritedynentsymsize(s, DT_PLTRELSZ, lookup(".rela.plt", 0));
671 elfwritedynentsym(s, DT_JMPREL, lookup(".rela.plt", 0));
672
673 // Do not write DT_NULL. elfdynhash will finish it.
674 }
675 }
676
677 void
678 shsym(ElfShdr *sh, Sym *s)
679 {
680 vlong addr;
681 addr = symaddr(s);
682 if(sh->flags&SHF_ALLOC)
683 sh->addr = addr;
684 sh->off = datoff(addr);
685 sh->size = s->size;
686 }
687
688 void
689 phsh(ElfPhdr *ph, ElfShdr *sh)
690 {
691 ph->vaddr = sh->addr;
692 ph->paddr = ph->vaddr;
693 ph->off = sh->off;
694 ph->filesz = sh->size;
695 ph->memsz = sh->size;
696 ph->align = sh->addralign;
697 }
698
699 void
700 asmb(void)
701 {
702 int32 magic;
703 int a, dynsym;
704 vlong vl, startva, symo, machlink;
705 ElfEhdr *eh;
706 ElfPhdr *ph, *pph;
707 ElfShdr *sh;
708 Section *sect;
709 int o;
710
711 if(debug['v'])
712 Bprint(&bso, "%5.2f asmb\n", cputime());
713 Bflush(&bso);
714
715 elftextsh = 0;
716
717 if(debug['v'])
718 Bprint(&bso, "%5.2f codeblk\n", cputime());
719 Bflush(&bso);
720
721 sect = segtext.sect;
722 cseek(sect->vaddr - segtext.vaddr + segtext.fileoff);
723 codeblk(sect->vaddr, sect->len);
724
725 /* output read-only data in text segment (rodata, gosymtab and pclntab) */
726 for(sect = sect->next; sect != nil; sect = sect->next) {
727 cseek(sect->vaddr - segtext.vaddr + segtext.fileoff);
728 datblk(sect->vaddr, sect->len);
729 }
730
731 if(debug['v'])
732 Bprint(&bso, "%5.2f datblk\n", cputime());
733 Bflush(&bso);
734
735 cseek(segdata.fileoff);
736 datblk(segdata.vaddr, segdata.filelen);
737
738 machlink = 0;
739 if(HEADTYPE == Hdarwin)
740 machlink = domacholink();
741
742 switch(HEADTYPE) {
743 default:
744 diag("unknown header type %d", HEADTYPE);
745 case Hplan9x32:
746 case Helf:
747 break;
748 case Hdarwin:
749 debug['8'] = 1; /* 64-bit addresses */
750 break;
751 case Hlinux:
752 case Hfreebsd:
753 case Hopenbsd:
754 debug['8'] = 1; /* 64-bit addresses */
755 /* index of elf text section; needed by asmelfsym, double-checked below */
756 /* !debug['d'] causes extra sections before the .text section */
757 elftextsh = 2;
758 if(!debug['d']) {
759 elftextsh += 10;
760 if(elfverneed)
761 elftextsh += 2;
762 }
763 break;
764 case Hwindows:
765 break;
766 }
767
768 symsize = 0;
769 spsize = 0;
770 lcsize = 0;
771 symo = 0;
772 if(!debug['s']) {
773 if(debug['v'])
774 Bprint(&bso, "%5.2f sym\n", cputime());
775 Bflush(&bso);
776 switch(HEADTYPE) {
777 default:
778 case Hplan9x32:
779 case Helf:
780 debug['s'] = 1;
781 symo = HEADR+segtext.len+segdata.filelen;
782 break;
783 case Hdarwin:
784 symo = rnd(HEADR+segtext.len, INITRND)+rnd(segdata.filelen, INITRND)+machlink;
785 break;
786 case Hlinux:
787 case Hfreebsd:
788 case Hopenbsd:
789 symo = rnd(HEADR+segtext.len, INITRND)+segdata.filelen;
790 symo = rnd(symo, INITRND);
791 break;
792 case Hwindows:
793 symo = rnd(HEADR+segtext.filelen, PEFILEALIGN)+segdata.filelen;
794 symo = rnd(symo, PEFILEALIGN);
795 break;
796 }
797 cseek(symo);
798 switch(HEADTYPE) {
799 default:
800 if(iself) {
801 cseek(symo);
802 asmelfsym();
803 cflush();
804 cwrite(elfstrdat, elfstrsize);
805
806 if(debug['v'])
807 Bprint(&bso, "%5.2f dwarf\n", cputime());
808
809 dwarfemitdebugsections();
810 }
811 break;
812 case Hdarwin:
813 case Hwindows:
814 if(debug['v'])
815 Bprint(&bso, "%5.2f dwarf\n", cputime());
816
817 dwarfemitdebugsections();
818 break;
819 }
820 }
821
822 if(debug['v'])
823 Bprint(&bso, "%5.2f headr\n", cputime());
824 Bflush(&bso);
825 cseek(0L);
826 switch(HEADTYPE) {
827 default:
828 case Hplan9x32: /* plan9 */
829 magic = 4*26*26+7;
830 magic |= 0x00008000; /* fat header */
831 lputb(magic); /* magic */
832 lputb(segtext.filelen); /* sizes */
833 lputb(segdata.filelen);
834 lputb(segdata.len - segdata.filelen);
835 lputb(symsize); /* nsyms */
836 vl = entryvalue();
837 lputb(PADDR(vl)); /* va of entry */
838 lputb(spsize); /* sp offsets */
839 lputb(lcsize); /* line offsets */
840 vputb(vl); /* va of entry */
841 break;
842 case Hplan9x64: /* plan9 */
843 magic = 4*26*26+7;
844 lputb(magic); /* magic */
845 lputb(segtext.filelen); /* sizes */
846 lputb(segdata.filelen);
847 lputb(segdata.len - segdata.filelen);
848 lputb(symsize); /* nsyms */
849 lputb(entryvalue()); /* va of entry */
850 lputb(spsize); /* sp offsets */
851 lputb(lcsize); /* line offsets */
852 break;
853 case Hdarwin:
854 asmbmacho();
855 break;
856 case Hlinux:
857 case Hfreebsd:
858 case Hopenbsd:
859 /* elf amd-64 */
860
861 eh = getElfEhdr();
862 startva = INITTEXT - HEADR;
863
864 /* This null SHdr must appear before all others */
865 newElfShdr(elfstr[ElfStrEmpty]);
866
867 /* program header info */
868 pph = newElfPhdr();
869 pph->type = PT_PHDR;
870 pph->flags = PF_R + PF_X;
871 pph->off = eh->ehsize;
872 pph->vaddr = INITTEXT - HEADR + pph->off;
873 pph->paddr = INITTEXT - HEADR + pph->off;
874 pph->align = INITRND;
875
876 /*
877 * PHDR must be in a loaded segment. Adjust the text
878 * segment boundaries downwards to include it.
879 */
880 o = segtext.vaddr - pph->vaddr;
881 segtext.vaddr -= o;
882 segtext.len += o;
883 o = segtext.fileoff - pph->off;
884 segtext.fileoff -= o;
885 segtext.filelen += o;
886
887 if(!debug['d']) {
888 /* interpreter */
889 sh = newElfShdr(elfstr[ElfStrInterp]);
890 sh->type = SHT_PROGBITS;
891 sh->flags = SHF_ALLOC;
892 sh->addralign = 1;
893 if(interpreter == nil) {
894 switch(HEADTYPE) {
895 case Hlinux:
896 interpreter = linuxdynld;
897 break;
898 case Hfreebsd:
899 interpreter = freebsddynld;
900 break;
901 case Hopenbsd:
902 interpreter = openbsddynld;
903 break;
904 }
905 }
906 elfinterp(sh, startva, interpreter);
907
908 ph = newElfPhdr();
909 ph->type = PT_INTERP;
910 ph->flags = PF_R;
911 phsh(ph, sh);
912 }
913
914 elfphload(&segtext);
915 elfphload(&segdata);
916
917 /* Dynamic linking sections */
918 if (!debug['d']) { /* -d suppresses dynamic loader format */
919 /* S headers for dynamic linking */
920 sh = newElfShdr(elfstr[ElfStrGot]);
921 sh->type = SHT_PROGBITS;
922 sh->flags = SHF_ALLOC+SHF_WRITE;
923 sh->entsize = 8;
924 sh->addralign = 8;
925 shsym(sh, lookup(".got", 0));
926
927 sh = newElfShdr(elfstr[ElfStrGotPlt]);
928 sh->type = SHT_PROGBITS;
929 sh->flags = SHF_ALLOC+SHF_WRITE;
930 sh->entsize = 8;
931 sh->addralign = 8;
932 shsym(sh, lookup(".got.plt", 0));
933
934 dynsym = eh->shnum;
935 sh = newElfShdr(elfstr[ElfStrDynsym]);
936 sh->type = SHT_DYNSYM;
937 sh->flags = SHF_ALLOC;
938 sh->entsize = ELF64SYMSIZE;
939 sh->addralign = 8;
940 sh->link = dynsym+1; // dynstr
941 // sh->info = index of first non-local symbol (number of local symbols)
942 shsym(sh, lookup(".dynsym", 0));
943
944 sh = newElfShdr(elfstr[ElfStrDynstr]);
945 sh->type = SHT_STRTAB;
946 sh->flags = SHF_ALLOC;
947 sh->addralign = 1;
948 shsym(sh, lookup(".dynstr", 0));
949
950 if(elfverneed) {
951 sh = newElfShdr(elfstr[ElfStrGnuVersion]);
952 sh->type = SHT_GNU_VERSYM;
953 sh->flags = SHF_ALLOC;
954 sh->addralign = 2;
955 sh->link = dynsym;
956 sh->entsize = 2;
957 shsym(sh, lookup(".gnu.version", 0));
958
959 sh = newElfShdr(elfstr[ElfStrGnuVersionR]);
960 sh->type = SHT_GNU_VERNEED;
961 sh->flags = SHF_ALLOC;
962 sh->addralign = 8;
963 sh->info = elfverneed;
964 sh->link = dynsym+1; // dynstr
965 shsym(sh, lookup(".gnu.version_r", 0));
966 }
967
968 sh = newElfShdr(elfstr[ElfStrRelaPlt]);
969 sh->type = SHT_RELA;
970 sh->flags = SHF_ALLOC;
971 sh->entsize = ELF64RELASIZE;
972 sh->addralign = 8;
973 sh->link = dynsym;
974 sh->info = eh->shnum; // .plt
975 shsym(sh, lookup(".rela.plt", 0));
976
977 sh = newElfShdr(elfstr[ElfStrPlt]);
978 sh->type = SHT_PROGBITS;
979 sh->flags = SHF_ALLOC+SHF_EXECINSTR;
980 sh->entsize = 16;
981 sh->addralign = 4;
982 shsym(sh, lookup(".plt", 0));
983
984 sh = newElfShdr(elfstr[ElfStrHash]);
985 sh->type = SHT_HASH;
986 sh->flags = SHF_ALLOC;
987 sh->entsize = 4;
988 sh->addralign = 8;
989 sh->link = dynsym;
990 shsym(sh, lookup(".hash", 0));
991
992 sh = newElfShdr(elfstr[ElfStrRela]);
993 sh->type = SHT_RELA;
994 sh->flags = SHF_ALLOC;
995 sh->entsize = ELF64RELASIZE;
996 sh->addralign = 8;
997 sh->link = dynsym;
998 shsym(sh, lookup(".rela", 0));
999
1000 /* sh and PT_DYNAMIC for .dynamic section */
1001 sh = newElfShdr(elfstr[ElfStrDynamic]);
1002 sh->type = SHT_DYNAMIC;
1003 sh->flags = SHF_ALLOC+SHF_WRITE;
1004 sh->entsize = 16;
1005 sh->addralign = 8;
1006 sh->link = dynsym+1; // dynstr
1007 shsym(sh, lookup(".dynamic", 0));
1008 ph = newElfPhdr();
1009 ph->type = PT_DYNAMIC;
1010 ph->flags = PF_R + PF_W;
1011 phsh(ph, sh);
1012
1013 /*
1014 * Thread-local storage segment (really just size).
1015 */
1016 if(tlsoffset != 0) {
1017 ph = newElfPhdr();
1018 ph->type = PT_TLS;
1019 ph->flags = PF_R;
1020 ph->memsz = -tlsoffset;
1021 ph->align = 8;
1022 }
1023 }
1024
1025 ph = newElfPhdr();
1026 ph->type = PT_GNU_STACK;
1027 ph->flags = PF_W+PF_R;
1028 ph->align = 8;
1029
1030 sh = newElfShstrtab(elfstr[ElfStrShstrtab]);
1031 sh->type = SHT_STRTAB;
1032 sh->addralign = 1;
1033 shsym(sh, lookup(".shstrtab", 0));
1034
1035 if(elftextsh != eh->shnum)
1036 diag("elftextsh = %d, want %d", elftextsh, eh->shnum);
1037 for(sect=segtext.sect; sect!=nil; sect=sect->next)
1038 elfshbits(sect);
1039 for(sect=segdata.sect; sect!=nil; sect=sect->next)
1040 elfshbits(sect);
1041
1042 if (!debug['s']) {
1043 sh = newElfShdr(elfstr[ElfStrSymtab]);
1044 sh->type = SHT_SYMTAB;
1045 sh->off = symo;
1046 sh->size = symsize;
1047 sh->addralign = 8;
1048 sh->entsize = 24;
1049 sh->link = eh->shnum; // link to strtab
1050
1051 sh = newElfShdr(elfstr[ElfStrStrtab]);
1052 sh->type = SHT_STRTAB;
1053 sh->off = symo+symsize;
1054 sh->size = elfstrsize;
1055 sh->addralign = 1;
1056
1057 dwarfaddelfheaders();
1058 }
1059
1060 /* Main header */
1061 eh->ident[EI_MAG0] = '\177';
1062 eh->ident[EI_MAG1] = 'E';
1063 eh->ident[EI_MAG2] = 'L';
1064 eh->ident[EI_MAG3] = 'F';
1065 if(HEADTYPE == Hfreebsd)
1066 eh->ident[EI_OSABI] = ELFOSABI_FREEBSD;
1067 else if(HEADTYPE == Hopenbsd)
1068 eh->ident[EI_OSABI] = ELFOSABI_OPENBSD;
1069 eh->ident[EI_CLASS] = ELFCLASS64;
1070 eh->ident[EI_DATA] = ELFDATA2LSB;
1071 eh->ident[EI_VERSION] = EV_CURRENT;
1072
1073 eh->type = ET_EXEC;
1074 eh->machine = EM_X86_64;
1075 eh->version = EV_CURRENT;
1076 eh->entry = entryvalue();
1077
1078 pph->filesz = eh->phnum * eh->phentsize;
1079 pph->memsz = pph->filesz;
1080
1081 cseek(0);
1082 a = 0;
1083 a += elfwritehdr();
1084 a += elfwritephdrs();
1085 a += elfwriteshdrs();
1086 cflush();
1087 if(a+elfwriteinterp() > ELFRESERVE)
1088 diag("ELFRESERVE too small: %d > %d", a, ELFRESERVE);
1089 break;
1090 case Hwindows:
1091 asmbpe();
1092 break;
1093 }
1094 cflush();
1095 }
1096
1097 vlong
1098 rnd(vlong v, vlong r)
1099 {
1100 vlong c;
1101
1102 if(r <= 0)
1103 return v;
1104 v += r - 1;
1105 c = v % r;
1106 if(c < 0)
1107 c += r;
1108 v -= c;
1109 return v;
1110 }
1111
1112 void
1113 genasmsym(void (*put)(Sym*, char*, int, vlong, vlong, int, Sym*))
1114 {
1115 Auto *a;
1116 Sym *s;
1117
1118 s = lookup("etext", 0);
1119 if(s->type == STEXT)
1120 put(s, s->name, 'T', s->value, s->size, s->version, 0);
1121
1122 for(s=allsym; s!=S; s=s->allsym) {
1123 if(s->hide)
1124 continue;
1125 switch(s->type&~SSUB) {
1126 case SCONST:
1127 case SRODATA:
1128 case SDATA:
1129 case SELFROSECT:
1130 case SMACHOGOT:
1131 case STYPE:
1132 case SSTRING:
1133 case SGOSTRING:
1134 case SWINDOWS:
1135 if(!s->reachable)
1136 continue;
1137 put(s, s->name, 'D', symaddr(s), s->size, s->version, s->gotype);
1138 continue;
1139
1140 case SBSS:
1141 if(!s->reachable)
1142 continue;
1143 put(s, s->name, 'B', symaddr(s), s->size, s->version, s->gotype);
1144 continue;
1145
1146 case SFILE:
1147 put(nil, s->name, 'f', s->value, 0, s->version, 0);
1148 continue;
1149 }
1150 }
1151
1152 for(s = textp; s != nil; s = s->next) {
1153 if(s->text == nil)
1154 continue;
1155
1156 /* filenames first */
1157 for(a=s->autom; a; a=a->link)
1158 if(a->type == D_FILE)
1159 put(nil, a->asym->name, 'z', a->aoffset, 0, 0, 0);
1160 else
1161 if(a->type == D_FILE1)
1162 put(nil, a->asym->name, 'Z', a->aoffset, 0, 0, 0);
1163
1164 put(s, s->name, 'T', s->value, s->size, s->version, s->gotype);
1165
1166 /* frame, auto and param after */
1167 put(nil, ".frame", 'm', s->text->to.offset+8, 0, 0, 0);
1168
1169 for(a=s->autom; a; a=a->link)
1170 if(a->type == D_AUTO)
1171 put(nil, a->asym->name, 'a', -a->aoffset, 0, 0, a->gotype);
1172 else
1173 if(a->type == D_PARAM)
1174 put(nil, a->asym->name, 'p', a->aoffset, 0, 0, a->gotype);
1175 }
1176 if(debug['v'] || debug['n'])
1177 Bprint(&bso, "symsize = %ud\n", symsize);
1178 Bflush(&bso);
1179 }