void relocsym(Sym *s) { Reloc *r; Prog p; int32 i, off, siz, fl; vlong o; uchar *cast; cursym = s; memset(&p, 0, sizeof p); for(r=s->r; r<s->r+s->nr; r++) { off = r->off; siz = r->siz; if(off < 0 || off+(siz&~Rbig) > s->np) { diag("%s: invalid relocation %d+%d not in [%d,%d)", s->name, off, siz&~Rbig, 0, s->np); continue; } if(r->sym != S && (r->sym->type == 0 || r->sym->type == SXREF)) { diag("%s: not defined", r->sym->name); continue; } if(r->type >= 256) continue; if(r->sym != S && r->sym->type == SDYNIMPORT) diag("unhandled relocation for %s (type %d rtype %d)", r->sym->name, r->sym->type, r->type); if(r->sym != S && !r->sym->reachable) diag("unreachable sym in relocation: %s %s", s->name, r->sym->name); switch(r->type) { default: o = 0; if(archreloc(r, s, &o) < 0) diag("unknown reloc %d", r->type); break; case D_ADDR: o = symaddr(r->sym) + r->add; break; case D_PCREL: // r->sym can be null when CALL $(constant) is transformed from absoulte PC to relative PC call. o = 0; if(r->sym) o += symaddr(r->sym); o += r->add - (s->value + r->off + r->siz); break; case D_SIZE: o = r->sym->size + r->add; break; } //print("relocate %s %p %s => %p %p %p %p [%p]\n", s->name, s->value+off, r->sym ? r->sym->name : "<nil>", (void*)symaddr(r->sym), (void*)s->value, (void*)r->off, (void*)r->siz, (void*)o); switch(siz) { default: cursym = s; diag("bad reloc size %#ux for %s", siz, r->sym->name); case 4 + Rbig: fl = o; s->p[off] = fl>>24; s->p[off+1] = fl>>16; s->p[off+2] = fl>>8; s->p[off+3] = fl; break; case 4 + Rlittle: fl = o; s->p[off] = fl; s->p[off+1] = fl>>8; s->p[off+2] = fl>>16; s->p[off+3] = fl>>24; break; case 4: fl = o; cast = (uchar*)&fl; for(i=0; i<4; i++) s->p[off+i] = cast[inuxi4[i]]; break; case 8: cast = (uchar*)&o; for(i=0; i<8; i++) s->p[off+i] = cast[inuxi8[i]]; break; } } }
void relocsym(Sym *s) { Reloc *r; Sym *rs; Prog p; int32 i, off, siz, fl; vlong o; uchar *cast; cursym = s; memset(&p, 0, sizeof p); for(r=s->r; r<s->r+s->nr; r++) { off = r->off; siz = r->siz; if(off < 0 || off+siz > s->np) { diag("%s: invalid relocation %d+%d not in [%d,%d)", s->name, off, siz, 0, s->np); continue; } if(r->sym != S && (r->sym->type & SMASK == 0 || r->sym->type & SMASK == SXREF)) { diag("%s: not defined", r->sym->name); continue; } if(r->type >= 256) continue; if(r->sym != S && r->sym->type == SDYNIMPORT) diag("unhandled relocation for %s (type %d rtype %d)", r->sym->name, r->sym->type, r->type); if(r->sym != S && !r->sym->reachable) diag("unreachable sym in relocation: %s %s", s->name, r->sym->name); switch(r->type) { default: o = 0; if(isobj || archreloc(r, s, &o) < 0) diag("unknown reloc %d", r->type); break; case D_ADDR: o = symaddr(r->sym) + r->add; if(isobj && r->sym->type != SCONST) { if(thechar == '6') o = 0; else { // set up addend for eventual relocation via outer symbol rs = r->sym; while(rs->outer != nil) rs = rs->outer; o -= symaddr(rs); } } break; case D_PCREL: // r->sym can be null when CALL $(constant) is transformed from absolute PC to relative PC call. o = 0; if(r->sym) o += symaddr(r->sym); o += r->add - (s->value + r->off + r->siz); if(isobj && r->sym->type != SCONST) { if(thechar == '6') o = 0; else o = r->add - r->siz; } break; case D_SIZE: o = r->sym->size + r->add; break; } //print("relocate %s %p %s => %p %p %p %p [%p]\n", s->name, s->value+off, r->sym ? r->sym->name : "<nil>", (void*)symaddr(r->sym), (void*)s->value, (void*)r->off, (void*)r->siz, (void*)o); switch(siz) { default: cursym = s; diag("bad reloc size %#ux for %s", siz, r->sym->name); case 4: fl = o; cast = (uchar*)&fl; for(i=0; i<4; i++) s->p[off+i] = cast[inuxi4[i]]; break; case 8: cast = (uchar*)&o; for(i=0; i<8; i++) s->p[off+i] = cast[inuxi8[i]]; break; } } }
void relocsym(Sym *s) { Reloc *r; Sym *rs; Prog p; int32 i, off, siz, fl; vlong o; uchar *cast; cursym = s; memset(&p, 0, sizeof p); for(r=s->r; r<s->r+s->nr; r++) { r->done = 1; off = r->off; siz = r->siz; if(off < 0 || off+siz > s->np) { diag("%s: invalid relocation %d+%d not in [%d,%d)", s->name, off, siz, 0, s->np); continue; } if(r->sym != S && (r->sym->type & SMASK == 0 || r->sym->type & SMASK == SXREF)) { diag("%s: not defined", r->sym->name); continue; } if(r->type >= 256) continue; if(r->sym != S && r->sym->type == SDYNIMPORT) diag("unhandled relocation for %s (type %d rtype %d)", r->sym->name, r->sym->type, r->type); if(r->sym != S && !r->sym->reachable) diag("unreachable sym in relocation: %s %s", s->name, r->sym->name); switch(r->type) { default: o = 0; if(linkmode == LinkExternal || archreloc(r, s, &o) < 0) diag("unknown reloc %d", r->type); break; case D_ADDR: if(linkmode == LinkExternal && r->sym->type != SCONST) { r->done = 0; // set up addend for eventual relocation via outer symbol. rs = r->sym; r->xadd = r->add; while(rs->outer != nil) { r->xadd += symaddr(rs) - symaddr(rs->outer); rs = rs->outer; } if(rs->type != SHOSTOBJ && rs->sect == nil) diag("missing section for %s", rs->name); r->xsym = rs; o = r->xadd; if(iself) { if(thechar == '6') o = 0; } else if(HEADTYPE == Hdarwin) { if(rs->type != SHOSTOBJ) o += symaddr(rs); } else { diag("unhandled pcrel relocation for %s", headtype); } break; } o = symaddr(r->sym) + r->add; break; case D_PCREL: // r->sym can be null when CALL $(constant) is transformed from absolute PC to relative PC call. if(linkmode == LinkExternal && r->sym && r->sym->type != SCONST && r->sym->sect != cursym->sect) { r->done = 0; // set up addend for eventual relocation via outer symbol. rs = r->sym; r->xadd = r->add; while(rs->outer != nil) { r->xadd += symaddr(rs) - symaddr(rs->outer); rs = rs->outer; } r->xadd -= r->siz; // relative to address after the relocated chunk if(rs->type != SHOSTOBJ && rs->sect == nil) diag("missing section for %s", rs->name); r->xsym = rs; o = r->xadd; if(iself) { if(thechar == '6') o = 0; } else if(HEADTYPE == Hdarwin) { if(rs->type != SHOSTOBJ) o += symaddr(rs) - rs->sect->vaddr; o -= r->off; // WTF? } else { diag("unhandled pcrel relocation for %s", headtype); } break; } o = 0; if(r->sym) o += symaddr(r->sym); o += r->add - (s->value + r->off + r->siz); break; case D_SIZE: o = r->sym->size + r->add; break; } //print("relocate %s %p %s => %p %p %p %p [%p]\n", s->name, s->value+off, r->sym ? r->sym->name : "<nil>", (void*)symaddr(r->sym), (void*)s->value, (void*)r->off, (void*)r->siz, (void*)o); switch(siz) { default: cursym = s; diag("bad reloc size %#ux for %s", siz, r->sym->name); case 4: fl = o; cast = (uchar*)&fl; for(i=0; i<4; i++) s->p[off+i] = cast[inuxi4[i]]; break; case 8: cast = (uchar*)&o; for(i=0; i<8; i++) s->p[off+i] = cast[inuxi8[i]]; break; } } }
void relocsym(LSym *s) { Reloc *r; LSym *rs; int32 i, off, siz, fl; vlong o; uchar *cast; ctxt->cursym = s; for(r=s->r; r<s->r+s->nr; r++) { r->done = 1; off = r->off; siz = r->siz; if(off < 0 || off+siz > s->np) { diag("%s: invalid relocation %d+%d not in [%d,%d)", s->name, off, siz, 0, s->np); continue; } if(r->sym != S && ((r->sym->type & (SMASK | SHIDDEN)) == 0 || (r->sym->type & SMASK) == SXREF)) { diag("%s: not defined", r->sym->name); continue; } if(r->type >= 256) continue; if(r->siz == 0) // informational relocation - no work to do continue; // Solaris needs the ability to reference dynimport symbols. if(HEADTYPE != Hsolaris && r->sym != S && r->sym->type == SDYNIMPORT) diag("unhandled relocation for %s (type %d rtype %d)", r->sym->name, r->sym->type, r->type); if(r->sym != S && r->sym->type != STLSBSS && !r->sym->reachable) diag("unreachable sym in relocation: %s %s", s->name, r->sym->name); // Android emulates runtime.tlsg as a regular variable. if (r->type == R_TLS && strcmp(goos, "android") == 0) r->type = R_ADDR; switch(r->type) { default: o = 0; if(archreloc(r, s, &o) < 0) diag("unknown reloc %d", r->type); break; case R_TLS: if(linkmode == LinkInternal && iself && thechar == '5') { // On ELF ARM, the thread pointer is 8 bytes before // the start of the thread-local data block, so add 8 // to the actual TLS offset (r->sym->value). // This 8 seems to be a fundamental constant of // ELF on ARM (or maybe Glibc on ARM); it is not // related to the fact that our own TLS storage happens // to take up 8 bytes. o = 8 + r->sym->value; break; } r->done = 0; o = 0; if(thechar != '6') o = r->add; break; case R_TLS_LE: if(linkmode == LinkExternal && iself && HEADTYPE != Hopenbsd) { r->done = 0; r->sym = ctxt->tlsg; r->xsym = ctxt->tlsg; r->xadd = r->add; o = 0; if(thechar != '6') o = r->add; break; } o = ctxt->tlsoffset + r->add; break; case R_TLS_IE: if(linkmode == LinkExternal && iself && HEADTYPE != Hopenbsd) { r->done = 0; r->sym = ctxt->tlsg; r->xsym = ctxt->tlsg; r->xadd = r->add; o = 0; if(thechar != '6') o = r->add; break; } if(iself || ctxt->headtype == Hplan9) o = ctxt->tlsoffset + r->add; else if(ctxt->headtype == Hwindows) o = r->add; else sysfatal("unexpected R_TLS_IE relocation for %s", headstr(ctxt->headtype)); break; case R_ADDR: if(linkmode == LinkExternal && r->sym->type != SCONST) { r->done = 0; // set up addend for eventual relocation via outer symbol. rs = r->sym; r->xadd = r->add; while(rs->outer != nil) { r->xadd += symaddr(rs) - symaddr(rs->outer); rs = rs->outer; } if(rs->type != SHOSTOBJ && rs->type != SDYNIMPORT && rs->sect == nil) diag("missing section for %s", rs->name); r->xsym = rs; o = r->xadd; if(iself) { if(thechar == '6') o = 0; } else if(HEADTYPE == Hdarwin) { if(rs->type != SHOSTOBJ) o += symaddr(rs); } else { diag("unhandled pcrel relocation for %s", headstring); } break; } o = symaddr(r->sym) + r->add; // On amd64, 4-byte offsets will be sign-extended, so it is impossible to // access more than 2GB of static data; fail at link time is better than // fail at runtime. See http://golang.org/issue/7980. // Instead of special casing only amd64, we treat this as an error on all // 64-bit architectures so as to be future-proof. if((int32)o < 0 && PtrSize > 4 && siz == 4) { diag("non-pc-relative relocation address is too big: %#llux", o); errorexit(); } break; case R_CALL: case R_PCREL: // r->sym can be null when CALL $(constant) is transformed from absolute PC to relative PC call. if(linkmode == LinkExternal && r->sym && r->sym->type != SCONST && r->sym->sect != ctxt->cursym->sect) { r->done = 0; // set up addend for eventual relocation via outer symbol. rs = r->sym; r->xadd = r->add; while(rs->outer != nil) { r->xadd += symaddr(rs) - symaddr(rs->outer); rs = rs->outer; } r->xadd -= r->siz; // relative to address after the relocated chunk if(rs->type != SHOSTOBJ && rs->type != SDYNIMPORT && rs->sect == nil) diag("missing section for %s", rs->name); r->xsym = rs; o = r->xadd; if(iself) { if(thechar == '6') o = 0; } else if(HEADTYPE == Hdarwin) { if(r->type == R_CALL) { if(rs->type != SHOSTOBJ) o += symaddr(rs) - rs->sect->vaddr; o -= r->off; // relative to section offset, not symbol } else { o += r->siz; } } else { diag("unhandled pcrel relocation for %s", headstring); } break; } o = 0; if(r->sym) o += symaddr(r->sym); // NOTE: The (int32) cast on the next line works around a bug in Plan 9's 8c // compiler. The expression s->value + r->off + r->siz is int32 + int32 + // uchar, and Plan 9 8c incorrectly treats the expression as type uint32 // instead of int32, causing incorrect values when sign extended for adding // to o. The bug only occurs on Plan 9, because this C program is compiled by // the standard host compiler (gcc on most other systems). o += r->add - (s->value + r->off + (int32)r->siz); break; case R_SIZE: o = r->sym->size + r->add; break; } //print("relocate %s %#llux (%#llux+%#llux, size %d) => %s %#llux +%#llx [%llx]\n", s->name, (uvlong)(s->value+off), (uvlong)s->value, (uvlong)r->off, r->siz, r->sym ? r->sym->name : "<nil>", (uvlong)symaddr(r->sym), (vlong)r->add, (vlong)o); switch(siz) { default: ctxt->cursym = s; diag("bad reloc size %#ux for %s", siz, r->sym->name); case 1: // TODO(rsc): Remove. s->p[off] = (int8)o; break; case 4: if(r->type == R_PCREL || r->type == R_CALL) { if(o != (int32)o) diag("pc-relative relocation address is too big: %#llx", o); } else { if(o != (int32)o && o != (uint32)o) diag("non-pc-relative relocation address is too big: %#llux", o); } fl = o; cast = (uchar*)&fl; for(i=0; i<4; i++) s->p[off+i] = cast[inuxi4[i]]; break; case 8: cast = (uchar*)&o; for(i=0; i<8; i++) s->p[off+i] = cast[inuxi8[i]]; break; } } }