]> Cypherpunks repositories - gostls13.git/commitdiff
ld: reading of ELF object files
authorRuss Cox <rsc@golang.org>
Wed, 8 Dec 2010 18:52:59 +0000 (13:52 -0500)
committerRuss Cox <rsc@golang.org>
Wed, 8 Dec 2010 18:52:59 +0000 (13:52 -0500)
R=iant
CC=golang-dev
https://golang.org/cl/3507041

src/cmd/ld/ldelf.c [new file with mode: 0644]

diff --git a/src/cmd/ld/ldelf.c b/src/cmd/ld/ldelf.c
new file mode 100644 (file)
index 0000000..6d5b541
--- /dev/null
@@ -0,0 +1,822 @@
+/*
+Derived from Plan 9 from User Space's src/libmach/elf.h, elf.c
+http://code.swtch.com/plan9port/src/tip/src/libmach/
+
+       Copyright © 2004 Russ Cox.
+       Portions Copyright © 2008-2010 Google Inc.
+       Portions Copyright © 2010 The Go Authors.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+#include       "l.h"
+#include       "lib.h"
+#include       "../ld/elf.h"
+
+enum
+{
+       ElfClassNone = 0,
+       ElfClass32,
+       ElfClass64,
+
+       ElfDataNone = 0,
+       ElfDataLsb,
+       ElfDataMsb,
+
+       ElfTypeNone = 0,
+       ElfTypeRelocatable,
+       ElfTypeExecutable,
+       ElfTypeSharedObject,
+       ElfTypeCore,
+       /* 0xFF00 - 0xFFFF reserved for processor-specific types */
+
+       ElfMachNone = 0,
+       ElfMach32100,           /* AT&T WE 32100 */
+       ElfMachSparc,           /* SPARC */
+       ElfMach386,             /* Intel 80386 */
+       ElfMach68000,           /* Motorola 68000 */
+       ElfMach88000,           /* Motorola 88000 */
+       ElfMach486,             /* Intel 80486, no longer used */
+       ElfMach860,             /* Intel 80860 */
+       ElfMachMips,            /* MIPS RS3000 */
+       ElfMachS370,            /* IBM System/370 */
+       ElfMachMipsLe,  /* MIPS RS3000 LE */
+       ElfMachParisc = 15,             /* HP PA RISC */
+       ElfMachVpp500 = 17,     /* Fujitsu VPP500 */
+       ElfMachSparc32Plus,     /* SPARC V8+ */
+       ElfMach960,             /* Intel 80960 */
+       ElfMachPower,           /* PowerPC */
+       ElfMachPower64, /* PowerPC 64 */
+       ElfMachS390,            /* IBM System/390 */
+       ElfMachV800 = 36,       /* NEC V800 */
+       ElfMachFr20,            /* Fujitsu FR20 */
+       ElfMachRh32,            /* TRW RH-32 */
+       ElfMachRce,             /* Motorola RCE */
+       ElfMachArm,             /* ARM */
+       ElfMachAlpha,           /* Digital Alpha */
+       ElfMachSH,              /* Hitachi SH */
+       ElfMachSparc9,          /* SPARC V9 */
+       ElfMachAmd64 = 62,
+       /* and the list goes on... */
+
+       ElfAbiNone = 0,
+       ElfAbiSystemV = 0,      /* [sic] */
+       ElfAbiHPUX,
+       ElfAbiNetBSD,
+       ElfAbiLinux,
+       ElfAbiSolaris = 6,
+       ElfAbiAix,
+       ElfAbiIrix,
+       ElfAbiFreeBSD,
+       ElfAbiTru64,
+       ElfAbiModesto,
+       ElfAbiOpenBSD,
+       ElfAbiARM = 97,
+       ElfAbiEmbedded = 255,
+
+       /* some of sections 0xFF00 - 0xFFFF reserved for various things */
+       ElfSectNone = 0,
+       ElfSectProgbits,
+       ElfSectSymtab,
+       ElfSectStrtab,
+       ElfSectRela,
+       ElfSectHash,
+       ElfSectDynamic,
+       ElfSectNote,
+       ElfSectNobits,
+       ElfSectRel,
+       ElfSectShlib,
+       ElfSectDynsym,
+
+       ElfSectFlagWrite = 0x1,
+       ElfSectFlagAlloc = 0x2,
+       ElfSectFlagExec = 0x4,
+       /* 0xF0000000 are reserved for processor specific */
+
+       ElfSymBindLocal = 0,
+       ElfSymBindGlobal,
+       ElfSymBindWeak,
+       /* 13-15 reserved */
+
+       ElfSymTypeNone = 0,
+       ElfSymTypeObject,
+       ElfSymTypeFunc,
+       ElfSymTypeSection,
+       ElfSymTypeFile,
+       /* 13-15 reserved */
+
+       ElfSymShnNone = 0,
+       ElfSymShnAbs = 0xFFF1,
+       ElfSymShnCommon = 0xFFF2,
+       /* 0xFF00-0xFF1F reserved for processors */
+       /* 0xFF20-0xFF3F reserved for operating systems */
+
+       ElfProgNone = 0,
+       ElfProgLoad,
+       ElfProgDynamic,
+       ElfProgInterp,
+       ElfProgNote,
+       ElfProgShlib,
+       ElfProgPhdr,
+
+       ElfProgFlagExec = 0x1,
+       ElfProgFlagWrite = 0x2,
+       ElfProgFlagRead = 0x4,
+
+       ElfNotePrStatus = 1,
+       ElfNotePrFpreg = 2,
+       ElfNotePrPsinfo = 3,
+       ElfNotePrTaskstruct = 4,
+       ElfNotePrAuxv = 6,
+       ElfNotePrXfpreg = 0x46e62b7f    /* for gdb/386 */
+};
+
+typedef struct ElfHdrBytes ElfHdrBytes;
+typedef struct ElfSectBytes ElfSectBytes;
+typedef struct ElfProgBytes ElfProgBytes;
+typedef struct ElfSymBytes ElfSymBytes;
+
+typedef struct ElfHdrBytes64 ElfHdrBytes64;
+typedef struct ElfSectBytes64 ElfSectBytes64;
+typedef struct ElfProgBytes64 ElfProgBytes64;
+typedef struct ElfSymBytes64 ElfSymBytes64;
+
+struct ElfHdrBytes
+{
+       uchar   ident[16];
+       uchar   type[2];
+       uchar   machine[2];
+       uchar   version[4];
+       uchar   entry[4];
+       uchar   phoff[4];
+       uchar   shoff[4];
+       uchar   flags[4];
+       uchar   ehsize[2];
+       uchar   phentsize[2];
+       uchar   phnum[2];
+       uchar   shentsize[2];
+       uchar   shnum[2];
+       uchar   shstrndx[2];
+};
+
+struct ElfHdrBytes64
+{
+       uchar   ident[16];
+       uchar   type[2];
+       uchar   machine[2];
+       uchar   version[4];
+       uchar   entry[8];
+       uchar   phoff[8];
+       uchar   shoff[8];
+       uchar   flags[4];
+       uchar   ehsize[2];
+       uchar   phentsize[2];
+       uchar   phnum[2];
+       uchar   shentsize[2];
+       uchar   shnum[2];
+       uchar   shstrndx[2];
+};
+
+struct ElfSectBytes
+{
+       uchar   name[4];
+       uchar   type[4];
+       uchar   flags[4];
+       uchar   addr[4];
+       uchar   off[4];
+       uchar   size[4];
+       uchar   link[4];
+       uchar   info[4];
+       uchar   align[4];
+       uchar   entsize[4];
+};
+
+struct ElfSectBytes64
+{
+       uchar   name[4];
+       uchar   type[4];
+       uchar   flags[8];
+       uchar   addr[8];
+       uchar   off[8];
+       uchar   size[8];
+       uchar   link[4];
+       uchar   info[4];
+       uchar   align[8];
+       uchar   entsize[8];
+};
+
+struct ElfSymBytes
+{
+       uchar   name[4];
+       uchar   value[4];
+       uchar   size[4];
+       uchar   info;   /* top4: bind, bottom4: type */
+       uchar   other;
+       uchar   shndx[2];
+};
+
+struct ElfSymBytes64
+{
+       uchar   name[4];
+       uchar   info;   /* top4: bind, bottom4: type */
+       uchar   other;
+       uchar   shndx[2];
+       uchar   value[8];
+       uchar   size[8];
+};
+
+typedef struct ElfSect ElfSect;
+typedef struct ElfObj ElfObj;
+typedef struct ElfSym ElfSym;
+
+struct ElfSect
+{
+       char            *name;
+       uint32  type;
+       uint64  flags;
+       uint64  addr;
+       uint64  off;
+       uint64  size;
+       uint32  link;
+       uint32  info;
+       uint64  align;
+       uint64  entsize;
+       uchar   *base;
+       Sym     *sym;
+};
+
+struct ElfObj
+{
+       Biobuf  *f;
+       int64   base;   // offset in f where ELF begins
+       int64   len;            // length of ELF
+       int     is64;
+       char    *name;
+
+       Endian  *e;
+       ElfSect *sect;
+       uint            nsect;
+       char            *shstrtab;
+       int             nsymtab;
+       ElfSect *symtab;
+       ElfSect *symstr;
+
+       uint32  type;
+       uint32  machine;
+       uint32  version;
+       uint64  entry;
+       uint64  phoff;
+       uint64  shoff;
+       uint32  flags;
+       uint32  ehsize;
+       uint32  phentsize;
+       uint32  phnum;
+       uint32  shentsize;
+       uint32  shnum;
+       uint32  shstrndx;
+};
+
+struct ElfSym
+{
+       char*   name;
+       uint64  value;
+       uint64  size;
+       uchar   bind;
+       uchar   type;
+       uchar   other;
+       uint16  shndx;
+       Sym*    sym;
+};
+
+uchar ElfMagic[4] = { 0x7F, 'E', 'L', 'F' };
+
+static ElfSect*        section(ElfObj*, char*);
+static int     map(ElfObj*, ElfSect*);
+static int     readsym(ElfObj*, int i, ElfSym*);
+static int     reltype(char*, int, uchar*);
+
+void
+ldelf(Biobuf *f, char *pkg, int64 len, char *pn)
+{
+       int32 base;
+       uint64 add, info;
+       char *name;
+       int i, j, rela, is64, n;
+       uchar hdrbuf[64];
+       uchar *p, *dp;
+       ElfHdrBytes *hdr;
+       ElfObj *obj;
+       ElfSect *sect, *rsect, *text, *data, *bss, *rodata;
+       ElfSym sym;
+       Endian *e;
+       Reloc *r, *rp;
+       Sym *s;
+
+       if(debug['v'])
+               Bprint(&bso, "%5.2f ldelf %s\n", cputime(), pn);
+
+       version++;
+       base = Boffset(f);
+
+       if(Bread(f, &hdrbuf, sizeof hdrbuf) != sizeof hdrbuf)
+               goto bad;
+       hdr = (ElfHdrBytes*)&hdrbuf;
+       if(memcmp(hdr->ident, ElfMagic, 4) != 0)
+               goto bad;
+       switch(hdr->ident[5]) {
+       case ElfDataLsb:
+               e = &le;
+               break;
+       case ElfDataMsb:
+               e = &be;
+               break;
+       default:
+               goto bad;
+       }
+
+       // read header
+       obj = mal(sizeof *obj);
+       obj->e = e;
+       obj->f = f;
+       obj->base = base;
+       obj->len = len;
+       obj->name = pn;
+       
+       is64 = 0;
+       if(hdr->ident[4] == ElfClass64) {
+               ElfHdrBytes64* hdr;
+
+               is64 = 1;
+               hdr = (ElfHdrBytes64*)hdrbuf;
+               obj->type = e->e16(hdr->type);
+               obj->machine = e->e16(hdr->machine);
+               obj->version = e->e32(hdr->version);
+               obj->phoff = e->e64(hdr->phoff);
+               obj->shoff = e->e64(hdr->shoff);
+               obj->flags = e->e32(hdr->flags);
+               obj->ehsize = e->e16(hdr->ehsize);
+               obj->phentsize = e->e16(hdr->phentsize);
+               obj->phnum = e->e16(hdr->phnum);
+               obj->shentsize = e->e16(hdr->shentsize);
+               obj->shnum = e->e16(hdr->shnum);
+               obj->shstrndx = e->e16(hdr->shstrndx);
+       } else {
+               obj->type = e->e16(hdr->type);
+               obj->machine = e->e16(hdr->machine);
+               obj->version = e->e32(hdr->version);
+               obj->entry = e->e32(hdr->entry);
+               obj->phoff = e->e32(hdr->phoff);
+               obj->shoff = e->e32(hdr->shoff);
+               obj->flags = e->e32(hdr->flags);
+               obj->ehsize = e->e16(hdr->ehsize);
+               obj->phentsize = e->e16(hdr->phentsize);
+               obj->phnum = e->e16(hdr->phnum);
+               obj->shentsize = e->e16(hdr->shentsize);
+               obj->shnum = e->e16(hdr->shnum);
+               obj->shstrndx = e->e16(hdr->shstrndx);
+       }
+       obj->is64 = is64;
+       
+       if(hdr->ident[6] != obj->version)
+               goto bad;
+
+       if(e->e16(hdr->type) != ElfTypeRelocatable) {
+               diag("%s: elf but not elf relocatable object");
+               return;
+       }
+
+       switch(thechar) {
+       default:
+               diag("%s: elf %s unimplemented", thestring);
+               return;
+       case '5':
+               if(e != &le || obj->machine != ElfMachArm || hdr->ident[4] != ElfClass32) {
+                       diag("%s: elf object but not arm", pn);
+                       return;
+               }
+               break;
+       case '6':
+               if(e != &le || obj->machine != ElfMachAmd64 || hdr->ident[4] != ElfClass64) {
+                       diag("%s: elf object but not amd64", pn);
+                       return;
+               }
+               break;
+       case '8':
+               if(e != &le || obj->machine != ElfMach386 || hdr->ident[4] != ElfClass32) {
+                       diag("%s: elf object but not 386", pn);
+                       return;
+               }
+               break;
+       }
+
+       // load section list into memory.
+       obj->sect = mal(obj->shnum*sizeof obj->sect[0]);
+       obj->nsect = obj->shnum;
+       for(i=0; i<obj->nsect; i++) {
+               if(Bseek(f, base+obj->shoff+i*obj->shentsize, 0) < 0)
+                       goto bad;
+               sect = &obj->sect[i];
+               if(is64) {
+                       ElfSectBytes64 b;
+
+                       werrstr("short read");
+                       if(Bread(f, &b, sizeof b) != sizeof b)
+                               goto bad;
+
+                       sect->name = (char*)(uintptr)e->e32(b.name);
+                       sect->type = e->e32(b.type);
+                       sect->flags = e->e64(b.flags);
+                       sect->addr = e->e64(b.addr);
+                       sect->off = e->e64(b.off);
+                       sect->size = e->e64(b.size);
+                       sect->link = e->e32(b.link);
+                       sect->info = e->e32(b.info);
+                       sect->align = e->e64(b.align);
+                       sect->entsize = e->e64(b.entsize);
+               } else {
+                       ElfSectBytes b;
+
+                       werrstr("short read");
+                       if(Bread(f, &b, sizeof b) != sizeof b)
+                               goto bad;
+               
+                       sect->name = (char*)(uintptr)e->e32(b.name);
+                       sect->type = e->e32(b.type);
+                       sect->flags = e->e32(b.flags);
+                       sect->addr = e->e32(b.addr);
+                       sect->off = e->e32(b.off);
+                       sect->size = e->e32(b.size);
+                       sect->link = e->e32(b.link);
+                       sect->info = e->e32(b.info);
+                       sect->align = e->e32(b.align);
+                       sect->entsize = e->e32(b.entsize);
+               }
+       }
+
+       // read section string table and translate names
+       if(obj->shstrndx >= obj->nsect) {
+               werrstr("shstrndx out of range %d >= %d", obj->shstrndx, obj->nsect);
+               goto bad;
+       }
+       sect = &obj->sect[obj->shstrndx];
+       if(map(obj, sect) < 0)
+               goto bad;
+       for(i=0; i<obj->nsect; i++)
+               if(obj->sect[i].name != nil)
+                       obj->sect[i].name = (char*)sect->base + (uintptr)obj->sect[i].name;
+       
+       // load string table for symbols into memory.
+       obj->symtab = section(obj, ".symtab");
+       if(obj->symtab == nil) {
+               diag("%s: elf object has no symbol table", pn);
+               return;
+       }
+       if(obj->symtab->link <= 0 || obj->symtab->link >= obj->nsect) {
+               diag("%s: elf object has symbol table with invalid string table link", pn);
+               return;
+       }
+       obj->symstr = &obj->sect[obj->symtab->link];
+       if(is64)
+               obj->nsymtab = obj->symtab->size / sizeof(ElfSymBytes64);
+       else
+               obj->nsymtab = obj->symtab->size / sizeof(ElfSymBytes);
+       
+       if(map(obj, obj->symtab) < 0)
+               goto bad;
+       if(map(obj, obj->symstr) < 0)
+               goto bad;
+
+       // load text and data segments into memory.
+       // they are not as small as the section lists, but we'll need
+       // the memory anyway for the symbol images, so we might
+       // as well use one large chunk.
+       text = section(obj, ".text");
+       if(text && map(obj, text) < 0)
+               goto bad;
+       data = section(obj, ".data");
+       if(data && map(obj, data) < 0)
+               goto bad;
+       bss = section(obj, ".bss");
+       rodata = section(obj, ".rodata");
+       if(rodata && map(obj, rodata) < 0)
+               goto bad;
+       
+       // create symbols for mapped sections
+       for(i=0; i<obj->nsect; i++) {
+               sect = &obj->sect[i];
+               if(sect->type != ElfSectProgbits || !(sect->flags&ElfSectFlagAlloc))
+                       continue;
+               if(map(obj, sect) < 0)
+                       goto bad;
+               
+               name = smprint("%s(%s)", pn, sect->name);
+               s = lookup(name, version);
+               free(name);
+               switch(sect->flags&(ElfSectFlagAlloc|ElfSectFlagWrite|ElfSectFlagExec)) {
+               default:
+                       werrstr("unexpected flags for ELF section %s", sect->name);
+                       goto bad;
+               case ElfSectFlagAlloc:
+                       s->type = SRODATA;
+                       break;
+               case ElfSectFlagAlloc + ElfSectFlagWrite:
+                       s->type = SDATA;
+                       break;
+               case ElfSectFlagAlloc + ElfSectFlagExec:
+                       s->type = STEXT;
+                       break;
+               }
+               s->p = sect->base;
+               s->np = sect->size;
+               s->size = sect->size;
+               if(s->type == STEXT) {
+                       if(etextp)
+                               etextp->next = s;
+                       else
+                               textp = s;
+                       etextp = s;
+               }
+               sect->sym = s;
+       }               
+
+       // load relocations
+       for(i=0; i<obj->nsect; i++) {
+               rsect = &obj->sect[i];
+               if(rsect->type != ElfSectRela && rsect->type != ElfSectRel)
+                       continue;
+               if(rsect->info >= obj->nsect || obj->sect[rsect->info].base == nil)
+                       continue;
+               sect = &obj->sect[rsect->info];
+               if(map(obj, rsect) < 0)
+                       goto bad;
+               rela = rsect->type == ElfSectRela;
+               n = rsect->size/(4+4*is64)/(2+rela);
+               r = mal(n*sizeof r[0]);
+               p = rsect->base;
+               dp = sect->base;
+               for(j=0; j<n; j++) {
+                       add = 0;
+                       rp = &r[j];
+                       if(is64) {
+                               // 64-bit rel/rela
+                               rp->off = e->e64(p);
+                               p += 8;
+                               info = e->e64(p);
+                               p += 8;
+                               if(rela) {
+                                       add = e->e64(p);
+                                       p += 8;
+                               }
+                       } else {
+                               // 32-bit rel/rela
+                               rp->off = e->e32(p);
+                               p += 4;
+                               info = e->e32(p);
+                               info = info>>8<<32 | (info&0xff);       // convert to 64-bit info
+                               p += 4;
+                               if(rela) {
+                                       add = e->e32(p);
+                                       p += 4;
+                               }
+                       }
+                       if(readsym(obj, info>>32, &sym) < 0)
+                               goto bad;
+                       if(sym.sym == nil) {
+                               werrstr("reloc of invalid sym %s shndx=%d type=%d", sym.name, sym.shndx, sym.type);
+                               goto bad;
+                       }
+                       rp->sym = sym.sym;
+                       rp->type = reltype(pn, (uint32)info, &rp->siz);
+                       if(rela)
+                               rp->add = add;
+                       else {
+                               // load addend from image
+                               if(rp->siz == 4)
+                                       rp->add = e->e32(sect->base+rp->off);
+                               else if(rp->siz == 8)
+                                       rp->add = e->e64(sect->base+rp->off);
+                               else
+                                       diag("invalid rela size %d", rp->siz);
+                       }
+               }
+               qsort(r, n, sizeof r[0], rbyoff);       // just in case
+               
+               s = sect->sym;
+               s->r = r;
+               s->nr = n;
+       }
+
+       // enter sub-symbols into symbol table.
+       // symbol 0 is the null symbol.
+       for(i=1; i<obj->nsymtab; i++) {
+               if(readsym(obj, i, &sym) < 0)
+                       goto bad;
+               if(sym.type != ElfSymTypeFunc && sym.type != ElfSymTypeObject && sym.type != ElfSymTypeNone)
+                       continue;
+               if(sym.shndx == ElfSymShnCommon) {
+                       s = sym.sym;
+                       if(s->size < sym.size)
+                               s->size = sym.size;
+                       if(s->type == 0 || s->type == SXREF)
+                               s->type = SBSS;
+                       continue;
+               }
+               if(sym.shndx >= obj->nsect || sym.shndx == 0)
+                       continue;
+               sect = obj->sect+sym.shndx;
+               if(sect->sym == nil) {
+                       diag("%s: sym#%d: ignoring %s in section %d (type %d)", pn, i, sym.name, sym.shndx, sym.type);
+                       continue;
+               }
+               s = sym.sym;
+               s->sub = sect->sym->sub;
+               sect->sym->sub = s;
+               s->type = sect->sym->type | SSUB;
+               s->dynimplib = nil;  // satisfy dynimport
+               s->dynimpname = nil;  // satisfy dynimport
+               s->value = sym.value;
+               s->size = sym.size;
+               s->outer = sect->sym;
+               if(sect->sym->type == STEXT) {
+                       Prog *p;
+
+                       if(s->text != P)
+                               diag("%s: duplicate definition of %s", pn, s->name);
+                       // build a TEXT instruction with a unique pc
+                       // just to make the rest of the linker happy.
+                       // TODO: this is too 6l-specific ?
+                       p = prg();
+                       p->as = ATEXT;
+                       p->from.type = D_EXTERN;
+                       p->from.sym = s;
+                       p->from.scale = 7;
+                       p->to.type = D_CONST;
+                       p->link = nil;
+                       p->pc = pc++;
+                       s->text = p;
+
+                       etextp->next = s;
+                       etextp = s;
+               }
+       }
+       return;
+
+bad:
+       diag("%s: malformed elf file: %r", pn);
+}
+
+static ElfSect*
+section(ElfObj *obj, char *name)
+{
+       int i;
+       
+       for(i=0; i<obj->nsect; i++)
+               if(obj->sect[i].name && name && strcmp(obj->sect[i].name, name) == 0)
+                       return &obj->sect[i];
+       return nil;
+}
+
+static int
+map(ElfObj *obj, ElfSect *sect)
+{
+       if(sect->base != nil)
+               return 0;
+
+       if(sect->off+sect->size > obj->len) {
+               werrstr("elf section past end of file");
+               return -1;
+       }
+
+       sect->base = mal(sect->size);
+       werrstr("short read");
+       if(Bseek(obj->f, obj->base+sect->off, 0) < 0 || Bread(obj->f, sect->base, sect->size) != sect->size)
+               return -1;
+       
+       return 0;
+}
+
+static int
+readsym(ElfObj *obj, int i, ElfSym *sym)
+{
+       Sym *s;
+
+       if(i >= obj->nsymtab || i < 0) {
+               werrstr("invalid elf symbol index");
+               return -1;
+       }
+
+       if(obj->is64) {
+               ElfSymBytes64 *b;
+               
+               b = (ElfSymBytes64*)(obj->symtab->base + i*sizeof *b);
+               sym->name = (char*)obj->symstr->base + obj->e->e32(b->name);
+               sym->value = obj->e->e64(b->value);
+               sym->size = obj->e->e64(b->size);
+               sym->shndx = obj->e->e16(b->shndx);
+               sym->bind = b->info>>4;
+               sym->type = b->info&0xf;
+               sym->other = b->other;
+       } else {
+               ElfSymBytes *b;
+               
+               b = (ElfSymBytes*)(obj->symtab->base + i*sizeof *b);
+               sym->name = (char*)obj->symstr->base + obj->e->e32(b->name);
+               sym->value = obj->e->e32(b->value);
+               sym->size = obj->e->e32(b->size);
+               sym->shndx = obj->e->e16(b->shndx);
+               sym->bind = b->info>>4;
+               sym->type = b->info&0xf;
+               sym->other = b->other;
+       }
+
+       s = nil;
+       if(strcmp(sym->name, "_GLOBAL_OFFSET_TABLE_") == 0)
+               sym->name = ".got";
+       if(strcmp(sym->name, "__stack_chk_fail_local") == 0)
+               sym->other = 0;  // rewrite hidden -> default visibility
+       switch(sym->type) {
+       case ElfSymTypeSection:
+               s = obj->sect[sym->shndx].sym;
+               break;
+       case ElfSymTypeObject:
+       case ElfSymTypeFunc:
+       case ElfSymTypeNone:
+               switch(sym->bind) {
+               case ElfSymBindGlobal:
+                       if(sym->other != 2) {
+                               s = lookup(sym->name, 0);
+                               break;
+                       }
+                       // fall through
+               case ElfSymBindLocal:
+                       s = lookup(sym->name, version);
+                       break;
+               default:
+                       werrstr("%s: invalid symbol binding %d", sym->name, sym->bind);
+                       return -1;
+               }
+               break;
+       }
+       if(s != nil && s->type == 0 && sym->type != ElfSymTypeSection)
+               s->type = SXREF;
+       sym->sym = s;
+
+       return 0;
+}
+
+int
+rbyoff(const void *va, const void *vb)
+{
+       Reloc *a, *b;
+       
+       a = (Reloc*)va;
+       b = (Reloc*)vb;
+       if(a->off < b->off)
+               return -1;
+       if(a->off > b->off)
+               return +1;
+       return 0;
+}
+
+#define R(x, y) ((x)|((y)<<24))
+
+static int
+reltype(char *pn, int elftype, uchar *siz)
+{
+       switch(R(thechar, elftype)) {
+       default:
+               diag("%s: unknown relocation type %d; compiled without -fpic?", pn, elftype);
+       case R('6', R_X86_64_PC32):
+       case R('6', R_X86_64_PLT32):
+       case R('6', R_X86_64_GOTPCREL):
+       case R('8', R_386_32):
+       case R('8', R_386_PC32):
+       case R('8', R_386_GOT32):
+       case R('8', R_386_PLT32):
+       case R('8', R_386_GOTOFF):
+       case R('8', R_386_GOTPC):
+               *siz = 4;
+               break;
+       case R('6', R_X86_64_64):
+               *siz = 8;
+               break;
+       }
+
+       return 256+elftype;
+}