diff --git a/src/cmd/5l/asm.c b/src/cmd/5l/asm.c index f412490561..e1377defe1 100644 --- a/src/cmd/5l/asm.c +++ b/src/cmd/5l/asm.c @@ -239,6 +239,35 @@ adddynrel(Sym *s, Reloc *r) diag("unsupported relocation for dynamic symbol %s (type=%d stype=%d)", targ->name, r->type, targ->type); } +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) { @@ -573,6 +602,9 @@ asmb(void) if(debug['v']) Bprint(&bso, "%5.2f dwarf\n", cputime()); dwarfemitdebugsections(); + + if(isobj) + elfemitreloc(); } break; case Hplan9x32: @@ -809,6 +841,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); @@ -1481,6 +1514,7 @@ if(debug['G']) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->name, p- // 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; diff --git a/src/cmd/6l/asm.c b/src/cmd/6l/asm.c index eb2c79ca15..5fb75ba4d0 100644 --- a/src/cmd/6l/asm.c +++ b/src/cmd/6l/asm.c @@ -284,6 +284,37 @@ adddynrel(Sym *s, Reloc *r) diag("unsupported relocation for dynamic symbol %s (type=%d stype=%d)", targ->name, r->type, targ->type); } +int +elfreloc1(Reloc *r, vlong off, int32 elfsym, vlong add) +{ + VPUT(off); + + switch(r->type) { + default: + return -1; + + case D_ADDR: + if(r->siz == 4) + VPUT(R_X86_64_32 | (uint64)elfsym<<32); + else if(r->siz == 8) + VPUT(R_X86_64_64 | (uint64)elfsym<<32); + else + return -1; + break; + + case D_PCREL: + if(r->siz == 4) + VPUT(R_X86_64_PC32 | (uint64)elfsym<<32); + else + return -1; + add -= r->siz; + break; + } + + VPUT(add); + return 0; +} + int archreloc(Reloc *r, Sym *s, vlong *val) { @@ -674,6 +705,9 @@ asmb(void) Bprint(&bso, "%5.2f dwarf\n", cputime()); dwarfemitdebugsections(); + + if(isobj) + elfemitreloc(); } break; case Hplan9x64: diff --git a/src/cmd/6l/obj.c b/src/cmd/6l/obj.c index e381b90a76..1cb4bd2aaa 100644 --- a/src/cmd/6l/obj.c +++ b/src/cmd/6l/obj.c @@ -108,6 +108,7 @@ main(int argc, char *argv[]) flagcount("d", "disable dynamic executable", &debug['d']); flagcount("f", "ignore version mismatch", &debug['f']); flagcount("g", "disable go package data checks", &debug['g']); + flagcount("hostobj", "generate host object file", &isobj); flagstr("k", "sym: set field tracking symbol", &tracksym); flagcount("n", "dump symbol table", &debug['n']); flagstr("o", "outfile: set output file", &outfile); @@ -130,6 +131,15 @@ main(int argc, char *argv[]) if(HEADTYPE == -1) HEADTYPE = headtype(goos); + if(isobj) { + switch(HEADTYPE) { + default: + sysfatal("cannot use -hostobj with -H %s", headstr(HEADTYPE)); + case Hlinux: + break; + } + } + if(outfile == nil) { if(HEADTYPE == Hwindows) outfile = "6.out.exe"; diff --git a/src/cmd/8l/asm.c b/src/cmd/8l/asm.c index 2cdf4ff2ab..76ebdb9135 100644 --- a/src/cmd/8l/asm.c +++ b/src/cmd/8l/asm.c @@ -266,6 +266,35 @@ adddynrel(Sym *s, Reloc *r) diag("unsupported relocation for dynamic symbol %s (type=%d stype=%d)", targ->name, r->type, targ->type); } +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_386_32 | elfsym<<8); + else + return -1; + break; + + case D_PCREL: + if(r->siz == 4) + LPUT(R_386_PC32 | elfsym<<8); + else + return -1; + break; + } + + return 0; +} + void elfsetupplt(void) { @@ -633,6 +662,9 @@ asmb(void) if(debug['v']) Bprint(&bso, "%5.2f dwarf\n", cputime()); dwarfemitdebugsections(); + + if(isobj) + elfemitreloc(); } break; case Hplan9x32: diff --git a/src/cmd/8l/obj.c b/src/cmd/8l/obj.c index 74820e6334..c334a81f60 100644 --- a/src/cmd/8l/obj.c +++ b/src/cmd/8l/obj.c @@ -114,6 +114,7 @@ main(int argc, char *argv[]) flagcount("d", "disable dynamic executable", &debug['d']); flagcount("f", "ignore version mismatch", &debug['f']); flagcount("g", "disable go package data checks", &debug['g']); + flagcount("hostobj", "generate host object file", &hostobj); flagstr("k", "sym: set field tracking symbol", &tracksym); flagstr("o", "outfile: set output file", &outfile); flagcount("p", "insert profiling code", &debug['p']); @@ -135,6 +136,15 @@ main(int argc, char *argv[]) if(HEADTYPE == -1) HEADTYPE = headtype(goos); + if(isobj) { + switch(HEADTYPE) { + default: + sysfatal("cannot use -hostobj with -H %s", headstr(HEADTYPE)); + case Hlinux: + break; + } + } + if(outfile == nil) { if(HEADTYPE == Hwindows) outfile = "8.out.exe"; diff --git a/src/cmd/ld/data.c b/src/cmd/ld/data.c index 30e1309d24..e557881e94 100644 --- a/src/cmd/ld/data.c +++ b/src/cmd/ld/data.c @@ -146,6 +146,7 @@ void relocsym(Sym *s) { Reloc *r; + Sym *rs; Prog p; int32 i, off, siz, fl; vlong o; @@ -176,18 +177,35 @@ relocsym(Sym *s) switch(r->type) { default: o = 0; - if(archreloc(r, s, &o) < 0) + if(isobj || archreloc(r, s, &o) < 0) diag("unknown reloc %d", r->type); break; case D_ADDR: o = symaddr(r->sym) + r->add; + if(isobj && r->sym->type != SCONST) { + if(thechar == '6') + o = 0; + else { + // set up addend for eventual relocation via outer symbol + rs = r->sym; + while(rs->outer != nil) + rs = rs->outer; + o -= symaddr(rs); + } + } break; case D_PCREL: - // r->sym can be null when CALL $(constant) is transformed from absoulte PC to relative PC call. + // r->sym can be null when CALL $(constant) is transformed from absolute PC to relative PC call. o = 0; if(r->sym) o += symaddr(r->sym); o += r->add - (s->value + r->off + r->siz); + if(isobj && r->sym->type != SCONST) { + if(thechar == '6') + o = 0; + else + o = r->add - r->siz; + } break; case D_SIZE: o = r->sym->size + r->add; diff --git a/src/cmd/ld/elf.c b/src/cmd/ld/elf.c index d0b5fa8304..9793feea03 100644 --- a/src/cmd/ld/elf.c +++ b/src/cmd/ld/elf.c @@ -758,7 +758,8 @@ elfshbits(Section *sect) sh->flags |= SHF_EXECINSTR; if(sect->rwx & 2) sh->flags |= SHF_WRITE; - sh->addr = sect->vaddr; + if(!isobj) + sh->addr = sect->vaddr; sh->addralign = PtrSize; sh->size = sect->len; sh->off = sect->seg->fileoff + sect->vaddr - sect->seg->vaddr; @@ -766,6 +767,117 @@ elfshbits(Section *sect) return sh; } +ElfShdr* +elfshreloc(Section *sect) +{ + int typ; + ElfShdr *sh; + char *prefix; + char buf[100]; + + // If main section is SHT_NOBITS, nothing to relocate. + // Also nothing to relocate in .shstrtab. + if(sect->vaddr >= sect->seg->vaddr + sect->seg->filelen) + return nil; + if(strcmp(sect->name, ".shstrtab") == 0) + return nil; + + if(thechar == '6') { + prefix = ".rela"; + typ = SHT_RELA; + } else { + prefix = ".rel"; + typ = SHT_REL; + } + + snprint(buf, sizeof buf, "%s%s", prefix, sect->name); + sh = elfshname(buf); + sh->type = typ; + sh->entsize = PtrSize*(2+(typ==SHT_RELA)); + sh->link = elfshname(".symtab")->shnum; + sh->info = sect->elfsect->shnum; + sh->off = sect->reloff; + sh->size = sect->rellen; + sh->addralign = PtrSize; + return sh; +} + +void +elfrelocsect(Section *sect, Sym *first) +{ + Sym *sym, *rs; + int32 eaddr; + Reloc *r; + int64 add; + + // If main section is SHT_NOBITS, nothing to relocate. + // Also nothing to relocate in .shstrtab. + if(sect->vaddr >= sect->seg->vaddr + sect->seg->filelen) + return; + if(strcmp(sect->name, ".shstrtab") == 0) + return; + + sect->reloff = cpos(); + for(sym = first; sym != nil; sym = sym->next) { + if(!sym->reachable) + continue; + if(sym->value >= sect->vaddr) + break; + } + + eaddr = sect->vaddr + sect->len; + for(; sym != nil; sym = sym->next) { + if(!sym->reachable) + continue; + if(sym->value >= eaddr) + break; + cursym = sym; + + for(r = sym->r; r < sym->r+sym->nr; r++) { + // Ignore relocations handled by reloc already. + switch(r->type) { + case D_SIZE: + continue; + case D_ADDR: + case D_PCREL: + if(r->sym->type == SCONST) + continue; + break; + } + + add = r->add; + rs = r->sym; + while(rs->outer != nil) { + add += rs->value - rs->outer->value; + rs = rs->outer; + } + + if(rs->elfsym == 0) + diag("reloc %d to non-elf symbol %s (rs=%s) %d", r->type, r->sym->name, rs->name, rs->type); + + if(elfreloc1(r, sym->value - sect->vaddr + r->off, rs->elfsym, add) < 0) + diag("unsupported obj reloc %d/%d to %s", r->type, r->siz, r->sym->name); + } + } + + sect->rellen = cpos() - sect->reloff; +} + +void +elfemitreloc(void) +{ + Section *sect; + + while(cpos()&7) + cput(0); + + elfrelocsect(segtext.sect, textp); + for(sect=segtext.sect->next; sect!=nil; sect=sect->next) + elfrelocsect(sect, datap); + for(sect=segdata.sect; sect!=nil; sect=sect->next) + elfrelocsect(sect, datap); +} + void doelf(void) { @@ -800,6 +912,33 @@ doelf(void) addstring(shstrtab, ".gcbss"); addstring(shstrtab, ".gosymtab"); addstring(shstrtab, ".gopclntab"); + + if(isobj) { + debug['s'] = 0; + debug['d'] = 1; + + if(thechar == '6') { + addstring(shstrtab, ".rela.text"); + addstring(shstrtab, ".rela.rodata"); + addstring(shstrtab, ".rela.typelink"); + addstring(shstrtab, ".rela.gcdata"); + addstring(shstrtab, ".rela.gcbss"); + 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.gcdata"); + addstring(shstrtab, ".rel.gcbss"); + addstring(shstrtab, ".rel.gosymtab"); + addstring(shstrtab, ".rel.gopclntab"); + addstring(shstrtab, ".rel.noptrdata"); + addstring(shstrtab, ".rel.data"); + } + } if(!debug['s']) { addstring(shstrtab, ".symtab"); @@ -1004,6 +1143,14 @@ asmbelf(vlong symo) startva = INITTEXT - HEADR; resoff = ELFRESERVE; + + pph = nil; + if(isobj) { + /* skip program headers */ + eh->phoff = 0; + eh->phentsize = 0; + goto elfobj; + } /* program header info */ pph = newElfPhdr(); @@ -1238,17 +1385,29 @@ asmbelf(vlong symo) ph->flags = 0x2a00; // mprotect, randexec, emutramp disabled ph->align = PtrSize; +elfobj: sh = elfshname(".shstrtab"); sh->type = SHT_STRTAB; sh->addralign = 1; shsym(sh, lookup(".shstrtab", 0)); eh->shstrndx = sh->shnum; + // put these sections early in the list + elfshname(".symtab"); + elfshname(".strtab"); + for(sect=segtext.sect; sect!=nil; sect=sect->next) elfshbits(sect); for(sect=segdata.sect; sect!=nil; sect=sect->next) elfshbits(sect); + if(isobj) { + for(sect=segtext.sect; sect!=nil; sect=sect->next) + elfshreloc(sect); + for(sect=segdata.sect; sect!=nil; sect=sect->next) + elfshreloc(sect); + } + if(!debug['s']) { sh = elfshname(".symtab"); sh->type = SHT_SYMTAB; @@ -1265,7 +1424,9 @@ asmbelf(vlong symo) sh->size = elfstrsize; sh->addralign = 1; - dwarfaddelfheaders(); + // TODO(rsc): Enable for isobj too, once we know it works. + if(!isobj) + dwarfaddelfheaders(); } /* Main header */ @@ -1288,14 +1449,20 @@ asmbelf(vlong symo) if(flag_shared) eh->type = ET_DYN; + else if(isobj) + eh->type = ET_REL; else eh->type = ET_EXEC; - eh->version = EV_CURRENT; - eh->entry = entryvalue(); + if(!isobj) + eh->entry = entryvalue(); - pph->filesz = eh->phnum * eh->phentsize; - pph->memsz = pph->filesz; + eh->version = EV_CURRENT; + + if(pph != nil) { + pph->filesz = eh->phnum * eh->phentsize; + pph->memsz = pph->filesz; + } cseek(0); a = 0; @@ -1304,12 +1471,14 @@ asmbelf(vlong symo) a += elfwriteshdrs(); if(!debug['d']) a += elfwriteinterp(); - if(HEADTYPE == Hnetbsd) - a += elfwritenetbsdsig(); - if(HEADTYPE == Hopenbsd) - a += elfwriteopenbsdsig(); - if(buildinfolen > 0) - a += elfwritebuildinfo(); + if(!isobj) { + if(HEADTYPE == Hnetbsd) + a += elfwritenetbsdsig(); + if(HEADTYPE == Hopenbsd) + a += elfwriteopenbsdsig(); + if(buildinfolen > 0) + a += elfwritebuildinfo(); + } if(a > ELFRESERVE) diag("ELFRESERVE too small: %d > %d", a, ELFRESERVE); } diff --git a/src/cmd/ld/elf.h b/src/cmd/ld/elf.h index 9f1e16a88a..3e22125b21 100644 --- a/src/cmd/ld/elf.h +++ b/src/cmd/ld/elf.h @@ -1005,6 +1005,7 @@ extern char linuxdynld[]; extern char freebsddynld[]; extern char netbsddynld[]; extern char openbsddynld[]; +int elfreloc1(Reloc*, vlong off, int32 elfsym, vlong add); EXTERN int elfstrsize; EXTERN char* elfstrdat; diff --git a/src/cmd/ld/lib.c b/src/cmd/ld/lib.c index 2c59fb8de2..d388236996 100644 --- a/src/cmd/ld/lib.c +++ b/src/cmd/ld/lib.c @@ -1400,6 +1400,19 @@ headtype(char *name) return -1; // not reached } +char* +headstr(int v) +{ + static char buf[20]; + int i; + + for(i=0; headers[i].name; i++) + if(v == headers[i].val) + return headers[i].name; + snprint(buf, sizeof buf, "%d", v); + return buf; +} + void undef(void) { diff --git a/src/cmd/ld/lib.h b/src/cmd/ld/lib.h index 0f3ce7f693..c134729f66 100644 --- a/src/cmd/ld/lib.h +++ b/src/cmd/ld/lib.h @@ -109,6 +109,8 @@ struct Section Section *next; // in segment list Segment *seg; struct Elf64_Shdr *elfsect; + uvlong reloff; + uvlong rellen; }; extern char symname[]; @@ -137,6 +139,7 @@ EXTERN char* thestring; EXTERN int ndynexp; EXTERN int havedynamic; EXTERN int iscgo; +EXTERN int isobj; EXTERN int elfglobalsymndx; EXTERN int flag_race; EXTERN int flag_shared; @@ -302,6 +305,7 @@ EXTERN char* headstring; extern Header headers[]; int headtype(char*); +char* headstr(int); void setheadtype(char*); int Yconv(Fmt*); diff --git a/src/cmd/ld/symtab.c b/src/cmd/ld/symtab.c index 12fad085d6..42e367291d 100644 --- a/src/cmd/ld/symtab.c +++ b/src/cmd/ld/symtab.c @@ -126,6 +126,8 @@ putelfsym(Sym *x, char *s, int t, vlong addr, vlong size, int ver, Sym *go) return; off = putelfstr(s); + if(isobj) + addr -= xo->sect->vaddr; putelfsyment(off, addr, size, (bind<<4)|(type&0xf), xo->sect->elfsect->shnum, (x->type & SHIDDEN) ? 2 : 0); x->elfsym = numelfsym++; }