diff options
Diffstat (limited to 'src/cmd/5l/asm.c')
-rw-r--r-- | src/cmd/5l/asm.c | 1055 |
1 files changed, 550 insertions, 505 deletions
diff --git a/src/cmd/5l/asm.c b/src/cmd/5l/asm.c index b36a982d1..04f2a9c6c 100644 --- a/src/cmd/5l/asm.c +++ b/src/cmd/5l/asm.c @@ -33,10 +33,14 @@ #include "l.h" #include "../ld/lib.h" #include "../ld/elf.h" +#include "../ld/dwarf.h" static Prog *PP; -char linuxdynld[] = "/lib/ld-linux.so.2"; +char linuxdynld[] = "/lib/ld-linux.so.3"; // 2 for OABI, 3 for EABI +char freebsddynld[] = "/usr/libexec/ld-elf.so.1"; +char openbsddynld[] = "XXX"; +char netbsddynld[] = "/libexec/ld.elf_so"; int32 entryvalue(void) @@ -55,32 +59,6 @@ entryvalue(void) return s->value; } -enum { - ElfStrEmpty, - ElfStrInterp, - ElfStrHash, - ElfStrGot, - ElfStrGotPlt, - ElfStrDynamic, - ElfStrDynsym, - ElfStrDynstr, - ElfStrRel, - ElfStrText, - ElfStrData, - ElfStrBss, - ElfStrSymtab, - ElfStrStrtab, - ElfStrShstrtab, - ElfStrRelPlt, - ElfStrPlt, - ElfStrNoteNetbsdIdent, - ElfStrNoPtrData, - ElfStrNoPtrBss, - NElfStr -}; - -vlong elfstr[NElfStr]; - static int needlib(char *name) { @@ -103,164 +81,437 @@ needlib(char *name) int nelfsym = 1; +static void addpltsym(Sym*); +static void addgotsym(Sym*); +static void addgotsyminternal(Sym*); + +// Preserve highest 8 bits of a, and do addition to lower 24-bit +// of a and b; used to adjust ARM branch intruction's target +static int32 +braddoff(int32 a, int32 b) +{ + return (((uint32)a) & 0xff000000U) | (0x00ffffffU & (uint32)(a + b)); +} + +Sym * +lookuprel(void) +{ + return lookup(".rel", 0); +} + void -adddynrel(Sym *s, Reloc *r) +adddynrela(Sym *rel, Sym *s, Reloc *r) { - USED(s); - USED(r); - diag("adddynrel: unsupported binary format"); + addaddrplus(rel, s, r->off); + adduint32(rel, R_ARM_RELATIVE); } void -adddynsym(Sym *s) +adddynrel(Sym *s, Reloc *r) { - USED(s); - diag("adddynsym: not implemented"); + Sym *targ, *rel; + + 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_ARM_PLT32: + r->type = D_CALL; + if(targ->dynimpname != nil && !targ->dynexport) { + addpltsym(targ); + r->sym = lookup(".plt", 0); + r->add = braddoff(r->add, targ->plt / 4); + } + return; + + case 256 + R_ARM_THM_PC22: // R_ARM_THM_CALL + diag("R_ARM_THM_CALL, are you using -marm?"); + errorexit(); + return; + + case 256 + R_ARM_GOT32: // R_ARM_GOT_BREL + if(targ->dynimpname == nil || targ->dynexport) { + addgotsyminternal(targ); + } else { + addgotsym(targ); + } + r->type = D_CONST; // write r->add during relocsym + r->sym = S; + r->add += targ->got; + return; + + case 256 + R_ARM_GOT_PREL: // GOT(S) + A - P + if(targ->dynimpname == nil || targ->dynexport) { + addgotsyminternal(targ); + } else { + addgotsym(targ); + } + r->type = D_PCREL; + r->sym = lookup(".got", 0); + r->add += targ->got + 4; + return; + + case 256 + R_ARM_GOTOFF: // R_ARM_GOTOFF32 + r->type = D_GOTOFF; + return; + + case 256 + R_ARM_GOTPC: // R_ARM_BASE_PREL + r->type = D_PCREL; + r->sym = lookup(".got", 0); + r->add += 4; + return; + + case 256 + R_ARM_CALL: + r->type = D_CALL; + if(targ->dynimpname != nil && !targ->dynexport) { + addpltsym(targ); + r->sym = lookup(".plt", 0); + r->add = braddoff(r->add, targ->plt / 4); + } + return; + + case 256 + R_ARM_REL32: // R_ARM_REL32 + r->type = D_PCREL; + r->add += 4; + return; + + case 256 + R_ARM_ABS32: + if(targ->dynimpname != nil && !targ->dynexport) + diag("unexpected R_ARM_ABS32 relocation for dynamic symbol %s", targ->name); + r->type = D_ADDR; + return; + + case 256 + R_ARM_V4BX: + // we can just ignore this, because we are targeting ARM V5+ anyway + if(r->sym) { + // R_ARM_V4BX is ABS relocation, so this symbol is a dummy symbol, ignore it + r->sym->type = 0; + } + r->sym = S; + return; + + case 256 + R_ARM_PC24: + case 256 + R_ARM_JUMP24: + r->type = D_CALL; + if(targ->dynimpname != nil && !targ->dynexport) { + addpltsym(targ); + r->sym = lookup(".plt", 0); + r->add = braddoff(r->add, targ->plt / 4); + } + 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_ARM_GLOB_DAT)); // we need a S + A dynmic reloc + r->type = D_CONST; // write r->add during relocsym + r->sym = S; + return; + } + break; + } + + cursym = s; + diag("unsupported relocation for dynamic symbol %s (type=%d stype=%d)", targ->name, r->type, targ->type); } -static void +int +elfreloc1(Reloc *r, vlong off, int32 elfsym, vlong add) +{ + USED(add); // written to obj file by ../ld/data.c's reloc + + LPUT(off); + + switch(r->type) { + default: + return -1; + + case D_ADDR: + if(r->siz == 4) + LPUT(R_ARM_ABS32 | elfsym<<8); + else + return -1; + break; + + case D_PCREL: + if(r->siz == 4) + LPUT(R_ARM_REL32 | elfsym<<8); + else + return -1; + break; + } + + return 0; +} + +void elfsetupplt(void) { - // TODO + Sym *plt, *got; + + plt = lookup(".plt", 0); + got = lookup(".got.plt", 0); + if(plt->size == 0) { + // str lr, [sp, #-4]! + adduint32(plt, 0xe52de004); + // ldr lr, [pc, #4] + adduint32(plt, 0xe59fe004); + // add lr, pc, lr + adduint32(plt, 0xe08fe00e); + // ldr pc, [lr, #8]! + adduint32(plt, 0xe5bef008); + // .word &GLOBAL_OFFSET_TABLE[0] - . + addpcrelplus(plt, got, 4); + + // the first .plt entry requires 3 .plt.got entries + adduint32(got, 0); + adduint32(got, 0); + adduint32(got, 0); + } } int archreloc(Reloc *r, Sym *s, vlong *val) { - USED(r); - USED(s); - USED(val); - return -1; + 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; + // The following three arch specific relocations are only for generation of + // Linux/ARM ELF's PLT entry (3 assembler instruction) + case D_PLT0: // add ip, pc, #0xXX00000 + if (symaddr(lookup(".got.plt", 0)) < symaddr(lookup(".plt", 0))) + diag(".got.plt should be placed after .plt section."); + *val = 0xe28fc600U + + (0xff & ((uint32)(symaddr(r->sym) - (symaddr(lookup(".plt", 0)) + r->off) + r->add) >> 20)); + return 0; + case D_PLT1: // add ip, ip, #0xYY000 + *val = 0xe28cca00U + + (0xff & ((uint32)(symaddr(r->sym) - (symaddr(lookup(".plt", 0)) + r->off) + r->add + 4) >> 12)); + return 0; + case D_PLT2: // ldr pc, [ip, #0xZZZ]! + *val = 0xe5bcf000U + + (0xfff & (uint32)(symaddr(r->sym) - (symaddr(lookup(".plt", 0)) + r->off) + r->add + 8)); + return 0; + case D_CALL: // bl XXXXXX or b YYYYYY + *val = braddoff((0xff000000U & (uint32)r->add), + (0xffffff & (uint32) + ((symaddr(r->sym) + ((uint32)r->add) * 4 - (s->value + r->off)) / 4))); + return 0; +} +return -1; } -void -adddynlib(char *lib) +static Reloc * +addpltreloc(Sym *plt, Sym *got, Sym *sym, int typ) { - Sym *s; +Reloc *r; + r = addrel(plt); + r->sym = got; + r->off = plt->size; + r->siz = 4; + r->type = typ; + r->add = sym->got - 8; + + plt->reachable = 1; + plt->size += 4; + symgrow(plt, plt->size); + + return r; +} + +static void +addpltsym(Sym *s) +{ + Sym *plt, *got, *rel; - if(!needlib(lib)) + if(s->plt >= 0) return; + + adddynsym(s); if(iself) { - s = lookup(".dynstr", 0); - if(s->size == 0) - addstring(s, ""); - elfwritedynent(lookup(".dynamic", 0), DT_NEEDED, addstring(s, lib)); + plt = lookup(".plt", 0); + got = lookup(".got.plt", 0); + rel = lookup(".rel.plt", 0); + if(plt->size == 0) + elfsetupplt(); + + // .got entry + s->got = got->size; + // In theory, all GOT should point to the first PLT entry, + // Linux/ARM's dynamic linker will do that for us, but FreeBSD/ARM's + // dynamic linker won't, so we'd better do it ourselves. + addaddrplus(got, plt, 0); + + // .plt entry, this depends on the .got entry + s->plt = plt->size; + addpltreloc(plt, got, s, D_PLT0); // add lr, pc, #0xXX00000 + addpltreloc(plt, got, s, D_PLT1); // add lr, lr, #0xYY000 + addpltreloc(plt, got, s, D_PLT2); // ldr pc, [lr, #0xZZZ]! + + // rel + addaddrplus(rel, got, s->got); + adduint32(rel, ELF32_R_INFO(s->dynid, R_ARM_JUMP_SLOT)); } else { - diag("adddynlib: unsupported binary format"); + diag("addpltsym: unsupported binary format"); + } +} + +static void +addgotsyminternal(Sym *s) +{ + Sym *got; + + if(s->got >= 0) + return; + + got = lookup(".got", 0); + s->got = got->size; + + addaddrplus(got, s, 0); + + if(iself) { + ; + } else { + diag("addgotsyminternal: 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_ARM_GLOB_DAT)); + } else { + diag("addgotsym: unsupported binary format"); } } void -doelf(void) +adddynsym(Sym *s) { - Sym *s, *shstrtab, *dynstr; + Sym *d; + int t; + char *name; - if(!iself) + if(s->dynid >= 0) 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[ElfStrNoPtrData] = addstring(shstrtab, ".noptrdata"); - elfstr[ElfStrData] = addstring(shstrtab, ".data"); - elfstr[ElfStrBss] = addstring(shstrtab, ".bss"); - elfstr[ElfStrNoPtrBss] = addstring(shstrtab, ".noptrbss"); - if(HEADTYPE == Hnetbsd) - elfstr[ElfStrNoteNetbsdIdent] = addstring(shstrtab, ".note.netbsd.ident"); - addstring(shstrtab, ".rodata"); - addstring(shstrtab, ".gosymtab"); - addstring(shstrtab, ".gopclntab"); - if(!debug['s']) { - elfstr[ElfStrSymtab] = addstring(shstrtab, ".symtab"); - elfstr[ElfStrStrtab] = addstring(shstrtab, ".strtab"); + if(s->dynimpname == nil) { + s->dynimpname = s->name; + //diag("adddynsym: no dynamic name for %s", s->name); } - 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"); - - /* dynamic symbol table - first entry all zeros */ - s = lookup(".dynsym", 0); - s->type = SELFROSECT; - s->reachable = 1; - s->value += ELF32SYMSIZE; - - /* dynamic string table */ - s = lookup(".dynstr", 0); - s->type = SELFROSECT; - s->reachable = 1; - if(s->size == 0) - addstring(s, ""); - dynstr = s; - /* relocation table */ - s = lookup(".rel", 0); - s->reachable = 1; - s->type = SELFROSECT; + if(iself) { + s->dynid = nelfsym++; - /* 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; + d = lookup(".dynsym", 0); - s = lookup(".rel.plt", 0); - s->reachable = 1; - s->type = SELFROSECT; - - elfsetupplt(); - - /* define dynamic elf table */ - s = lookup(".dynamic", 0); - s->reachable = 1; - s->type = SELFSECT; // writable - - /* - * .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)); - elfwritedynent(s, DT_DEBUG, 0); - elfwritedynent(s, DT_NULL, 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&SMASK) == 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 { + 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 { + diag("adddynlib: unsupported binary format"); } } @@ -276,48 +527,26 @@ datoff(vlong addr) } 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 t; - int a, dynsym; - uint32 fo, symo, startva, resoff; - ElfEhdr *eh; - ElfPhdr *ph, *pph; - ElfShdr *sh; + uint32 symo; Section *sect; - int o; + Sym *sym; + int i; if(debug['v']) Bprint(&bso, "%5.2f asmb\n", cputime()); Bflush(&bso); + if(iself) + asmbelfsetup(); + 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) */ + /* output read-only data in text segment (rodata, gosymtab, pclntab, ...) */ for(sect = sect->next; sect != nil; sect = sect->next) { cseek(sect->vaddr - segtext.vaddr + segtext.fileoff); datblk(sect->vaddr, sect->len); @@ -330,19 +559,6 @@ asmb(void) cseek(segdata.fileoff); datblk(segdata.vaddr, segdata.filelen); - 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; - } - if(HEADTYPE == Hnetbsd) - elftextsh += 1; - } - /* output symbol table */ symsize = 0; lcsize = 0; @@ -365,28 +581,43 @@ asmb(void) case Hplan9x32: symo = HEADR+segtext.len+segdata.filelen; break; - case Hnetbsd: - symo = rnd(segdata.filelen, 4096); - break; ElfSym: symo = rnd(HEADR+segtext.filelen, INITRND)+segdata.filelen; symo = rnd(symo, INITRND); break; } cseek(symo); - if(iself) { - if(debug['v']) - Bprint(&bso, "%5.2f elfsym\n", cputime()); - asmelfsym(); + 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(); + + if(isobj) + elfemitreloc(); + } + break; + case Hplan9x32: + asmplan9sym(); cflush(); - cwrite(elfstrdat, elfstrsize); - // if(debug['v']) - // Bprint(&bso, "%5.2f dwarf\n", cputime()); - // dwarfemitdebugsections(); + sym = lookup("pclntab", 0); + if(sym != nil) { + lcsize = sym->np; + for(i=0; i < lcsize; i++) + cput(sym->p[i]); + + cflush(); + } + break; } - cflush(); - } cursym = nil; @@ -395,6 +626,7 @@ asmb(void) Bflush(&bso); cseek(0L); switch(HEADTYPE) { + default: case Hnoheader: /* no header */ break; case Hrisc: /* aif for risc os */ @@ -409,7 +641,7 @@ asmb(void) - 8) / 4); /* BL - entry code */ lputl(0xef000011); /* SWI - exit code */ - lputl(textsize+HEADR); /* text size */ + lputl(segtext.filelen+HEADR); /* text size */ lputl(segdata.filelen); /* data size */ lputl(0); /* sym size */ @@ -429,7 +661,7 @@ asmb(void) break; case Hplan9x32: /* plan 9 */ lput(0x647); /* magic */ - lput(textsize); /* sizes */ + lput(segtext.filelen); /* sizes */ lput(segdata.filelen); lput(segdata.len - segdata.filelen); lput(symsize); /* nsyms */ @@ -437,16 +669,6 @@ asmb(void) lput(0L); lput(lcsize); break; - case Hnetbsd: /* boot for NetBSD */ - lput((143<<16)|0413); /* magic */ - lputl(rnd(HEADR+textsize, 4096)); - lputl(rnd(segdata.filelen, 4096)); - lputl(segdata.len - segdata.filelen); - lputl(symsize); /* nsyms */ - lputl(entryvalue()); /* va of entry */ - lputl(0L); - lputl(0L); - break; case Hixp1200: /* boot for IXP1200 */ break; case Hipaq: /* boot for ipaq */ @@ -456,216 +678,20 @@ asmb(void) lputl(0xe3300000); /* nop */ break; case Hlinux: - /* elf arm */ - eh = getElfEhdr(); - fo = HEADR; - startva = INITTEXT - fo; /* va of byte 0 of file */ - resoff = ELFRESERVE; - - /* 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 for dynamic linking */ - sh = newElfShdr(elfstr[ElfStrInterp]); - sh->type = SHT_PROGBITS; - sh->flags = SHF_ALLOC; - sh->addralign = 1; - if(interpreter == nil) - interpreter = linuxdynld; - resoff -= elfinterp(sh, startva, resoff, interpreter); - - ph = newElfPhdr(); - ph->type = PT_INTERP; - ph->flags = PF_R; - phsh(ph, sh); - } - - if(HEADTYPE == Hnetbsd) { - sh = newElfShdr(elfstr[ElfStrNoteNetbsdIdent]); - sh->type = SHT_NOTE; - sh->flags = SHF_ALLOC; - sh->addralign = 4; - resoff -= elfnetbsdsig(sh, startva, resoff); - - ph = newElfPhdr(); - ph->type = PT_NOTE; - 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)); - - 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; - - eh->type = ET_EXEC; - eh->machine = EM_ARM; - 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(); - a += elfwriteinterp(elfstr[ElfStrInterp]); - if(HEADTYPE == Hnetbsd) - a += elfwritenetbsdsig(elfstr[ElfStrNoteNetbsdIdent]); - if(a > ELFRESERVE) - diag("ELFRESERVE too small: %d > %d", a, ELFRESERVE); + case Hfreebsd: + case Hnetbsd: + case Hopenbsd: + asmbelf(symo); break; } cflush(); if(debug['c']){ - print("textsize=%d\n", textsize); + print("textsize=%ulld\n", segtext.filelen); print("datsize=%ulld\n", segdata.filelen); print("bsssize=%ulld\n", segdata.len - segdata.filelen); print("symsize=%d\n", symsize); print("lcsize=%d\n", lcsize); - print("total=%lld\n", textsize+segdata.len+symsize+lcsize); + print("total=%lld\n", segtext.filelen+segdata.len+symsize+lcsize); } } @@ -812,6 +838,7 @@ if(debug['G']) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->name, p- case 5: /* bra s */ v = -8; + // TODO: Use addrel. if(p->cond != P) v = (p->cond->pc - pc) - 8; o1 = opbra(p->as, p->scond); @@ -875,15 +902,22 @@ if(debug['G']) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->name, p- rel = addrel(cursym); rel->off = pc - cursym->value; rel->siz = 4; - rel->type = D_ADDR; rel->sym = p->to.sym; rel->add = p->to.offset; + if(flag_shared) { + rel->type = D_PCREL; + rel->add += pc - p->pcrel->pc - 8; + } else + rel->type = D_ADDR; o1 = 0; } break; case 12: /* movw $lcon, reg */ o1 = omvl(p, &p->from, p->to.reg); + if(o->flag & LPCREL) { + o2 = oprrr(AADD, p->scond) | p->to.reg | REGPC << 16 | p->to.reg << 12; + } break; case 13: /* op $lcon, [R], R */ @@ -1128,7 +1162,7 @@ if(debug['G']) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->name, p- r = p->reg; if(r == NREG) { r = rt; - if(p->as == AMOVF || p->as == AMOVD || p->as == ASQRTF || p->as == ASQRTD) + if(p->as == AMOVF || p->as == AMOVD || p->as == ASQRTF || p->as == ASQRTD || p->as == AABSF || p->as == AABSD) r = 0; } o1 |= rf | (r<<16) | (rt<<12); @@ -1188,13 +1222,23 @@ if(debug['G']) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->name, p- break; case 62: /* case R -> movw R<<2(PC),PC */ - o1 = olrr(p->from.reg, REGPC, REGPC, p->scond); - o1 |= 2<<7; + if(o->flag & LPCREL) { + o1 = oprrr(AADD, p->scond) | immrot(1) | p->from.reg << 16 | REGTMP << 12; + o2 = olrr(REGTMP, REGPC, REGTMP, p->scond); + o2 |= 2<<7; + o3 = oprrr(AADD, p->scond) | REGTMP | REGPC << 16 | REGPC << 12; + } else { + o1 = olrr(p->from.reg, REGPC, REGPC, p->scond); + o1 |= 2<<7; + } break; case 63: /* bcase */ - if(p->cond != P) + if(p->cond != P) { o1 = p->cond->pc; + if(flag_shared) + o1 = o1 - p->pcrel->pc - 16; + } break; /* reloc ops */ @@ -1203,6 +1247,10 @@ if(debug['G']) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->name, p- if(!o1) break; o2 = osr(p->as, p->from.reg, 0, REGTMP, p->scond); + if(o->flag & LPCREL) { + o3 = o2; + o2 = oprrr(AADD, p->scond) | REGTMP | REGPC << 16 | REGTMP << 12; + } break; case 65: /* mov/movbu addr,R */ @@ -1212,6 +1260,10 @@ if(debug['G']) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->name, p- o2 = olr(0, REGTMP, p->to.reg, p->scond); if(p->as == AMOVBU || p->as == AMOVB) o2 |= 1<<22; + if(o->flag & LPCREL) { + o3 = o2; + o2 = oprrr(AADD, p->scond) | REGTMP | REGPC << 16 | REGTMP << 12; + } break; case 68: /* floating point store -> ADDR */ @@ -1219,6 +1271,10 @@ if(debug['G']) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->name, p- if(!o1) break; o2 = ofsr(p->as, p->from.reg, 0, REGTMP, p->scond, p); + if(o->flag & LPCREL) { + o3 = o2; + o2 = oprrr(AADD, p->scond) | REGTMP | REGPC << 16 | REGTMP << 12; + } break; case 69: /* floating point load <- ADDR */ @@ -1226,6 +1282,10 @@ if(debug['G']) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->name, p- if(!o1) break; o2 = ofsr(p->as, p->to.reg, 0, REGTMP, p->scond, p) | (1<<20); + if(o->flag & LPCREL) { + o3 = o2; + o2 = oprrr(AADD, p->scond) | REGTMP | REGPC << 16 | REGTMP << 12; + } break; /* ArmV4 ops: */ @@ -1422,12 +1482,57 @@ if(debug['G']) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->name, p- o2 ^= (1<<5)|(1<<6); else if(p->as == AMOVH) o2 ^= (1<<6); + if(o->flag & LPCREL) { + o3 = o2; + o2 = oprrr(AADD, p->scond) | REGTMP | REGPC << 16 | REGTMP << 12; + } break; case 94: /* movh/movhu R,addr -> strh */ o1 = omvl(p, &p->to, REGTMP); if(!o1) break; o2 = oshr(p->from.reg, 0, REGTMP, p->scond); + if(o->flag & LPCREL) { + o3 = o2; + o2 = oprrr(AADD, p->scond) | REGTMP | REGPC << 16 | REGTMP << 12; + } + break; + case 95: /* PLD off(reg) */ + o1 = 0xf5d0f000; + o1 |= p->from.reg << 16; + if(p->from.offset < 0) { + o1 &= ~(1 << 23); + o1 |= (-p->from.offset) & 0xfff; + } else + o1 |= p->from.offset & 0xfff; + break; + case 96: /* UNDEF */ + // This is supposed to be something that stops execution. + // It's not supposed to be reached, ever, but if it is, we'd + // like to be able to tell how we got there. Assemble as + // BL $0 + // TODO: Use addrel. + v = (0 - pc) - 8; + o1 = opbra(ABL, C_SCOND_NONE); + o1 |= (v >> 2) & 0xffffff; + break; + case 97: /* CLZ Rm, Rd */ + o1 = oprrr(p->as, p->scond); + o1 |= p->to.reg << 12; + o1 |= p->from.reg; + break; + case 98: /* MULW{T,B} Rs, Rm, Rd */ + o1 = oprrr(p->as, p->scond); + o1 |= p->to.reg << 16; + o1 |= p->from.reg << 8; + o1 |= p->reg; + break; + case 99: /* MULAW{T,B} Rs, Rm, Rn, Rd */ + o1 = oprrr(p->as, p->scond); + o1 |= p->to.reg << 12; + o1 |= p->from.reg << 8; + o1 |= p->reg; + o1 |= p->to.offset << 16; break; } @@ -1547,6 +1652,8 @@ oprrr(int a, int sc) case ADIVF: return o | (0xe<<24) | (0x8<<20) | (0xa<<8) | (0<<4); case ASQRTD: return o | (0xe<<24) | (0xb<<20) | (1<<16) | (0xb<<8) | (0xc<<4); case ASQRTF: return o | (0xe<<24) | (0xb<<20) | (1<<16) | (0xa<<8) | (0xc<<4); + case AABSD: return o | (0xe<<24) | (0xb<<20) | (0<<16) | (0xb<<8) | (0xc<<4); + case AABSF: return o | (0xe<<24) | (0xb<<20) | (0<<16) | (0xa<<8) | (0xc<<4); case ACMPD: return o | (0xe<<24) | (0xb<<20) | (4<<16) | (0xb<<8) | (0xc<<4); case ACMPF: return o | (0xe<<24) | (0xb<<20) | (4<<16) | (0xa<<8) | (0xc<<4); @@ -1586,6 +1693,19 @@ oprrr(int a, int sc) return o | (0xe<<24) | (0x1<<20) | (0xb<<8) | (1<<4); case ACMP+AEND: // cmp imm return o | (0x3<<24) | (0x5<<20); + + case ACLZ: + // CLZ doesn't support .S + return (o & (0xf<<28)) | (0x16f<<16) | (0xf1<<4); + + case AMULWT: + return (o & (0xf<<28)) | (0x12 << 20) | (0xe<<4); + case AMULWB: + return (o & (0xf<<28)) | (0x12 << 20) | (0xa<<4); + case AMULAWT: + return (o & (0xf<<28)) | (0x12 << 20) | (0xc<<4); + case AMULAWB: + return (o & (0xf<<28)) | (0x12 << 20) | (0x8<<4); } diag("bad rrr %d", a); prasm(curp); @@ -1789,7 +1909,8 @@ omvl(Prog *p, Adr *a, int dr) int chipzero(Ieee *e) { - if(e->l != 0 || e->h != 0) + // We use GOARM=7 to gate the use of VFPv3 vmov (imm) instructions. + if(goarm < 7 || e->l != 0 || e->h != 0) return -1; return 0; } @@ -1800,6 +1921,10 @@ chipfloat(Ieee *e) int n; ulong h; + // We use GOARM=7 to gate the use of VFPv3 vmov (imm) instructions. + if(goarm < 7) + goto no; + if(e->l != 0 || (e->h&0xffff) != 0) goto no; h = e->h & 0x7fc00000; @@ -1824,83 +1949,3 @@ chipfloat(Ieee *e) no: return -1; } - - -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) { - case SCONST: - case SRODATA: - case SDATA: - case SELFROSECT: - case STYPE: - case SSTRING: - case SGOSTRING: - case SNOPTRDATA: - case SSYMTAB: - case SPCLNTAB: - if(!s->reachable) - continue; - put(s, s->name, 'D', s->value, s->size, s->version, s->gotype); - continue; - - case SBSS: - case SNOPTRBSS: - if(!s->reachable) - continue; - if(s->np > 0) - diag("%s should not be bss (size=%d type=%d special=%d)", s->name, (int)s->np, s->type, s->special); - put(s, s->name, 'B', s->value, 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) { - /* 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 = %ud\n", symsize); - Bflush(&bso); -} - -void -setpersrc(Sym *s) -{ - USED(s); -} |