diff options
Diffstat (limited to 'src/cmd/8l/asm.c')
-rw-r--r-- | src/cmd/8l/asm.c | 1253 |
1 files changed, 1253 insertions, 0 deletions
diff --git a/src/cmd/8l/asm.c b/src/cmd/8l/asm.c new file mode 100644 index 000000000..22abd8049 --- /dev/null +++ b/src/cmd/8l/asm.c @@ -0,0 +1,1253 @@ +// Inferno utils/8l/asm.c +// http://code.google.com/p/inferno-os/source/browse/utils/8l/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 + +char linuxdynld[] = "/lib/ld-linux.so.2"; +char freebsddynld[] = "/usr/libexec/ld-elf.so.1"; + +int32 +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; +} + +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, + ElfStrRel, + ElfStrText, + ElfStrData, + ElfStrBss, + ElfStrShstrtab, + ElfStrSymtab, + ElfStrStrtab, + ElfStrRelPlt, + ElfStrPlt, + ElfStrGnuVersion, + ElfStrGnuVersionR, + NElfStr +}; + +vlong elfstr[NElfStr]; + +static int +needlib(char *name) +{ + char *p; + Sym *s; + + if(*name == '\0') + return 0; + + /* reuse hash code in symbol table */ + p = smprint(".dynlib.%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, *rel, *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_386_PC32: + if(targ->dynimpname != nil && !targ->dynexport) + diag("unexpected R_386_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_386_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_386_GOT32: + if(targ->dynimpname == nil || targ->dynexport) { + // have symbol + // turn MOVL of GOT entry into LEAL of symbol itself + if(r->off < 2 || s->p[r->off-2] != 0x8b) { + diag("unexpected GOT reloc for non-dynamic symbol %s", targ->name); + return; + } + s->p[r->off-2] = 0x8d; + r->type = D_GOTOFF; + return; + } + addgotsym(targ); + r->type = D_CONST; // write r->add during relocsym + r->sym = S; + r->add += targ->got; + return; + + case 256 + R_386_GOTOFF: + r->type = D_GOTOFF; + return; + + case 256 + R_386_GOTPC: + r->type = D_PCREL; + r->sym = lookup(".got", 0); + r->add += 4; + return; + + case 256 + R_386_32: + if(targ->dynimpname != nil && !targ->dynexport) + diag("unexpected R_386_32 relocation for dynamic symbol %s", targ->name); + r->type = D_ADDR; + return; + + case 512 + MACHO_GENERIC_RELOC_VANILLA*2 + 0: + r->type = D_ADDR; + if(targ->dynimpname != nil && !targ->dynexport) + diag("unexpected reloc for dynamic symbol %s", targ->name); + return; + + case 512 + MACHO_GENERIC_RELOC_VANILLA*2 + 1: + if(targ->dynimpname != nil && !targ->dynexport) { + addpltsym(targ); + r->sym = lookup(".plt", 0); + r->add = targ->plt; + r->type = D_PCREL; + return; + } + r->type = D_PCREL; + return; + + case 512 + MACHO_FAKE_GOTPCREL: + if(targ->dynimpname == nil || targ->dynexport) { + // have symbol + // turn MOVL of GOT entry into LEAL of symbol itself + if(r->off < 2 || s->p[r->off-2] != 0x8b) { + diag("unexpected GOT reloc for non-dynamic symbol %s", targ->name); + return; + } + s->p[r->off-2] = 0x8d; + r->type = D_PCREL; + return; + } + addgotsym(targ); + r->sym = lookup(".got", 0); + r->add += targ->got; + r->type = D_PCREL; + 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); + rel = lookup(".rel", 0); + addaddrplus(rel, s, r->off); + adduint32(rel, ELF32_R_INFO(targ->dynid, R_386_32)); + r->type = D_CONST; // write r->add during relocsym + r->sym = S; + 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(targ); + got = lookup(".got", 0); + s->type = got->type | SSUB; + s->outer = got; + s->sub = got->sub; + got->sub = s; + s->value = got->size; + adduint32(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); +} + +static void +elfsetupplt(void) +{ + Sym *plt, *got; + + plt = lookup(".plt", 0); + got = lookup(".got.plt", 0); + if(plt->size == 0) { + // pushl got+4 + adduint8(plt, 0xff); + adduint8(plt, 0x35); + addaddrplus(plt, got, 4); + + // jmp *got+8 + adduint8(plt, 0xff); + adduint8(plt, 0x25); + addaddrplus(plt, got, 8); + + // zero pad + adduint32(plt, 0); + + // assume got->size == 0 too + addaddrplus(got, lookup(".dynamic", 0), 0); + adduint32(got, 0); + adduint32(got, 0); + } +} + +int +archreloc(Reloc *r, Sym *s, vlong *val) +{ + USED(s); + switch(r->type) { + case D_CONST: + *val = r->add; + return 0; + case D_GOTOFF: + *val = symaddr(r->sym) + r->add - symaddr(lookup(".got", 0)); + return 0; + } + return -1; +} + +static void +addpltsym(Sym *s) +{ + Sym *plt, *got, *rel; + + if(s->plt >= 0) + return; + + adddynsym(s); + + if(iself) { + plt = lookup(".plt", 0); + got = lookup(".got.plt", 0); + rel = lookup(".rel.plt", 0); + if(plt->size == 0) + elfsetupplt(); + + // jmpq *got+size + adduint8(plt, 0xff); + adduint8(plt, 0x25); + addaddrplus(plt, got, got->size); + + // add to got: pointer to current pos in plt + addaddrplus(got, plt, plt->size); + + // pushl $x + adduint8(plt, 0x68); + adduint32(plt, rel->size); + + // jmp .plt + adduint8(plt, 0xe9); + adduint32(plt, -(plt->size+4)); + + // rel + addaddrplus(rel, got, got->size-4); + adduint32(rel, ELF32_R_INFO(s->dynid, R_386_JMP_SLOT)); + + s->plt = plt->size - 16; + } else if(HEADTYPE == Hdarwin) { + // Same laziness as in 6l. + + Sym *plt; + + plt = lookup(".plt", 0); + + addgotsym(s); + + adduint32(lookup(".linkedit.plt", 0), s->dynid); + + // jmpq *got+size(IP) + s->plt = plt->size; + + adduint8(plt, 0xff); + adduint8(plt, 0x25); + addaddrplus(plt, lookup(".got", 0), s->got); + } else { + diag("addpltsym: unsupported binary format"); + } +} + +static void +addgotsym(Sym *s) +{ + Sym *got, *rel; + + if(s->got >= 0) + return; + + adddynsym(s); + got = lookup(".got", 0); + s->got = got->size; + adduint32(got, 0); + + if(iself) { + rel = lookup(".rel", 0); + addaddrplus(rel, got, s->got); + adduint32(rel, ELF32_R_INFO(s->dynid, R_386_GLOB_DAT)); + } else if(HEADTYPE == Hdarwin) { + 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, *(int32*)0); + + if(iself) { + s->dynid = nelfsym++; + + d = lookup(".dynsym", 0); + + /* name */ + name = s->dynimpname; + if(name == nil) + name = s->name; + adduint32(d, addstring(lookup(".dynstr", 0), name)); + + /* value */ + if(s->type == SDYNIMPORT) + adduint32(d, 0); + else + addaddr(d, s); + + /* size */ + adduint32(d, 0); + + /* type */ + t = STB_GLOBAL << 4; + if(s->dynexport && s->type == STEXT) + t |= STT_FUNC; + else + t |= STT_OBJECT; + adduint8(d, t); + adduint8(d, 0); + + /* shndx */ + 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); + } + } else if(HEADTYPE == Hdarwin) { + // Mach-O symbol nlist32 + d = lookup(".dynsym", 0); + name = s->dynimpname; + if(name == nil) + name = s->name; + s->dynid = d->size/12; + // darwin still puts _ prefixes on all C symbols + str = lookup(".dynstr", 0); + adduint32(d, str->size); + adduint8(str, '_'); + addstring(str, name); + adduint8(d, 0x01); // type - N_EXT - external symbol + adduint8(d, 0); // section + adduint16(d, 0); // desc + adduint32(d, 0); // value + } else if(HEADTYPE != Hwindows) { + 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 == Hdarwin) { + machoadddynlib(lib); + } else if(HEADTYPE != Hwindows) { + diag("adddynlib: unsupported binary format"); + } +} + +void +doelf(void) +{ + Sym *s, *shstrtab, *dynstr; + + if(!iself) + return; + + /* predefine strings we need for section headers */ + shstrtab = lookup(".shstrtab", 0); + shstrtab->type = SELFROSECT; + 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"); + addstring(shstrtab, ".gosymtab"); + addstring(shstrtab, ".gopclntab"); + if(!debug['s']) { + 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[ElfStrRel] = addstring(shstrtab, ".rel"); + elfstr[ElfStrRelPlt] = addstring(shstrtab, ".rel.plt"); + elfstr[ElfStrPlt] = addstring(shstrtab, ".plt"); + elfstr[ElfStrGnuVersion] = addstring(shstrtab, ".gnu.version"); + elfstr[ElfStrGnuVersionR] = addstring(shstrtab, ".gnu.version_r"); + + /* dynamic symbol table - first entry all zeros */ + s = lookup(".dynsym", 0); + s->type = SELFROSECT; + s->reachable = 1; + s->size += ELF32SYMSIZE; + + /* dynamic string table */ + s = lookup(".dynstr", 0); + s->reachable = 1; + s->type = SELFROSECT; + if(s->size == 0) + addstring(s, ""); + dynstr = s; + + /* relocation table */ + s = lookup(".rel", 0); + s->reachable = 1; + s->type = SELFROSECT; + + /* global offset table */ + s = lookup(".got", 0); + s->reachable = 1; + s->type = SELFSECT; // writable + + /* hash */ + s = lookup(".hash", 0); + s->reachable = 1; + s->type = SELFROSECT; + + /* got.plt */ + s = lookup(".got.plt", 0); + s->reachable = 1; + s->type = SELFSECT; // writable + + s = lookup(".plt", 0); + s->reachable = 1; + s->type = SELFROSECT; + + s = lookup(".rel.plt", 0); + s->reachable = 1; + s->type = SELFROSECT; + + s = lookup(".gnu.version", 0); + s->reachable = 1; + s->type = SELFROSECT; + + s = lookup(".gnu.version_r", 0); + s->reachable = 1; + s->type = SELFROSECT; + + elfsetupplt(); + + /* define dynamic elf table */ + s = lookup(".dynamic", 0); + s->reachable = 1; + s->type = SELFROSECT; + + /* + * .dynamic table + */ + elfwritedynentsym(s, DT_HASH, lookup(".hash", 0)); + elfwritedynentsym(s, DT_SYMTAB, lookup(".dynsym", 0)); + elfwritedynent(s, DT_SYMENT, ELF32SYMSIZE); + elfwritedynentsym(s, DT_STRTAB, lookup(".dynstr", 0)); + elfwritedynentsymsize(s, DT_STRSZ, lookup(".dynstr", 0)); + elfwritedynentsym(s, DT_REL, lookup(".rel", 0)); + elfwritedynentsymsize(s, DT_RELSZ, lookup(".rel", 0)); + elfwritedynent(s, DT_RELENT, ELF32RELSIZE); + if(rpath) + elfwritedynent(s, DT_RUNPATH, addstring(dynstr, rpath)); + elfwritedynentsym(s, DT_PLTGOT, lookup(".got.plt", 0)); + elfwritedynent(s, DT_PLTREL, DT_REL); + elfwritedynentsymsize(s, DT_PLTRELSZ, lookup(".rel.plt", 0)); + elfwritedynentsym(s, DT_JMPREL, lookup(".rel.plt", 0)); + + // Do not write DT_NULL. elfdynhash will finish it. + } +} + +void +shsym(Elf64_Shdr *sh, Sym *s) +{ + vlong addr; + addr = symaddr(s); + if(sh->flags&SHF_ALLOC) + sh->addr = addr; + sh->off = datoff(addr); + sh->size = s->size; +} + +void +phsh(Elf64_Phdr *ph, Elf64_Shdr *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 v, magic; + int a, dynsym; + uint32 symo, startva, machlink; + ElfEhdr *eh; + ElfPhdr *ph, *pph; + ElfShdr *sh; + Section *sect; + Sym *sym; + int o; + int i; + + if(debug['v']) + Bprint(&bso, "%5.2f asmb\n", cputime()); + Bflush(&bso); + + sect = segtext.sect; + cseek(sect->vaddr - segtext.vaddr + segtext.fileoff); + codeblk(sect->vaddr, sect->len); + + /* output read-only data in text segment (rodata, gosymtab and pclntab) */ + for(sect = sect->next; sect != nil; sect = sect->next) { + cseek(sect->vaddr - segtext.vaddr + segtext.fileoff); + datblk(sect->vaddr, sect->len); + } + + 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) + machlink = domacholink(); + + if(iself) { + /* index of elf text section; needed by asmelfsym, double-checked below */ + /* !debug['d'] causes extra sections before the .text section */ + elftextsh = 2; + if(!debug['d']) { + elftextsh += 10; + if(elfverneed) + elftextsh += 2; + } + } + + symsize = 0; + spsize = 0; + lcsize = 0; + symo = 0; + if(!debug['s']) { + // TODO: rationalize + if(debug['v']) + Bprint(&bso, "%5.2f sym\n", cputime()); + Bflush(&bso); + switch(HEADTYPE) { + default: + if(iself) + goto Elfsym; + case Hgarbunix: + symo = rnd(HEADR+segtext.filelen, 8192)+segdata.filelen; + break; + case Hunixcoff: + symo = rnd(HEADR+segtext.filelen, INITRND)+segdata.filelen; + break; + case Hplan9x32: + symo = HEADR+segtext.filelen+segdata.filelen; + break; + case Hmsdoscom: + case Hmsdosexe: + debug['s'] = 1; + symo = HEADR+segtext.filelen+segdata.filelen; + break; + case Hdarwin: + symo = rnd(HEADR+segtext.filelen, INITRND)+rnd(segdata.filelen, INITRND)+machlink; + break; + Elfsym: + symo = rnd(HEADR+segtext.filelen, 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) { + if(debug['v']) + Bprint(&bso, "%5.2f elfsym\n", cputime()); + asmelfsym(); + cflush(); + cwrite(elfstrdat, elfstrsize); + + if(debug['v']) + Bprint(&bso, "%5.2f dwarf\n", cputime()); + dwarfemitdebugsections(); + } + break; + case Hplan9x32: + asmplan9sym(); + cflush(); + + sym = lookup("pclntab", 0); + if(sym != nil) { + lcsize = sym->np; + for(i=0; i < lcsize; i++) + cput(sym->p[i]); + + cflush(); + } + break; + case Hdarwin: + case Hwindows: + if(debug['v']) + Bprint(&bso, "%5.2f dwarf\n", cputime()); + dwarfemitdebugsections(); + break; + } + } + if(debug['v']) + Bprint(&bso, "%5.2f headr\n", cputime()); + Bflush(&bso); + cseek(0L); + switch(HEADTYPE) { + default: + if(iself) + goto Elfput; + case Hgarbunix: /* garbage */ + lputb(0x160L<<16); /* magic and sections */ + lputb(0L); /* time and date */ + lputb(rnd(HEADR+segtext.filelen, 4096)+segdata.filelen); + lputb(symsize); /* nsyms */ + lputb((0x38L<<16)|7L); /* size of optional hdr and flags */ + lputb((0413<<16)|0437L); /* magic and version */ + lputb(rnd(HEADR+segtext.filelen, 4096)); /* sizes */ + lputb(segdata.filelen); + lputb(segdata.len - segdata.filelen); + lputb(entryvalue()); /* va of entry */ + lputb(INITTEXT-HEADR); /* va of base of text */ + lputb(segdata.vaddr); /* va of base of data */ + lputb(segdata.vaddr+segdata.filelen); /* va of base of bss */ + lputb(~0L); /* gp reg mask */ + lputb(0L); + lputb(0L); + lputb(0L); + lputb(0L); + lputb(~0L); /* gp value ?? */ + break; + case Hunixcoff: /* unix coff */ + /* + * file header + */ + lputl(0x0004014c); /* 4 sections, magic */ + lputl(0); /* unix time stamp */ + lputl(0); /* symbol table */ + lputl(0); /* nsyms */ + lputl(0x0003001c); /* flags, sizeof a.out header */ + /* + * a.out header + */ + lputl(0x10b); /* magic, version stamp */ + lputl(rnd(segtext.filelen, INITRND)); /* text sizes */ + lputl(segdata.filelen); /* data sizes */ + lputl(segdata.len - segdata.filelen); /* bss sizes */ + lputb(entryvalue()); /* va of entry */ + lputl(INITTEXT); /* text start */ + lputl(segdata.vaddr); /* data start */ + /* + * text section header + */ + s8put(".text"); + lputl(HEADR); /* pa */ + lputl(HEADR); /* va */ + lputl(segtext.filelen); /* text size */ + lputl(HEADR); /* file offset */ + lputl(0); /* relocation */ + lputl(0); /* line numbers */ + lputl(0); /* relocation, line numbers */ + lputl(0x20); /* flags text only */ + /* + * data section header + */ + s8put(".data"); + lputl(segdata.vaddr); /* pa */ + lputl(segdata.vaddr); /* va */ + lputl(segdata.filelen); /* data size */ + lputl(HEADR+segtext.filelen); /* file offset */ + lputl(0); /* relocation */ + lputl(0); /* line numbers */ + lputl(0); /* relocation, line numbers */ + lputl(0x40); /* flags data only */ + /* + * bss section header + */ + s8put(".bss"); + lputl(segdata.vaddr+segdata.filelen); /* pa */ + lputl(segdata.vaddr+segdata.filelen); /* va */ + lputl(segdata.len - segdata.filelen); /* bss size */ + lputl(0); /* file offset */ + lputl(0); /* relocation */ + lputl(0); /* line numbers */ + lputl(0); /* relocation, line numbers */ + lputl(0x80); /* flags bss only */ + /* + * comment section header + */ + s8put(".comment"); + lputl(0); /* pa */ + lputl(0); /* va */ + lputl(symsize+lcsize); /* comment size */ + lputl(HEADR+segtext.filelen+segdata.filelen); /* file offset */ + lputl(HEADR+segtext.filelen+segdata.filelen); /* offset of syms */ + lputl(HEADR+segtext.filelen+segdata.filelen+symsize);/* offset of line numbers */ + lputl(0); /* relocation, line numbers */ + lputl(0x200); /* flags comment only */ + break; + case Hplan9x32: /* plan9 */ + magic = 4*11*11+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 Hmsdoscom: + /* MS-DOS .COM */ + break; + case Hmsdosexe: + /* fake MS-DOS .EXE */ + v = rnd(HEADR+segtext.filelen, INITRND)+segdata.filelen; + wputl(0x5A4D); /* 'MZ' */ + wputl(v % 512); /* bytes in last page */ + wputl(rnd(v, 512)/512); /* total number of pages */ + wputl(0x0000); /* number of reloc items */ + v = rnd(HEADR-(INITTEXT & 0xFFFF), 16); + wputl(v/16); /* size of header */ + wputl(0x0000); /* minimum allocation */ + wputl(0xFFFF); /* maximum allocation */ + wputl(0x0000); /* initial ss value */ + wputl(0x0100); /* initial sp value */ + wputl(0x0000); /* complemented checksum */ + v = entryvalue(); + wputl(v); /* initial ip value (!) */ + wputl(0x0000); /* initial cs value */ + wputl(0x0000); + wputl(0x0000); + wputl(0x003E); /* reloc table offset */ + wputl(0x0000); /* overlay number */ + break; + + case Hdarwin: + asmbmacho(); + break; + + Elfput: + eh = getElfEhdr(); + startva = INITTEXT - HEADR; + + /* This null SHdr must appear before all others */ + 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; + + /* + * PHDR must be in a loaded segment. Adjust the text + * segment boundaries downwards to include it. + */ + 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 = newElfShdr(elfstr[ElfStrInterp]); + 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; + } + } + 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 = 4; + sh->addralign = 4; + shsym(sh, lookup(".got", 0)); + + sh = newElfShdr(elfstr[ElfStrGotPlt]); + sh->type = SHT_PROGBITS; + sh->flags = SHF_ALLOC+SHF_WRITE; + sh->entsize = 4; + sh->addralign = 4; + shsym(sh, lookup(".got.plt", 0)); + + dynsym = eh->shnum; + sh = newElfShdr(elfstr[ElfStrDynsym]); + sh->type = SHT_DYNSYM; + sh->flags = SHF_ALLOC; + sh->entsize = ELF32SYMSIZE; + sh->addralign = 4; + 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)); + + if(elfverneed) { + sh = newElfShdr(elfstr[ElfStrGnuVersion]); + sh->type = SHT_GNU_VERSYM; + sh->flags = SHF_ALLOC; + sh->addralign = 2; + sh->link = dynsym; + sh->entsize = 2; + shsym(sh, lookup(".gnu.version", 0)); + + sh = newElfShdr(elfstr[ElfStrGnuVersionR]); + sh->type = SHT_GNU_VERNEED; + sh->flags = SHF_ALLOC; + sh->addralign = 4; + sh->info = elfverneed; + sh->link = dynsym+1; // dynstr + shsym(sh, lookup(".gnu.version_r", 0)); + } + + sh = newElfShdr(elfstr[ElfStrRelPlt]); + sh->type = SHT_REL; + sh->flags = SHF_ALLOC; + sh->entsize = ELF32RELSIZE; + sh->addralign = 4; + sh->link = dynsym; + sh->info = eh->shnum; // .plt + shsym(sh, lookup(".rel.plt", 0)); + + sh = newElfShdr(elfstr[ElfStrPlt]); + sh->type = SHT_PROGBITS; + sh->flags = SHF_ALLOC+SHF_EXECINSTR; + sh->entsize = 4; + 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 = 4; + sh->link = dynsym; + shsym(sh, lookup(".hash", 0)); + + sh = newElfShdr(elfstr[ElfStrRel]); + sh->type = SHT_REL; + sh->flags = SHF_ALLOC; + sh->entsize = ELF32RELSIZE; + sh->addralign = 4; + sh->link = dynsym; + shsym(sh, lookup(".rel", 0)); + + /* sh and PT_DYNAMIC for .dynamic section */ + sh = newElfShdr(elfstr[ElfStrDynamic]); + sh->type = SHT_DYNAMIC; + sh->flags = SHF_ALLOC+SHF_WRITE; + sh->entsize = 8; + sh->addralign = 4; + 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 = 4; + } + } + + ph = newElfPhdr(); + ph->type = PT_GNU_STACK; + ph->flags = PF_W+PF_R; + ph->align = 4; + + sh = newElfShstrtab(elfstr[ElfStrShstrtab]); + sh->type = SHT_STRTAB; + sh->addralign = 1; + shsym(sh, lookup(".shstrtab", 0)); + + 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[ElfStrSymtab]); + sh->type = SHT_SYMTAB; + sh->off = symo; + sh->size = symsize; + sh->addralign = 4; + sh->entsize = 16; + sh->link = eh->shnum; // link to strtab + + sh = newElfShdr(elfstr[ElfStrStrtab]); + 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'; + eh->ident[EI_CLASS] = ELFCLASS32; + eh->ident[EI_DATA] = ELFDATA2LSB; + eh->ident[EI_VERSION] = EV_CURRENT; + switch(HEADTYPE) { + case Hfreebsd: + eh->ident[EI_OSABI] = 9; + break; + } + + eh->type = ET_EXEC; + eh->machine = EM_386; + eh->version = EV_CURRENT; + eh->entry = entryvalue(); + + if(pph != nil) { + pph->filesz = eh->phnum * eh->phentsize; + pph->memsz = pph->filesz; + } + + cseek(0); + a = 0; + a += elfwritehdr(); + a += elfwritephdrs(); + a += elfwriteshdrs(); + cflush(); + if(a+elfwriteinterp() > ELFRESERVE) + diag("ELFRESERVE too small: %d > %d", a, ELFRESERVE); + break; + + case Hwindows: + asmbpe(); + break; + } + cflush(); +} + +void +s8put(char *n) +{ + char name[8]; + int i; + + strncpy(name, n, sizeof(name)); + for(i=0; i<sizeof(name); i++) + cput(name[i]); +} + +int32 +rnd(int32 v, int32 r) +{ + int32 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; + + s = lookup("etext", 0); + if(s->type == STEXT) + put(s, s->name, 'T', s->value, s->size, s->version, 0); + + for(h=0; h<NHASH; h++) { + for(s=hash[h]; s!=S; s=s->hash) { + if(s->hide) + continue; + switch(s->type&~SSUB) { + case SCONST: + case SRODATA: + case SDATA: + case SELFROSECT: + case SMACHO: + case SMACHOGOT: + case STYPE: + case SSTRING: + case SGOSTRING: + 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+4, 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 = %d\n", symsize); + Bflush(&bso); +} |