// Inferno utils/6l/asm.c // http://code.google.com/p/inferno-os/source/browse/utils/6l/asm.c // // Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. // Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) // Portions Copyright © 1997-1999 Vita Nuova Limited // Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) // Portions Copyright © 2004,2006 Bruce Ellis // Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) // Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others // Portions Copyright © 2009 The Go Authors. All rights reserved. // // 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. // Writing object files. #include "l.h" #include "../ld/lib.h" #include "../ld/elf.h" #include "../ld/dwarf.h" #include "../ld/macho.h" #include "../ld/pe.h" #define Dbufslop 100 #define PADDR(a) ((uint32)(a) & ~0x80000000) char linuxdynld[] = "/lib64/ld-linux-x86-64.so.2"; char freebsddynld[] = "/libexec/ld-elf.so.1"; char zeroes[32]; vlong entryvalue(void) { char *a; Sym *s; a = INITENTRY; if(*a >= '0' && *a <= '9') return atolwhex(a); s = lookup(a, 0); if(s->type == 0) return INITTEXT; if(s->type != STEXT) diag("entry not text: %s", s->name); return s->value; } void wputl(uint16 w) { cput(w); cput(w>>8); } void wputb(uint16 w) { cput(w>>8); cput(w); } void lputb(int32 l) { cput(l>>24); cput(l>>16); cput(l>>8); cput(l); } void vputb(uint64 v) { lputb(v>>32); lputb(v); } void lputl(int32 l) { cput(l); cput(l>>8); cput(l>>16); cput(l>>24); } void vputl(uint64 v) { lputl(v); lputl(v>>32); } vlong datoff(vlong addr) { if(addr >= segdata.vaddr) return addr - segdata.vaddr + segdata.fileoff; if(addr >= segtext.vaddr) return addr - segtext.vaddr + segtext.fileoff; diag("datoff %#llx", addr); return 0; } enum { ElfStrEmpty, ElfStrInterp, ElfStrHash, ElfStrGot, ElfStrGotPlt, ElfStrDynamic, ElfStrDynsym, ElfStrDynstr, ElfStrRela, ElfStrText, ElfStrData, ElfStrBss, ElfStrGosymcounts, ElfStrGosymtab, ElfStrGopclntab, ElfStrShstrtab, ElfStrSymtab, ElfStrStrtab, ElfStrRelaPlt, ElfStrPlt, NElfStr }; vlong elfstr[NElfStr]; static int needlib(char *name) { char *p; Sym *s; /* reuse hash code in symbol table */ p = smprint(".elfload.%s", name); s = lookup(p, 0); if(s->type == 0) { s->type = 100; // avoid SDATA, etc. return 1; } return 0; } int nelfsym = 1; static void addpltsym(Sym*); static void addgotsym(Sym*); void adddynrel(Sym *s, Reloc *r) { Sym *targ, *rela, *got; targ = r->sym; 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->dynimpname != nil && !targ->dynexport) 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->dynimpname != nil && !targ->dynexport) { addpltsym(targ); r->sym = lookup(".plt", 0); r->add += targ->plt; } return; case 256 + R_X86_64_GOTPCREL: if(targ->dynimpname == nil || targ->dynexport) { // 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 targ->dynimpname = targ->name; } addgotsym(targ); r->type = D_PCREL; r->sym = lookup(".got", 0); r->add += 4; r->add += targ->got; return; case 256 + R_X86_64_64: if(targ->dynimpname != nil && !targ->dynexport) 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->dynimpname != nil && !targ->dynexport) diag("unexpected reloc for dynamic symbol %s", targ->name); return; case 512 + MACHO_X86_64_RELOC_BRANCH*2 + 1: if(targ->dynimpname != nil && !targ->dynexport) { addpltsym(targ); r->sym = lookup(".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->dynimpname != nil && !targ->dynexport) diag("unexpected pc-relative reloc for dynamic symbol %s", targ->name); return; case 512 + MACHO_X86_64_RELOC_GOT_LOAD*2 + 1: if(targ->dynimpname == nil || targ->dynexport) { // 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->dynimpname == nil || targ->dynexport) diag("unexpected GOT reloc for non-dynamic symbol %s", targ->name); addgotsym(targ); r->type = D_PCREL; r->sym = lookup(".got", 0); r->add += targ->got; return; } // Handle references to ELF symbols from our own object files. if(targ->dynimpname == nil || targ->dynexport) return; switch(r->type) { case D_PCREL: addpltsym(targ); r->sym = lookup(".plt", 0); r->add = targ->plt; return; case D_ADDR: if(s->type != SDATA) break; if(iself) { adddynsym(targ); rela = lookup(".rela", 0); addaddrplus(rela, s, r->off); if(r->siz == 8) adduint64(rela, ELF64_R_INFO(targ->dynid, R_X86_64_64)); else adduint64(rela, ELF64_R_INFO(targ->dynid, R_X86_64_32)); adduint64(rela, r->add); r->type = 256; // ignore during relocsym return; } if(HEADTYPE == 6 && 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(targ); got = lookup(".got", 0); s->type = got->type | SSUB; s->outer = got; s->sub = got->sub; got->sub = s; s->value = got->size; adduint64(got, 0); adduint32(lookup(".linkedit.got", 0), targ->dynid); r->type = 256; // ignore during relocsym return; } break; } cursym = s; diag("unsupported relocation for dynamic symbol %s (type=%d stype=%d)", targ->name, r->type, targ->type); } int archreloc(Reloc *r, Sym *s, vlong *val) { return -1; } static void elfsetupplt(void) { Sym *plt, *got; plt = lookup(".plt", 0); got = lookup(".got.plt", 0); if(plt->size == 0) { // pushq got+8(IP) adduint8(plt, 0xff); adduint8(plt, 0x35); addpcrelplus(plt, got, 8); // jmpq got+16(IP) adduint8(plt, 0xff); adduint8(plt, 0x25); addpcrelplus(plt, got, 16); // nopl 0(AX) adduint32(plt, 0x00401f0f); // assume got->size == 0 too addaddrplus(got, lookup(".dynamic", 0), 0); adduint64(got, 0); adduint64(got, 0); } } static void addpltsym(Sym *s) { if(s->plt >= 0) return; adddynsym(s); if(iself) { Sym *plt, *got, *rela; plt = lookup(".plt", 0); got = lookup(".got.plt", 0); rela = lookup(".rela.plt", 0); if(plt->size == 0) elfsetupplt(); // jmpq *got+size(IP) adduint8(plt, 0xff); adduint8(plt, 0x25); addpcrelplus(plt, got, got->size); // add to got: pointer to current pos in plt addaddrplus(got, plt, plt->size); // pushq $x adduint8(plt, 0x68); adduint32(plt, (got->size-24-8)/8); // jmpq .plt adduint8(plt, 0xe9); adduint32(plt, -(plt->size+4)); // rela addaddrplus(rela, got, got->size-8); adduint64(rela, ELF64_R_INFO(s->dynid, R_X86_64_JMP_SLOT)); adduint64(rela, 0); s->plt = plt->size - 16; } else if(HEADTYPE == 6) { // Mach-O // 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. Sym *plt; addgotsym(s); plt = lookup(".plt", 0); adduint32(lookup(".linkedit.plt", 0), s->dynid); // jmpq *got+size(IP) s->plt = plt->size; adduint8(plt, 0xff); adduint8(plt, 0x25); addpcrelplus(plt, lookup(".got", 0), s->got); } else { diag("addpltsym: unsupported binary format"); } } static void addgotsym(Sym *s) { Sym *got, *rela; if(s->got >= 0) return; adddynsym(s); got = lookup(".got", 0); s->got = got->size; adduint64(got, 0); if(iself) { rela = lookup(".rela", 0); addaddrplus(rela, got, s->got); adduint64(rela, ELF64_R_INFO(s->dynid, R_X86_64_GLOB_DAT)); adduint64(rela, 0); } else if(HEADTYPE == 6) { // Mach-O adduint32(lookup(".linkedit.got", 0), s->dynid); } else { diag("addgotsym: unsupported binary format"); } } void adddynsym(Sym *s) { Sym *d, *str; int t; char *name; if(s->dynid >= 0) return; if(s->dynimpname == nil) diag("adddynsym: no dynamic name for %s", s->name); if(iself) { s->dynid = nelfsym++; d = lookup(".dynsym", 0); name = s->dynimpname; if(name == nil) name = s->name; adduint32(d, addstring(lookup(".dynstr", 0), name)); /* type */ t = STB_GLOBAL << 4; if(s->dynexport && s->type == STEXT) t |= STT_FUNC; else t |= STT_OBJECT; adduint8(d, t); /* reserved */ adduint8(d, 0); /* section where symbol is defined */ if(!s->dynexport && s->dynimpname != nil) adduint16(d, SHN_UNDEF); else { switch(s->type) { default: case STEXT: t = 11; break; case SRODATA: t = 12; break; case SDATA: t = 13; break; case SBSS: t = 14; break; } adduint16(d, t); } /* value */ if(s->type == SDYNIMPORT) adduint64(d, 0); else addaddr(d, s); /* size of object */ adduint64(d, 0); if(!s->dynexport && s->dynimplib && needlib(s->dynimplib)) { elfwritedynent(lookup(".dynamic", 0), DT_NEEDED, addstring(lookup(".dynstr", 0), s->dynimplib)); } } else if(HEADTYPE == 6) { // Mach-o symbol nlist64 d = lookup(".dynsym", 0); name = s->dynimpname; if(name == nil) name = s->name; s->dynid = d->size/16; // darwin still puts _ prefixes on all C symbols str = lookup(".dynstr", 0); adduint32(d, str->size); adduint8(str, '_'); addstring(str, name); if(s->type == SDYNIMPORT) { adduint8(d, 0x01); // type - N_EXT - external symbol adduint8(d, 0); // section } else { adduint8(d, 0x0f); switch(s->type) { default: case STEXT: adduint8(d, 1); break; case SDATA: adduint8(d, 2); break; case SBSS: adduint8(d, 4); break; } } adduint16(d, 0); // desc if(s->type == SDYNIMPORT) adduint64(d, 0); // value else addaddr(d, s); } else { diag("adddynsym: unsupported binary format"); } } void adddynlib(char *lib) { Sym *s; if(!needlib(lib)) return; if(iself) { s = lookup(".dynstr", 0); if(s->size == 0) addstring(s, ""); elfwritedynent(lookup(".dynamic", 0), DT_NEEDED, addstring(s, lib)); } else if(HEADTYPE == 6) { // Mach-O machoadddynlib(lib); } else { diag("adddynlib: unsupported binary format"); } } void doelf(void) { Sym *s, *shstrtab, *dynstr; if(HEADTYPE != 7 && HEADTYPE != 9) return; /* predefine strings we need for section headers */ shstrtab = lookup(".shstrtab", 0); shstrtab->type = SELFDATA; shstrtab->reachable = 1; elfstr[ElfStrEmpty] = addstring(shstrtab, ""); elfstr[ElfStrText] = addstring(shstrtab, ".text"); elfstr[ElfStrData] = addstring(shstrtab, ".data"); elfstr[ElfStrBss] = addstring(shstrtab, ".bss"); addstring(shstrtab, ".elfdata"); addstring(shstrtab, ".rodata"); if(!debug['s']) { elfstr[ElfStrGosymcounts] = addstring(shstrtab, ".gosymcounts"); elfstr[ElfStrGosymtab] = addstring(shstrtab, ".gosymtab"); elfstr[ElfStrGopclntab] = addstring(shstrtab, ".gopclntab"); elfstr[ElfStrSymtab] = addstring(shstrtab, ".symtab"); elfstr[ElfStrStrtab] = addstring(shstrtab, ".strtab"); dwarfaddshstrings(shstrtab); } elfstr[ElfStrShstrtab] = addstring(shstrtab, ".shstrtab"); if(!debug['d']) { /* -d suppresses dynamic loader format */ elfstr[ElfStrInterp] = addstring(shstrtab, ".interp"); elfstr[ElfStrHash] = addstring(shstrtab, ".hash"); elfstr[ElfStrGot] = addstring(shstrtab, ".got"); elfstr[ElfStrGotPlt] = addstring(shstrtab, ".got.plt"); elfstr[ElfStrDynamic] = addstring(shstrtab, ".dynamic"); elfstr[ElfStrDynsym] = addstring(shstrtab, ".dynsym"); elfstr[ElfStrDynstr] = addstring(shstrtab, ".dynstr"); elfstr[ElfStrRela] = addstring(shstrtab, ".rela"); elfstr[ElfStrRelaPlt] = addstring(shstrtab, ".rela.plt"); elfstr[ElfStrPlt] = addstring(shstrtab, ".plt"); /* dynamic symbol table - first entry all zeros */ s = lookup(".dynsym", 0); s->type = SELFDATA; s->reachable = 1; s->size += ELF64SYMSIZE; /* dynamic string table */ s = lookup(".dynstr", 0); s->type = SELFDATA; s->reachable = 1; if(s->size == 0) addstring(s, ""); dynstr = s; /* relocation table */ s = lookup(".rela", 0); s->reachable = 1; s->type = SELFDATA; /* global offset table */ s = lookup(".got", 0); s->reachable = 1; s->type = SDATA; // writable, so not SELFDATA /* hash */ s = lookup(".hash", 0); s->reachable = 1; s->type = SELFDATA; s = lookup(".got.plt", 0); s->reachable = 1; s->type = SDATA; // writable, not SELFDATA s = lookup(".plt", 0); s->reachable = 1; s->type = SELFDATA; elfsetupplt(); s = lookup(".rela.plt", 0); s->reachable = 1; s->type = SELFDATA; /* define dynamic elf table */ s = lookup(".dynamic", 0); s->reachable = 1; s->type = SELFDATA; /* * .dynamic table */ elfwritedynentsym(s, DT_HASH, lookup(".hash", 0)); elfwritedynentsym(s, DT_SYMTAB, lookup(".dynsym", 0)); elfwritedynent(s, DT_SYMENT, ELF64SYMSIZE); elfwritedynentsym(s, DT_STRTAB, lookup(".dynstr", 0)); elfwritedynentsymsize(s, DT_STRSZ, lookup(".dynstr", 0)); elfwritedynentsym(s, DT_RELA, lookup(".rela", 0)); elfwritedynentsymsize(s, DT_RELASZ, lookup(".rela", 0)); elfwritedynent(s, DT_RELAENT, ELF64RELASIZE); if(rpath) elfwritedynent(s, DT_RUNPATH, addstring(dynstr, rpath)); elfwritedynentsym(s, DT_PLTGOT, lookup(".got.plt", 0)); elfwritedynent(s, DT_PLTREL, DT_RELA); elfwritedynentsymsize(s, DT_PLTRELSZ, lookup(".rela.plt", 0)); elfwritedynentsym(s, DT_JMPREL, lookup(".rela.plt", 0)); elfwritedynent(s, DT_NULL, 0); } } void shsym(ElfShdr *sh, Sym *s) { sh->addr = symaddr(s); sh->off = datoff(sh->addr); sh->size = s->size; } void phsh(ElfPhdr *ph, ElfShdr *sh) { ph->vaddr = sh->addr; ph->paddr = ph->vaddr; ph->off = sh->off; ph->filesz = sh->size; ph->memsz = sh->size; ph->align = sh->addralign; } void asmb(void) { int32 magic; int a, dynsym; vlong vl, va, startva, fo, w, symo, elfsymo, elfstro, elfsymsize, machlink; ElfEhdr *eh; ElfPhdr *ph, *pph; ElfShdr *sh; Section *sect; if(debug['v']) Bprint(&bso, "%5.2f asmb\n", cputime()); Bflush(&bso); elftextsh = 0; elfsymsize = 0; elfstro = 0; elfsymo = 0; if(debug['v']) Bprint(&bso, "%5.2f codeblk\n", cputime()); Bflush(&bso); sect = segtext.sect; seek(cout, sect->vaddr - segtext.vaddr + segtext.fileoff, 0); codeblk(sect->vaddr, sect->len); /* output read-only data in text segment */ sect = segtext.sect->next; seek(cout, sect->vaddr - segtext.vaddr + segtext.fileoff, 0); datblk(sect->vaddr, sect->len); if(debug['v']) Bprint(&bso, "%5.2f datblk\n", cputime()); Bflush(&bso); seek(cout, segdata.fileoff, 0); datblk(segdata.vaddr, segdata.filelen); machlink = 0; if(HEADTYPE == 6) machlink = domacholink(); switch(HEADTYPE) { default: diag("unknown header type %d", HEADTYPE); case 2: case 5: break; case 6: debug['8'] = 1; /* 64-bit addresses */ break; case 7: case 9: debug['8'] = 1; /* 64-bit addresses */ /* index of elf text section; needed by asmelfsym, double-checked below */ /* !debug['d'] causes extra sections before the .text section */ elftextsh = 1; if(!debug['d']) elftextsh += 10; break; case 10: 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 2: case 5: debug['s'] = 1; symo = HEADR+segtext.len+segdata.filelen; break; case 6: symo = rnd(HEADR+segtext.len, INITRND)+rnd(segdata.filelen, INITRND)+machlink; break; case 7: case 9: symo = rnd(HEADR+segtext.len, INITRND)+segdata.filelen; symo = rnd(symo, INITRND); break; case 10: symo = rnd(HEADR+segtext.filelen, PEFILEALIGN)+segdata.filelen; symo = rnd(symo, PEFILEALIGN); break; } /* * the symbol information is stored as * 32-bit symbol table size * 32-bit line number table size * symbol table * line number table */ seek(cout, symo+8, 0); if(debug['v']) Bprint(&bso, "%5.2f sp\n", cputime()); Bflush(&bso); if(debug['v']) Bprint(&bso, "%5.2f pc\n", cputime()); Bflush(&bso); if(!debug['s']) strnput("", INITRND-(8+symsize+lcsize)%INITRND); cflush(); seek(cout, symo, 0); lputl(symsize); lputl(lcsize); cflush(); if(HEADTYPE != 10 && !debug['s']) { elfsymo = symo+8+symsize+lcsize; seek(cout, elfsymo, 0); asmelfsym64(); cflush(); elfstro = seek(cout, 0, 1); elfsymsize = elfstro - elfsymo; ewrite(cout, elfstrdat, elfstrsize); if(debug['v']) Bprint(&bso, "%5.2f dwarf\n", cputime()); dwarfemitdebugsections(); } } if(debug['v']) Bprint(&bso, "%5.2f headr\n", cputime()); Bflush(&bso); seek(cout, 0L, 0); switch(HEADTYPE) { default: case 2: /* 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 3: /* plan9 */ magic = 4*26*26+7; lputb(magic); /* magic */ lputb(segtext.filelen); /* sizes */ lputb(segdata.filelen); lputb(segdata.len - segdata.filelen); lputb(symsize); /* nsyms */ lputb(entryvalue()); /* va of entry */ lputb(spsize); /* sp offsets */ lputb(lcsize); /* line offsets */ break; case 6: asmbmacho(); break; case 7: case 9: /* elf amd-64 */ eh = getElfEhdr(); fo = HEADR; startva = INITTEXT - HEADR; va = startva + fo; w = segtext.filelen; /* This null SHdr must appear before all others */ sh = newElfShdr(elfstr[ElfStrEmpty]); /* program header info */ pph = newElfPhdr(); pph->type = PT_PHDR; pph->flags = PF_R + PF_X; pph->off = eh->ehsize; pph->vaddr = INITTEXT - HEADR + pph->off; pph->paddr = INITTEXT - HEADR + pph->off; pph->align = INITRND; if(!debug['d']) { /* interpreter */ sh = newElfShdr(elfstr[ElfStrInterp]); sh->type = SHT_PROGBITS; sh->flags = SHF_ALLOC; sh->addralign = 1; if(interpreter == nil) { switch(HEADTYPE) { case 7: interpreter = linuxdynld; break; case 9: interpreter = freebsddynld; break; } } elfinterp(sh, startva, interpreter); ph = newElfPhdr(); ph->type = PT_INTERP; ph->flags = PF_R; phsh(ph, sh); } elfphload(&segtext); elfphload(&segdata); /* Dynamic linking sections */ if (!debug['d']) { /* -d suppresses dynamic loader format */ /* S headers for dynamic linking */ sh = newElfShdr(elfstr[ElfStrGot]); sh->type = SHT_PROGBITS; sh->flags = SHF_ALLOC+SHF_WRITE; sh->entsize = 8; sh->addralign = 8; shsym(sh, lookup(".got", 0)); sh = newElfShdr(elfstr[ElfStrGotPlt]); sh->type = SHT_PROGBITS; sh->flags = SHF_ALLOC+SHF_WRITE; sh->entsize = 8; sh->addralign = 8; shsym(sh, lookup(".got.plt", 0)); dynsym = eh->shnum; sh = newElfShdr(elfstr[ElfStrDynsym]); sh->type = SHT_DYNSYM; sh->flags = SHF_ALLOC; sh->entsize = ELF64SYMSIZE; sh->addralign = 8; sh->link = dynsym+1; // dynstr // sh->info = index of first non-local symbol (number of local symbols) shsym(sh, lookup(".dynsym", 0)); sh = newElfShdr(elfstr[ElfStrDynstr]); sh->type = SHT_STRTAB; sh->flags = SHF_ALLOC; sh->addralign = 1; shsym(sh, lookup(".dynstr", 0)); sh = newElfShdr(elfstr[ElfStrRelaPlt]); sh->type = SHT_RELA; sh->flags = SHF_ALLOC; sh->entsize = ELF64RELASIZE; sh->addralign = 8; sh->link = dynsym; sh->info = eh->shnum; // .plt shsym(sh, lookup(".rela.plt", 0)); sh = newElfShdr(elfstr[ElfStrPlt]); sh->type = SHT_PROGBITS; sh->flags = SHF_ALLOC+SHF_EXECINSTR; sh->entsize = 16; sh->addralign = 4; shsym(sh, lookup(".plt", 0)); sh = newElfShdr(elfstr[ElfStrHash]); sh->type = SHT_HASH; sh->flags = SHF_ALLOC; sh->entsize = 4; sh->addralign = 8; sh->link = dynsym; shsym(sh, lookup(".hash", 0)); sh = newElfShdr(elfstr[ElfStrRela]); sh->type = SHT_RELA; sh->flags = SHF_ALLOC; sh->entsize = ELF64RELASIZE; sh->addralign = 8; sh->link = dynsym; shsym(sh, lookup(".rela", 0)); /* sh and PT_DYNAMIC for .dynamic section */ sh = newElfShdr(elfstr[ElfStrDynamic]); sh->type = SHT_DYNAMIC; sh->flags = SHF_ALLOC+SHF_WRITE; sh->entsize = 16; sh->addralign = 8; sh->link = dynsym+1; // dynstr shsym(sh, lookup(".dynamic", 0)); ph = newElfPhdr(); ph->type = PT_DYNAMIC; ph->flags = PF_R + PF_W; phsh(ph, sh); /* * Thread-local storage segment (really just size). */ if(tlsoffset != 0) { ph = newElfPhdr(); ph->type = PT_TLS; ph->flags = PF_R; ph->memsz = -tlsoffset; ph->align = 8; } } ph = newElfPhdr(); ph->type = PT_GNU_STACK; ph->flags = PF_W+PF_R; ph->align = 8; if(elftextsh != eh->shnum) diag("elftextsh = %d, want %d", elftextsh, eh->shnum); for(sect=segtext.sect; sect!=nil; sect=sect->next) elfshbits(sect); for(sect=segdata.sect; sect!=nil; sect=sect->next) elfshbits(sect); if (!debug['s']) { sh = newElfShdr(elfstr[ElfStrGosymtab]); sh->type = SHT_PROGBITS; sh->flags = SHF_ALLOC; sh->addralign = 1; shsym(sh, lookup("symtab", 0)); sh = newElfShdr(elfstr[ElfStrGopclntab]); sh->type = SHT_PROGBITS; sh->flags = SHF_ALLOC; sh->addralign = 1; shsym(sh, lookup("pclntab", 0)); sh = newElfShdr(elfstr[ElfStrSymtab]); sh->type = SHT_SYMTAB; sh->off = elfsymo; sh->size = elfsymsize; sh->addralign = 8; sh->entsize = 24; sh->link = eh->shnum; // link to strtab sh = newElfShdr(elfstr[ElfStrStrtab]); sh->type = SHT_STRTAB; sh->off = elfstro; sh->size = elfstrsize; sh->addralign = 1; dwarfaddelfheaders(); } sh = newElfShstrtab(elfstr[ElfStrShstrtab]); sh->type = SHT_STRTAB; sh->addralign = 1; shsym(sh, lookup(".shstrtab", 0)); /* Main header */ eh->ident[EI_MAG0] = '\177'; eh->ident[EI_MAG1] = 'E'; eh->ident[EI_MAG2] = 'L'; eh->ident[EI_MAG3] = 'F'; if(HEADTYPE == 9) eh->ident[EI_OSABI] = 9; eh->ident[EI_CLASS] = ELFCLASS64; eh->ident[EI_DATA] = ELFDATA2LSB; eh->ident[EI_VERSION] = EV_CURRENT; eh->type = ET_EXEC; eh->machine = EM_X86_64; eh->version = EV_CURRENT; eh->entry = entryvalue(); pph->filesz = eh->phnum * eh->phentsize; pph->memsz = pph->filesz; seek(cout, 0, 0); a = 0; a += elfwritehdr(); a += elfwritephdrs(); a += elfwriteshdrs(); cflush(); if(a+elfwriteinterp() > ELFRESERVE) diag("ELFRESERVE too small: %d > %d", a, ELFRESERVE); break; case 10: asmbpe(); break; } cflush(); } void cflush(void) { int n; n = sizeof(buf.cbuf) - cbc; if(n) ewrite(cout, buf.cbuf, n); cbp = buf.cbuf; cbc = sizeof(buf.cbuf); } /* Current position in file */ vlong cpos(void) { return seek(cout, 0, 1) + sizeof(buf.cbuf) - cbc; } vlong rnd(vlong v, vlong r) { vlong c; if(r <= 0) return v; v += r - 1; c = v % r; if(c < 0) c += r; v -= c; return v; } void genasmsym(void (*put)(Sym*, char*, int, vlong, vlong, int, Sym*)) { Auto *a; Sym *s; int h; for(h=0; hhash) { switch(s->type&~SSUB) { case SCONST: case SRODATA: case SDATA: case SELFDATA: case SMACHOGOT: case SWINDOWS: if(!s->reachable) continue; put(s, s->name, 'D', symaddr(s), s->size, s->version, s->gotype); continue; case SBSS: if(!s->reachable) continue; put(s, s->name, 'B', symaddr(s), s->size, s->version, s->gotype); continue; case SFILE: put(nil, s->name, 'f', s->value, 0, s->version, 0); continue; } } } for(s = textp; s != nil; s = s->next) { if(s->text == nil) continue; /* filenames first */ for(a=s->autom; a; a=a->link) if(a->type == D_FILE) put(nil, a->asym->name, 'z', a->aoffset, 0, 0, 0); else if(a->type == D_FILE1) put(nil, a->asym->name, 'Z', a->aoffset, 0, 0, 0); put(s, s->name, 'T', s->value, s->size, s->version, s->gotype); /* frame, auto and param after */ put(nil, ".frame", 'm', s->text->to.offset+8, 0, 0, 0); for(a=s->autom; a; a=a->link) if(a->type == D_AUTO) put(nil, a->asym->name, 'a', -a->aoffset, 0, 0, a->gotype); else if(a->type == D_PARAM) put(nil, a->asym->name, 'p', a->aoffset, 0, 0, a->gotype); } if(debug['v'] || debug['n']) Bprint(&bso, "symsize = %ud\n", symsize); Bflush(&bso); }