diff options
author | Ondřej Surý <ondrej@sury.org> | 2011-09-13 13:13:40 +0200 |
---|---|---|
committer | Ondřej Surý <ondrej@sury.org> | 2011-09-13 13:13:40 +0200 |
commit | 5ff4c17907d5b19510a62e08fd8d3b11e62b431d (patch) | |
tree | c0650497e988f47be9c6f2324fa692a52dea82e1 /src/cmd/ld/elf.c | |
parent | 80f18fc933cf3f3e829c5455a1023d69f7b86e52 (diff) | |
download | golang-upstream/60.tar.gz |
Imported Upstream version 60upstream/60
Diffstat (limited to 'src/cmd/ld/elf.c')
-rw-r--r-- | src/cmd/ld/elf.c | 564 |
1 files changed, 564 insertions, 0 deletions
diff --git a/src/cmd/ld/elf.c b/src/cmd/ld/elf.c new file mode 100644 index 000000000..f9f9ef6b2 --- /dev/null +++ b/src/cmd/ld/elf.c @@ -0,0 +1,564 @@ +// 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; + cseek(ELFRESERVE-n); + cwrite(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 = 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) { + 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; i<nbucket; i++) + adduint32(s, buckets[i]); + for(i = 0; i<nsym; i++) + adduint32(s, chain[i]); + + free(chain); + free(buckets); + + // version symbols + dynstr = lookup(".dynstr", 0); + s = lookup(".gnu.version_r", 0); + i = 2; + nfile = 0; + for(l=needlib; l; l=l->next) { + 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; i<nsym; i++) { + if(i == 0) + adduint16(s, 0); // first entry - no symbol + else if(need[i] == nil) + adduint16(s, 1); // global + else + adduint16(s, need[i]->num); + } + + 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; i<nelfstr; i++) { + if(strcmp(sect->name, elfstr[i].s) == 0) { + off = elfstr[i].off; + goto found; + } + } + diag("cannot find elf name %s", sect->name); + errorexit(); + return nil; + +found: + for(i=0; i<hdr.shnum; i++) { + sh = shdr[i]; + if(sh->name == off) + return sh; + } + + 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; +} |