// 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 32 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; /* 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 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) { 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* newElfShstrtab(vlong name) { hdr.shstrndx = hdr.shnum; return newElfShdr(name); } ElfShdr* newElfShdr(vlong name) { ElfShdr *e; e = mal(sizeof *e); e->name = name; 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(Sym *s, int tag, uint64 val) { if(elf64) { adduint64(s, tag); adduint64(s, val); } else { adduint32(s, tag); adduint32(s, val); } } void elfwritedynentsym(Sym *s, int tag, Sym *t) { if(elf64) adduint64(s, tag); else adduint32(s, tag); addaddr(s, t); } void elfwritedynentsymsize(Sym *s, int tag, Sym *t) { if(elf64) adduint64(s, tag); else adduint32(s, tag); addsize(s, t); } int elfwriteinterp(void) { int n; if(interp == nil) return 0; n = strlen(interp)+1; seek(cout, ELFRESERVE-n, 0); ewrite(cout, interp, n); return n; } void elfinterp(ElfShdr *sh, uint64 startva, char *p) { int n; interp = p; n = strlen(interp)+1; sh->addr = startva + ELFRESERVE - n; sh->off = ELFRESERVE - n; sh->size = n; } 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) { Sym *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 = lookup(".hash", 0); s->type = SELFDATA; 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) { 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=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->dynimpname; if(name == nil) name = sy->name; hc = elfhash((uchar*)name); b = hc % nbucket; chain[sy->dynid] = buckets[b]; buckets[b] = sy->dynid; } adduint32(s, nbucket); adduint32(s, nsym); for(i = 0; inext) { nfile++; // header adduint16(s, 1); // table version j = 0; for(x=l->aux; x; x=x->next) j++; adduint16(s, j); // aux count adduint32(s, addstring(dynstr, l->file)); // file string offset adduint32(s, 16); // offset from header to first aux if(l->next) adduint32(s, 16+j*16); // offset from this header to next else adduint32(s, 0); for(x=l->aux; x; x=x->next) { x->num = i++; // aux struct adduint32(s, elfhash((uchar*)x->vers)); // hash adduint16(s, 0); // flags adduint16(s, x->num); // other - index we refer to this by adduint32(s, addstring(dynstr, x->vers)); // version string offset if(x->next) adduint32(s, 16); // offset from this aux to next else adduint32(s, 0); } } // version references s = lookup(".gnu.version", 0); for(i=0; inum); } free(need); s = lookup(".dynamic", 0); elfverneed = nfile; if(elfverneed) { elfwritedynentsym(s, DT_VERNEED, lookup(".gnu.version_r", 0)); elfwritedynent(s, DT_VERNEEDNUM, nfile); elfwritedynentsym(s, DT_VERSYM, lookup(".gnu.version", 0)); } 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* elfshbits(Section *sect) { int i, off; ElfShdr *sh; for(i=0; iname, elfstr[i].s) == 0) { off = elfstr[i].off; goto found; } } diag("cannot find elf name %s", sect->name); errorexit(); return nil; found: sh = newElfShdr(off); 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; sh->addr = sect->vaddr; sh->addralign = PtrSize; sh->size = sect->len; sh->off = sect->seg->fileoff + sect->vaddr - sect->seg->vaddr; return sh; }