github.com/bgentry/go@v0.0.0-20150121062915-6cf5a733d54d/src/cmd/5l/asm.c (about) 1 // Inferno utils/5l/asm.c 2 // http://code.google.com/p/inferno-os/source/browse/utils/5l/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 38 39 char linuxdynld[] = "/lib/ld-linux.so.3"; // 2 for OABI, 3 for EABI 40 char freebsddynld[] = "/usr/libexec/ld-elf.so.1"; 41 char openbsddynld[] = "XXX"; 42 char netbsddynld[] = "/libexec/ld.elf_so"; 43 char dragonflydynld[] = "XXX"; 44 char solarisdynld[] = "XXX"; 45 46 static int 47 needlib(char *name) 48 { 49 char *p; 50 LSym *s; 51 52 if(*name == '\0') 53 return 0; 54 55 /* reuse hash code in symbol table */ 56 p = smprint(".dynlib.%s", name); 57 s = linklookup(ctxt, p, 0); 58 free(p); 59 if(s->type == 0) { 60 s->type = 100; // avoid SDATA, etc. 61 return 1; 62 } 63 return 0; 64 } 65 66 int nelfsym = 1; 67 68 static void addpltsym(Link*, LSym*); 69 static void addgotsym(Link*, LSym*); 70 static void addgotsyminternal(Link*, LSym*); 71 72 void 73 gentext(void) 74 { 75 } 76 77 // Preserve highest 8 bits of a, and do addition to lower 24-bit 78 // of a and b; used to adjust ARM branch intruction's target 79 static int32 80 braddoff(int32 a, int32 b) 81 { 82 return (((uint32)a) & 0xff000000U) | (0x00ffffffU & (uint32)(a + b)); 83 } 84 85 void 86 adddynrela(LSym *rel, LSym *s, Reloc *r) 87 { 88 addaddrplus(ctxt, rel, s, r->off); 89 adduint32(ctxt, rel, R_ARM_RELATIVE); 90 } 91 92 void 93 adddynrel(LSym *s, Reloc *r) 94 { 95 LSym *targ, *rel; 96 97 targ = r->sym; 98 ctxt->cursym = s; 99 100 switch(r->type) { 101 default: 102 if(r->type >= 256) { 103 diag("unexpected relocation type %d", r->type); 104 return; 105 } 106 break; 107 108 // Handle relocations found in ELF object files. 109 case 256 + R_ARM_PLT32: 110 r->type = R_CALLARM; 111 if(targ->type == SDYNIMPORT) { 112 addpltsym(ctxt, targ); 113 r->sym = linklookup(ctxt, ".plt", 0); 114 r->add = braddoff(r->add, targ->plt / 4); 115 } 116 return; 117 118 case 256 + R_ARM_THM_PC22: // R_ARM_THM_CALL 119 diag("R_ARM_THM_CALL, are you using -marm?"); 120 errorexit(); 121 return; 122 123 case 256 + R_ARM_GOT32: // R_ARM_GOT_BREL 124 if(targ->type != SDYNIMPORT) { 125 addgotsyminternal(ctxt, targ); 126 } else { 127 addgotsym(ctxt, targ); 128 } 129 r->type = R_CONST; // write r->add during relocsym 130 r->sym = S; 131 r->add += targ->got; 132 return; 133 134 case 256 + R_ARM_GOT_PREL: // GOT(S) + A - P 135 if(targ->type != SDYNIMPORT) { 136 addgotsyminternal(ctxt, targ); 137 } else { 138 addgotsym(ctxt, targ); 139 } 140 r->type = R_PCREL; 141 r->sym = linklookup(ctxt, ".got", 0); 142 r->add += targ->got + 4; 143 return; 144 145 case 256 + R_ARM_GOTOFF: // R_ARM_GOTOFF32 146 r->type = R_GOTOFF; 147 return; 148 149 case 256 + R_ARM_GOTPC: // R_ARM_BASE_PREL 150 r->type = R_PCREL; 151 r->sym = linklookup(ctxt, ".got", 0); 152 r->add += 4; 153 return; 154 155 case 256 + R_ARM_CALL: 156 r->type = R_CALLARM; 157 if(targ->type == SDYNIMPORT) { 158 addpltsym(ctxt, targ); 159 r->sym = linklookup(ctxt, ".plt", 0); 160 r->add = braddoff(r->add, targ->plt / 4); 161 } 162 return; 163 164 case 256 + R_ARM_REL32: // R_ARM_REL32 165 r->type = R_PCREL; 166 r->add += 4; 167 return; 168 169 case 256 + R_ARM_ABS32: 170 if(targ->type == SDYNIMPORT) 171 diag("unexpected R_ARM_ABS32 relocation for dynamic symbol %s", targ->name); 172 r->type = R_ADDR; 173 return; 174 175 case 256 + R_ARM_V4BX: 176 // we can just ignore this, because we are targeting ARM V5+ anyway 177 if(r->sym) { 178 // R_ARM_V4BX is ABS relocation, so this symbol is a dummy symbol, ignore it 179 r->sym->type = 0; 180 } 181 r->sym = S; 182 return; 183 184 case 256 + R_ARM_PC24: 185 case 256 + R_ARM_JUMP24: 186 r->type = R_CALLARM; 187 if(targ->type == SDYNIMPORT) { 188 addpltsym(ctxt, targ); 189 r->sym = linklookup(ctxt, ".plt", 0); 190 r->add = braddoff(r->add, targ->plt / 4); 191 } 192 return; 193 } 194 195 // Handle references to ELF symbols from our own object files. 196 if(targ->type != SDYNIMPORT) 197 return; 198 199 switch(r->type) { 200 case R_CALLARM: 201 addpltsym(ctxt, targ); 202 r->sym = linklookup(ctxt, ".plt", 0); 203 r->add = targ->plt; 204 return; 205 206 case R_ADDR: 207 if(s->type != SDATA) 208 break; 209 if(iself) { 210 adddynsym(ctxt, targ); 211 rel = linklookup(ctxt, ".rel", 0); 212 addaddrplus(ctxt, rel, s, r->off); 213 adduint32(ctxt, rel, ELF32_R_INFO(targ->dynid, R_ARM_GLOB_DAT)); // we need a S + A dynmic reloc 214 r->type = R_CONST; // write r->add during relocsym 215 r->sym = S; 216 return; 217 } 218 break; 219 } 220 221 ctxt->cursym = s; 222 diag("unsupported relocation for dynamic symbol %s (type=%d stype=%d)", targ->name, r->type, targ->type); 223 } 224 225 int 226 elfreloc1(Reloc *r, vlong sectoff) 227 { 228 int32 elfsym; 229 230 LPUT(sectoff); 231 232 elfsym = r->xsym->elfsym; 233 switch(r->type) { 234 default: 235 return -1; 236 237 case R_ADDR: 238 if(r->siz == 4) 239 LPUT(R_ARM_ABS32 | elfsym<<8); 240 else 241 return -1; 242 break; 243 244 case R_PCREL: 245 if(r->siz == 4) 246 LPUT(R_ARM_REL32 | elfsym<<8); 247 else 248 return -1; 249 break; 250 251 case R_CALLARM: 252 if(r->siz == 4) { 253 if((r->add & 0xff000000) == 0xeb000000) // BL 254 LPUT(R_ARM_CALL | elfsym<<8); 255 else 256 LPUT(R_ARM_JUMP24 | elfsym<<8); 257 } else 258 return -1; 259 break; 260 261 case R_TLS: 262 if(r->siz == 4) { 263 if(flag_shared) 264 LPUT(R_ARM_TLS_IE32 | elfsym<<8); 265 else 266 LPUT(R_ARM_TLS_LE32 | elfsym<<8); 267 } else 268 return -1; 269 break; 270 } 271 272 return 0; 273 } 274 275 void 276 elfsetupplt(void) 277 { 278 LSym *plt, *got; 279 280 plt = linklookup(ctxt, ".plt", 0); 281 got = linklookup(ctxt, ".got.plt", 0); 282 if(plt->size == 0) { 283 // str lr, [sp, #-4]! 284 adduint32(ctxt, plt, 0xe52de004); 285 // ldr lr, [pc, #4] 286 adduint32(ctxt, plt, 0xe59fe004); 287 // add lr, pc, lr 288 adduint32(ctxt, plt, 0xe08fe00e); 289 // ldr pc, [lr, #8]! 290 adduint32(ctxt, plt, 0xe5bef008); 291 // .word &GLOBAL_OFFSET_TABLE[0] - . 292 addpcrelplus(ctxt, plt, got, 4); 293 294 // the first .plt entry requires 3 .plt.got entries 295 adduint32(ctxt, got, 0); 296 adduint32(ctxt, got, 0); 297 adduint32(ctxt, got, 0); 298 } 299 } 300 301 int 302 machoreloc1(Reloc *r, vlong sectoff) 303 { 304 USED(r); 305 USED(sectoff); 306 307 return -1; 308 } 309 310 311 int 312 archreloc(Reloc *r, LSym *s, vlong *val) 313 { 314 LSym *rs; 315 316 if(linkmode == LinkExternal) { 317 switch(r->type) { 318 case R_CALLARM: 319 r->done = 0; 320 321 // set up addend for eventual relocation via outer symbol. 322 rs = r->sym; 323 r->xadd = r->add; 324 if(r->xadd & 0x800000) 325 r->xadd |= ~0xffffff; 326 r->xadd *= 4; 327 while(rs->outer != nil) { 328 r->xadd += symaddr(rs) - symaddr(rs->outer); 329 rs = rs->outer; 330 } 331 332 if(rs->type != SHOSTOBJ && rs->sect == nil) 333 diag("missing section for %s", rs->name); 334 r->xsym = rs; 335 336 *val = braddoff((0xff000000U & (uint32)r->add), 337 (0xffffff & (uint32)(r->xadd / 4))); 338 return 0; 339 } 340 return -1; 341 } 342 switch(r->type) { 343 case R_CONST: 344 *val = r->add; 345 return 0; 346 case R_GOTOFF: 347 *val = symaddr(r->sym) + r->add - symaddr(linklookup(ctxt, ".got", 0)); 348 return 0; 349 // The following three arch specific relocations are only for generation of 350 // Linux/ARM ELF's PLT entry (3 assembler instruction) 351 case R_PLT0: // add ip, pc, #0xXX00000 352 if (symaddr(linklookup(ctxt, ".got.plt", 0)) < symaddr(linklookup(ctxt, ".plt", 0))) 353 diag(".got.plt should be placed after .plt section."); 354 *val = 0xe28fc600U + 355 (0xff & ((uint32)(symaddr(r->sym) - (symaddr(linklookup(ctxt, ".plt", 0)) + r->off) + r->add) >> 20)); 356 return 0; 357 case R_PLT1: // add ip, ip, #0xYY000 358 *val = 0xe28cca00U + 359 (0xff & ((uint32)(symaddr(r->sym) - (symaddr(linklookup(ctxt, ".plt", 0)) + r->off) + r->add + 4) >> 12)); 360 return 0; 361 case R_PLT2: // ldr pc, [ip, #0xZZZ]! 362 *val = 0xe5bcf000U + 363 (0xfff & (uint32)(symaddr(r->sym) - (symaddr(linklookup(ctxt, ".plt", 0)) + r->off) + r->add + 8)); 364 return 0; 365 case R_CALLARM: // bl XXXXXX or b YYYYYY 366 *val = braddoff((0xff000000U & (uint32)r->add), 367 (0xffffff & (uint32) 368 ((symaddr(r->sym) + ((uint32)r->add) * 4 - (s->value + r->off)) / 4))); 369 return 0; 370 } 371 return -1; 372 } 373 374 vlong 375 archrelocvariant(Reloc *r, LSym *s, vlong t) 376 { 377 USED(r); 378 USED(s); 379 sysfatal("unexpected relocation variant"); 380 return t; 381 } 382 383 static Reloc * 384 addpltreloc(Link *ctxt, LSym *plt, LSym *got, LSym *sym, int typ) 385 { 386 Reloc *r; 387 388 r = addrel(plt); 389 r->sym = got; 390 r->off = plt->size; 391 r->siz = 4; 392 r->type = typ; 393 r->add = sym->got - 8; 394 395 plt->reachable = 1; 396 plt->size += 4; 397 symgrow(ctxt, plt, plt->size); 398 399 return r; 400 } 401 402 static void 403 addpltsym(Link *ctxt, LSym *s) 404 { 405 LSym *plt, *got, *rel; 406 407 if(s->plt >= 0) 408 return; 409 410 adddynsym(ctxt, s); 411 412 if(iself) { 413 plt = linklookup(ctxt, ".plt", 0); 414 got = linklookup(ctxt, ".got.plt", 0); 415 rel = linklookup(ctxt, ".rel.plt", 0); 416 if(plt->size == 0) 417 elfsetupplt(); 418 419 // .got entry 420 s->got = got->size; 421 // In theory, all GOT should point to the first PLT entry, 422 // Linux/ARM's dynamic linker will do that for us, but FreeBSD/ARM's 423 // dynamic linker won't, so we'd better do it ourselves. 424 addaddrplus(ctxt, got, plt, 0); 425 426 // .plt entry, this depends on the .got entry 427 s->plt = plt->size; 428 addpltreloc(ctxt, plt, got, s, R_PLT0); // add lr, pc, #0xXX00000 429 addpltreloc(ctxt, plt, got, s, R_PLT1); // add lr, lr, #0xYY000 430 addpltreloc(ctxt, plt, got, s, R_PLT2); // ldr pc, [lr, #0xZZZ]! 431 432 // rel 433 addaddrplus(ctxt, rel, got, s->got); 434 adduint32(ctxt, rel, ELF32_R_INFO(s->dynid, R_ARM_JUMP_SLOT)); 435 } else { 436 diag("addpltsym: unsupported binary format"); 437 } 438 } 439 440 static void 441 addgotsyminternal(Link *ctxt, LSym *s) 442 { 443 LSym *got; 444 445 if(s->got >= 0) 446 return; 447 448 got = linklookup(ctxt, ".got", 0); 449 s->got = got->size; 450 451 addaddrplus(ctxt, got, s, 0); 452 453 if(iself) { 454 ; 455 } else { 456 diag("addgotsyminternal: unsupported binary format"); 457 } 458 } 459 460 static void 461 addgotsym(Link *ctxt, LSym *s) 462 { 463 LSym *got, *rel; 464 465 if(s->got >= 0) 466 return; 467 468 adddynsym(ctxt, s); 469 got = linklookup(ctxt, ".got", 0); 470 s->got = got->size; 471 adduint32(ctxt, got, 0); 472 473 if(iself) { 474 rel = linklookup(ctxt, ".rel", 0); 475 addaddrplus(ctxt, rel, got, s->got); 476 adduint32(ctxt, rel, ELF32_R_INFO(s->dynid, R_ARM_GLOB_DAT)); 477 } else { 478 diag("addgotsym: unsupported binary format"); 479 } 480 } 481 482 void 483 adddynsym(Link *ctxt, LSym *s) 484 { 485 LSym *d; 486 int t; 487 char *name; 488 489 if(s->dynid >= 0) 490 return; 491 492 if(iself) { 493 s->dynid = nelfsym++; 494 495 d = linklookup(ctxt, ".dynsym", 0); 496 497 /* name */ 498 name = s->extname; 499 adduint32(ctxt, d, addstring(linklookup(ctxt, ".dynstr", 0), name)); 500 501 /* value */ 502 if(s->type == SDYNIMPORT) 503 adduint32(ctxt, d, 0); 504 else 505 addaddr(ctxt, d, s); 506 507 /* size */ 508 adduint32(ctxt, d, 0); 509 510 /* type */ 511 t = STB_GLOBAL << 4; 512 if((s->cgoexport & CgoExportDynamic) && (s->type&SMASK) == STEXT) 513 t |= STT_FUNC; 514 else 515 t |= STT_OBJECT; 516 adduint8(ctxt, d, t); 517 adduint8(ctxt, d, 0); 518 519 /* shndx */ 520 if(s->type == SDYNIMPORT) 521 adduint16(ctxt, d, SHN_UNDEF); 522 else 523 adduint16(ctxt, d, 1); 524 } else { 525 diag("adddynsym: unsupported binary format"); 526 } 527 } 528 529 void 530 adddynlib(char *lib) 531 { 532 LSym *s; 533 534 if(!needlib(lib)) 535 return; 536 537 if(iself) { 538 s = linklookup(ctxt, ".dynstr", 0); 539 if(s->size == 0) 540 addstring(s, ""); 541 elfwritedynent(linklookup(ctxt, ".dynamic", 0), DT_NEEDED, addstring(s, lib)); 542 } else { 543 diag("adddynlib: unsupported binary format"); 544 } 545 } 546 547 void 548 asmb(void) 549 { 550 uint32 symo; 551 Section *sect; 552 LSym *sym; 553 int i; 554 555 if(debug['v']) 556 Bprint(&bso, "%5.2f asmb\n", cputime()); 557 Bflush(&bso); 558 559 if(iself) 560 asmbelfsetup(); 561 562 sect = segtext.sect; 563 cseek(sect->vaddr - segtext.vaddr + segtext.fileoff); 564 codeblk(sect->vaddr, sect->len); 565 for(sect = sect->next; sect != nil; sect = sect->next) { 566 cseek(sect->vaddr - segtext.vaddr + segtext.fileoff); 567 datblk(sect->vaddr, sect->len); 568 } 569 570 if(segrodata.filelen > 0) { 571 if(debug['v']) 572 Bprint(&bso, "%5.2f rodatblk\n", cputime()); 573 Bflush(&bso); 574 575 cseek(segrodata.fileoff); 576 datblk(segrodata.vaddr, segrodata.filelen); 577 } 578 579 if(debug['v']) 580 Bprint(&bso, "%5.2f datblk\n", cputime()); 581 Bflush(&bso); 582 583 cseek(segdata.fileoff); 584 datblk(segdata.vaddr, segdata.filelen); 585 586 /* output symbol table */ 587 symsize = 0; 588 lcsize = 0; 589 symo = 0; 590 if(!debug['s']) { 591 // TODO: rationalize 592 if(debug['v']) 593 Bprint(&bso, "%5.2f sym\n", cputime()); 594 Bflush(&bso); 595 switch(HEADTYPE) { 596 default: 597 if(iself) 598 goto ElfSym; 599 case Hplan9: 600 symo = segdata.fileoff+segdata.filelen; 601 break; 602 ElfSym: 603 symo = segdata.fileoff+segdata.filelen; 604 symo = rnd(symo, INITRND); 605 break; 606 } 607 cseek(symo); 608 switch(HEADTYPE) { 609 default: 610 if(iself) { 611 if(debug['v']) 612 Bprint(&bso, "%5.2f elfsym\n", cputime()); 613 asmelfsym(); 614 cflush(); 615 cwrite(elfstrdat, elfstrsize); 616 617 if(debug['v']) 618 Bprint(&bso, "%5.2f dwarf\n", cputime()); 619 dwarfemitdebugsections(); 620 621 if(linkmode == LinkExternal) 622 elfemitreloc(); 623 } 624 break; 625 case Hplan9: 626 asmplan9sym(); 627 cflush(); 628 629 sym = linklookup(ctxt, "pclntab", 0); 630 if(sym != nil) { 631 lcsize = sym->np; 632 for(i=0; i < lcsize; i++) 633 cput(sym->p[i]); 634 635 cflush(); 636 } 637 break; 638 } 639 } 640 641 ctxt->cursym = nil; 642 if(debug['v']) 643 Bprint(&bso, "%5.2f header\n", cputime()); 644 Bflush(&bso); 645 cseek(0L); 646 switch(HEADTYPE) { 647 default: 648 case Hplan9: /* plan 9 */ 649 LPUT(0x647); /* magic */ 650 LPUT(segtext.filelen); /* sizes */ 651 LPUT(segdata.filelen); 652 LPUT(segdata.len - segdata.filelen); 653 LPUT(symsize); /* nsyms */ 654 LPUT(entryvalue()); /* va of entry */ 655 LPUT(0L); 656 LPUT(lcsize); 657 break; 658 case Hlinux: 659 case Hfreebsd: 660 case Hnetbsd: 661 case Hopenbsd: 662 case Hnacl: 663 asmbelf(symo); 664 break; 665 } 666 cflush(); 667 if(debug['c']){ 668 print("textsize=%ulld\n", segtext.filelen); 669 print("datsize=%ulld\n", segdata.filelen); 670 print("bsssize=%ulld\n", segdata.len - segdata.filelen); 671 print("symsize=%d\n", symsize); 672 print("lcsize=%d\n", lcsize); 673 print("total=%lld\n", segtext.filelen+segdata.len+symsize+lcsize); 674 } 675 } 676 677 int32 678 rnd(int32 v, int32 r) 679 { 680 int32 c; 681 682 if(r <= 0) 683 return v; 684 v += r - 1; 685 c = v % r; 686 if(c < 0) 687 c += r; 688 v -= c; 689 return v; 690 }