diff options
Diffstat (limited to 'src/cmd/ld/ldelf.c')
-rw-r--r-- | src/cmd/ld/ldelf.c | 816 |
1 files changed, 816 insertions, 0 deletions
diff --git a/src/cmd/ld/ldelf.c b/src/cmd/ld/ldelf.c new file mode 100644 index 000000000..44bbe68ee --- /dev/null +++ b/src/cmd/ld/ldelf.c @@ -0,0 +1,816 @@ +/* +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; + 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 = ≤ + 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) { + // 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)", 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; + } + if(sect->type == ElfSectProgbits) { + 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("%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); + } + } + 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; + if(!s->dynexport) { + 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. + p = prg(); + p->as = ATEXT; + p->from.type = D_EXTERN; + p->from.sym = s; + p->textflag = 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; +} |