| /* |
| 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; |
| LSym *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; |
| LSym* 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*, int); |
| static int reltype(char*, int, uchar*); |
| |
| int |
| valuecmp(LSym *a, LSym *b) |
| { |
| if(a->value < b->value) |
| return -1; |
| if(a->value > b->value) |
| return +1; |
| return 0; |
| } |
| |
| 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; |
| ElfHdrBytes *hdr; |
| ElfObj *obj; |
| ElfSect *sect, *rsect; |
| ElfSym sym; |
| Endian *e; |
| Reloc *r, *rp; |
| LSym *s; |
| LSym **symbols; |
| |
| symbols = nil; |
| |
| if(debug['v']) |
| Bprint(&bso, "%5.2f ldelf %s\n", cputime(), pn); |
| |
| ctxt->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 = ≤ |
| 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", pn); |
| return; |
| } |
| |
| switch(thechar) { |
| default: |
| diag("%s: elf %s unimplemented", pn, 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) { |
| // our work is done here - no symbols means nothing can refer to this file |
| 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. |
| |
| // create symbols for mapped sections |
| for(i=0; i<obj->nsect; i++) { |
| sect = &obj->sect[i]; |
| if((sect->type != ElfSectProgbits && sect->type != ElfSectNobits) || !(sect->flags&ElfSectFlagAlloc)) |
| continue; |
| if(sect->type != ElfSectNobits && map(obj, sect) < 0) |
| goto bad; |
| |
| name = smprint("%s(%s)", pkg, sect->name); |
| s = linklookup(ctxt, name, ctxt->version); |
| free(name); |
| switch((int)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: |
| if(sect->type == ElfSectNobits) |
| s->type = SNOPTRBSS; |
| else |
| s->type = SNOPTRDATA; |
| break; |
| case ElfSectFlagAlloc + ElfSectFlagExec: |
| s->type = STEXT; |
| break; |
| } |
| if(sect->type == ElfSectProgbits) { |
| s->p = sect->base; |
| s->np = sect->size; |
| } |
| s->size = sect->size; |
| s->align = sect->align; |
| sect->sym = s; |
| } |
| |
| // enter sub-symbols into symbol table. |
| // symbol 0 is the null symbol. |
| symbols = malloc(obj->nsymtab * sizeof(symbols[0])); |
| if(symbols == nil) { |
| diag("out of memory"); |
| errorexit(); |
| } |
| for(i=1; i<obj->nsymtab; i++) { |
| if(readsym(obj, i, &sym, 1) < 0) |
| goto bad; |
| symbols[i] = sym.sym; |
| 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 = SNOPTRBSS; |
| continue; |
| } |
| if(sym.shndx >= obj->nsect || sym.shndx == 0) |
| continue; |
| // even when we pass needSym == 1 to readsym, it might still return nil to skip some unwanted symbols |
| if(sym.sym == S) |
| continue; |
| sect = obj->sect+sym.shndx; |
| if(sect->sym == nil) { |
| if(strncmp(sym.name, ".Linfo_string", 13) == 0) // clang does this |
| continue; |
| diag("%s: sym#%d: ignoring %s in section %d (type %d)", pn, i, sym.name, sym.shndx, sym.type); |
| continue; |
| } |
| s = sym.sym; |
| if(s->outer != S) { |
| if(s->dupok) |
| continue; |
| diag("%s: duplicate symbol reference: %s in both %s and %s", pn, s->name, s->outer->name, sect->sym->name); |
| errorexit(); |
| } |
| s->sub = sect->sym->sub; |
| sect->sym->sub = s; |
| s->type = sect->sym->type | (s->type&~SMASK) | SSUB; |
| if(!(s->cgoexport & CgoExportDynamic)) |
| s->dynimplib = nil; // satisfy dynimport |
| s->value = sym.value; |
| s->size = sym.size; |
| s->outer = sect->sym; |
| if(sect->sym->type == STEXT) { |
| if(s->external && !s->dupok) |
| diag("%s: duplicate definition of %s", pn, s->name); |
| s->external = 1; |
| } |
| } |
| |
| // Sort outer lists by address, adding to textp. |
| // This keeps textp in increasing address order. |
| for(i=0; i<obj->nsect; i++) { |
| s = obj->sect[i].sym; |
| if(s == S) |
| continue; |
| if(s->sub) |
| s->sub = listsort(s->sub, valuecmp, offsetof(LSym, sub)); |
| if(s->type == STEXT) { |
| if(s->onlist) |
| sysfatal("symbol %s listed multiple times", s->name); |
| s->onlist = 1; |
| if(ctxt->etextp) |
| ctxt->etextp->next = s; |
| else |
| ctxt->textp = s; |
| ctxt->etextp = s; |
| for(s = s->sub; s != S; s = s->sub) { |
| if(s->onlist) |
| sysfatal("symbol %s listed multiple times", s->name); |
| s->onlist = 1; |
| ctxt->etextp->next = s; |
| ctxt->etextp = 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; |
| 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((info & 0xffffffff) == 0) { // skip R_*_NONE relocation |
| j--; |
| n--; |
| continue; |
| } |
| if((info >> 32) == 0) { // absolute relocation, don't bother reading the null symbol |
| rp->sym = S; |
| } else { |
| if(readsym(obj, info>>32, &sym, 0) < 0) |
| goto bad; |
| sym.sym = symbols[info>>32]; |
| if(sym.sym == nil) { |
| werrstr("%s#%d: reloc of invalid sym #%d %s shndx=%d type=%d", |
| sect->sym->name, j, (int)(info>>32), 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); |
| } |
| if(rp->siz == 4) |
| rp->add = (int32)rp->add; |
| //print("rel %s %d %d %s %#llx\n", sect->sym->name, rp->type, rp->siz, rp->sym->name, rp->add); |
| } |
| qsort(r, n, sizeof r[0], rbyoff); // just in case |
| |
| s = sect->sym; |
| s->r = r; |
| s->nr = n; |
| } |
| free(symbols); |
| |
| return; |
| |
| bad: |
| diag("%s: malformed elf file: %r", pn); |
| free(symbols); |
| } |
| |
| 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, int needSym) |
| { |
| LSym *s; |
| |
| if(i >= obj->nsymtab || i < 0) { |
| werrstr("invalid elf symbol index"); |
| return -1; |
| } |
| if(i == 0) { |
| diag("readym: read null symbol!"); |
| } |
| |
| 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"; |
| switch(sym->type) { |
| case ElfSymTypeSection: |
| s = obj->sect[sym->shndx].sym; |
| break; |
| case ElfSymTypeObject: |
| case ElfSymTypeFunc: |
| case ElfSymTypeNone: |
| switch(sym->bind) { |
| case ElfSymBindGlobal: |
| if(needSym) { |
| s = linklookup(ctxt, sym->name, 0); |
| // for global scoped hidden symbols we should insert it into |
| // symbol hash table, but mark them as hidden. |
| // __i686.get_pc_thunk.bx is allowed to be duplicated, to |
| // workaround that we set dupok. |
| // TODO(minux): correctly handle __i686.get_pc_thunk.bx without |
| // set dupok generally. See http://codereview.appspot.com/5823055/ |
| // comment #5 for details. |
| if(s && sym->other == 2) { |
| s->type |= SHIDDEN; |
| s->dupok = 1; |
| } |
| } |
| break; |
| case ElfSymBindLocal: |
| if(!(thechar == '5' && (strncmp(sym->name, "$a", 2) == 0 || strncmp(sym->name, "$d", 2) == 0))) // binutils for arm generate these mapping symbols, ignore these |
| if(needSym) { |
| // local names and hidden visiblity global names are unique |
| // and should only reference by its index, not name, so we |
| // don't bother to add them into hash table |
| s = linknewsym(ctxt, sym->name, ctxt->version); |
| s->type |= SHIDDEN; |
| } |
| break; |
| case ElfSymBindWeak: |
| if(needSym) { |
| s = linknewsym(ctxt, sym->name, 0); |
| if(sym->other == 2) |
| s->type |= SHIDDEN; |
| } |
| 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('5', R_ARM_ABS32): |
| case R('5', R_ARM_GOT32): |
| case R('5', R_ARM_PLT32): |
| case R('5', R_ARM_GOTOFF): |
| case R('5', R_ARM_GOTPC): |
| case R('5', R_ARM_THM_PC22): |
| case R('5', R_ARM_REL32): |
| case R('5', R_ARM_CALL): |
| case R('5', R_ARM_V4BX): |
| case R('5', R_ARM_GOT_PREL): |
| case R('5', R_ARM_PC24): |
| case R('5', R_ARM_JUMP24): |
| 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; |
| } |