| /* |
| Derived from Plan 9 from User Space's src/libmach/elf.h, elf.c |
| http://code.swtch.com/plan9port/src/tip/src/libmach/ |
| |
| Copyright © 2004 Russ Cox. |
| Portions Copyright © 2008-2010 Google Inc. |
| Portions Copyright © 2010 The Go Authors. |
| |
| Permission is hereby granted, free of charge, to any person obtaining a copy |
| of this software and associated documentation files (the "Software"), to deal |
| in the Software without restriction, including without limitation the rights |
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| copies of the Software, and to permit persons to whom the Software is |
| furnished to do so, subject to the following conditions: |
| |
| The above copyright notice and this permission notice shall be included in |
| all copies or substantial portions of the Software. |
| |
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
| THE SOFTWARE. |
| */ |
| |
| #include "l.h" |
| #include "lib.h" |
| |
| enum { |
| MACHO_FAKE_GOTPCREL = 100, // from macho.h |
| |
| N_EXT = 0x01, |
| N_TYPE = 0x1e, |
| N_STAB = 0xe0, |
| }; |
| |
| typedef struct MachoObj MachoObj; |
| typedef struct MachoCmd MachoCmd; |
| typedef struct MachoSeg MachoSeg; |
| typedef struct MachoSect MachoSect; |
| typedef struct MachoRel MachoRel; |
| typedef struct MachoSymtab MachoSymtab; |
| typedef struct MachoSym MachoSym; |
| typedef struct MachoDysymtab MachoDysymtab; |
| |
| enum |
| { |
| MachoCpuVax = 1, |
| MachoCpu68000 = 6, |
| MachoCpu386 = 7, |
| MachoCpuAmd64 = 0x1000007, |
| MachoCpuMips = 8, |
| MachoCpu98000 = 10, |
| MachoCpuHppa = 11, |
| MachoCpuArm = 12, |
| MachoCpu88000 = 13, |
| MachoCpuSparc = 14, |
| MachoCpu860 = 15, |
| MachoCpuAlpha = 16, |
| MachoCpuPower = 18, |
| |
| MachoCmdSegment = 1, |
| MachoCmdSymtab = 2, |
| MachoCmdSymseg = 3, |
| MachoCmdThread = 4, |
| MachoCmdDysymtab = 11, |
| MachoCmdSegment64 = 25, |
| |
| MachoFileObject = 1, |
| MachoFileExecutable = 2, |
| MachoFileFvmlib = 3, |
| MachoFileCore = 4, |
| MachoFilePreload = 5, |
| }; |
| |
| struct MachoSeg |
| { |
| char name[16+1]; |
| uint64 vmaddr; |
| uint64 vmsize; |
| uint32 fileoff; |
| uint32 filesz; |
| uint32 maxprot; |
| uint32 initprot; |
| uint32 nsect; |
| uint32 flags; |
| MachoSect *sect; |
| }; |
| |
| struct MachoSect |
| { |
| char name[16+1]; |
| char segname[16+1]; |
| uint64 addr; |
| uint64 size; |
| uint32 off; |
| uint32 align; |
| uint32 reloff; |
| uint32 nreloc; |
| uint32 flags; |
| uint32 res1; |
| uint32 res2; |
| LSym *sym; |
| |
| MachoRel *rel; |
| }; |
| |
| struct MachoRel |
| { |
| uint32 addr; |
| uint32 symnum; |
| uint8 pcrel; |
| uint8 length; |
| uint8 extrn; |
| uint8 type; |
| uint8 scattered; |
| uint32 value; |
| }; |
| |
| struct MachoSymtab |
| { |
| uint32 symoff; |
| uint32 nsym; |
| uint32 stroff; |
| uint32 strsize; |
| |
| char *str; |
| MachoSym *sym; |
| }; |
| |
| struct MachoSym |
| { |
| char *name; |
| uint8 type; |
| uint8 sectnum; |
| uint16 desc; |
| char kind; |
| uint64 value; |
| LSym *sym; |
| }; |
| |
| struct MachoDysymtab |
| { |
| uint32 ilocalsym; |
| uint32 nlocalsym; |
| uint32 iextdefsym; |
| uint32 nextdefsym; |
| uint32 iundefsym; |
| uint32 nundefsym; |
| uint32 tocoff; |
| uint32 ntoc; |
| uint32 modtaboff; |
| uint32 nmodtab; |
| uint32 extrefsymoff; |
| uint32 nextrefsyms; |
| uint32 indirectsymoff; |
| uint32 nindirectsyms; |
| uint32 extreloff; |
| uint32 nextrel; |
| uint32 locreloff; |
| uint32 nlocrel; |
| uint32 *indir; |
| }; |
| |
| struct MachoCmd |
| { |
| int type; |
| uint32 off; |
| uint32 size; |
| MachoSeg seg; |
| MachoSymtab sym; |
| MachoDysymtab dsym; |
| }; |
| |
| struct MachoObj |
| { |
| Biobuf *f; |
| int64 base; // off in f where Mach-O begins |
| int64 len; // length of Mach-O |
| int is64; |
| char *name; |
| |
| Endian *e; |
| uint cputype; |
| uint subcputype; |
| uint32 filetype; |
| uint32 flags; |
| MachoCmd *cmd; |
| uint ncmd; |
| }; |
| |
| static int |
| unpackcmd(uchar *p, MachoObj *m, MachoCmd *c, uint type, uint sz) |
| { |
| uint32 (*e4)(uchar*); |
| uint64 (*e8)(uchar*); |
| MachoSect *s; |
| int i; |
| |
| e4 = m->e->e32; |
| e8 = m->e->e64; |
| |
| c->type = type; |
| c->size = sz; |
| switch(type){ |
| default: |
| return -1; |
| case MachoCmdSegment: |
| if(sz < 56) |
| return -1; |
| strecpy(c->seg.name, c->seg.name+sizeof c->seg.name, (char*)p+8); |
| c->seg.vmaddr = e4(p+24); |
| c->seg.vmsize = e4(p+28); |
| c->seg.fileoff = e4(p+32); |
| c->seg.filesz = e4(p+36); |
| c->seg.maxprot = e4(p+40); |
| c->seg.initprot = e4(p+44); |
| c->seg.nsect = e4(p+48); |
| c->seg.flags = e4(p+52); |
| c->seg.sect = mal(c->seg.nsect * sizeof c->seg.sect[0]); |
| if(sz < 56+c->seg.nsect*68) |
| return -1; |
| p += 56; |
| for(i=0; i<c->seg.nsect; i++) { |
| s = &c->seg.sect[i]; |
| strecpy(s->name, s->name+sizeof s->name, (char*)p+0); |
| strecpy(s->segname, s->segname+sizeof s->segname, (char*)p+16); |
| s->addr = e4(p+32); |
| s->size = e4(p+36); |
| s->off = e4(p+40); |
| s->align = e4(p+44); |
| s->reloff = e4(p+48); |
| s->nreloc = e4(p+52); |
| s->flags = e4(p+56); |
| s->res1 = e4(p+60); |
| s->res2 = e4(p+64); |
| p += 68; |
| } |
| break; |
| case MachoCmdSegment64: |
| if(sz < 72) |
| return -1; |
| strecpy(c->seg.name, c->seg.name+sizeof c->seg.name, (char*)p+8); |
| c->seg.vmaddr = e8(p+24); |
| c->seg.vmsize = e8(p+32); |
| c->seg.fileoff = e8(p+40); |
| c->seg.filesz = e8(p+48); |
| c->seg.maxprot = e4(p+56); |
| c->seg.initprot = e4(p+60); |
| c->seg.nsect = e4(p+64); |
| c->seg.flags = e4(p+68); |
| c->seg.sect = mal(c->seg.nsect * sizeof c->seg.sect[0]); |
| if(sz < 72+c->seg.nsect*80) |
| return -1; |
| p += 72; |
| for(i=0; i<c->seg.nsect; i++) { |
| s = &c->seg.sect[i]; |
| strecpy(s->name, s->name+sizeof s->name, (char*)p+0); |
| strecpy(s->segname, s->segname+sizeof s->segname, (char*)p+16); |
| s->addr = e8(p+32); |
| s->size = e8(p+40); |
| s->off = e4(p+48); |
| s->align = e4(p+52); |
| s->reloff = e4(p+56); |
| s->nreloc = e4(p+60); |
| s->flags = e4(p+64); |
| s->res1 = e4(p+68); |
| s->res2 = e4(p+72); |
| // p+76 is reserved |
| p += 80; |
| } |
| break; |
| case MachoCmdSymtab: |
| if(sz < 24) |
| return -1; |
| c->sym.symoff = e4(p+8); |
| c->sym.nsym = e4(p+12); |
| c->sym.stroff = e4(p+16); |
| c->sym.strsize = e4(p+20); |
| break; |
| case MachoCmdDysymtab: |
| if(sz < 80) |
| return -1; |
| c->dsym.ilocalsym = e4(p+8); |
| c->dsym.nlocalsym = e4(p+12); |
| c->dsym.iextdefsym = e4(p+16); |
| c->dsym.nextdefsym = e4(p+20); |
| c->dsym.iundefsym = e4(p+24); |
| c->dsym.nundefsym = e4(p+28); |
| c->dsym.tocoff = e4(p+32); |
| c->dsym.ntoc = e4(p+36); |
| c->dsym.modtaboff = e4(p+40); |
| c->dsym.nmodtab = e4(p+44); |
| c->dsym.extrefsymoff = e4(p+48); |
| c->dsym.nextrefsyms = e4(p+52); |
| c->dsym.indirectsymoff = e4(p+56); |
| c->dsym.nindirectsyms = e4(p+60); |
| c->dsym.extreloff = e4(p+64); |
| c->dsym.nextrel = e4(p+68); |
| c->dsym.locreloff = e4(p+72); |
| c->dsym.nlocrel = e4(p+76); |
| break; |
| } |
| return 0; |
| } |
| |
| static int |
| macholoadrel(MachoObj *m, MachoSect *sect) |
| { |
| MachoRel *rel, *r; |
| uchar *buf, *p; |
| int i, n; |
| uint32 v; |
| |
| if(sect->rel != nil || sect->nreloc == 0) |
| return 0; |
| rel = mal(sect->nreloc * sizeof r[0]); |
| n = sect->nreloc * 8; |
| buf = mal(n); |
| if(Bseek(m->f, m->base + sect->reloff, 0) < 0 || Bread(m->f, buf, n) != n) |
| return -1; |
| for(i=0; i<sect->nreloc; i++) { |
| r = &rel[i]; |
| p = buf+i*8; |
| r->addr = m->e->e32(p); |
| |
| // TODO(rsc): Wrong interpretation for big-endian bitfields? |
| if(r->addr & 0x80000000) { |
| // scatterbrained relocation |
| r->scattered = 1; |
| v = r->addr >> 24; |
| r->addr &= 0xFFFFFF; |
| r->type = v & 0xF; |
| v >>= 4; |
| r->length = 1<<(v&3); |
| v >>= 2; |
| r->pcrel = v & 1; |
| r->value = m->e->e32(p+4); |
| } else { |
| v = m->e->e32(p+4); |
| r->symnum = v & 0xFFFFFF; |
| v >>= 24; |
| r->pcrel = v&1; |
| v >>= 1; |
| r->length = 1<<(v&3); |
| v >>= 2; |
| r->extrn = v&1; |
| v >>= 1; |
| r->type = v; |
| } |
| } |
| sect->rel = rel; |
| return 0; |
| } |
| |
| static int |
| macholoaddsym(MachoObj *m, MachoDysymtab *d) |
| { |
| uchar *p; |
| int i, n; |
| |
| n = d->nindirectsyms; |
| |
| p = mal(n*4); |
| if(Bseek(m->f, m->base + d->indirectsymoff, 0) < 0 || Bread(m->f, p, n*4) != n*4) |
| return -1; |
| |
| d->indir = (uint32*)p; |
| for(i=0; i<n; i++) |
| d->indir[i] = m->e->e32(p+4*i); |
| return 0; |
| } |
| |
| static int |
| macholoadsym(MachoObj *m, MachoSymtab *symtab) |
| { |
| char *strbuf; |
| uchar *symbuf, *p; |
| int i, n, symsize; |
| MachoSym *sym, *s; |
| uint32 v; |
| |
| if(symtab->sym != nil) |
| return 0; |
| |
| strbuf = mal(symtab->strsize); |
| if(Bseek(m->f, m->base + symtab->stroff, 0) < 0 || Bread(m->f, strbuf, symtab->strsize) != symtab->strsize) |
| return -1; |
| |
| symsize = 12; |
| if(m->is64) |
| symsize = 16; |
| n = symtab->nsym * symsize; |
| symbuf = mal(n); |
| if(Bseek(m->f, m->base + symtab->symoff, 0) < 0 || Bread(m->f, symbuf, n) != n) |
| return -1; |
| sym = mal(symtab->nsym * sizeof sym[0]); |
| p = symbuf; |
| for(i=0; i<symtab->nsym; i++) { |
| s = &sym[i]; |
| v = m->e->e32(p); |
| if(v >= symtab->strsize) |
| return -1; |
| s->name = strbuf + v; |
| s->type = p[4]; |
| s->sectnum = p[5]; |
| s->desc = m->e->e16(p+6); |
| if(m->is64) |
| s->value = m->e->e64(p+8); |
| else |
| s->value = m->e->e32(p+8); |
| p += symsize; |
| } |
| symtab->str = strbuf; |
| symtab->sym = sym; |
| return 0; |
| } |
| |
| void |
| ldmacho(Biobuf *f, char *pkg, int64 len, char *pn) |
| { |
| int i, j, is64; |
| uint64 secaddr; |
| uchar hdr[7*4], *cmdp; |
| uchar tmp[4]; |
| uchar *dat; |
| ulong ncmd, cmdsz, ty, sz, off; |
| MachoObj *m; |
| Endian *e; |
| int64 base; |
| MachoSect *sect; |
| MachoRel *rel; |
| LSym *s, *s1, *outer; |
| MachoCmd *c; |
| MachoSymtab *symtab; |
| MachoDysymtab *dsymtab; |
| MachoSym *sym; |
| Reloc *r, *rp; |
| char *name; |
| |
| ctxt->version++; |
| base = Boffset(f); |
| if(Bread(f, hdr, sizeof hdr) != sizeof hdr) |
| goto bad; |
| |
| if((be.e32(hdr)&~1) == 0xFEEDFACE){ |
| e = &be; |
| }else if((le.e32(hdr)&~1) == 0xFEEDFACE){ |
| e = ≤ |
| }else{ |
| werrstr("bad magic - not mach-o file"); |
| goto bad; |
| } |
| |
| is64 = e->e32(hdr) == 0xFEEDFACF; |
| ncmd = e->e32(hdr+4*4); |
| cmdsz = e->e32(hdr+5*4); |
| if(ncmd > 0x10000 || cmdsz >= 0x01000000){ |
| werrstr("implausible mach-o header ncmd=%lud cmdsz=%lud", ncmd, cmdsz); |
| goto bad; |
| } |
| if(is64) |
| Bread(f, tmp, 4); // skip reserved word in header |
| |
| m = mal(sizeof(*m)+ncmd*sizeof(MachoCmd)+cmdsz); |
| m->f = f; |
| m->e = e; |
| m->cputype = e->e32(hdr+1*4); |
| m->subcputype = e->e32(hdr+2*4); |
| m->filetype = e->e32(hdr+3*4); |
| m->ncmd = ncmd; |
| m->flags = e->e32(hdr+6*4); |
| m->is64 = is64; |
| m->base = base; |
| m->len = len; |
| m->name = pn; |
| |
| switch(thechar) { |
| default: |
| diag("%s: mach-o %s unimplemented", pn, thestring); |
| return; |
| case '6': |
| if(e != &le || m->cputype != MachoCpuAmd64) { |
| diag("%s: mach-o object but not amd64", pn); |
| return; |
| } |
| break; |
| case '8': |
| if(e != &le || m->cputype != MachoCpu386) { |
| diag("%s: mach-o object but not 386", pn); |
| return; |
| } |
| break; |
| } |
| |
| m->cmd = (MachoCmd*)(m+1); |
| off = sizeof hdr; |
| cmdp = (uchar*)(m->cmd+ncmd); |
| if(Bread(f, cmdp, cmdsz) != cmdsz){ |
| werrstr("reading cmds: %r"); |
| goto bad; |
| } |
| |
| // read and parse load commands |
| c = nil; |
| symtab = nil; |
| dsymtab = nil; |
| USED(dsymtab); |
| for(i=0; i<ncmd; i++){ |
| ty = e->e32(cmdp); |
| sz = e->e32(cmdp+4); |
| m->cmd[i].off = off; |
| unpackcmd(cmdp, m, &m->cmd[i], ty, sz); |
| cmdp += sz; |
| off += sz; |
| if(ty == MachoCmdSymtab) { |
| if(symtab != nil) { |
| werrstr("multiple symbol tables"); |
| goto bad; |
| } |
| symtab = &m->cmd[i].sym; |
| macholoadsym(m, symtab); |
| } |
| if(ty == MachoCmdDysymtab) { |
| dsymtab = &m->cmd[i].dsym; |
| macholoaddsym(m, dsymtab); |
| } |
| if((is64 && ty == MachoCmdSegment64) || (!is64 && ty == MachoCmdSegment)) { |
| if(c != nil) { |
| werrstr("multiple load commands"); |
| goto bad; |
| } |
| c = &m->cmd[i]; |
| } |
| } |
| |
| // load text and data segments into memory. |
| // they are not as small as the load commands, but we'll need |
| // the memory anyway for the symbol images, so we might |
| // as well use one large chunk. |
| if(c == nil) { |
| werrstr("no load command"); |
| goto bad; |
| } |
| if(symtab == nil) { |
| // our work is done here - no symbols means nothing can refer to this file |
| return; |
| } |
| |
| if(c->seg.fileoff+c->seg.filesz >= len) { |
| werrstr("load segment out of range"); |
| goto bad; |
| } |
| |
| dat = mal(c->seg.filesz); |
| if(Bseek(f, m->base + c->seg.fileoff, 0) < 0 || Bread(f, dat, c->seg.filesz) != c->seg.filesz) { |
| werrstr("cannot load object data: %r"); |
| goto bad; |
| } |
| |
| for(i=0; i<c->seg.nsect; i++) { |
| sect = &c->seg.sect[i]; |
| if(strcmp(sect->segname, "__TEXT") != 0 && strcmp(sect->segname, "__DATA") != 0) |
| continue; |
| if(strcmp(sect->name, "__eh_frame") == 0) |
| continue; |
| name = smprint("%s(%s/%s)", pkg, sect->segname, sect->name); |
| s = linklookup(ctxt, name, ctxt->version); |
| if(s->type != 0) { |
| werrstr("duplicate %s/%s", sect->segname, sect->name); |
| goto bad; |
| } |
| free(name); |
| |
| s->np = sect->size; |
| s->size = s->np; |
| if((sect->flags & 0xff) == 1) // S_ZEROFILL |
| s->p = mal(s->size); |
| else { |
| s->p = dat + sect->addr - c->seg.vmaddr; |
| } |
| |
| if(strcmp(sect->segname, "__TEXT") == 0) { |
| if(strcmp(sect->name, "__text") == 0) |
| s->type = STEXT; |
| else |
| s->type = SRODATA; |
| } else { |
| if (strcmp(sect->name, "__bss") == 0) { |
| s->type = SBSS; |
| s->np = 0; |
| } else |
| s->type = SDATA; |
| } |
| sect->sym = s; |
| } |
| |
| // enter sub-symbols into symbol table. |
| // have to guess sizes from next symbol. |
| for(i=0; i<symtab->nsym; i++) { |
| int v; |
| sym = &symtab->sym[i]; |
| if(sym->type&N_STAB) |
| continue; |
| // TODO: check sym->type against outer->type. |
| name = sym->name; |
| if(name[0] == '_' && name[1] != '\0') |
| name++; |
| v = 0; |
| if(!(sym->type&N_EXT)) |
| v = ctxt->version; |
| s = linklookup(ctxt, name, v); |
| if(!(sym->type&N_EXT)) |
| s->dupok = 1; |
| sym->sym = s; |
| if(sym->sectnum == 0) // undefined |
| continue; |
| if(sym->sectnum > c->seg.nsect) { |
| werrstr("reference to invalid section %d", sym->sectnum); |
| goto bad; |
| } |
| sect = &c->seg.sect[sym->sectnum-1]; |
| outer = sect->sym; |
| if(outer == nil) { |
| werrstr("reference to invalid section %s/%s", sect->segname, sect->name); |
| continue; |
| } |
| if(s->outer != S) { |
| if(s->dupok) |
| continue; |
| diag("%s: duplicate symbol reference: %s in both %s and %s", pn, s->name, s->outer->name, sect->sym->name); |
| errorexit(); |
| } |
| s->type = outer->type | SSUB; |
| s->sub = outer->sub; |
| outer->sub = s; |
| s->outer = outer; |
| s->value = sym->value - sect->addr; |
| if(!(s->cgoexport & CgoExportDynamic)) |
| s->dynimplib = nil; // satisfy dynimport |
| if(outer->type == STEXT) { |
| if(s->external && !s->dupok) |
| diag("%s: duplicate definition of %s", pn, s->name); |
| s->external = 1; |
| } |
| sym->sym = s; |
| } |
| |
| // Sort outer lists by address, adding to textp. |
| // This keeps textp in increasing address order. |
| for(i=0; i<c->seg.nsect; i++) { |
| sect = &c->seg.sect[i]; |
| if((s = sect->sym) == S) |
| continue; |
| if(s->sub) { |
| s->sub = listsort(s->sub, valuecmp, offsetof(LSym, sub)); |
| |
| // assign sizes, now that we know symbols in sorted order. |
| for(s1 = s->sub; s1 != S; s1 = s1->sub) { |
| if(s1->sub) |
| s1->size = s1->sub->value - s1->value; |
| else |
| s1->size = s->value + s->size - s1->value; |
| } |
| } |
| if(s->type == STEXT) { |
| if(s->onlist) |
| sysfatal("symbol %s listed multiple times", s->name); |
| s->onlist = 1; |
| if(ctxt->etextp) |
| ctxt->etextp->next = s; |
| else |
| ctxt->textp = s; |
| ctxt->etextp = s; |
| for(s1 = s->sub; s1 != S; s1 = s1->sub) { |
| if(s1->onlist) |
| sysfatal("symbol %s listed multiple times", s1->name); |
| s1->onlist = 1; |
| ctxt->etextp->next = s1; |
| ctxt->etextp = s1; |
| } |
| } |
| } |
| |
| // load relocations |
| for(i=0; i<c->seg.nsect; i++) { |
| sect = &c->seg.sect[i]; |
| if((s = sect->sym) == S) |
| continue; |
| macholoadrel(m, sect); |
| if(sect->rel == nil) |
| continue; |
| r = mal(sect->nreloc*sizeof r[0]); |
| rp = r; |
| rel = sect->rel; |
| for(j=0; j<sect->nreloc; j++, rel++) { |
| if(rel->scattered) { |
| int k; |
| MachoSect *ks; |
| |
| if(thechar != '8') { |
| // mach-o only uses scattered relocation on 32-bit platforms |
| diag("unexpected scattered relocation"); |
| continue; |
| } |
| |
| // on 386, rewrite scattered 4/1 relocation and some |
| // scattered 2/1 relocation into the pseudo-pc-relative |
| // reference that it is. |
| // assume that the second in the pair is in this section |
| // and use that as the pc-relative base. |
| if(j+1 >= sect->nreloc) { |
| werrstr("unsupported scattered relocation %d", (int)rel->type); |
| goto bad; |
| } |
| if(!(rel+1)->scattered || (rel+1)->type != 1 || |
| (rel->type != 4 && rel->type != 2) || |
| (rel+1)->value < sect->addr || (rel+1)->value >= sect->addr+sect->size) { |
| werrstr("unsupported scattered relocation %d/%d", (int)rel->type, (int)(rel+1)->type); |
| goto bad; |
| } |
| |
| rp->siz = rel->length; |
| rp->off = rel->addr; |
| |
| // NOTE(rsc): I haven't worked out why (really when) |
| // we should ignore the addend on a |
| // scattered relocation, but it seems that the |
| // common case is we ignore it. |
| // It's likely that this is not strictly correct |
| // and that the math should look something |
| // like the non-scattered case below. |
| rp->add = 0; |
| |
| // want to make it pc-relative aka relative to rp->off+4 |
| // but the scatter asks for relative to off = (rel+1)->value - sect->addr. |
| // adjust rp->add accordingly. |
| rp->type = R_PCREL; |
| rp->add += (rp->off+4) - ((rel+1)->value - sect->addr); |
| |
| // now consider the desired symbol. |
| // find the section where it lives. |
| for(k=0; k<c->seg.nsect; k++) { |
| ks = &c->seg.sect[k]; |
| if(ks->addr <= rel->value && rel->value < ks->addr+ks->size) |
| goto foundk; |
| } |
| werrstr("unsupported scattered relocation: invalid address %#ux", rel->addr); |
| goto bad; |
| foundk: |
| if(ks->sym != S) { |
| rp->sym = ks->sym; |
| rp->add += rel->value - ks->addr; |
| } else if(strcmp(ks->segname, "__IMPORT") == 0 && strcmp(ks->name, "__pointers") == 0) { |
| // handle reference to __IMPORT/__pointers. |
| // how much worse can this get? |
| // why are we supporting 386 on the mac anyway? |
| rp->type = 512 + MACHO_FAKE_GOTPCREL; |
| // figure out which pointer this is a reference to. |
| k = ks->res1 + (rel->value - ks->addr) / 4; |
| // load indirect table for __pointers |
| // fetch symbol number |
| if(dsymtab == nil || k < 0 || k >= dsymtab->nindirectsyms || dsymtab->indir == nil) { |
| werrstr("invalid scattered relocation: indirect symbol reference out of range"); |
| goto bad; |
| } |
| k = dsymtab->indir[k]; |
| if(k < 0 || k >= symtab->nsym) { |
| werrstr("invalid scattered relocation: symbol reference out of range"); |
| goto bad; |
| } |
| rp->sym = symtab->sym[k].sym; |
| } else { |
| werrstr("unsupported scattered relocation: reference to %s/%s", ks->segname, ks->name); |
| goto bad; |
| } |
| rp++; |
| // skip #1 of 2 rel; continue skips #2 of 2. |
| rel++; |
| j++; |
| continue; |
| } |
| |
| rp->siz = rel->length; |
| rp->type = 512 + (rel->type<<1) + rel->pcrel; |
| rp->off = rel->addr; |
| |
| // Handle X86_64_RELOC_SIGNED referencing a section (rel->extrn == 0). |
| if (thechar == '6' && rel->extrn == 0 && rel->type == 1) { |
| // Calculate the addend as the offset into the section. |
| // |
| // The rip-relative offset stored in the object file is encoded |
| // as follows: |
| // |
| // movsd 0x00000360(%rip),%xmm0 |
| // |
| // To get the absolute address of the value this rip-relative address is pointing |
| // to, we must add the address of the next instruction to it. This is done by |
| // taking the address of the relocation and adding 4 to it (since the rip-relative |
| // offset can at most be 32 bits long). To calculate the offset into the section the |
| // relocation is referencing, we subtract the vaddr of the start of the referenced |
| // section found in the original object file. |
| // |
| // [For future reference, see Darwin's /usr/include/mach-o/x86_64/reloc.h] |
| secaddr = c->seg.sect[rel->symnum-1].addr; |
| rp->add = (int32)e->e32(s->p+rp->off) + rp->off + 4 - secaddr; |
| } else |
| rp->add = (int32)e->e32(s->p+rp->off); |
| |
| // For i386 Mach-O PC-relative, the addend is written such that |
| // it *is* the PC being subtracted. Use that to make |
| // it match our version of PC-relative. |
| if(rel->pcrel && thechar == '8') |
| rp->add += rp->off+rp->siz; |
| if(!rel->extrn) { |
| if(rel->symnum < 1 || rel->symnum > c->seg.nsect) { |
| werrstr("invalid relocation: section reference out of range %d vs %d", rel->symnum, c->seg.nsect); |
| goto bad; |
| } |
| rp->sym = c->seg.sect[rel->symnum-1].sym; |
| if(rp->sym == nil) { |
| werrstr("invalid relocation: %s", c->seg.sect[rel->symnum-1].name); |
| goto bad; |
| } |
| // References to symbols in other sections |
| // include that information in the addend. |
| // We only care about the delta from the |
| // section base. |
| if(thechar == '8') |
| rp->add -= c->seg.sect[rel->symnum-1].addr; |
| } else { |
| if(rel->symnum >= symtab->nsym) { |
| werrstr("invalid relocation: symbol reference out of range"); |
| goto bad; |
| } |
| rp->sym = symtab->sym[rel->symnum].sym; |
| } |
| rp++; |
| } |
| qsort(r, rp - r, sizeof r[0], rbyoff); |
| s->r = r; |
| s->nr = rp - r; |
| } |
| return; |
| |
| bad: |
| diag("%s: malformed mach-o file: %r", pn); |
| } |