// Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. #include "l.h" #include "lib.h" #include "../ld/elf.h" /* * We use the 64-bit data structures on both 32- and 64-bit machines * 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 48 int iself; static int elf64; static ElfEhdr hdr; static ElfPhdr *phdr[NSECT]; static ElfShdr *shdr[NSECT]; static char *interp; typedef struct Elfstring Elfstring; struct Elfstring { char *s; int off; }; 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. */ void elfinit(void) { iself = 1; switch(thechar) { // 64-bit architectures case '6': elf64 = 1; hdr.phoff = ELF64HDRSIZE; /* Must be be ELF64HDRSIZE: first PHdr must follow ELF header */ hdr.shoff = ELF64HDRSIZE; /* Will move as we add PHeaders */ hdr.ehsize = ELF64HDRSIZE; /* Must be ELF64HDRSIZE */ hdr.phentsize = ELF64PHDRSIZE; /* Must be ELF64PHDRSIZE */ hdr.shentsize = ELF64SHDRSIZE; /* Must be ELF64SHDRSIZE */ break; // 32-bit architectures case '5': // we use EABI on both linux/arm and freebsd/arm. if(HEADTYPE == Hlinux || HEADTYPE == Hfreebsd) 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 */ hdr.ehsize = ELF32HDRSIZE; /* Must be ELF32HDRSIZE */ hdr.phentsize = ELF32PHDRSIZE; /* Must be ELF32PHDRSIZE */ hdr.shentsize = ELF32SHDRSIZE; /* Must be ELF32SHDRSIZE */ } } void elf64phdr(ElfPhdr *e) { LPUT(e->type); LPUT(e->flags); VPUT(e->off); VPUT(e->vaddr); VPUT(e->paddr); VPUT(e->filesz); VPUT(e->memsz); VPUT(e->align); } void elf32phdr(ElfPhdr *e) { int frag; if(e->type == PT_LOAD) { // Correct ELF loaders will do this implicitly, // but buggy ELF loaders like the one in some // versions of QEMU won't. frag = e->vaddr&(e->align-1); e->off -= frag; e->vaddr -= frag; e->paddr -= frag; e->filesz += frag; e->memsz += frag; } LPUT(e->type); LPUT(e->off); LPUT(e->vaddr); LPUT(e->paddr); LPUT(e->filesz); LPUT(e->memsz); LPUT(e->flags); LPUT(e->align); } void elf64shdr(ElfShdr *e) { LPUT(e->name); LPUT(e->type); VPUT(e->flags); VPUT(e->addr); VPUT(e->off); VPUT(e->size); LPUT(e->link); LPUT(e->info); VPUT(e->addralign); VPUT(e->entsize); } void elf32shdr(ElfShdr *e) { LPUT(e->name); LPUT(e->type); LPUT(e->flags); LPUT(e->addr); LPUT(e->off); LPUT(e->size); LPUT(e->link); LPUT(e->info); LPUT(e->addralign); LPUT(e->entsize); } uint32 elfwriteshdrs(void) { int i; if (elf64) { for (i = 0; i < hdr.shnum; i++) elf64shdr(shdr[i]); return hdr.shnum * ELF64SHDRSIZE; } for (i = 0; i < hdr.shnum; i++) elf32shdr(shdr[i]); return hdr.shnum * ELF32SHDRSIZE; } void elfsetstring(char *s, int off) { if(nelfstr >= nelem(elfstr)) { diag("too many elf strings"); errorexit(); } elfstr[nelfstr].s = s; elfstr[nelfstr].off = off; nelfstr++; } uint32 elfwritephdrs(void) { int i; if (elf64) { for (i = 0; i < hdr.phnum; i++) elf64phdr(phdr[i]); return hdr.phnum * ELF64PHDRSIZE; } for (i = 0; i < hdr.phnum; i++) elf32phdr(phdr[i]); return hdr.phnum * ELF32PHDRSIZE; } ElfPhdr* newElfPhdr(void) { ElfPhdr *e; e = mal(sizeof *e); if (hdr.phnum >= NSECT) diag("too many phdrs"); else phdr[hdr.phnum++] = e; if (elf64) hdr.shoff += ELF64PHDRSIZE; else hdr.shoff += ELF32PHDRSIZE; return e; } 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 { shdr[hdr.shnum++] = e; } return e; } ElfEhdr* getElfEhdr(void) { return &hdr; } uint32 elf64writehdr(void) { int i; for (i = 0; i < EI_NIDENT; i++) cput(hdr.ident[i]); WPUT(hdr.type); WPUT(hdr.machine); LPUT(hdr.version); VPUT(hdr.entry); VPUT(hdr.phoff); VPUT(hdr.shoff); LPUT(hdr.flags); WPUT(hdr.ehsize); WPUT(hdr.phentsize); WPUT(hdr.phnum); WPUT(hdr.shentsize); WPUT(hdr.shnum); WPUT(hdr.shstrndx); return ELF64HDRSIZE; } uint32 elf32writehdr(void) { int i; for (i = 0; i < EI_NIDENT; i++) cput(hdr.ident[i]); WPUT(hdr.type); WPUT(hdr.machine); LPUT(hdr.version); LPUT(hdr.entry); LPUT(hdr.phoff); LPUT(hdr.shoff); LPUT(hdr.flags); WPUT(hdr.ehsize); WPUT(hdr.phentsize); WPUT(hdr.phnum); WPUT(hdr.shentsize); WPUT(hdr.shnum); WPUT(hdr.shstrndx); return ELF32HDRSIZE; } uint32 elfwritehdr(void) { if(elf64) return elf64writehdr(); return elf32writehdr(); } /* Taken directly from the definition document for ELF64 */ uint32 elfhash(uchar *name) { uint32 h = 0, g; while (*name) { h = (h << 4) + *name++; if (g = h & 0xf0000000) h ^= g >> 24; h &= 0x0fffffff; } return h; } void elfwritedynent(LSym *s, int tag, uint64 val) { if(elf64) { adduint64(ctxt, s, tag); adduint64(ctxt, s, val); } else { adduint32(ctxt, s, tag); adduint32(ctxt, s, val); } } void elfwritedynentsym(LSym *s, int tag, LSym *t) { if(elf64) adduint64(ctxt, s, tag); else adduint32(ctxt, s, tag); addaddr(ctxt, s, t); } void elfwritedynentsymsize(LSym *s, int tag, LSym *t) { if(elf64) adduint64(ctxt, s, tag); else adduint32(ctxt, s, tag); addsize(ctxt, s, t); } int elfinterp(ElfShdr *sh, uint64 startva, uint64 resoff, char *p) { int n; interp = p; n = strlen(interp)+1; sh->addr = startva + resoff - n; sh->off = resoff - n; sh->size = n; return n; } int elfwriteinterp(void) { ElfShdr *sh; sh = elfshname(".interp"); cseek(sh->off); cwrite(interp, sh->size); return sh->size; } 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 - resoff % 4; 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 */ int elfnetbsdsig(ElfShdr *sh, uint64 startva, uint64 resoff) { int n; n = rnd(ELF_NOTE_NETBSD_NAMESZ, 4) + rnd(ELF_NOTE_NETBSD_DESCSZ, 4); return elfnote(sh, startva, resoff, n); } int elfwritenetbsdsig(void) { ElfShdr *sh; // 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; // 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; typedef struct Elfaux Elfaux; typedef struct Elflib Elflib; struct Elflib { Elflib *next; Elfaux *aux; char *file; }; struct Elfaux { Elfaux *next; int num; char *vers; }; Elfaux* addelflib(Elflib **list, char *file, char *vers) { Elflib *lib; Elfaux *aux; for(lib=*list; lib; lib=lib->next) if(strcmp(lib->file, file) == 0) goto havelib; lib = mal(sizeof *lib); lib->next = *list; lib->file = file; *list = lib; havelib: for(aux=lib->aux; aux; aux=aux->next) if(strcmp(aux->vers, vers) == 0) goto haveaux; aux = mal(sizeof *aux); aux->next = lib->aux; aux->vers = vers; lib->aux = aux; haveaux: return aux; } void elfdynhash(void) { LSym *s, *sy, *dynstr; int i, j, nbucket, b, nfile; uint32 hc, *chain, *buckets; int nsym; char *name; Elfaux **need; Elflib *needlib; Elflib *l; Elfaux *x; if(!iself) return; nsym = nelfsym; s = linklookup(ctxt, ".hash", 0); s->type = SELFROSECT; s->reachable = 1; i = nsym; nbucket = 1; while(i > 0) { ++nbucket; i >>= 1; } needlib = nil; need = malloc(nsym * sizeof need[0]); chain = malloc(nsym * sizeof chain[0]); buckets = malloc(nbucket * sizeof buckets[0]); if(need == nil || chain == nil || buckets == nil) { ctxt->cursym = nil; diag("out of memory"); errorexit(); } memset(need, 0, nsym * sizeof need[0]); memset(chain, 0, nsym * sizeof chain[0]); memset(buckets, 0, nbucket * sizeof buckets[0]); for(sy=ctxt->allsym; sy!=S; sy=sy->allsym) { if (sy->dynid <= 0) continue; if(sy->dynimpvers) need[sy->dynid] = addelflib(&needlib, sy->dynimplib, sy->dynimpvers); name = sy->extname; hc = elfhash((uchar*)name); b = hc % nbucket; chain[sy->dynid] = buckets[b]; buckets[b] = sy->dynid; } adduint32(ctxt, s, nbucket); adduint32(ctxt, s, nsym); for(i = 0; inext) { nfile++; // header adduint16(ctxt, s, 1); // table version j = 0; for(x=l->aux; x; x=x->next) j++; adduint16(ctxt, s, j); // aux count adduint32(ctxt, s, addstring(dynstr, l->file)); // file string offset adduint32(ctxt, s, 16); // offset from header to first aux if(l->next) adduint32(ctxt, s, 16+j*16); // offset from this header to next else adduint32(ctxt, s, 0); for(x=l->aux; x; x=x->next) { x->num = i++; // aux struct adduint32(ctxt, s, elfhash((uchar*)x->vers)); // hash adduint16(ctxt, s, 0); // flags adduint16(ctxt, s, x->num); // other - index we refer to this by adduint32(ctxt, s, addstring(dynstr, x->vers)); // version string offset if(x->next) adduint32(ctxt, s, 16); // offset from this aux to next else adduint32(ctxt, s, 0); } } // version references s = linklookup(ctxt, ".gnu.version", 0); for(i=0; inum); } free(need); s = linklookup(ctxt, ".dynamic", 0); elfverneed = nfile; if(elfverneed) { elfwritedynentsym(s, DT_VERNEED, linklookup(ctxt, ".gnu.version_r", 0)); elfwritedynent(s, DT_VERNEEDNUM, nfile); elfwritedynentsym(s, DT_VERSYM, linklookup(ctxt, ".gnu.version", 0)); } if(thechar == '6') { sy = linklookup(ctxt, ".rela.plt", 0); if(sy->size > 0) { elfwritedynent(s, DT_PLTREL, DT_RELA); elfwritedynentsymsize(s, DT_PLTRELSZ, sy); elfwritedynentsym(s, DT_JMPREL, sy); } } else { sy = linklookup(ctxt, ".rel.plt", 0); if(sy->size > 0) { elfwritedynent(s, DT_PLTREL, DT_REL); elfwritedynentsymsize(s, DT_PLTRELSZ, sy); elfwritedynentsym(s, DT_JMPREL, sy); } } elfwritedynent(s, DT_NULL, 0); } ElfPhdr* elfphload(Segment *seg) { ElfPhdr *ph; ph = newElfPhdr(); ph->type = PT_LOAD; if(seg->rwx & 4) ph->flags |= PF_R; if(seg->rwx & 2) ph->flags |= PF_W; if(seg->rwx & 1) ph->flags |= PF_X; ph->vaddr = seg->vaddr; ph->paddr = seg->vaddr; ph->memsz = seg->len; ph->off = seg->fileoff; ph->filesz = seg->filelen; ph->align = INITRND; return ph; } ElfShdr* elfshname(char *name) { int i, off; ElfShdr *sh; for(i=0; iname == 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 sh->type = SHT_NOBITS; sh->flags = SHF_ALLOC; if(sect->rwx & 1) sh->flags |= SHF_EXECINSTR; if(sect->rwx & 2) sh->flags |= SHF_WRITE; if(strcmp(sect->name, ".tbss") == 0) { if(strcmp(goos, "android") != 0) sh->flags |= SHF_TLS; // no TLS on android sh->type = SHT_NOBITS; } if(linkmode != LinkExternal) sh->addr = sect->vaddr; sh->addralign = sect->align; 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 || strcmp(sect->name, ".tbss") == 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 = RegSize*(2+(typ==SHT_RELA)); sh->link = elfshname(".symtab")->shnum; sh->info = sect->elfsect->shnum; sh->off = sect->reloff; sh->size = sect->rellen; sh->addralign = RegSize; return sh; } void elfrelocsect(Section *sect, LSym *first) { LSym *sym; int32 eaddr; Reloc *r; // 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; ctxt->cursym = sym; for(r = sym->r; r < sym->r+sym->nr; r++) { if(r->done) continue; if(r->xsym == nil) { diag("missing xsym in relocation"); continue; } if(r->xsym->elfsym == 0) diag("reloc %d to non-elf symbol %s (outer=%s) %d", r->type, r->sym->name, r->xsym->name, r->sym->type); if(elfreloc1(r, sym->value+r->off - sect->vaddr) < 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, ctxt->textp); for(sect=segtext.sect->next; sect!=nil; sect=sect->next) elfrelocsect(sect, datap); for(sect=segrodata.sect; sect!=nil; sect=sect->next) elfrelocsect(sect, datap); for(sect=segdata.sect; sect!=nil; sect=sect->next) elfrelocsect(sect, datap); } void doelf(void) { LSym *s, *shstrtab, *dynstr; if(!iself) return; /* predefine strings we need for section headers */ shstrtab = linklookup(ctxt, ".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"); // generate .tbss section (except for OpenBSD where it's not supported) // for dynamic internal linker or external linking, so that various // binutils could correctly calculate PT_TLS size. // see http://golang.org/issue/5200. if(HEADTYPE != Hopenbsd) if(!debug['d'] || linkmode == LinkExternal) addstring(shstrtab, ".tbss"); 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"); addstring(shstrtab, ".gosymtab"); addstring(shstrtab, ".gopclntab"); if(linkmode == LinkExternal) { debug_s = debug['s']; debug['s'] = 0; debug['d'] = 1; if(thechar == '6') { addstring(shstrtab, ".rela.text"); addstring(shstrtab, ".rela.rodata"); addstring(shstrtab, ".rela.typelink"); 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.gosymtab"); addstring(shstrtab, ".rel.gopclntab"); addstring(shstrtab, ".rel.noptrdata"); addstring(shstrtab, ".rel.data"); } // add a .note.GNU-stack section to mark the stack as non-executable addstring(shstrtab, ".note.GNU-stack"); } if(flag_shared) { addstring(shstrtab, ".init_array"); if(thechar == '6') addstring(shstrtab, ".rela.init_array"); else addstring(shstrtab, ".rel.init_array"); } 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 = linklookup(ctxt, ".dynsym", 0); s->type = SELFROSECT; s->reachable = 1; if(thechar == '6') s->size += ELF64SYMSIZE; else s->size += ELF32SYMSIZE; /* dynamic string table */ s = linklookup(ctxt, ".dynstr", 0); s->type = SELFROSECT; s->reachable = 1; if(s->size == 0) addstring(s, ""); dynstr = s; /* relocation table */ if(thechar == '6') s = linklookup(ctxt, ".rela", 0); else s = linklookup(ctxt, ".rel", 0); s->reachable = 1; s->type = SELFROSECT; /* global offset table */ s = linklookup(ctxt, ".got", 0); s->reachable = 1; s->type = SELFSECT; // writable /* hash */ s = linklookup(ctxt, ".hash", 0); s->reachable = 1; s->type = SELFROSECT; s = linklookup(ctxt, ".got.plt", 0); s->reachable = 1; s->type = SELFSECT; // writable s = linklookup(ctxt, ".plt", 0); s->reachable = 1; s->type = SELFRXSECT; elfsetupplt(); if(thechar == '6') s = linklookup(ctxt, ".rela.plt", 0); else s = linklookup(ctxt, ".rel.plt", 0); s->reachable = 1; s->type = SELFROSECT; s = linklookup(ctxt, ".gnu.version", 0); s->reachable = 1; s->type = SELFROSECT; s = linklookup(ctxt, ".gnu.version_r", 0); s->reachable = 1; s->type = SELFROSECT; /* define dynamic elf table */ s = linklookup(ctxt, ".dynamic", 0); s->reachable = 1; s->type = SELFSECT; // writable /* * .dynamic table */ elfwritedynentsym(s, DT_HASH, linklookup(ctxt, ".hash", 0)); elfwritedynentsym(s, DT_SYMTAB, linklookup(ctxt, ".dynsym", 0)); if(thechar == '6') elfwritedynent(s, DT_SYMENT, ELF64SYMSIZE); else elfwritedynent(s, DT_SYMENT, ELF32SYMSIZE); elfwritedynentsym(s, DT_STRTAB, linklookup(ctxt, ".dynstr", 0)); elfwritedynentsymsize(s, DT_STRSZ, linklookup(ctxt, ".dynstr", 0)); if(thechar == '6') { elfwritedynentsym(s, DT_RELA, linklookup(ctxt, ".rela", 0)); elfwritedynentsymsize(s, DT_RELASZ, linklookup(ctxt, ".rela", 0)); elfwritedynent(s, DT_RELAENT, ELF64RELASIZE); } else { elfwritedynentsym(s, DT_REL, linklookup(ctxt, ".rel", 0)); elfwritedynentsymsize(s, DT_RELSZ, linklookup(ctxt, ".rel", 0)); elfwritedynent(s, DT_RELENT, ELF32RELSIZE); } if(rpath) elfwritedynent(s, DT_RUNPATH, addstring(dynstr, rpath)); elfwritedynentsym(s, DT_PLTGOT, linklookup(ctxt, ".got.plt", 0)); // Solaris dynamic linker can't handle an empty .rela.plt if // DT_JMPREL is emitted so we have to defer generation of DT_PLTREL, // DT_PLTRELSZ, and DT_JMPREL dynamic entries until after we know the // size of .rel(a).plt section. elfwritedynent(s, DT_DEBUG, 0); // Do not write DT_NULL. elfdynhash will finish it. } } void shsym(ElfShdr *sh, LSym *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=segrodata.sect; sect!=nil; sect=sect->next) elfshalloc(sect); for(sect=segdata.sect; sect!=nil; sect=sect->next) elfshalloc(sect); } void asmbelf(vlong symo) { vlong 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(linkmode == LinkExternal) { /* skip program headers */ eh->phoff = 0; eh->phentsize = 0; goto elfobj; } /* program header info */ pph = newElfPhdr(); pph->type = PT_PHDR; pph->flags = PF_R; 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. * Except on NaCl where it must not be loaded. */ if(HEADTYPE != Hnacl) { 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; case Hdragonfly: interpreter = dragonflydynld; break; case Hsolaris: interpreter = solarisdynld; 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); if(segrodata.sect != nil) elfphload(&segrodata); 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(elf64) sh->entsize = ELF64SYMSIZE; else sh->entsize = ELF32SYMSIZE; sh->addralign = RegSize; sh->link = elfshname(".dynstr")->shnum; // sh->info = index of first non-local symbol (number of local symbols) shsym(sh, linklookup(ctxt, ".dynsym", 0)); sh = elfshname(".dynstr"); sh->type = SHT_STRTAB; sh->flags = SHF_ALLOC; sh->addralign = 1; shsym(sh, linklookup(ctxt, ".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, linklookup(ctxt, ".gnu.version", 0)); sh = elfshname(".gnu.version_r"); sh->type = SHT_GNU_VERNEED; sh->flags = SHF_ALLOC; sh->addralign = RegSize; sh->info = elfverneed; sh->link = elfshname(".dynstr")->shnum; shsym(sh, linklookup(ctxt, ".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 = RegSize; sh->link = elfshname(".dynsym")->shnum; sh->info = elfshname(".plt")->shnum; shsym(sh, linklookup(ctxt, ".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, linklookup(ctxt, ".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, linklookup(ctxt, ".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, linklookup(ctxt, ".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, linklookup(ctxt, ".plt", 0)); sh = elfshname(".got"); sh->type = SHT_PROGBITS; sh->flags = SHF_ALLOC+SHF_WRITE; sh->entsize = RegSize; sh->addralign = RegSize; shsym(sh, linklookup(ctxt, ".got", 0)); sh = elfshname(".got.plt"); sh->type = SHT_PROGBITS; sh->flags = SHF_ALLOC+SHF_WRITE; sh->entsize = RegSize; sh->addralign = RegSize; shsym(sh, linklookup(ctxt, ".got.plt", 0)); sh = elfshname(".hash"); sh->type = SHT_HASH; sh->flags = SHF_ALLOC; sh->entsize = 4; sh->addralign = RegSize; sh->link = elfshname(".dynsym")->shnum; shsym(sh, linklookup(ctxt, ".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*RegSize; sh->addralign = RegSize; sh->link = elfshname(".dynstr")->shnum; shsym(sh, linklookup(ctxt, ".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(ctxt->tlsoffset != 0 && HEADTYPE != Hopenbsd) { ph = newElfPhdr(); ph->type = PT_TLS; ph->flags = PF_R; ph->memsz = -ctxt->tlsoffset; ph->align = RegSize; } } if(HEADTYPE == Hlinux) { ph = newElfPhdr(); ph->type = PT_GNU_STACK; ph->flags = PF_W+PF_R; ph->align = RegSize; ph = newElfPhdr(); ph->type = PT_PAX_FLAGS; ph->flags = 0x2a00; // mprotect, randexec, emutramp disabled ph->align = RegSize; } elfobj: sh = elfshname(".shstrtab"); sh->type = SHT_STRTAB; sh->addralign = 1; shsym(sh, linklookup(ctxt, ".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=segrodata.sect; sect!=nil; sect=sect->next) elfshbits(sect); for(sect=segdata.sect; sect!=nil; sect=sect->next) elfshbits(sect); if(linkmode == LinkExternal) { for(sect=segtext.sect; sect!=nil; sect=sect->next) elfshreloc(sect); for(sect=segrodata.sect; sect!=nil; sect=sect->next) elfshreloc(sect); for(sect=segdata.sect; sect!=nil; sect=sect->next) elfshreloc(sect); // add a .note.GNU-stack section to mark the stack as non-executable sh = elfshname(".note.GNU-stack"); sh->type = SHT_PROGBITS; sh->addralign = 1; sh->flags = 0; } // generate .tbss section for dynamic internal linking (except for OpenBSD) // external linking generates .tbss in data.c if(linkmode == LinkInternal && !debug['d'] && HEADTYPE != Hopenbsd) { sh = elfshname(".tbss"); sh->type = SHT_NOBITS; sh->addralign = RegSize; sh->size = -ctxt->tlsoffset; sh->flags = SHF_ALLOC | SHF_TLS | SHF_WRITE; } if(!debug['s']) { sh = elfshname(".symtab"); sh->type = SHT_SYMTAB; sh->off = symo; sh->size = symsize; sh->addralign = RegSize; sh->entsize = 8+2*RegSize; 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; 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; else if(HEADTYPE == Hdragonfly) eh->ident[EI_OSABI] = ELFOSABI_NONE; if(elf64) eh->ident[EI_CLASS] = ELFCLASS64; else eh->ident[EI_CLASS] = ELFCLASS32; eh->ident[EI_DATA] = ELFDATA2LSB; eh->ident[EI_VERSION] = EV_CURRENT; if(linkmode == LinkExternal) eh->type = ET_REL; else eh->type = ET_EXEC; if(linkmode != LinkExternal) 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(linkmode != LinkExternal) { if(HEADTYPE == Hnetbsd) a += elfwritenetbsdsig(); if(HEADTYPE == Hopenbsd) a += elfwriteopenbsdsig(); if(buildinfolen > 0) a += elfwritebuildinfo(); } if(a > ELFRESERVE) diag("ELFRESERVE too small: %lld > %d", a, ELFRESERVE); }