void elfsetupplt(void) { LSym *plt, *got; plt = linklookup(ctxt, ".plt", 0); got = linklookup(ctxt, ".got.plt", 0); if(plt->size == 0) { // pushl got+4 adduint8(ctxt, plt, 0xff); adduint8(ctxt, plt, 0x35); addaddrplus(ctxt, plt, got, 4); // jmp *got+8 adduint8(ctxt, plt, 0xff); adduint8(ctxt, plt, 0x25); addaddrplus(ctxt, plt, got, 8); // zero pad adduint32(ctxt, plt, 0); // assume got->size == 0 too addaddrplus(ctxt, got, linklookup(ctxt, ".dynamic", 0), 0); adduint32(ctxt, got, 0); adduint32(ctxt, got, 0); } }
static int readsym(PeObj *obj, int i, PeSym **y) { LSym *s; PeSym *sym; char *name, *p; if(i >= obj->npesym || i < 0) { werrstr("invalid pe symbol index"); return -1; } sym = &obj->pesym[i]; *y = sym; if(sym->name[0] == '.') // .section name = obj->sect[sym->sectnum-1].sym->name; else { name = sym->name; if(strncmp(name, "__imp_", 6) == 0) name = &name[6]; // __imp_Name => Name if(thechar == '8' && name[0] == '_') name = &name[1]; // _Name => Name } // remove last @XXX p = strchr(name, '@'); if(p) *p = 0; switch(sym->type) { default: werrstr("%s: invalid symbol type %d", sym->name, sym->type); return -1; case IMAGE_SYM_DTYPE_FUNCTION: case IMAGE_SYM_DTYPE_NULL: switch(sym->sclass) { case IMAGE_SYM_CLASS_EXTERNAL: //global s = linklookup(ctxt, name, 0); break; case IMAGE_SYM_CLASS_NULL: case IMAGE_SYM_CLASS_STATIC: case IMAGE_SYM_CLASS_LABEL: s = linklookup(ctxt, name, ctxt->version); s->dupok = 1; break; default: werrstr("%s: invalid symbol binding %d", sym->name, sym->sclass); return -1; } break; } if(s != nil && s->type == 0 && !(sym->sclass == IMAGE_SYM_CLASS_STATIC && sym->value == 0)) s->type = SXREF; if(strncmp(sym->name, "__imp_", 6) == 0) s->got = -2; // flag for __imp_ sym->sym = s; return 0; }
void addstrdata(char *name, char *value) { LSym *s, *sp; char *p; uchar reachable; p = smprint("%s.str", name); sp = linklookup(ctxt, p, 0); free(p); addstring(sp, value); sp->type = SRODATA; s = linklookup(ctxt, name, 0); s->size = 0; s->dupok = 1; reachable = s->reachable; addaddr(ctxt, s, sp); adduint32(ctxt, s, strlen(value)); if(PtrSize == 8) adduint32(ctxt, s, 0); // round struct to pointer width // addstring, addaddr, etc., mark the symbols as reachable. // In this case that is not necessarily true, so stick to what // we know before entering this function. s->reachable = reachable; sp->reachable = reachable; }
void elfsetupplt(void) { LSym *plt, *got; plt = linklookup(ctxt, ".plt", 0); got = linklookup(ctxt, ".got.plt", 0); if(plt->size == 0) { // pushq got+8(IP) adduint8(ctxt, plt, 0xff); adduint8(ctxt, plt, 0x35); addpcrelplus(ctxt, plt, got, 8); // jmpq got+16(IP) adduint8(ctxt, plt, 0xff); adduint8(ctxt, plt, 0x25); addpcrelplus(ctxt, plt, got, 16); // nopl 0(AX) adduint32(ctxt, plt, 0x00401f0f); // assume got->size == 0 too addaddrplus(ctxt, got, linklookup(ctxt, ".dynamic", 0), 0); adduint64(ctxt, got, 0); adduint64(ctxt, got, 0); } }
// assign addresses to text void textaddress(void) { uvlong va; Section *sect; LSym *sym, *sub; addsection(&segtext, ".text", 05); // Assign PCs in text segment. // Could parallelize, by assigning to text // and then letting threads copy down, but probably not worth it. sect = segtext.sect; sect->align = funcalign; linklookup(ctxt, "runtime.text", 0)->sect = sect; linklookup(ctxt, "runtime.etext", 0)->sect = sect; va = INITTEXT; sect->vaddr = va; for(sym = ctxt->textp; sym != nil; sym = sym->next) { sym->sect = sect; if(sym->type & SSUB) continue; if(sym->align != 0) va = rnd(va, sym->align); else va = rnd(va, funcalign); sym->value = 0; for(sub = sym; sub != S; sub = sub->sub) sub->value += va; if(sym->size == 0 && sym->sub != S) ctxt->cursym = sym; va += sym->size; } sect->len = va - sect->vaddr; }
void adddynsym(Link *ctxt, LSym *s) { LSym *d; int t; char *name; if(s->dynid >= 0) return; if(iself) { s->dynid = nelfsym++; d = linklookup(ctxt, ".dynsym", 0); name = s->extname; adduint32(ctxt, d, addstring(linklookup(ctxt, ".dynstr", 0), name)); /* type */ t = STB_GLOBAL << 4; if(s->cgoexport && (s->type&SMASK) == STEXT) t |= STT_FUNC; else t |= STT_OBJECT; adduint8(ctxt, d, t); /* reserved */ adduint8(ctxt, d, 0); /* section where symbol is defined */ if(s->type == SDYNIMPORT) adduint16(ctxt, d, SHN_UNDEF); else adduint16(ctxt, d, 1); /* value */ if(s->type == SDYNIMPORT) adduint64(ctxt, d, 0); else addaddr(ctxt, d, s); /* size of object */ adduint64(ctxt, d, s->size); if(!(s->cgoexport & CgoExportDynamic) && s->dynimplib && needlib(s->dynimplib)) { elfwritedynent(linklookup(ctxt, ".dynamic", 0), DT_NEEDED, addstring(linklookup(ctxt, ".dynstr", 0), s->dynimplib)); } } else if(HEADTYPE == Hdarwin) { diag("adddynsym: missed symbol %s (%s)", s->name, s->extname); } else if(HEADTYPE == Hwindows) { // already taken care of } else { diag("adddynsym: unsupported binary format"); } }
void zerosig(char *sp) { LSym *s; s = linklookup(ctxt, sp, 0); s->sig = 0; }
LSym* linksym(Sym *s) { char *p; if(s == nil) return nil; if(s->lsym != nil) return s->lsym; if(isblanksym(s)) s->lsym = linklookup(ctxt, "_", 0); else { p = smprint("%s.%s", s->pkg->prefix, s->name); s->lsym = linklookup(ctxt, p, 0); free(p); } return s->lsym; }
void adddynlib(char *lib) { LSym *s; if(!needlib(lib)) return; if(iself) { s = linklookup(ctxt, ".dynstr", 0); if(s->size == 0) addstring(s, ""); elfwritedynent(linklookup(ctxt, ".dynamic", 0), DT_NEEDED, addstring(s, lib)); } else if(HEADTYPE == Hdarwin) { machoadddynlib(lib); } else { diag("adddynlib: unsupported binary format"); } }
void dynrelocsym(LSym *s) { Reloc *r; if(HEADTYPE == Hwindows) { LSym *rel, *targ; rel = linklookup(ctxt, ".rel", 0); if(s == rel) return; for(r=s->r; r<s->r+s->nr; r++) { targ = r->sym; if(targ == nil) continue; if(!targ->reachable) diag("internal inconsistency: dynamic symbol %s is not reachable.", targ->name); if(r->sym->plt == -2 && r->sym->got != -2) { // make dynimport JMP table for PE object files. targ->plt = rel->size; r->sym = rel; r->add = targ->plt; // jmp *addr if(thechar == '8') { adduint8(ctxt, rel, 0xff); adduint8(ctxt, rel, 0x25); addaddr(ctxt, rel, targ); adduint8(ctxt, rel, 0x90); adduint8(ctxt, rel, 0x90); } else { adduint8(ctxt, rel, 0xff); adduint8(ctxt, rel, 0x24); adduint8(ctxt, rel, 0x25); addaddrplus4(ctxt, rel, targ, 0); adduint8(ctxt, rel, 0x90); } } else if(r->sym->plt >= 0) { r->sym = rel; r->add = targ->plt; } } return; } for(r=s->r; r<s->r+s->nr; r++) { if(r->sym != S && r->sym->type == SDYNIMPORT || r->type >= 256) { if(r->sym != S && !r->sym->reachable) diag("internal inconsistency: dynamic symbol %s is not reachable.", r->sym->name); adddynrel(s, r); } } }
void dope(void) { LSym *rel; /* relocation table */ rel = linklookup(ctxt, ".rel", 0); rel->reachable = 1; rel->type = SELFROSECT; initdynimport(); initdynexport(); }
static void addpltsym(Link *ctxt, LSym *s) { LSym *plt, *got, *rel; if(s->plt >= 0) return; adddynsym(ctxt, s); if(iself) { plt = linklookup(ctxt, ".plt", 0); got = linklookup(ctxt, ".got.plt", 0); rel = linklookup(ctxt, ".rel.plt", 0); if(plt->size == 0) elfsetupplt(); // jmpq *got+size adduint8(ctxt, plt, 0xff); adduint8(ctxt, plt, 0x25); addaddrplus(ctxt, plt, got, got->size); // add to got: pointer to current pos in plt addaddrplus(ctxt, got, plt, plt->size); // pushl $x adduint8(ctxt, plt, 0x68); adduint32(ctxt, plt, rel->size); // jmp .plt adduint8(ctxt, plt, 0xe9); adduint32(ctxt, plt, -(plt->size+4)); // rel addaddrplus(ctxt, rel, got, got->size-4); adduint32(ctxt, rel, ELF32_R_INFO(s->dynid, R_386_JMP_SLOT)); s->plt = plt->size - 16; } else if(HEADTYPE == Hdarwin) { // Same laziness as in 6l. LSym *plt; plt = linklookup(ctxt, ".plt", 0); addgotsym(ctxt, s); adduint32(ctxt, linklookup(ctxt, ".linkedit.plt", 0), s->dynid); // jmpq *got+size(IP) s->plt = plt->size; adduint8(ctxt, plt, 0xff); adduint8(ctxt, plt, 0x25); addaddrplus(ctxt, plt, linklookup(ctxt, ".got", 0), s->got); } else { diag("addpltsym: unsupported binary format"); } }
static void addgotsym(Link *ctxt, LSym *s) { LSym *got, *rel; if(s->got >= 0) return; adddynsym(ctxt, s); got = linklookup(ctxt, ".got", 0); s->got = got->size; adduint32(ctxt, got, 0); if(iself) { rel = linklookup(ctxt, ".rel", 0); addaddrplus(ctxt, rel, got, s->got); adduint32(ctxt, rel, ELF32_R_INFO(s->dynid, R_386_GLOB_DAT)); } else if(HEADTYPE == Hdarwin) { adduint32(ctxt, linklookup(ctxt, ".linkedit.got", 0), s->dynid); } else { diag("addgotsym: unsupported binary format"); } }
int archreloc(Reloc *r, LSym *s, vlong *val) { USED(s); if(linkmode == LinkExternal) return -1; switch(r->type) { case D_CONST: *val = r->add; return 0; case D_GOTOFF: *val = symaddr(r->sym) + r->add - symaddr(linklookup(ctxt, ".got", 0)); return 0; } return -1; }
void libinit(void) { char *suffix, *suffixsep; fmtinstall('i', iconv); fmtinstall('Y', Yconv); fmtinstall('Z', Zconv); mywhatsys(); // get goroot, goarch, goos if(strcmp(goarch, thestring) != 0) print("goarch is not known: %s\n", goarch); // add goroot to the end of the libdir list. suffix = ""; suffixsep = ""; if(flag_installsuffix != nil) { suffixsep = "_"; suffix = flag_installsuffix; } else if(flag_race) { suffixsep = "_"; suffix = "race"; } Lflag(smprint("%s/pkg/%s_%s%s%s", goroot, goos, goarch, suffixsep, suffix)); // Unix doesn't like it when we write to a running (or, sometimes, // recently run) binary, so remove the output file before writing it. // On Windows 7, remove() can force the following create() to fail. #ifndef _WIN32 remove(outfile); #endif cout = create(outfile, 1, 0775); if(cout < 0) { diag("cannot create %s: %r", outfile); errorexit(); } if(INITENTRY == nil) { INITENTRY = mal(strlen(goarch)+strlen(goos)+20); if(!flag_shared) { sprint(INITENTRY, "_rt0_%s_%s", goarch, goos); } else { sprint(INITENTRY, "_rt0_%s_%s_lib", goarch, goos); } } linklookup(ctxt, INITENTRY, 0)->type = SXREF; }
static Dll* initdynimport(void) { Imp *m; Dll *d; LSym *s, *dynamic; dr = nil; m = nil; for(s = ctxt->allsym; s != S; s = s->allsym) { if(!s->reachable || s->type != SDYNIMPORT) continue; for(d = dr; d != nil; d = d->next) { if(strcmp(d->name,s->dynimplib) == 0) { m = mal(sizeof *m); break; } } if(d == nil) { d = mal(sizeof *d); d->name = s->dynimplib; d->next = dr; dr = d; m = mal(sizeof *m); } m->s = s; m->next = d->ms; d->ms = m; } dynamic = linklookup(ctxt, ".windynamic", 0); dynamic->reachable = 1; dynamic->type = SWINDOWS; for(d = dr; d != nil; d = d->next) { for(m = d->ms; m != nil; m = m->next) { m->s->type = SWINDOWS | SSUB; m->s->sub = dynamic->sub; dynamic->sub = m->s; m->s->value = dynamic->size; dynamic->size += PtrSize; } dynamic->size += PtrSize; } return dr; }
static int needlib(char *name) { char *p; LSym *s; if(*name == '\0') return 0; /* reuse hash code in symbol table */ p = smprint(".elfload.%s", name); s = linklookup(ctxt, p, 0); free(p); if(s->type == 0) { s->type = 100; // avoid SDATA, etc. return 1; } return 0; }
static void addexcept(IMAGE_SECTION_HEADER *text) { IMAGE_SECTION_HEADER *pdata, *xdata; vlong startoff; uvlong n; LSym *sym; USED(text); if(thechar != '6') return; // write unwind info sym = linklookup(ctxt, "runtime.sigtramp", 0); startoff = cpos(); lputl(9); // version=1, flags=UNW_FLAG_EHANDLER, rest 0 lputl(sym->value - PEBASE); lputl(0); n = cpos() - startoff; xdata = addpesection(".xdata", n, n); xdata->Characteristics = IMAGE_SCN_MEM_READ| IMAGE_SCN_CNT_INITIALIZED_DATA; chksectoff(xdata, startoff); strnput("", xdata->SizeOfRawData - n); // write a function table entry for the whole text segment startoff = cpos(); lputl(text->VirtualAddress); lputl(text->VirtualAddress + text->VirtualSize); lputl(xdata->VirtualAddress); n = cpos() - startoff; pdata = addpesection(".pdata", n, n); pdata->Characteristics = IMAGE_SCN_MEM_READ| IMAGE_SCN_CNT_INITIALIZED_DATA; chksectoff(pdata, startoff); strnput("", pdata->SizeOfRawData - n); dd[IMAGE_DIRECTORY_ENTRY_EXCEPTION].VirtualAddress = pdata->VirtualAddress; dd[IMAGE_DIRECTORY_ENTRY_EXCEPTION].Size = pdata->VirtualSize; }
static void addpltsym(LSym *s) { if(s->plt >= 0) return; adddynsym(ctxt, s); if(iself) { LSym *plt, *got, *rela; plt = linklookup(ctxt, ".plt", 0); got = linklookup(ctxt, ".got.plt", 0); rela = linklookup(ctxt, ".rela.plt", 0); if(plt->size == 0) elfsetupplt(); // jmpq *got+size(IP) adduint8(ctxt, plt, 0xff); adduint8(ctxt, plt, 0x25); addpcrelplus(ctxt, plt, got, got->size); // add to got: pointer to current pos in plt addaddrplus(ctxt, got, plt, plt->size); // pushq $x adduint8(ctxt, plt, 0x68); adduint32(ctxt, plt, (got->size-24-8)/8); // jmpq .plt adduint8(ctxt, plt, 0xe9); adduint32(ctxt, plt, -(plt->size+4)); // rela addaddrplus(ctxt, rela, got, got->size-8); adduint64(ctxt, rela, ELF64_R_INFO(s->dynid, R_X86_64_JMP_SLOT)); adduint64(ctxt, rela, 0); s->plt = plt->size - 16; } else if(HEADTYPE == Hdarwin) { // To do lazy symbol lookup right, we're supposed // to tell the dynamic loader which library each // symbol comes from and format the link info // section just so. I'm too lazy (ha!) to do that // so for now we'll just use non-lazy pointers, // which don't need to be told which library to use. // // http://networkpx.blogspot.com/2009/09/about-lcdyldinfoonly-command.html // has details about what we're avoiding. LSym *plt; addgotsym(s); plt = linklookup(ctxt, ".plt", 0); adduint32(ctxt, linklookup(ctxt, ".linkedit.plt", 0), s->dynid); // jmpq *got+size(IP) s->plt = plt->size; adduint8(ctxt, plt, 0xff); adduint8(ctxt, plt, 0x25); addpcrelplus(ctxt, plt, linklookup(ctxt, ".got", 0), s->got); } else { diag("addpltsym: unsupported binary format"); } }
void loadlib(void) { int i, w, x; LSym *s, *gmsym; char* cgostrsym; if(flag_shared) { s = linklookup(ctxt, "runtime.islibrary", 0); s->dupok = 1; adduint8(ctxt, s, 1); } loadinternal("runtime"); if(thechar == '5') loadinternal("math"); if(flag_race) loadinternal("runtime/race"); for(i=0; i<ctxt->libraryp; i++) { if(debug['v'] > 1) Bprint(&bso, "%5.2f autolib: %s (from %s)\n", cputime(), ctxt->library[i].file, ctxt->library[i].objref); iscgo |= strcmp(ctxt->library[i].pkg, "runtime/cgo") == 0; objfile(ctxt->library[i].file, ctxt->library[i].pkg); } if(linkmode == LinkExternal && !iscgo) { // This indicates a user requested -linkmode=external. // The startup code uses an import of runtime/cgo to decide // whether to initialize the TLS. So give it one. This could // be handled differently but it's an unusual case. loadinternal("runtime/cgo"); // Pretend that we really imported the package. s = linklookup(ctxt, "go.importpath.runtime/cgo.", 0); s->type = SDATA; s->dupok = 1; s->reachable = 1; // Provided by the code that imports the package. // Since we are simulating the import, we have to provide this string. cgostrsym = "go.string.\"runtime/cgo\""; if(linkrlookup(ctxt, cgostrsym, 0) == nil) addstrdata(cgostrsym, "runtime/cgo"); } if(linkmode == LinkAuto) { if(iscgo && externalobj) linkmode = LinkExternal; else linkmode = LinkInternal; } if(linkmode == LinkInternal) { // Drop all the cgo_import_static declarations. // Turns out we won't be needing them. for(s = ctxt->allsym; s != S; s = s->allsym) if(s->type == SHOSTOBJ) { // If a symbol was marked both // cgo_import_static and cgo_import_dynamic, // then we want to make it cgo_import_dynamic // now. if(s->extname != nil && s->dynimplib != nil && s->cgoexport == 0) { s->type = SDYNIMPORT; } else s->type = 0; } } gmsym = linklookup(ctxt, "runtime.tlsgm", 0); gmsym->type = STLSBSS; gmsym->size = 2*PtrSize; gmsym->hide = 1; gmsym->reachable = 1; // Now that we know the link mode, trim the dynexp list. x = CgoExportDynamic; if(linkmode == LinkExternal) x = CgoExportStatic; w = 0; for(i=0; i<ndynexp; i++) if(dynexp[i]->cgoexport & x) dynexp[w++] = dynexp[i]; ndynexp = w; // In internal link mode, read the host object files. if(linkmode == LinkInternal) hostobjs(); else hostlinksetup(); // We've loaded all the code now. // If there are no dynamic libraries needed, gcc disables dynamic linking. // Because of this, glibc's dynamic ELF loader occasionally (like in version 2.13) // assumes that a dynamic binary always refers to at least one dynamic library. // Rather than be a source of test cases for glibc, disable dynamic linking // the same way that gcc would. // // Exception: on OS X, programs such as Shark only work with dynamic // binaries, so leave it enabled on OS X (Mach-O) binaries. // Also leave it enabled on Solaris which doesn't support // statically linked binaries. if(!flag_shared && !havedynamic && HEADTYPE != Hdarwin && HEADTYPE != Hsolaris) debug['d'] = 1; importcycles(); }
void asmbelf(vlong symo) { vlong a, o; vlong startva, resoff; ElfEhdr *eh; ElfPhdr *ph, *pph, *pnote; ElfShdr *sh; Section *sect; eh = getElfEhdr(); switch(thechar) { default: diag("unknown architecture in asmbelf"); errorexit(); case '5': eh->machine = EM_ARM; break; case '6': eh->machine = EM_X86_64; break; case '8': eh->machine = EM_386; break; } startva = INITTEXT - HEADR; resoff = ELFRESERVE; pph = nil; if(linkmode == LinkExternal) { /* skip program headers */ eh->phoff = 0; eh->phentsize = 0; goto elfobj; } /* program header info */ pph = newElfPhdr(); pph->type = PT_PHDR; pph->flags = PF_R; pph->off = eh->ehsize; pph->vaddr = INITTEXT - HEADR + pph->off; pph->paddr = INITTEXT - HEADR + pph->off; pph->align = INITRND; /* * PHDR must be in a loaded segment. Adjust the text * segment boundaries downwards to include it. * Except on NaCl where it must not be loaded. */ if(HEADTYPE != Hnacl) { o = segtext.vaddr - pph->vaddr; segtext.vaddr -= o; segtext.len += o; o = segtext.fileoff - pph->off; segtext.fileoff -= o; segtext.filelen += o; } if(!debug['d']) { /* interpreter */ sh = elfshname(".interp"); sh->type = SHT_PROGBITS; sh->flags = SHF_ALLOC; sh->addralign = 1; if(interpreter == nil) { switch(HEADTYPE) { case Hlinux: interpreter = linuxdynld; break; case Hfreebsd: interpreter = freebsddynld; break; case Hnetbsd: interpreter = netbsddynld; break; case Hopenbsd: interpreter = openbsddynld; break; case Hdragonfly: interpreter = dragonflydynld; break; case Hsolaris: interpreter = solarisdynld; break; } } resoff -= elfinterp(sh, startva, resoff, interpreter); ph = newElfPhdr(); ph->type = PT_INTERP; ph->flags = PF_R; phsh(ph, sh); } pnote = nil; if(HEADTYPE == Hnetbsd || HEADTYPE == Hopenbsd) { sh = nil; switch(HEADTYPE) { case Hnetbsd: sh = elfshname(".note.netbsd.ident"); resoff -= elfnetbsdsig(sh, startva, resoff); break; case Hopenbsd: sh = elfshname(".note.openbsd.ident"); resoff -= elfopenbsdsig(sh, startva, resoff); break; } pnote = newElfPhdr(); pnote->type = PT_NOTE; pnote->flags = PF_R; phsh(pnote, sh); } if(buildinfolen > 0) { sh = elfshname(".note.gnu.build-id"); resoff -= elfbuildinfo(sh, startva, resoff); if(pnote == nil) { pnote = newElfPhdr(); pnote->type = PT_NOTE; pnote->flags = PF_R; } phsh(pnote, sh); } // Additions to the reserved area must be above this line. USED(resoff); elfphload(&segtext); if(segrodata.sect != nil) elfphload(&segrodata); elfphload(&segdata); /* Dynamic linking sections */ if(!debug['d']) { /* -d suppresses dynamic loader format */ sh = elfshname(".dynsym"); sh->type = SHT_DYNSYM; sh->flags = SHF_ALLOC; if(elf64) sh->entsize = ELF64SYMSIZE; else sh->entsize = ELF32SYMSIZE; sh->addralign = RegSize; sh->link = elfshname(".dynstr")->shnum; // sh->info = index of first non-local symbol (number of local symbols) shsym(sh, linklookup(ctxt, ".dynsym", 0)); sh = elfshname(".dynstr"); sh->type = SHT_STRTAB; sh->flags = SHF_ALLOC; sh->addralign = 1; shsym(sh, linklookup(ctxt, ".dynstr", 0)); if(elfverneed) { sh = elfshname(".gnu.version"); sh->type = SHT_GNU_VERSYM; sh->flags = SHF_ALLOC; sh->addralign = 2; sh->link = elfshname(".dynsym")->shnum; sh->entsize = 2; shsym(sh, linklookup(ctxt, ".gnu.version", 0)); sh = elfshname(".gnu.version_r"); sh->type = SHT_GNU_VERNEED; sh->flags = SHF_ALLOC; sh->addralign = RegSize; sh->info = elfverneed; sh->link = elfshname(".dynstr")->shnum; shsym(sh, linklookup(ctxt, ".gnu.version_r", 0)); } switch(eh->machine) { case EM_X86_64: sh = elfshname(".rela.plt"); sh->type = SHT_RELA; sh->flags = SHF_ALLOC; sh->entsize = ELF64RELASIZE; sh->addralign = RegSize; sh->link = elfshname(".dynsym")->shnum; sh->info = elfshname(".plt")->shnum; shsym(sh, linklookup(ctxt, ".rela.plt", 0)); sh = elfshname(".rela"); sh->type = SHT_RELA; sh->flags = SHF_ALLOC; sh->entsize = ELF64RELASIZE; sh->addralign = 8; sh->link = elfshname(".dynsym")->shnum; shsym(sh, linklookup(ctxt, ".rela", 0)); break; default: sh = elfshname(".rel.plt"); sh->type = SHT_REL; sh->flags = SHF_ALLOC; sh->entsize = ELF32RELSIZE; sh->link = elfshname(".dynsym")->shnum; shsym(sh, linklookup(ctxt, ".rel.plt", 0)); sh = elfshname(".rel"); sh->type = SHT_REL; sh->flags = SHF_ALLOC; sh->entsize = ELF32RELSIZE; sh->addralign = 4; sh->link = elfshname(".dynsym")->shnum; shsym(sh, linklookup(ctxt, ".rel", 0)); break; } sh = elfshname(".plt"); sh->type = SHT_PROGBITS; sh->flags = SHF_ALLOC+SHF_EXECINSTR; if(eh->machine == EM_X86_64) sh->entsize = 16; else sh->entsize = 4; sh->addralign = 4; shsym(sh, linklookup(ctxt, ".plt", 0)); sh = elfshname(".got"); sh->type = SHT_PROGBITS; sh->flags = SHF_ALLOC+SHF_WRITE; sh->entsize = RegSize; sh->addralign = RegSize; shsym(sh, linklookup(ctxt, ".got", 0)); sh = elfshname(".got.plt"); sh->type = SHT_PROGBITS; sh->flags = SHF_ALLOC+SHF_WRITE; sh->entsize = RegSize; sh->addralign = RegSize; shsym(sh, linklookup(ctxt, ".got.plt", 0)); sh = elfshname(".hash"); sh->type = SHT_HASH; sh->flags = SHF_ALLOC; sh->entsize = 4; sh->addralign = RegSize; sh->link = elfshname(".dynsym")->shnum; shsym(sh, linklookup(ctxt, ".hash", 0)); /* sh and PT_DYNAMIC for .dynamic section */ sh = elfshname(".dynamic"); sh->type = SHT_DYNAMIC; sh->flags = SHF_ALLOC+SHF_WRITE; sh->entsize = 2*RegSize; sh->addralign = RegSize; sh->link = elfshname(".dynstr")->shnum; shsym(sh, linklookup(ctxt, ".dynamic", 0)); ph = newElfPhdr(); ph->type = PT_DYNAMIC; ph->flags = PF_R + PF_W; phsh(ph, sh); /* * Thread-local storage segment (really just size). */ // Do not emit PT_TLS for OpenBSD since ld.so(1) does // not currently support it. This is handled // appropriately in runtime/cgo. if(ctxt->tlsoffset != 0 && HEADTYPE != Hopenbsd) { ph = newElfPhdr(); ph->type = PT_TLS; ph->flags = PF_R; ph->memsz = -ctxt->tlsoffset; ph->align = RegSize; } } if(HEADTYPE == Hlinux) { ph = newElfPhdr(); ph->type = PT_GNU_STACK; ph->flags = PF_W+PF_R; ph->align = RegSize; ph = newElfPhdr(); ph->type = PT_PAX_FLAGS; ph->flags = 0x2a00; // mprotect, randexec, emutramp disabled ph->align = RegSize; } elfobj: sh = elfshname(".shstrtab"); sh->type = SHT_STRTAB; sh->addralign = 1; shsym(sh, linklookup(ctxt, ".shstrtab", 0)); eh->shstrndx = sh->shnum; // put these sections early in the list if(!debug['s']) { elfshname(".symtab"); elfshname(".strtab"); } for(sect=segtext.sect; sect!=nil; sect=sect->next) elfshbits(sect); for(sect=segrodata.sect; sect!=nil; sect=sect->next) elfshbits(sect); for(sect=segdata.sect; sect!=nil; sect=sect->next) elfshbits(sect); if(linkmode == LinkExternal) { for(sect=segtext.sect; sect!=nil; sect=sect->next) elfshreloc(sect); for(sect=segrodata.sect; sect!=nil; sect=sect->next) elfshreloc(sect); for(sect=segdata.sect; sect!=nil; sect=sect->next) elfshreloc(sect); // add a .note.GNU-stack section to mark the stack as non-executable sh = elfshname(".note.GNU-stack"); sh->type = SHT_PROGBITS; sh->addralign = 1; sh->flags = 0; } // generate .tbss section for dynamic internal linking (except for OpenBSD) // external linking generates .tbss in data.c if(linkmode == LinkInternal && !debug['d'] && HEADTYPE != Hopenbsd) { sh = elfshname(".tbss"); sh->type = SHT_NOBITS; sh->addralign = RegSize; sh->size = -ctxt->tlsoffset; sh->flags = SHF_ALLOC | SHF_TLS | SHF_WRITE; } if(!debug['s']) { sh = elfshname(".symtab"); sh->type = SHT_SYMTAB; sh->off = symo; sh->size = symsize; sh->addralign = RegSize; sh->entsize = 8+2*RegSize; sh->link = elfshname(".strtab")->shnum; sh->info = elfglobalsymndx; sh = elfshname(".strtab"); sh->type = SHT_STRTAB; sh->off = symo+symsize; sh->size = elfstrsize; sh->addralign = 1; dwarfaddelfheaders(); } /* Main header */ eh->ident[EI_MAG0] = '\177'; eh->ident[EI_MAG1] = 'E'; eh->ident[EI_MAG2] = 'L'; eh->ident[EI_MAG3] = 'F'; if(HEADTYPE == Hfreebsd) eh->ident[EI_OSABI] = ELFOSABI_FREEBSD; else if(HEADTYPE == Hnetbsd) eh->ident[EI_OSABI] = ELFOSABI_NETBSD; else if(HEADTYPE == Hopenbsd) eh->ident[EI_OSABI] = ELFOSABI_OPENBSD; else if(HEADTYPE == Hdragonfly) eh->ident[EI_OSABI] = ELFOSABI_NONE; if(elf64) eh->ident[EI_CLASS] = ELFCLASS64; else eh->ident[EI_CLASS] = ELFCLASS32; eh->ident[EI_DATA] = ELFDATA2LSB; eh->ident[EI_VERSION] = EV_CURRENT; if(linkmode == LinkExternal) eh->type = ET_REL; else eh->type = ET_EXEC; if(linkmode != LinkExternal) eh->entry = entryvalue(); eh->version = EV_CURRENT; if(pph != nil) { pph->filesz = eh->phnum * eh->phentsize; pph->memsz = pph->filesz; } cseek(0); a = 0; a += elfwritehdr(); a += elfwritephdrs(); a += elfwriteshdrs(); if(!debug['d']) a += elfwriteinterp(); if(linkmode != LinkExternal) { if(HEADTYPE == Hnetbsd) a += elfwritenetbsdsig(); if(HEADTYPE == Hopenbsd) a += elfwriteopenbsdsig(); if(buildinfolen > 0) a += elfwritebuildinfo(); } if(a > ELFRESERVE) diag("ELFRESERVE too small: %lld > %d", a, ELFRESERVE); }
void main(int argc, char *argv[]) { linkarchinit(); ctxt = linknew(thelinkarch); ctxt->thechar = thechar; ctxt->thestring = thestring; ctxt->diag = diag; ctxt->bso = &bso; Binit(&bso, 1, OWRITE); listinit(); memset(debug, 0, sizeof(debug)); nerrors = 0; outfile = nil; HEADTYPE = -1; INITTEXT = -1; INITDAT = -1; INITRND = -1; INITENTRY = 0; linkmode = LinkAuto; nuxiinit(); if(thechar == '5' && ctxt->goarm == 5) debug['F'] = 1; flagcount("1", "use alternate profiling code", &debug['1']); if(thechar == '6') flagcount("8", "assume 64-bit addresses", &debug['8']); flagfn1("B", "info: define ELF NT_GNU_BUILD_ID note", addbuildinfo); flagint64("D", "addr: data address", &INITDAT); flagstr("E", "sym: entry symbol", &INITENTRY); if(thechar == '5') flagcount("G", "debug pseudo-ops", &debug['G']); flagfn1("I", "interp: set ELF interp", setinterp); flagfn1("L", "dir: add dir to library path", Lflag); flagfn1("H", "head: header type", setheadtype); flagcount("K", "add stack underflow checks", &debug['K']); if(thechar == '5') flagcount("M", "disable software div/mod", &debug['M']); flagcount("O", "print pc-line tables", &debug['O']); flagcount("Q", "debug byte-register code gen", &debug['Q']); if(thechar == '5') flagcount("P", "debug code generation", &debug['P']); flagint32("R", "rnd: address rounding", &INITRND); flagcount("S", "check type signatures", &debug['S']); flagint64("T", "addr: text address", &INITTEXT); flagfn0("V", "print version and exit", doversion); flagcount("W", "disassemble input", &debug['W']); flagfn2("X", "name value: define string data", addstrdata); flagcount("Z", "clear stack frame on entry", &debug['Z']); flagcount("a", "disassemble output", &debug['a']); flagcount("c", "dump call graph", &debug['c']); flagcount("d", "disable dynamic executable", &debug['d']); flagstr("extld", "linker to run in external mode", &extld); flagstr("extldflags", "flags for external linker", &extldflags); flagcount("f", "ignore version mismatch", &debug['f']); flagcount("g", "disable go package data checks", &debug['g']); flagstr("installsuffix", "pkg directory suffix", &flag_installsuffix); flagstr("k", "sym: set field tracking symbol", &tracksym); flagfn1("linkmode", "mode: set link mode (internal, external, auto)", setlinkmode); flagcount("n", "dump symbol table", &debug['n']); flagstr("o", "outfile: set output file", &outfile); flagstr("r", "dir1:dir2:...: set ELF dynamic linker search path", &rpath); flagcount("race", "enable race detector", &flag_race); flagcount("s", "disable symbol table", &debug['s']); if(thechar == '5' || thechar == '6') flagcount("shared", "generate shared object (implies -linkmode external)", &flag_shared); flagstr("tmpdir", "leave temporary files in this directory", &tmpdir); flagcount("u", "reject unsafe packages", &debug['u']); flagcount("v", "print link trace", &debug['v']); flagcount("w", "disable DWARF generation", &debug['w']); flagparse(&argc, &argv, usage); ctxt->bso = &bso; ctxt->debugdivmod = debug['M']; ctxt->debugfloat = debug['F']; ctxt->debughist = debug['O']; ctxt->debugpcln = debug['O']; ctxt->debugread = debug['W']; ctxt->debugstack = debug['K']; ctxt->debugvlog = debug['v']; if(argc != 1) usage(); if(outfile == nil) { if(HEADTYPE == Hwindows) outfile = smprint("%c.out.exe", thechar); else outfile = smprint("%c.out", thechar); } libinit(); // creates outfile if(HEADTYPE == -1) HEADTYPE = headtype(goos); ctxt->headtype = HEADTYPE; if (headstring == nil) headstring = headstr(HEADTYPE); archinit(); ctxt->debugfloat = debug['F']; if(debug['v']) Bprint(&bso, "HEADER = -H%d -T0x%llux -D0x%llux -R0x%ux\n", HEADTYPE, INITTEXT, INITDAT, INITRND); Bflush(&bso); cbp = buf.cbuf; cbc = sizeof(buf.cbuf); addlibpath(ctxt, "command line", "command line", argv[0], "main"); loadlib(); if(thechar == '5') { // mark some functions that are only referenced after linker code editing if(debug['F']) mark(linkrlookup(ctxt, "_sfloat", 0)); mark(linklookup(ctxt, "runtime.read_tls_fallback", 0)); } deadcode(); callgraph(); paramspace = "SP"; /* (FP) now (SP) on output */ doelf(); if(HEADTYPE == Hdarwin) domacho(); dostkcheck(); if(HEADTYPE == Hwindows) dope(); addexport(); textaddress(); pclntab(); symtab(); dodata(); address(); doweak(); reloc(); asmb(); undef(); hostlink(); if(debug['v']) { Bprint(&bso, "%5.2f cpu time\n", cputime()); Bprint(&bso, "%d symbols\n", ctxt->nsymbol); Bprint(&bso, "%d sizeof adr\n", sizeof(Addr)); Bprint(&bso, "%d sizeof prog\n", sizeof(Prog)); Bprint(&bso, "%lld liveness data\n", liveness); } Bflush(&bso); errorexit(); }
// This is a simplified copy of linklinefmt above. // It doesn't allow printing the full stack, and it returns the file name and line number separately. // TODO: Unify with linklinefmt somehow. void linkgetline(Link *ctxt, int32 line, LSym **f, int32 *l) { struct { Hist* incl; /* start of this include file */ int32 idel; /* delta line number to apply to include */ Hist* line; /* start of this #line directive */ int32 ldel; /* delta line number to apply to #line */ } a[HISTSZ]; int32 lno, d, dlno; int n; Hist *h; char buf[1024], *file; lno = line; n = 0; for(h=ctxt->hist; h!=nil; h=h->link) { if(h->offset < 0) continue; if(lno < h->line) break; if(h->name) { if(h->offset > 0) { // #line directive if(n > 0 && n < HISTSZ) { a[n-1].line = h; a[n-1].ldel = h->line - h->offset + 1; } } else { // beginning of file if(n < HISTSZ) { a[n].incl = h; a[n].idel = h->line; a[n].line = 0; } n++; } continue; } n--; if(n > 0 && n < HISTSZ) { d = h->line - a[n].incl->line; a[n-1].ldel += d; a[n-1].idel += d; } } if(n > HISTSZ) n = HISTSZ; if(n <= 0) { *f = linklookup(ctxt, "??", HistVersion); *l = 0; return; } n--; if(a[n].line) { file = a[n].line->name; dlno = a[n].ldel-1; } else { file = a[n].incl->name; dlno = a[n].idel-1; } if((!ctxt->windows && file[0] == '/') || (ctxt->windows && file[1] == ':')) snprint(buf, sizeof buf, "%s", file); else snprint(buf, sizeof buf, "%s/%s", ctxt->pathname, file); lno -= dlno; *f = linklookup(ctxt, buf, HistVersion); *l = lno; }
void ldelf(Biobuf *f, char *pkg, int64 len, char *pn) { int32 base; uint64 add, info; char *name; int i, j, rela, is64, n, flag; uchar hdrbuf[64]; uchar *p; ElfHdrBytes *hdr; ElfObj *obj; ElfSect *sect, *rsect; ElfSym sym; Endian *e; Reloc *r, *rp; LSym *s; LSym **symbols; symbols = nil; if(debug['v']) Bprint(&bso, "%5.2f ldelf %s\n", cputime(), pn); ctxt->version++; base = Boffset(f); if(Bread(f, hdrbuf, sizeof hdrbuf) != sizeof hdrbuf) goto bad; hdr = (ElfHdrBytes*)hdrbuf; if(memcmp(hdr->ident, ElfMagic, 4) != 0) goto bad; switch(hdr->ident[5]) { case ElfDataLsb: e = ≤ break; case ElfDataMsb: e = &be; break; default: goto bad; } // read header obj = mal(sizeof *obj); obj->e = e; obj->f = f; obj->base = base; obj->len = len; obj->name = pn; is64 = 0; if(hdr->ident[4] == ElfClass64) { ElfHdrBytes64* hdr; is64 = 1; hdr = (ElfHdrBytes64*)hdrbuf; obj->type = e->e16(hdr->type); obj->machine = e->e16(hdr->machine); obj->version = e->e32(hdr->version); obj->phoff = e->e64(hdr->phoff); obj->shoff = e->e64(hdr->shoff); obj->flags = e->e32(hdr->flags); obj->ehsize = e->e16(hdr->ehsize); obj->phentsize = e->e16(hdr->phentsize); obj->phnum = e->e16(hdr->phnum); obj->shentsize = e->e16(hdr->shentsize); obj->shnum = e->e16(hdr->shnum); obj->shstrndx = e->e16(hdr->shstrndx); } else { obj->type = e->e16(hdr->type); obj->machine = e->e16(hdr->machine); obj->version = e->e32(hdr->version); obj->entry = e->e32(hdr->entry); obj->phoff = e->e32(hdr->phoff); obj->shoff = e->e32(hdr->shoff); obj->flags = e->e32(hdr->flags); obj->ehsize = e->e16(hdr->ehsize); obj->phentsize = e->e16(hdr->phentsize); obj->phnum = e->e16(hdr->phnum); obj->shentsize = e->e16(hdr->shentsize); obj->shnum = e->e16(hdr->shnum); obj->shstrndx = e->e16(hdr->shstrndx); } obj->is64 = is64; if(hdr->ident[6] != obj->version) goto bad; if(e->e16(hdr->type) != ElfTypeRelocatable) { diag("%s: elf but not elf relocatable object", pn); return; } switch(thechar) { default: diag("%s: elf %s unimplemented", pn, thestring); return; case '5': if(e != &le || obj->machine != ElfMachArm || hdr->ident[4] != ElfClass32) { diag("%s: elf object but not arm", pn); return; } break; case '6': if(e != &le || obj->machine != ElfMachAmd64 || hdr->ident[4] != ElfClass64) { diag("%s: elf object but not amd64", pn); return; } break; case '8': if(e != &le || obj->machine != ElfMach386 || hdr->ident[4] != ElfClass32) { diag("%s: elf object but not 386", pn); return; } break; case '9': if(obj->machine != ElfMachPower64 || hdr->ident[4] != ElfClass64) { diag("%s: elf object but not ppc64", pn); return; } break; } // load section list into memory. obj->sect = mal(obj->shnum*sizeof obj->sect[0]); obj->nsect = obj->shnum; for(i=0; i<obj->nsect; i++) { if(Bseek(f, base+obj->shoff+i*obj->shentsize, 0) < 0) goto bad; sect = &obj->sect[i]; if(is64) { ElfSectBytes64 b; werrstr("short read"); if(Bread(f, &b, sizeof b) != sizeof b) goto bad; sect->name = (char*)(uintptr)e->e32(b.name); sect->type = e->e32(b.type); sect->flags = e->e64(b.flags); sect->addr = e->e64(b.addr); sect->off = e->e64(b.off); sect->size = e->e64(b.size); sect->link = e->e32(b.link); sect->info = e->e32(b.info); sect->align = e->e64(b.align); sect->entsize = e->e64(b.entsize); } else { ElfSectBytes b; werrstr("short read"); if(Bread(f, &b, sizeof b) != sizeof b) goto bad; sect->name = (char*)(uintptr)e->e32(b.name); sect->type = e->e32(b.type); sect->flags = e->e32(b.flags); sect->addr = e->e32(b.addr); sect->off = e->e32(b.off); sect->size = e->e32(b.size); sect->link = e->e32(b.link); sect->info = e->e32(b.info); sect->align = e->e32(b.align); sect->entsize = e->e32(b.entsize); } } // read section string table and translate names if(obj->shstrndx >= obj->nsect) { werrstr("shstrndx out of range %d >= %d", obj->shstrndx, obj->nsect); goto bad; } sect = &obj->sect[obj->shstrndx]; if(map(obj, sect) < 0) goto bad; for(i=0; i<obj->nsect; i++) if(obj->sect[i].name != nil) obj->sect[i].name = (char*)sect->base + (uintptr)obj->sect[i].name; // load string table for symbols into memory. obj->symtab = section(obj, ".symtab"); if(obj->symtab == nil) { // our work is done here - no symbols means nothing can refer to this file return; } if(obj->symtab->link <= 0 || obj->symtab->link >= obj->nsect) { diag("%s: elf object has symbol table with invalid string table link", pn); return; } obj->symstr = &obj->sect[obj->symtab->link]; if(is64) obj->nsymtab = obj->symtab->size / sizeof(ElfSymBytes64); else obj->nsymtab = obj->symtab->size / sizeof(ElfSymBytes); if(map(obj, obj->symtab) < 0) goto bad; if(map(obj, obj->symstr) < 0) goto bad; // load text and data segments into memory. // they are not as small as the section lists, but we'll need // the memory anyway for the symbol images, so we might // as well use one large chunk. // create symbols for mapped sections for(i=0; i<obj->nsect; i++) { sect = &obj->sect[i]; if((sect->type != ElfSectProgbits && sect->type != ElfSectNobits) || !(sect->flags&ElfSectFlagAlloc)) continue; if(sect->type != ElfSectNobits && map(obj, sect) < 0) goto bad; name = smprint("%s(%s)", pkg, sect->name); s = linklookup(ctxt, name, ctxt->version); free(name); switch((int)sect->flags&(ElfSectFlagAlloc|ElfSectFlagWrite|ElfSectFlagExec)) { default: werrstr("unexpected flags for ELF section %s", sect->name); goto bad; case ElfSectFlagAlloc: s->type = SRODATA; break; case ElfSectFlagAlloc + ElfSectFlagWrite: if(sect->type == ElfSectNobits) s->type = SNOPTRBSS; else s->type = SNOPTRDATA; break; case ElfSectFlagAlloc + ElfSectFlagExec: s->type = STEXT; break; } if(strcmp(sect->name, ".got") == 0 || strcmp(sect->name, ".toc") == 0) s->type = SELFGOT; if(sect->type == ElfSectProgbits) { s->p = sect->base; s->np = sect->size; } s->size = sect->size; s->align = sect->align; sect->sym = s; } // enter sub-symbols into symbol table. // symbol 0 is the null symbol. symbols = malloc(obj->nsymtab * sizeof(symbols[0])); if(symbols == nil) { diag("out of memory"); errorexit(); } for(i=1; i<obj->nsymtab; i++) { if(readsym(obj, i, &sym, 1) < 0) goto bad; symbols[i] = sym.sym; if(sym.type != ElfSymTypeFunc && sym.type != ElfSymTypeObject && sym.type != ElfSymTypeNone) continue; if(sym.shndx == ElfSymShnCommon) { s = sym.sym; if(s->size < sym.size) s->size = sym.size; if(s->type == 0 || s->type == SXREF) s->type = SNOPTRBSS; continue; } if(sym.shndx >= obj->nsect || sym.shndx == 0) continue; // even when we pass needSym == 1 to readsym, it might still return nil to skip some unwanted symbols if(sym.sym == S) continue; sect = obj->sect+sym.shndx; if(sect->sym == nil) { if(strncmp(sym.name, ".Linfo_string", 13) == 0) // clang does this continue; diag("%s: sym#%d: ignoring %s in section %d (type %d)", pn, i, sym.name, sym.shndx, sym.type); continue; } s = sym.sym; 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->sub = sect->sym->sub; sect->sym->sub = s; s->type = sect->sym->type | (s->type&~SMASK) | SSUB; if(!(s->cgoexport & CgoExportDynamic)) s->dynimplib = nil; // satisfy dynimport s->value = sym.value; s->size = sym.size; s->outer = sect->sym; if(sect->sym->type == STEXT) { if(s->external && !s->dupok) diag("%s: duplicate definition of %s", pn, s->name); s->external = 1; } if(obj->machine == ElfMachPower64) { flag = sym.other >> 5; if(2 <= flag && flag <= 6) s->localentry = 1 << (flag - 2); else if(flag == 7) diag("%s: invalid sym.other 0x%x for %s", pn, sym.other, s->name); } }
void elfdynhash(void) { LSym *s, *sy, *dynstr; int i, j, nbucket, b, nfile; uint32 hc, *chain, *buckets; int nsym; char *name; Elfaux **need; Elflib *needlib; Elflib *l; Elfaux *x; if(!iself) return; nsym = nelfsym; s = linklookup(ctxt, ".hash", 0); s->type = SELFROSECT; s->reachable = 1; i = nsym; nbucket = 1; while(i > 0) { ++nbucket; i >>= 1; } needlib = nil; need = malloc(nsym * sizeof need[0]); chain = malloc(nsym * sizeof chain[0]); buckets = malloc(nbucket * sizeof buckets[0]); if(need == nil || chain == nil || buckets == nil) { ctxt->cursym = nil; diag("out of memory"); errorexit(); } memset(need, 0, nsym * sizeof need[0]); memset(chain, 0, nsym * sizeof chain[0]); memset(buckets, 0, nbucket * sizeof buckets[0]); for(sy=ctxt->allsym; sy!=S; sy=sy->allsym) { if (sy->dynid <= 0) continue; if(sy->dynimpvers) need[sy->dynid] = addelflib(&needlib, sy->dynimplib, sy->dynimpvers); name = sy->extname; hc = elfhash((uchar*)name); b = hc % nbucket; chain[sy->dynid] = buckets[b]; buckets[b] = sy->dynid; } adduint32(ctxt, s, nbucket); adduint32(ctxt, s, nsym); for(i = 0; i<nbucket; i++) adduint32(ctxt, s, buckets[i]); for(i = 0; i<nsym; i++) adduint32(ctxt, s, chain[i]); free(chain); free(buckets); // version symbols dynstr = linklookup(ctxt, ".dynstr", 0); s = linklookup(ctxt, ".gnu.version_r", 0); i = 2; nfile = 0; for(l=needlib; l; l=l->next) { nfile++; // header adduint16(ctxt, s, 1); // table version j = 0; for(x=l->aux; x; x=x->next) j++; adduint16(ctxt, s, j); // aux count adduint32(ctxt, s, addstring(dynstr, l->file)); // file string offset adduint32(ctxt, s, 16); // offset from header to first aux if(l->next) adduint32(ctxt, s, 16+j*16); // offset from this header to next else adduint32(ctxt, s, 0); for(x=l->aux; x; x=x->next) { x->num = i++; // aux struct adduint32(ctxt, s, elfhash((uchar*)x->vers)); // hash adduint16(ctxt, s, 0); // flags adduint16(ctxt, s, x->num); // other - index we refer to this by adduint32(ctxt, s, addstring(dynstr, x->vers)); // version string offset if(x->next) adduint32(ctxt, s, 16); // offset from this aux to next else adduint32(ctxt, s, 0); } } // version references s = linklookup(ctxt, ".gnu.version", 0); for(i=0; i<nsym; i++) { if(i == 0) adduint16(ctxt, s, 0); // first entry - no symbol else if(need[i] == nil) adduint16(ctxt, s, 1); // global else adduint16(ctxt, s, need[i]->num); } free(need); s = linklookup(ctxt, ".dynamic", 0); elfverneed = nfile; if(elfverneed) { elfwritedynentsym(s, DT_VERNEED, linklookup(ctxt, ".gnu.version_r", 0)); elfwritedynent(s, DT_VERNEEDNUM, nfile); elfwritedynentsym(s, DT_VERSYM, linklookup(ctxt, ".gnu.version", 0)); } if(thechar == '6') { sy = linklookup(ctxt, ".rela.plt", 0); if(sy->size > 0) { elfwritedynent(s, DT_PLTREL, DT_RELA); elfwritedynentsymsize(s, DT_PLTRELSZ, sy); elfwritedynentsym(s, DT_JMPREL, sy); } } else { sy = linklookup(ctxt, ".rel.plt", 0); if(sy->size > 0) { elfwritedynent(s, DT_PLTREL, DT_REL); elfwritedynentsymsize(s, DT_PLTRELSZ, sy); elfwritedynentsym(s, DT_JMPREL, sy); } } elfwritedynent(s, DT_NULL, 0); }
static void addimports(IMAGE_SECTION_HEADER *datsect) { IMAGE_SECTION_HEADER *isect; uvlong n, oftbase, ftbase; vlong startoff, endoff; Imp *m; Dll *d; LSym* dynamic; startoff = cpos(); dynamic = linklookup(ctxt, ".windynamic", 0); // skip import descriptor table (will write it later) n = 0; for(d = dr; d != nil; d = d->next) n++; cseek(startoff + sizeof(IMAGE_IMPORT_DESCRIPTOR) * (n + 1)); // write dll names for(d = dr; d != nil; d = d->next) { d->nameoff = cpos() - startoff; strput(d->name); } // write function names for(d = dr; d != nil; d = d->next) { for(m = d->ms; m != nil; m = m->next) { m->off = nextsectoff + cpos() - startoff; wputl(0); // hint strput(m->s->extname); } } // write OriginalFirstThunks oftbase = cpos() - startoff; n = cpos(); for(d = dr; d != nil; d = d->next) { d->thunkoff = cpos() - n; for(m = d->ms; m != nil; m = m->next) put(m->off); put(0); } // add pe section and pad it at the end n = cpos() - startoff; isect = addpesection(".idata", n, n); isect->Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA| IMAGE_SCN_MEM_READ|IMAGE_SCN_MEM_WRITE; chksectoff(isect, startoff); strnput("", isect->SizeOfRawData - n); endoff = cpos(); // write FirstThunks (allocated in .data section) ftbase = dynamic->value - datsect->VirtualAddress - PEBASE; cseek(datsect->PointerToRawData + ftbase); for(d = dr; d != nil; d = d->next) { for(m = d->ms; m != nil; m = m->next) put(m->off); put(0); } // finally write import descriptor table cseek(startoff); for(d = dr; d != nil; d = d->next) { lputl(isect->VirtualAddress + oftbase + d->thunkoff); lputl(0); lputl(0); lputl(isect->VirtualAddress + d->nameoff); lputl(datsect->VirtualAddress + ftbase + d->thunkoff); } lputl(0); //end lputl(0); lputl(0); lputl(0); lputl(0); // update data directory dd[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = isect->VirtualAddress; dd[IMAGE_DIRECTORY_ENTRY_IMPORT].Size = isect->VirtualSize; dd[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress = dynamic->value - PEBASE; dd[IMAGE_DIRECTORY_ENTRY_IAT].Size = dynamic->size; cseek(endoff); }
void adddynrel(LSym *s, Reloc *r) { LSym *targ, *rela, *got; targ = r->sym; ctxt->cursym = s; switch(r->type) { default: if(r->type >= 256) { diag("unexpected relocation type %d", r->type); return; } break; // Handle relocations found in ELF object files. case 256 + R_X86_64_PC32: if(targ->type == SDYNIMPORT) diag("unexpected R_X86_64_PC32 relocation for dynamic symbol %s", targ->name); if(targ->type == 0 || targ->type == SXREF) diag("unknown symbol %s in pcrel", targ->name); r->type = D_PCREL; r->add += 4; return; case 256 + R_X86_64_PLT32: r->type = D_PCREL; r->add += 4; if(targ->type == SDYNIMPORT) { addpltsym(targ); r->sym = linklookup(ctxt, ".plt", 0); r->add += targ->plt; } return; case 256 + R_X86_64_GOTPCREL: if(targ->type != SDYNIMPORT) { // have symbol if(r->off >= 2 && s->p[r->off-2] == 0x8b) { // turn MOVQ of GOT entry into LEAQ of symbol itself s->p[r->off-2] = 0x8d; r->type = D_PCREL; r->add += 4; return; } // fall back to using GOT and hope for the best (CMOV*) // TODO: just needs relocation, no need to put in .dynsym } addgotsym(targ); r->type = D_PCREL; r->sym = linklookup(ctxt, ".got", 0); r->add += 4; r->add += targ->got; return; case 256 + R_X86_64_64: if(targ->type == SDYNIMPORT) diag("unexpected R_X86_64_64 relocation for dynamic symbol %s", targ->name); r->type = D_ADDR; return; // Handle relocations found in Mach-O object files. case 512 + MACHO_X86_64_RELOC_UNSIGNED*2 + 0: case 512 + MACHO_X86_64_RELOC_SIGNED*2 + 0: case 512 + MACHO_X86_64_RELOC_BRANCH*2 + 0: // TODO: What is the difference between all these? r->type = D_ADDR; if(targ->type == SDYNIMPORT) diag("unexpected reloc for dynamic symbol %s", targ->name); return; case 512 + MACHO_X86_64_RELOC_BRANCH*2 + 1: if(targ->type == SDYNIMPORT) { addpltsym(targ); r->sym = linklookup(ctxt, ".plt", 0); r->add = targ->plt; r->type = D_PCREL; return; } // fall through case 512 + MACHO_X86_64_RELOC_UNSIGNED*2 + 1: case 512 + MACHO_X86_64_RELOC_SIGNED*2 + 1: case 512 + MACHO_X86_64_RELOC_SIGNED_1*2 + 1: case 512 + MACHO_X86_64_RELOC_SIGNED_2*2 + 1: case 512 + MACHO_X86_64_RELOC_SIGNED_4*2 + 1: r->type = D_PCREL; if(targ->type == SDYNIMPORT) diag("unexpected pc-relative reloc for dynamic symbol %s", targ->name); return; case 512 + MACHO_X86_64_RELOC_GOT_LOAD*2 + 1: if(targ->type != SDYNIMPORT) { // have symbol // turn MOVQ of GOT entry into LEAQ of symbol itself if(r->off < 2 || s->p[r->off-2] != 0x8b) { diag("unexpected GOT_LOAD reloc for non-dynamic symbol %s", targ->name); return; } s->p[r->off-2] = 0x8d; r->type = D_PCREL; return; } // fall through case 512 + MACHO_X86_64_RELOC_GOT*2 + 1: if(targ->type != SDYNIMPORT) diag("unexpected GOT reloc for non-dynamic symbol %s", targ->name); addgotsym(targ); r->type = D_PCREL; r->sym = linklookup(ctxt, ".got", 0); r->add += targ->got; return; } // Handle references to ELF symbols from our own object files. if(targ->type != SDYNIMPORT) return; switch(r->type) { case D_PCREL: addpltsym(targ); r->sym = linklookup(ctxt, ".plt", 0); r->add = targ->plt; return; case D_ADDR: if(s->type == STEXT && iself) { // The code is asking for the address of an external // function. We provide it with the address of the // correspondent GOT symbol. addgotsym(targ); r->sym = linklookup(ctxt, ".got", 0); r->add += targ->got; return; } if(s->type != SDATA) break; if(iself) { adddynsym(ctxt, targ); rela = linklookup(ctxt, ".rela", 0); addaddrplus(ctxt, rela, s, r->off); if(r->siz == 8) adduint64(ctxt, rela, ELF64_R_INFO(targ->dynid, R_X86_64_64)); else adduint64(ctxt, rela, ELF64_R_INFO(targ->dynid, R_X86_64_32)); adduint64(ctxt, rela, r->add); r->type = 256; // ignore during relocsym return; } if(HEADTYPE == Hdarwin && s->size == PtrSize && r->off == 0) { // Mach-O relocations are a royal pain to lay out. // They use a compact stateful bytecode representation // that is too much bother to deal with. // Instead, interpret the C declaration // void *_Cvar_stderr = &stderr; // as making _Cvar_stderr the name of a GOT entry // for stderr. This is separate from the usual GOT entry, // just in case the C code assigns to the variable, // and of course it only works for single pointers, // but we only need to support cgo and that's all it needs. adddynsym(ctxt, targ); got = linklookup(ctxt, ".got", 0); s->type = got->type | SSUB; s->outer = got; s->sub = got->sub; got->sub = s; s->value = got->size; adduint64(ctxt, got, 0); adduint32(ctxt, linklookup(ctxt, ".linkedit.got", 0), targ->dynid); r->type = 256; // ignore during relocsym return; } break; } ctxt->cursym = s; diag("unsupported relocation for dynamic symbol %s (type=%d stype=%d)", targ->name, r->type, targ->type); }
void asmb(void) { int32 magic; int i; vlong vl, symo, dwarfoff, machlink; Section *sect; LSym *sym; if(debug['v']) Bprint(&bso, "%5.2f asmb\n", cputime()); Bflush(&bso); if(debug['v']) Bprint(&bso, "%5.2f codeblk\n", cputime()); Bflush(&bso); if(iself) asmbelfsetup(); sect = segtext.sect; cseek(sect->vaddr - segtext.vaddr + segtext.fileoff); codeblk(sect->vaddr, sect->len); for(sect = sect->next; sect != nil; sect = sect->next) { cseek(sect->vaddr - segtext.vaddr + segtext.fileoff); datblk(sect->vaddr, sect->len); } if(segrodata.filelen > 0) { if(debug['v']) Bprint(&bso, "%5.2f rodatblk\n", cputime()); Bflush(&bso); cseek(segrodata.fileoff); datblk(segrodata.vaddr, segrodata.filelen); } if(debug['v']) Bprint(&bso, "%5.2f datblk\n", cputime()); Bflush(&bso); cseek(segdata.fileoff); datblk(segdata.vaddr, segdata.filelen); machlink = 0; if(HEADTYPE == Hdarwin) { if(debug['v']) Bprint(&bso, "%5.2f dwarf\n", cputime()); dwarfoff = rnd(HEADR+segtext.len, INITRND) + rnd(segdata.filelen, INITRND); cseek(dwarfoff); segdwarf.fileoff = cpos(); dwarfemitdebugsections(); segdwarf.filelen = cpos() - segdwarf.fileoff; machlink = domacholink(); } switch(HEADTYPE) { default: diag("unknown header type %d", HEADTYPE); case Hplan9: case Helf: break; case Hdarwin: debug['8'] = 1; /* 64-bit addresses */ break; case Hlinux: case Hfreebsd: case Hnetbsd: case Hopenbsd: case Hdragonfly: case Hsolaris: debug['8'] = 1; /* 64-bit addresses */ break; case Hwindows: break; } symsize = 0; spsize = 0; lcsize = 0; symo = 0; if(!debug['s']) { if(debug['v']) Bprint(&bso, "%5.2f sym\n", cputime()); Bflush(&bso); switch(HEADTYPE) { default: case Hplan9: case Helf: debug['s'] = 1; symo = HEADR+segtext.len+segdata.filelen; break; case Hdarwin: symo = rnd(HEADR+segtext.len, INITRND)+rnd(segdata.filelen, INITRND)+machlink; break; case Hlinux: case Hfreebsd: case Hnetbsd: case Hopenbsd: case Hdragonfly: case Hsolaris: symo = rnd(HEADR+segtext.len, INITRND)+rnd(segrodata.len, INITRND)+segdata.filelen; symo = rnd(symo, INITRND); break; case Hwindows: symo = rnd(HEADR+segtext.filelen, PEFILEALIGN)+segdata.filelen; symo = rnd(symo, PEFILEALIGN); break; } cseek(symo); switch(HEADTYPE) { default: if(iself) { cseek(symo); asmelfsym(); cflush(); cwrite(elfstrdat, elfstrsize); if(debug['v']) Bprint(&bso, "%5.2f dwarf\n", cputime()); dwarfemitdebugsections(); if(linkmode == LinkExternal) elfemitreloc(); } break; case Hplan9: asmplan9sym(); cflush(); sym = linklookup(ctxt, "pclntab", 0); if(sym != nil) { lcsize = sym->np; for(i=0; i < lcsize; i++) cput(sym->p[i]); cflush(); } break; case Hwindows: if(debug['v']) Bprint(&bso, "%5.2f dwarf\n", cputime()); dwarfemitdebugsections(); break; case Hdarwin: if(linkmode == LinkExternal) machoemitreloc(); break; } } if(debug['v']) Bprint(&bso, "%5.2f headr\n", cputime()); Bflush(&bso); cseek(0L); switch(HEADTYPE) { default: case Hplan9: /* plan9 */ magic = 4*26*26+7; magic |= 0x00008000; /* fat header */ lputb(magic); /* magic */ lputb(segtext.filelen); /* sizes */ lputb(segdata.filelen); lputb(segdata.len - segdata.filelen); lputb(symsize); /* nsyms */ vl = entryvalue(); lputb(PADDR(vl)); /* va of entry */ lputb(spsize); /* sp offsets */ lputb(lcsize); /* line offsets */ vputb(vl); /* va of entry */ break; case Hdarwin: asmbmacho(); break; case Hlinux: case Hfreebsd: case Hnetbsd: case Hopenbsd: case Hdragonfly: case Hsolaris: asmbelf(symo); break; case Hwindows: asmbpe(); break; } cflush(); }
void doelf(void) { LSym *s, *shstrtab, *dynstr; if(!iself) return; /* predefine strings we need for section headers */ shstrtab = linklookup(ctxt, ".shstrtab", 0); shstrtab->type = SELFROSECT; shstrtab->reachable = 1; addstring(shstrtab, ""); addstring(shstrtab, ".text"); addstring(shstrtab, ".noptrdata"); addstring(shstrtab, ".data"); addstring(shstrtab, ".bss"); addstring(shstrtab, ".noptrbss"); // generate .tbss section (except for OpenBSD where it's not supported) // for dynamic internal linker or external linking, so that various // binutils could correctly calculate PT_TLS size. // see http://golang.org/issue/5200. if(HEADTYPE != Hopenbsd) if(!debug['d'] || linkmode == LinkExternal) addstring(shstrtab, ".tbss"); if(HEADTYPE == Hnetbsd) addstring(shstrtab, ".note.netbsd.ident"); if(HEADTYPE == Hopenbsd) addstring(shstrtab, ".note.openbsd.ident"); if(buildinfolen > 0) addstring(shstrtab, ".note.gnu.build-id"); addstring(shstrtab, ".elfdata"); addstring(shstrtab, ".rodata"); addstring(shstrtab, ".typelink"); addstring(shstrtab, ".gosymtab"); addstring(shstrtab, ".gopclntab"); if(linkmode == LinkExternal) { debug_s = debug['s']; debug['s'] = 0; debug['d'] = 1; if(thechar == '6') { addstring(shstrtab, ".rela.text"); addstring(shstrtab, ".rela.rodata"); addstring(shstrtab, ".rela.typelink"); addstring(shstrtab, ".rela.gosymtab"); addstring(shstrtab, ".rela.gopclntab"); addstring(shstrtab, ".rela.noptrdata"); addstring(shstrtab, ".rela.data"); } else { addstring(shstrtab, ".rel.text"); addstring(shstrtab, ".rel.rodata"); addstring(shstrtab, ".rel.typelink"); addstring(shstrtab, ".rel.gosymtab"); addstring(shstrtab, ".rel.gopclntab"); addstring(shstrtab, ".rel.noptrdata"); addstring(shstrtab, ".rel.data"); } // add a .note.GNU-stack section to mark the stack as non-executable addstring(shstrtab, ".note.GNU-stack"); } if(flag_shared) { addstring(shstrtab, ".init_array"); if(thechar == '6') addstring(shstrtab, ".rela.init_array"); else addstring(shstrtab, ".rel.init_array"); } if(!debug['s']) { addstring(shstrtab, ".symtab"); addstring(shstrtab, ".strtab"); dwarfaddshstrings(shstrtab); } addstring(shstrtab, ".shstrtab"); if(!debug['d']) { /* -d suppresses dynamic loader format */ addstring(shstrtab, ".interp"); addstring(shstrtab, ".hash"); addstring(shstrtab, ".got"); addstring(shstrtab, ".got.plt"); addstring(shstrtab, ".dynamic"); addstring(shstrtab, ".dynsym"); addstring(shstrtab, ".dynstr"); if(thechar == '6') { addstring(shstrtab, ".rela"); addstring(shstrtab, ".rela.plt"); } else { addstring(shstrtab, ".rel"); addstring(shstrtab, ".rel.plt"); } addstring(shstrtab, ".plt"); addstring(shstrtab, ".gnu.version"); addstring(shstrtab, ".gnu.version_r"); /* dynamic symbol table - first entry all zeros */ s = linklookup(ctxt, ".dynsym", 0); s->type = SELFROSECT; s->reachable = 1; if(thechar == '6') s->size += ELF64SYMSIZE; else s->size += ELF32SYMSIZE; /* dynamic string table */ s = linklookup(ctxt, ".dynstr", 0); s->type = SELFROSECT; s->reachable = 1; if(s->size == 0) addstring(s, ""); dynstr = s; /* relocation table */ if(thechar == '6') s = linklookup(ctxt, ".rela", 0); else s = linklookup(ctxt, ".rel", 0); s->reachable = 1; s->type = SELFROSECT; /* global offset table */ s = linklookup(ctxt, ".got", 0); s->reachable = 1; s->type = SELFSECT; // writable /* hash */ s = linklookup(ctxt, ".hash", 0); s->reachable = 1; s->type = SELFROSECT; s = linklookup(ctxt, ".got.plt", 0); s->reachable = 1; s->type = SELFSECT; // writable s = linklookup(ctxt, ".plt", 0); s->reachable = 1; s->type = SELFRXSECT; elfsetupplt(); if(thechar == '6') s = linklookup(ctxt, ".rela.plt", 0); else s = linklookup(ctxt, ".rel.plt", 0); s->reachable = 1; s->type = SELFROSECT; s = linklookup(ctxt, ".gnu.version", 0); s->reachable = 1; s->type = SELFROSECT; s = linklookup(ctxt, ".gnu.version_r", 0); s->reachable = 1; s->type = SELFROSECT; /* define dynamic elf table */ s = linklookup(ctxt, ".dynamic", 0); s->reachable = 1; s->type = SELFSECT; // writable /* * .dynamic table */ elfwritedynentsym(s, DT_HASH, linklookup(ctxt, ".hash", 0)); elfwritedynentsym(s, DT_SYMTAB, linklookup(ctxt, ".dynsym", 0)); if(thechar == '6') elfwritedynent(s, DT_SYMENT, ELF64SYMSIZE); else elfwritedynent(s, DT_SYMENT, ELF32SYMSIZE); elfwritedynentsym(s, DT_STRTAB, linklookup(ctxt, ".dynstr", 0)); elfwritedynentsymsize(s, DT_STRSZ, linklookup(ctxt, ".dynstr", 0)); if(thechar == '6') { elfwritedynentsym(s, DT_RELA, linklookup(ctxt, ".rela", 0)); elfwritedynentsymsize(s, DT_RELASZ, linklookup(ctxt, ".rela", 0)); elfwritedynent(s, DT_RELAENT, ELF64RELASIZE); } else { elfwritedynentsym(s, DT_REL, linklookup(ctxt, ".rel", 0)); elfwritedynentsymsize(s, DT_RELSZ, linklookup(ctxt, ".rel", 0)); elfwritedynent(s, DT_RELENT, ELF32RELSIZE); } if(rpath) elfwritedynent(s, DT_RUNPATH, addstring(dynstr, rpath)); elfwritedynentsym(s, DT_PLTGOT, linklookup(ctxt, ".got.plt", 0)); // Solaris dynamic linker can't handle an empty .rela.plt if // DT_JMPREL is emitted so we have to defer generation of DT_PLTREL, // DT_PLTRELSZ, and DT_JMPREL dynamic entries until after we know the // size of .rel(a).plt section. elfwritedynent(s, DT_DEBUG, 0); // Do not write DT_NULL. elfdynhash will finish it. } }
void ldelf(Biobuf *f, char *pkg, int64 len, char *pn) { int32 base; uint64 add, info; char *name; int i, j, rela, is64, n; uchar hdrbuf[64]; uchar *p; ElfHdrBytes *hdr; ElfObj *obj; ElfSect *sect, *rsect; ElfSym sym; Endian *e; Reloc *r, *rp; LSym *s; LSym **symbols; symbols = nil; if(debug['v']) Bprint(&bso, "%5.2f ldelf %s\n", cputime(), pn); ctxt->version++; base = Boffset(f); if(Bread(f, hdrbuf, sizeof hdrbuf) != sizeof hdrbuf) goto bad; hdr = (ElfHdrBytes*)hdrbuf; if(memcmp(hdr->ident, ElfMagic, 4) != 0) goto bad; switch(hdr->ident[5]) { case ElfDataLsb: e = ≤ break; case ElfDataMsb: e = &be; break; default: goto bad; } // read header obj = mal(sizeof *obj); obj->e = e; obj->f = f; obj->base = base; obj->len = len; obj->name = pn; is64 = 0; if(hdr->ident[4] == ElfClass64) { ElfHdrBytes64* hdr; is64 = 1; hdr = (ElfHdrBytes64*)hdrbuf; obj->type = e->e16(hdr->type); obj->machine = e->e16(hdr->machine); obj->version = e->e32(hdr->version); obj->phoff = e->e64(hdr->phoff); obj->shoff = e->e64(hdr->shoff); obj->flags = e->e32(hdr->flags); obj->ehsize = e->e16(hdr->ehsize); obj->phentsize = e->e16(hdr->phentsize); obj->phnum = e->e16(hdr->phnum); obj->shentsize = e->e16(hdr->shentsize); obj->shnum = e->e16(hdr->shnum); obj->shstrndx = e->e16(hdr->shstrndx); } else { obj->type = e->e16(hdr->type); obj->machine = e->e16(hdr->machine); obj->version = e->e32(hdr->version); obj->entry = e->e32(hdr->entry); obj->phoff = e->e32(hdr->phoff); obj->shoff = e->e32(hdr->shoff); obj->flags = e->e32(hdr->flags); obj->ehsize = e->e16(hdr->ehsize); obj->phentsize = e->e16(hdr->phentsize); obj->phnum = e->e16(hdr->phnum); obj->shentsize = e->e16(hdr->shentsize); obj->shnum = e->e16(hdr->shnum); obj->shstrndx = e->e16(hdr->shstrndx); } obj->is64 = is64; if(hdr->ident[6] != obj->version) goto bad; if(e->e16(hdr->type) != ElfTypeRelocatable) { diag("%s: elf but not elf relocatable object", pn); return; } switch(thechar) { default: diag("%s: elf %s unimplemented", pn, thestring); return; case '5': if(e != &le || obj->machine != ElfMachArm || hdr->ident[4] != ElfClass32) { diag("%s: elf object but not arm", pn); return; } break; case '6': if(e != &le || obj->machine != ElfMachAmd64 || hdr->ident[4] != ElfClass64) { diag("%s: elf object but not amd64", pn); return; } break; case '8': if(e != &le || obj->machine != ElfMach386 || hdr->ident[4] != ElfClass32) { diag("%s: elf object but not 386", pn); return; } break; } // load section list into memory. obj->sect = mal(obj->shnum*sizeof obj->sect[0]); obj->nsect = obj->shnum; for(i=0; i<obj->nsect; i++) { if(Bseek(f, base+obj->shoff+i*obj->shentsize, 0) < 0) goto bad; sect = &obj->sect[i]; if(is64) { ElfSectBytes64 b; werrstr("short read"); if(Bread(f, &b, sizeof b) != sizeof b) goto bad; sect->name = (char*)(uintptr)e->e32(b.name); sect->type = e->e32(b.type); sect->flags = e->e64(b.flags); sect->addr = e->e64(b.addr); sect->off = e->e64(b.off); sect->size = e->e64(b.size); sect->link = e->e32(b.link); sect->info = e->e32(b.info); sect->align = e->e64(b.align); sect->entsize = e->e64(b.entsize); } else { ElfSectBytes b; werrstr("short read"); if(Bread(f, &b, sizeof b) != sizeof b) goto bad; sect->name = (char*)(uintptr)e->e32(b.name); sect->type = e->e32(b.type); sect->flags = e->e32(b.flags); sect->addr = e->e32(b.addr); sect->off = e->e32(b.off); sect->size = e->e32(b.size); sect->link = e->e32(b.link); sect->info = e->e32(b.info); sect->align = e->e32(b.align); sect->entsize = e->e32(b.entsize); } } // read section string table and translate names if(obj->shstrndx >= obj->nsect) { werrstr("shstrndx out of range %d >= %d", obj->shstrndx, obj->nsect); goto bad; } sect = &obj->sect[obj->shstrndx]; if(map(obj, sect) < 0) goto bad; for(i=0; i<obj->nsect; i++) if(obj->sect[i].name != nil) obj->sect[i].name = (char*)sect->base + (uintptr)obj->sect[i].name; // load string table for symbols into memory. obj->symtab = section(obj, ".symtab"); if(obj->symtab == nil) { // our work is done here - no symbols means nothing can refer to this file return; } if(obj->symtab->link <= 0 || obj->symtab->link >= obj->nsect) { diag("%s: elf object has symbol table with invalid string table link", pn); return; } obj->symstr = &obj->sect[obj->symtab->link]; if(is64) obj->nsymtab = obj->symtab->size / sizeof(ElfSymBytes64); else obj->nsymtab = obj->symtab->size / sizeof(ElfSymBytes); if(map(obj, obj->symtab) < 0) goto bad; if(map(obj, obj->symstr) < 0) goto bad; // load text and data segments into memory. // they are not as small as the section lists, but we'll need // the memory anyway for the symbol images, so we might // as well use one large chunk. // create symbols for mapped sections for(i=0; i<obj->nsect; i++) { sect = &obj->sect[i]; if((sect->type != ElfSectProgbits && sect->type != ElfSectNobits) || !(sect->flags&ElfSectFlagAlloc)) continue; if(sect->type != ElfSectNobits && map(obj, sect) < 0) goto bad; name = smprint("%s(%s)", pkg, sect->name); s = linklookup(ctxt, name, ctxt->version); free(name); switch((int)sect->flags&(ElfSectFlagAlloc|ElfSectFlagWrite|ElfSectFlagExec)) { default: werrstr("unexpected flags for ELF section %s", sect->name); goto bad; case ElfSectFlagAlloc: s->type = SRODATA; break; case ElfSectFlagAlloc + ElfSectFlagWrite: s->type = SNOPTRDATA; break; case ElfSectFlagAlloc + ElfSectFlagExec: s->type = STEXT; break; } if(sect->type == ElfSectProgbits) { s->p = sect->base; s->np = sect->size; } s->size = sect->size; s->align = sect->align; sect->sym = s; } // enter sub-symbols into symbol table. // symbol 0 is the null symbol. symbols = malloc(obj->nsymtab * sizeof(symbols[0])); if(symbols == nil) { diag("out of memory"); errorexit(); } for(i=1; i<obj->nsymtab; i++) { if(readsym(obj, i, &sym, 1) < 0) goto bad; symbols[i] = sym.sym; if(sym.type != ElfSymTypeFunc && sym.type != ElfSymTypeObject && sym.type != ElfSymTypeNone) continue; if(sym.shndx == ElfSymShnCommon) { s = sym.sym; if(s->size < sym.size) s->size = sym.size; if(s->type == 0 || s->type == SXREF) s->type = SNOPTRBSS; continue; } if(sym.shndx >= obj->nsect || sym.shndx == 0) continue; // even when we pass needSym == 1 to readsym, it might still return nil to skip some unwanted symbols if(sym.sym == S) continue; sect = obj->sect+sym.shndx; if(sect->sym == nil) { if(strncmp(sym.name, ".Linfo_string", 13) == 0) // clang does this continue; diag("%s: sym#%d: ignoring %s in section %d (type %d)", pn, i, sym.name, sym.shndx, sym.type); continue; } s = sym.sym; 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->sub = sect->sym->sub; sect->sym->sub = s; s->type = sect->sym->type | (s->type&~SMASK) | SSUB; if(!(s->cgoexport & CgoExportDynamic)) s->dynimplib = nil; // satisfy dynimport s->value = sym.value; s->size = sym.size; s->outer = sect->sym; if(sect->sym->type == STEXT) { if(s->external && !s->dupok) diag("%s: duplicate definition of %s", pn, s->name); s->external = 1; } } // Sort outer lists by address, adding to textp. // This keeps textp in increasing address order. for(i=0; i<obj->nsect; i++) { s = obj->sect[i].sym; if(s == S) continue; if(s->sub) s->sub = listsort(s->sub, valuecmp, offsetof(LSym, sub)); 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(s = s->sub; s != S; s = s->sub) { if(s->onlist) sysfatal("symbol %s listed multiple times", s->name); s->onlist = 1; ctxt->etextp->next = s; ctxt->etextp = s; } } } // load relocations for(i=0; i<obj->nsect; i++) { rsect = &obj->sect[i]; if(rsect->type != ElfSectRela && rsect->type != ElfSectRel) continue; if(rsect->info >= obj->nsect || obj->sect[rsect->info].base == nil) continue; sect = &obj->sect[rsect->info]; if(map(obj, rsect) < 0) goto bad; rela = rsect->type == ElfSectRela; n = rsect->size/(4+4*is64)/(2+rela); r = mal(n*sizeof r[0]); p = rsect->base; for(j=0; j<n; j++) { add = 0; rp = &r[j]; if(is64) { // 64-bit rel/rela rp->off = e->e64(p); p += 8; info = e->e64(p); p += 8; if(rela) { add = e->e64(p); p += 8; } } else { // 32-bit rel/rela rp->off = e->e32(p); p += 4; info = e->e32(p); info = info>>8<<32 | (info&0xff); // convert to 64-bit info p += 4; if(rela) { add = e->e32(p); p += 4; } } if((info & 0xffffffff) == 0) { // skip R_*_NONE relocation j--; n--; continue; } if((info >> 32) == 0) { // absolute relocation, don't bother reading the null symbol rp->sym = S; } else { if(readsym(obj, info>>32, &sym, 0) < 0) goto bad; sym.sym = symbols[info>>32]; if(sym.sym == nil) { werrstr("%s#%d: reloc of invalid sym #%d %s shndx=%d type=%d", sect->sym->name, j, (int)(info>>32), sym.name, sym.shndx, sym.type); goto bad; } rp->sym = sym.sym; } rp->type = reltype(pn, (uint32)info, &rp->siz); if(rela) rp->add = add; else { // load addend from image if(rp->siz == 4) rp->add = e->e32(sect->base+rp->off); else if(rp->siz == 8) rp->add = e->e64(sect->base+rp->off); else diag("invalid rela size %d", rp->siz); } if(rp->siz == 4) rp->add = (int32)rp->add; //print("rel %s %d %d %s %#llx\n", sect->sym->name, rp->type, rp->siz, rp->sym->name, rp->add); }