diff options
Diffstat (limited to 'src/cmd/ld/elf.c')
-rw-r--r-- | src/cmd/ld/elf.c | 954 |
1 files changed, 910 insertions, 44 deletions
diff --git a/src/cmd/ld/elf.c b/src/cmd/ld/elf.c index de9e6b854..630906653 100644 --- a/src/cmd/ld/elf.c +++ b/src/cmd/ld/elf.c @@ -11,7 +11,7 @@ * in order to write the code just once. The 64-bit data structure is * written in the 32-bit format on the 32-bit machines. */ -#define NSECT 32 +#define NSECT 48 int iself; @@ -31,6 +31,8 @@ struct Elfstring static Elfstring elfstr[100]; static int nelfstr; +static char buildinfo[32]; + /* Initialize the global variable that describes the ELF header. It will be updated as we write section and prog headers. @@ -52,6 +54,11 @@ elfinit(void) break; // 32-bit architectures + case '5': + // we only use EABI on linux/arm + if(HEADTYPE == Hlinux) + hdr.flags = 0x5000002; // has entry point, Version5 EABI + // fallthrough default: hdr.phoff = ELF32HDRSIZE; /* Must be be ELF32HDRSIZE: first PHdr must follow ELF header */ hdr.shoff = ELF32HDRSIZE; /* Will move as we add PHeaders */ @@ -190,19 +197,13 @@ newElfPhdr(void) } ElfShdr* -newElfShstrtab(vlong name) -{ - hdr.shstrndx = hdr.shnum; - return newElfShdr(name); -} - -ElfShdr* newElfShdr(vlong name) { ElfShdr *e; e = mal(sizeof *e); e->name = name; + e->shnum = hdr.shnum; if (hdr.shnum >= NSECT) { diag("too many shdrs"); } else { @@ -332,26 +333,53 @@ elfinterp(ElfShdr *sh, uint64 startva, uint64 resoff, char *p) } int -elfwriteinterp(vlong stridx) +elfwriteinterp(void) { - ElfShdr *sh = nil; - int i; - - for(i = 0; i < hdr.shnum; i++) - if(shdr[i]->name == stridx) - sh = shdr[i]; - if(sh == nil || interp == nil) - return 0; - + ElfShdr *sh; + + sh = elfshname(".interp"); cseek(sh->off); cwrite(interp, sh->size); return sh->size; } -// Defined in NetBSD's sys/exec_elf.h -#define ELF_NOTE_TYPE_NETBSD_TAG 1 +int +elfnote(ElfShdr *sh, uint64 startva, uint64 resoff, int sz) +{ + uint64 n; + + n = sizeof(Elf_Note) + sz + resoff % 4; + + sh->type = SHT_NOTE; + sh->flags = SHF_ALLOC; + sh->addralign = 4; + sh->addr = startva + resoff - n; + sh->off = resoff - n; + sh->size = n; + + return n; +} + +ElfShdr * +elfwritenotehdr(char *str, uint32 namesz, uint32 descsz, uint32 tag) +{ + ElfShdr *sh; + + sh = elfshname(str); + + // Write Elf_Note header. + cseek(sh->off); + LPUT(namesz); + LPUT(descsz); + LPUT(tag); + + return sh; +} + +// NetBSD Signature (as per sys/exec_elf.h) #define ELF_NOTE_NETBSD_NAMESZ 7 #define ELF_NOTE_NETBSD_DESCSZ 4 +#define ELF_NOTE_NETBSD_TAG 1 #define ELF_NOTE_NETBSD_NAME "NetBSD\0\0" #define ELF_NOTE_NETBSD_VERSION 599000000 /* NetBSD 5.99 */ @@ -360,37 +388,131 @@ elfnetbsdsig(ElfShdr *sh, uint64 startva, uint64 resoff) { int n; - n = sizeof(Elf_Note) + ELF_NOTE_NETBSD_NAMESZ + ELF_NOTE_NETBSD_DESCSZ + 1; - n += resoff % 4; - sh->addr = startva + resoff - n; - sh->off = resoff - n; - sh->size = n; - - return n; + n = ELF_NOTE_NETBSD_NAMESZ + ELF_NOTE_NETBSD_DESCSZ + 1; + return elfnote(sh, startva, resoff, n); } int -elfwritenetbsdsig(vlong stridx) { - ElfShdr *sh = nil; - int i; +elfwritenetbsdsig(void) +{ + ElfShdr *sh; - for(i = 0; i < hdr.shnum; i++) - if(shdr[i]->name == stridx) - sh = shdr[i]; + // Write Elf_Note header. + sh = elfwritenotehdr(".note.netbsd.ident", ELF_NOTE_NETBSD_NAMESZ, ELF_NOTE_NETBSD_DESCSZ, ELF_NOTE_NETBSD_TAG); if(sh == nil) return 0; - // Write Elf_Note header followed by NetBSD string. - cseek(sh->off); - LPUT(ELF_NOTE_NETBSD_NAMESZ); - LPUT(ELF_NOTE_NETBSD_DESCSZ); - LPUT(ELF_NOTE_TYPE_NETBSD_TAG); - cwrite(ELF_NOTE_NETBSD_NAME, 8); + // Followed by NetBSD string and version. + cwrite(ELF_NOTE_NETBSD_NAME, ELF_NOTE_NETBSD_NAMESZ + 1); LPUT(ELF_NOTE_NETBSD_VERSION); return sh->size; } +// OpenBSD Signature +#define ELF_NOTE_OPENBSD_NAMESZ 8 +#define ELF_NOTE_OPENBSD_DESCSZ 4 +#define ELF_NOTE_OPENBSD_TAG 1 +#define ELF_NOTE_OPENBSD_NAME "OpenBSD\0" +#define ELF_NOTE_OPENBSD_VERSION 0 + +int +elfopenbsdsig(ElfShdr *sh, uint64 startva, uint64 resoff) +{ + int n; + + n = ELF_NOTE_OPENBSD_NAMESZ + ELF_NOTE_OPENBSD_DESCSZ; + return elfnote(sh, startva, resoff, n); +} + +int +elfwriteopenbsdsig(void) +{ + ElfShdr *sh; + + // Write Elf_Note header. + sh = elfwritenotehdr(".note.openbsd.ident", ELF_NOTE_OPENBSD_NAMESZ, ELF_NOTE_OPENBSD_DESCSZ, ELF_NOTE_OPENBSD_TAG); + if(sh == nil) + return 0; + + // Followed by OpenBSD string and version. + cwrite(ELF_NOTE_OPENBSD_NAME, ELF_NOTE_OPENBSD_NAMESZ); + LPUT(ELF_NOTE_OPENBSD_VERSION); + + return sh->size; +} + +void +addbuildinfo(char *val) +{ + char *ov; + int i, b, j; + + if(val[0] != '0' || val[1] != 'x') { + fprint(2, "%s: -B argument must start with 0x: %s\n", argv0, val); + exits("usage"); + } + ov = val; + val += 2; + i = 0; + while(*val != '\0') { + if(val[1] == '\0') { + fprint(2, "%s: -B argument must have even number of digits: %s\n", argv0, ov); + exits("usage"); + } + b = 0; + for(j = 0; j < 2; j++, val++) { + b *= 16; + if(*val >= '0' && *val <= '9') + b += *val - '0'; + else if(*val >= 'a' && *val <= 'f') + b += *val - 'a' + 10; + else if(*val >= 'A' && *val <= 'F') + b += *val - 'A' + 10; + else { + fprint(2, "%s: -B argument contains invalid hex digit %c: %s\n", argv0, *val, ov); + exits("usage"); + } + } + if(i >= nelem(buildinfo)) { + fprint(2, "%s: -B option too long (max %d digits): %s\n", argv0, (int)nelem(buildinfo), ov); + exits("usage"); + } + buildinfo[i++] = b; + } + buildinfolen = i; +} + +// Build info note +#define ELF_NOTE_BUILDINFO_NAMESZ 4 +#define ELF_NOTE_BUILDINFO_TAG 3 +#define ELF_NOTE_BUILDINFO_NAME "GNU\0" + +int +elfbuildinfo(ElfShdr *sh, uint64 startva, uint64 resoff) +{ + int n; + + n = ELF_NOTE_BUILDINFO_NAMESZ + rnd(buildinfolen, 4); + return elfnote(sh, startva, resoff, n); +} + +int +elfwritebuildinfo(void) +{ + ElfShdr *sh; + + sh = elfwritenotehdr(".note.gnu.build-id", ELF_NOTE_BUILDINFO_NAMESZ, buildinfolen, ELF_NOTE_BUILDINFO_TAG); + if(sh == nil) + return 0; + + cwrite(ELF_NOTE_BUILDINFO_NAME, ELF_NOTE_BUILDINFO_NAMESZ); + cwrite(buildinfo, buildinfolen); + cwrite("\0\0\0", rnd(buildinfolen, 4) - buildinfolen); + + return sh->size; +} + extern int nelfsym; int elfverneed; @@ -584,18 +706,18 @@ elfphload(Segment *seg) } ElfShdr* -elfshbits(Section *sect) +elfshname(char *name) { int i, off; ElfShdr *sh; for(i=0; i<nelfstr; i++) { - if(strcmp(sect->name, elfstr[i].s) == 0) { + if(strcmp(name, elfstr[i].s) == 0) { off = elfstr[i].off; goto found; } } - diag("cannot find elf name %s", sect->name); + diag("cannot find elf name %s", name); errorexit(); return nil; @@ -605,8 +727,30 @@ found: if(sh->name == off) return sh; } - + sh = newElfShdr(off); + return sh; +} + +ElfShdr* +elfshalloc(Section *sect) +{ + ElfShdr *sh; + + sh = elfshname(sect->name); + sect->elfsect = sh; + return sh; +} + +ElfShdr* +elfshbits(Section *sect) +{ + ElfShdr *sh; + + sh = elfshalloc(sect); + if(sh->type > 0) + return sh; + if(sect->vaddr < sect->seg->vaddr + sect->seg->filelen) sh->type = SHT_PROGBITS; else @@ -616,10 +760,732 @@ found: 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; + + 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) +{ + Sym *s, *shstrtab, *dynstr; + + if(!iself) + return; + + /* predefine strings we need for section headers */ + shstrtab = lookup(".shstrtab", 0); + shstrtab->type = SELFROSECT; + shstrtab->reachable = 1; + + addstring(shstrtab, ""); + addstring(shstrtab, ".text"); + addstring(shstrtab, ".noptrdata"); + addstring(shstrtab, ".data"); + addstring(shstrtab, ".bss"); + addstring(shstrtab, ".noptrbss"); + if(HEADTYPE == Hnetbsd) + addstring(shstrtab, ".note.netbsd.ident"); + if(HEADTYPE == Hopenbsd) + addstring(shstrtab, ".note.openbsd.ident"); + if(buildinfolen > 0) + addstring(shstrtab, ".note.gnu.build-id"); + addstring(shstrtab, ".elfdata"); + addstring(shstrtab, ".rodata"); + addstring(shstrtab, ".typelink"); + if(flag_shared) + addstring(shstrtab, ".data.rel.ro"); + addstring(shstrtab, ".gcdata"); + 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"); + addstring(shstrtab, ".strtab"); + dwarfaddshstrings(shstrtab); + } + addstring(shstrtab, ".shstrtab"); + + if(!debug['d']) { /* -d suppresses dynamic loader format */ + addstring(shstrtab, ".interp"); + addstring(shstrtab, ".hash"); + addstring(shstrtab, ".got"); + addstring(shstrtab, ".got.plt"); + addstring(shstrtab, ".dynamic"); + addstring(shstrtab, ".dynsym"); + addstring(shstrtab, ".dynstr"); + if(thechar == '6') { + addstring(shstrtab, ".rela"); + addstring(shstrtab, ".rela.plt"); + } else { + addstring(shstrtab, ".rel"); + addstring(shstrtab, ".rel.plt"); + } + addstring(shstrtab, ".plt"); + addstring(shstrtab, ".gnu.version"); + addstring(shstrtab, ".gnu.version_r"); + + /* dynamic symbol table - first entry all zeros */ + s = lookup(".dynsym", 0); + s->type = SELFROSECT; + s->reachable = 1; + if(thechar == '6') + s->size += ELF64SYMSIZE; + else + s->size += ELF32SYMSIZE; + + /* dynamic string table */ + s = lookup(".dynstr", 0); + s->type = SELFROSECT; + s->reachable = 1; + if(s->size == 0) + addstring(s, ""); + dynstr = s; + + /* relocation table */ + if(thechar == '6') + s = lookup(".rela", 0); + else + 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; + + s = lookup(".got.plt", 0); + s->reachable = 1; + s->type = SELFSECT; // writable + + s = lookup(".plt", 0); + s->reachable = 1; + s->type = SELFROSECT; + + elfsetupplt(); + + if(thechar == '6') + s = lookup(".rela.plt", 0); + else + 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; + + /* 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)); + if(thechar == '6') + elfwritedynent(s, DT_SYMENT, ELF64SYMSIZE); + else + elfwritedynent(s, DT_SYMENT, ELF32SYMSIZE); + elfwritedynentsym(s, DT_STRTAB, lookup(".dynstr", 0)); + elfwritedynentsymsize(s, DT_STRSZ, lookup(".dynstr", 0)); + if(thechar == '6') { + elfwritedynentsym(s, DT_RELA, lookup(".rela", 0)); + elfwritedynentsymsize(s, DT_RELASZ, lookup(".rela", 0)); + elfwritedynent(s, DT_RELAENT, ELF64RELASIZE); + } else { + 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)); + + if(thechar == '6') { + elfwritedynent(s, DT_PLTREL, DT_RELA); + elfwritedynentsymsize(s, DT_PLTRELSZ, lookup(".rela.plt", 0)); + elfwritedynentsym(s, DT_JMPREL, lookup(".rela.plt", 0)); + } else { + 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); + + if(flag_shared) { + Sym *init_sym = lookup(LIBINITENTRY, 0); + if(init_sym->type != STEXT) + diag("entry not text: %s", init_sym->name); + elfwritedynentsym(s, DT_INIT, init_sym); + } + + // Do not write DT_NULL. elfdynhash will finish it. + } +} + +void +shsym(ElfShdr *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(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 +asmbelfsetup(void) +{ + Section *sect; + + /* This null SHdr must appear before all others */ + elfshname(""); + + for(sect=segtext.sect; sect!=nil; sect=sect->next) + elfshalloc(sect); + for(sect=segdata.sect; sect!=nil; sect=sect->next) + elfshalloc(sect); +} + +void +asmbelf(vlong symo) +{ + int a, o; + vlong startva, resoff; + ElfEhdr *eh; + ElfPhdr *ph, *pph, *pnote; + ElfShdr *sh; + Section *sect; + + eh = getElfEhdr(); + switch(thechar) { + default: + diag("unknown architecture in asmbelf"); + errorexit(); + case '5': + eh->machine = EM_ARM; + break; + case '6': + eh->machine = EM_X86_64; + break; + case '8': + eh->machine = EM_386; + break; + } + + startva = INITTEXT - HEADR; + resoff = ELFRESERVE; + + pph = nil; + if(isobj) { + /* skip program headers */ + eh->phoff = 0; + eh->phentsize = 0; + goto elfobj; + } + + /* 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 = elfshname(".interp"); + sh->type = SHT_PROGBITS; + sh->flags = SHF_ALLOC; + sh->addralign = 1; + if(interpreter == nil) { + switch(HEADTYPE) { + case Hlinux: + interpreter = linuxdynld; + break; + case Hfreebsd: + interpreter = freebsddynld; + break; + case Hnetbsd: + interpreter = netbsddynld; + break; + case Hopenbsd: + interpreter = openbsddynld; + break; + } + } + resoff -= elfinterp(sh, startva, resoff, interpreter); + + ph = newElfPhdr(); + ph->type = PT_INTERP; + ph->flags = PF_R; + phsh(ph, sh); + } + + pnote = nil; + if(HEADTYPE == Hnetbsd || HEADTYPE == Hopenbsd) { + sh = nil; + switch(HEADTYPE) { + case Hnetbsd: + sh = elfshname(".note.netbsd.ident"); + resoff -= elfnetbsdsig(sh, startva, resoff); + break; + case Hopenbsd: + sh = elfshname(".note.openbsd.ident"); + resoff -= elfopenbsdsig(sh, startva, resoff); + break; + } + + pnote = newElfPhdr(); + pnote->type = PT_NOTE; + pnote->flags = PF_R; + phsh(pnote, sh); + } + + if(buildinfolen > 0) { + sh = elfshname(".note.gnu.build-id"); + resoff -= elfbuildinfo(sh, startva, resoff); + + if(pnote == nil) { + pnote = newElfPhdr(); + pnote->type = PT_NOTE; + pnote->flags = PF_R; + } + phsh(pnote, sh); + } + + // Additions to the reserved area must be above this line. + USED(resoff); + + elfphload(&segtext); + elfphload(&segdata); + + /* Dynamic linking sections */ + if(!debug['d']) { /* -d suppresses dynamic loader format */ + sh = elfshname(".dynsym"); + sh->type = SHT_DYNSYM; + sh->flags = SHF_ALLOC; + if(PtrSize == 8) + sh->entsize = ELF64SYMSIZE; + else + sh->entsize = ELF32SYMSIZE; + sh->addralign = PtrSize; + sh->link = elfshname(".dynstr")->shnum; + // sh->info = index of first non-local symbol (number of local symbols) + shsym(sh, lookup(".dynsym", 0)); + + sh = elfshname(".dynstr"); + sh->type = SHT_STRTAB; + sh->flags = SHF_ALLOC; + sh->addralign = 1; + shsym(sh, lookup(".dynstr", 0)); + + if(elfverneed) { + sh = elfshname(".gnu.version"); + sh->type = SHT_GNU_VERSYM; + sh->flags = SHF_ALLOC; + sh->addralign = 2; + sh->link = elfshname(".dynsym")->shnum; + sh->entsize = 2; + shsym(sh, lookup(".gnu.version", 0)); + + sh = elfshname(".gnu.version_r"); + sh->type = SHT_GNU_VERNEED; + sh->flags = SHF_ALLOC; + sh->addralign = PtrSize; + sh->info = elfverneed; + sh->link = elfshname(".dynstr")->shnum; + shsym(sh, lookup(".gnu.version_r", 0)); + } + + switch(eh->machine) { + case EM_X86_64: + sh = elfshname(".rela.plt"); + sh->type = SHT_RELA; + sh->flags = SHF_ALLOC; + sh->entsize = ELF64RELASIZE; + sh->addralign = PtrSize; + sh->link = elfshname(".dynsym")->shnum; + sh->info = elfshname(".plt")->shnum; + shsym(sh, lookup(".rela.plt", 0)); + + sh = elfshname(".rela"); + sh->type = SHT_RELA; + sh->flags = SHF_ALLOC; + sh->entsize = ELF64RELASIZE; + sh->addralign = 8; + sh->link = elfshname(".dynsym")->shnum; + shsym(sh, lookup(".rela", 0)); + break; + + default: + sh = elfshname(".rel.plt"); + sh->type = SHT_REL; + sh->flags = SHF_ALLOC; + sh->entsize = ELF32RELSIZE; + sh->link = elfshname(".dynsym")->shnum; + shsym(sh, lookup(".rel.plt", 0)); + + sh = elfshname(".rel"); + sh->type = SHT_REL; + sh->flags = SHF_ALLOC; + sh->entsize = ELF32RELSIZE; + sh->addralign = 4; + sh->link = elfshname(".dynsym")->shnum; + shsym(sh, lookup(".rel", 0)); + break; + } + + sh = elfshname(".plt"); + sh->type = SHT_PROGBITS; + sh->flags = SHF_ALLOC+SHF_EXECINSTR; + if(eh->machine == EM_X86_64) + sh->entsize = 16; + else + sh->entsize = 4; + sh->addralign = 4; + shsym(sh, lookup(".plt", 0)); + + sh = elfshname(".got"); + sh->type = SHT_PROGBITS; + sh->flags = SHF_ALLOC+SHF_WRITE; + sh->entsize = PtrSize; + sh->addralign = PtrSize; + shsym(sh, lookup(".got", 0)); + + sh = elfshname(".got.plt"); + sh->type = SHT_PROGBITS; + sh->flags = SHF_ALLOC+SHF_WRITE; + sh->entsize = PtrSize; + sh->addralign = PtrSize; + shsym(sh, lookup(".got.plt", 0)); + + sh = elfshname(".hash"); + sh->type = SHT_HASH; + sh->flags = SHF_ALLOC; + sh->entsize = 4; + sh->addralign = PtrSize; + sh->link = elfshname(".dynsym")->shnum; + shsym(sh, lookup(".hash", 0)); + + /* sh and PT_DYNAMIC for .dynamic section */ + sh = elfshname(".dynamic"); + sh->type = SHT_DYNAMIC; + sh->flags = SHF_ALLOC+SHF_WRITE; + sh->entsize = 2*PtrSize; + sh->addralign = PtrSize; + sh->link = elfshname(".dynstr")->shnum; + 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). + */ + // Do not emit PT_TLS for OpenBSD since ld.so(1) does + // not currently support it. This is handled + // appropriately in runtime/cgo. + if(tlsoffset != 0 && HEADTYPE != Hopenbsd) { + ph = newElfPhdr(); + ph->type = PT_TLS; + ph->flags = PF_R; + ph->memsz = -tlsoffset; + ph->align = PtrSize; + } + } + + if(HEADTYPE == Hlinux) { + ph = newElfPhdr(); + ph->type = PT_GNU_STACK; + ph->flags = PF_W+PF_R; + ph->align = PtrSize; + + ph = newElfPhdr(); + ph->type = PT_PAX_FLAGS; + 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 + if(!debug['s']) { + 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; + sh->off = symo; + sh->size = symsize; + sh->addralign = PtrSize; + sh->entsize = 8+2*PtrSize; + sh->link = elfshname(".strtab")->shnum; + sh->info = elfglobalsymndx; + + sh = elfshname(".strtab"); + sh->type = SHT_STRTAB; + sh->off = symo+symsize; + sh->size = elfstrsize; + sh->addralign = 1; + + // TODO(rsc): Enable for isobj too, once we know it works. + if(!isobj) + dwarfaddelfheaders(); + } + + /* Main header */ + eh->ident[EI_MAG0] = '\177'; + eh->ident[EI_MAG1] = 'E'; + eh->ident[EI_MAG2] = 'L'; + eh->ident[EI_MAG3] = 'F'; + if(HEADTYPE == Hfreebsd) + eh->ident[EI_OSABI] = ELFOSABI_FREEBSD; + else if(HEADTYPE == Hnetbsd) + eh->ident[EI_OSABI] = ELFOSABI_NETBSD; + else if(HEADTYPE == Hopenbsd) + eh->ident[EI_OSABI] = ELFOSABI_OPENBSD; + if(PtrSize == 8) + eh->ident[EI_CLASS] = ELFCLASS64; + else + eh->ident[EI_CLASS] = ELFCLASS32; + eh->ident[EI_DATA] = ELFDATA2LSB; + eh->ident[EI_VERSION] = EV_CURRENT; + + if(flag_shared) + eh->type = ET_DYN; + else if(isobj) + eh->type = ET_REL; + else + eh->type = ET_EXEC; + + if(!isobj) + eh->entry = entryvalue(); + + eh->version = EV_CURRENT; + + if(pph != nil) { + pph->filesz = eh->phnum * eh->phentsize; + pph->memsz = pph->filesz; + } + + cseek(0); + a = 0; + a += elfwritehdr(); + a += elfwritephdrs(); + a += elfwriteshdrs(); + if(!debug['d']) + a += elfwriteinterp(); + if(!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); +} |