// Copyright 2010 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/pe.h" #define IMAGE_SCN_MEM_DISCARDABLE 0x2000000 #define IMAGE_SYM_UNDEFINED 0 #define IMAGE_SYM_ABSOLUTE (-1) #define IMAGE_SYM_DEBUG (-2) #define IMAGE_SYM_TYPE_NULL 0 #define IMAGE_SYM_TYPE_VOID 1 #define IMAGE_SYM_TYPE_CHAR 2 #define IMAGE_SYM_TYPE_SHORT 3 #define IMAGE_SYM_TYPE_INT 4 #define IMAGE_SYM_TYPE_LONG 5 #define IMAGE_SYM_TYPE_FLOAT 6 #define IMAGE_SYM_TYPE_DOUBLE 7 #define IMAGE_SYM_TYPE_STRUCT 8 #define IMAGE_SYM_TYPE_UNION 9 #define IMAGE_SYM_TYPE_ENUM 10 #define IMAGE_SYM_TYPE_MOE 11 #define IMAGE_SYM_TYPE_BYTE 12 #define IMAGE_SYM_TYPE_WORD 13 #define IMAGE_SYM_TYPE_UINT 14 #define IMAGE_SYM_TYPE_DWORD 15 #define IMAGE_SYM_TYPE_PCODE 32768 #define IMAGE_SYM_DTYPE_NULL 0 #define IMAGE_SYM_DTYPE_POINTER 0x10 #define IMAGE_SYM_DTYPE_FUNCTION 0x20 #define IMAGE_SYM_DTYPE_ARRAY 0x30 #define IMAGE_SYM_CLASS_END_OF_FUNCTION (-1) #define IMAGE_SYM_CLASS_NULL 0 #define IMAGE_SYM_CLASS_AUTOMATIC 1 #define IMAGE_SYM_CLASS_EXTERNAL 2 #define IMAGE_SYM_CLASS_STATIC 3 #define IMAGE_SYM_CLASS_REGISTER 4 #define IMAGE_SYM_CLASS_EXTERNAL_DEF 5 #define IMAGE_SYM_CLASS_LABEL 6 #define IMAGE_SYM_CLASS_UNDEFINED_LABEL 7 #define IMAGE_SYM_CLASS_MEMBER_OF_STRUCT 8 #define IMAGE_SYM_CLASS_ARGUMENT 9 #define IMAGE_SYM_CLASS_STRUCT_TAG 10 #define IMAGE_SYM_CLASS_MEMBER_OF_UNION 11 #define IMAGE_SYM_CLASS_UNION_TAG 12 #define IMAGE_SYM_CLASS_TYPE_DEFINITION 13 #define IMAGE_SYM_CLASS_UNDEFINED_STATIC 14 #define IMAGE_SYM_CLASS_ENUM_TAG 15 #define IMAGE_SYM_CLASS_MEMBER_OF_ENUM 16 #define IMAGE_SYM_CLASS_REGISTER_PARAM 17 #define IMAGE_SYM_CLASS_BIT_FIELD 18 #define IMAGE_SYM_CLASS_FAR_EXTERNAL 68 /* Not in PECOFF v8 spec */ #define IMAGE_SYM_CLASS_BLOCK 100 #define IMAGE_SYM_CLASS_FUNCTION 101 #define IMAGE_SYM_CLASS_END_OF_STRUCT 102 #define IMAGE_SYM_CLASS_FILE 103 #define IMAGE_SYM_CLASS_SECTION 104 #define IMAGE_SYM_CLASS_WEAK_EXTERNAL 105 #define IMAGE_SYM_CLASS_CLR_TOKEN 107 #define IMAGE_REL_I386_ABSOLUTE 0x0000 #define IMAGE_REL_I386_DIR16 0x0001 #define IMAGE_REL_I386_REL16 0x0002 #define IMAGE_REL_I386_DIR32 0x0006 #define IMAGE_REL_I386_DIR32NB 0x0007 #define IMAGE_REL_I386_SEG12 0x0009 #define IMAGE_REL_I386_SECTION 0x000A #define IMAGE_REL_I386_SECREL 0x000B #define IMAGE_REL_I386_TOKEN 0x000C #define IMAGE_REL_I386_SECREL7 0x000D #define IMAGE_REL_I386_REL32 0x0014 #define IMAGE_REL_AMD64_ABSOLUTE 0x0000 #define IMAGE_REL_AMD64_ADDR64 0x0001 // R_X86_64_64 #define IMAGE_REL_AMD64_ADDR32 0x0002 // R_X86_64_PC32 #define IMAGE_REL_AMD64_ADDR32NB 0x0003 #define IMAGE_REL_AMD64_REL32 0x0004 #define IMAGE_REL_AMD64_REL32_1 0x0005 #define IMAGE_REL_AMD64_REL32_2 0x0006 #define IMAGE_REL_AMD64_REL32_3 0x0007 #define IMAGE_REL_AMD64_REL32_4 0x0008 #define IMAGE_REL_AMD64_REL32_5 0x0009 #define IMAGE_REL_AMD64_SECTION 0x000A #define IMAGE_REL_AMD64_SECREL 0x000B #define IMAGE_REL_AMD64_SECREL7 0x000C #define IMAGE_REL_AMD64_TOKEN 0x000D #define IMAGE_REL_AMD64_SREL32 0x000E #define IMAGE_REL_AMD64_PAIR 0x000F #define IMAGE_REL_AMD64_SSPAN32 0x0010 typedef struct PeSym PeSym; typedef struct PeSect PeSect; typedef struct PeObj PeObj; struct PeSym { char* name; uint32 value; uint16 sectnum; uint16 type; uint8 sclass; uint8 aux; Sym* sym; }; struct PeSect { char* name; uchar* base; uint64 size; Sym* sym; IMAGE_SECTION_HEADER sh; }; struct PeObj { Biobuf *f; char *name; uint32 base; PeSect *sect; uint nsect; PeSym *pesym; uint npesym; IMAGE_FILE_HEADER fh; char* snames; }; static int map(PeObj *obj, PeSect *sect); static int readsym(PeObj *obj, int i, PeSym **sym); void ldpe(Biobuf *f, char *pkg, int64 len, char *pn) { char *name; int32 base; int i, j, l, numaux; PeObj *obj; PeSect *sect, *rsect; IMAGE_SECTION_HEADER sh; uchar symbuf[18]; Sym *s; Reloc *r, *rp; PeSym *sym; USED(len); if(debug['v']) Bprint(&bso, "%5.2f ldpe %s\n", cputime(), pn); sect = nil; version++; base = Boffset(f); obj = mal(sizeof *obj); obj->f = f; obj->base = base; obj->name = pn; // read header if(Bread(f, &obj->fh, sizeof obj->fh) != sizeof obj->fh) goto bad; // load section list obj->sect = mal(obj->fh.NumberOfSections*sizeof obj->sect[0]); obj->nsect = obj->fh.NumberOfSections; for(i=0; i < obj->fh.NumberOfSections; i++) { if(Bread(f, &obj->sect[i].sh, sizeof sh) != sizeof sh) goto bad; obj->sect[i].size = obj->sect[i].sh.SizeOfRawData; obj->sect[i].name = (char*)obj->sect[i].sh.Name; // TODO return error if found .cormeta } // load string table Bseek(f, base+obj->fh.PointerToSymbolTable+18*obj->fh.NumberOfSymbols, 0); if(Bread(f, &l, sizeof l) != sizeof l) goto bad; obj->snames = mal(l); Bseek(f, base+obj->fh.PointerToSymbolTable+18*obj->fh.NumberOfSymbols, 0); if(Bread(f, obj->snames, l) != l) goto bad; // read symbols obj->pesym = mal(obj->fh.NumberOfSymbols*sizeof obj->pesym[0]); obj->npesym = obj->fh.NumberOfSymbols; Bseek(f, base+obj->fh.PointerToSymbolTable, 0); for(i=0; ifh.NumberOfSymbols; i+=numaux+1) { Bseek(f, base+obj->fh.PointerToSymbolTable+sizeof(symbuf)*i, 0); if(Bread(f, symbuf, sizeof symbuf) != sizeof symbuf) goto bad; if((symbuf[0] == 0) && (symbuf[1] == 0) && (symbuf[2] == 0) && (symbuf[3] == 0)) { l = le32(&symbuf[4]); obj->pesym[i].name = (char*)&obj->snames[l]; } else { // sym name length <= 8 obj->pesym[i].name = mal(9); strncpy(obj->pesym[i].name, (char*)symbuf, 8); obj->pesym[i].name[8] = 0; } obj->pesym[i].value = le32(&symbuf[8]); obj->pesym[i].sectnum = le16(&symbuf[12]); obj->pesym[i].sclass = symbuf[16]; obj->pesym[i].aux = symbuf[17]; obj->pesym[i].type = le16(&symbuf[14]); numaux = obj->pesym[i].aux; if (numaux < 0) numaux = 0; } // create symbols for mapped sections for(i=0; insect; i++) { sect = &obj->sect[i]; if(sect->sh.Characteristics&IMAGE_SCN_MEM_DISCARDABLE) continue; if(map(obj, sect) < 0) goto bad; name = smprint("%s(%s)", pkg, sect->name); s = lookup(name, version); free(name); switch(sect->sh.Characteristics&(IMAGE_SCN_CNT_UNINITIALIZED_DATA|IMAGE_SCN_CNT_INITIALIZED_DATA| IMAGE_SCN_MEM_READ|IMAGE_SCN_MEM_WRITE|IMAGE_SCN_CNT_CODE|IMAGE_SCN_MEM_EXECUTE)) { case IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_MEM_READ: //.rdata s->type = SRODATA; break; case IMAGE_SCN_CNT_UNINITIALIZED_DATA|IMAGE_SCN_MEM_READ|IMAGE_SCN_MEM_WRITE: //.bss s->type = SBSS; break; case IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_MEM_READ|IMAGE_SCN_MEM_WRITE: //.data s->type = SDATA; break; case IMAGE_SCN_CNT_CODE|IMAGE_SCN_MEM_EXECUTE|IMAGE_SCN_MEM_READ: //.text s->type = STEXT; break; default: werrstr("unexpected flags for PE section %s", sect->name); goto bad; } 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; if(strcmp(sect->name, ".rsrc") == 0) setpersrc(sect->sym); } // load relocations for(i=0; insect; i++) { rsect = &obj->sect[i]; if(rsect->sym == 0 || rsect->sh.NumberOfRelocations == 0) continue; if(rsect->sh.Characteristics&IMAGE_SCN_MEM_DISCARDABLE) continue; r = mal(rsect->sh.NumberOfRelocations*sizeof r[0]); Bseek(f, obj->base+rsect->sh.PointerToRelocations, 0); for(j=0; jsh.NumberOfRelocations; j++) { rp = &r[j]; if(Bread(f, symbuf, 10) != 10) goto bad; uint32 rva, symindex; uint16 type; rva = le32(&symbuf[0]); symindex = le32(&symbuf[4]); type = le16(&symbuf[8]); if(readsym(obj, symindex, &sym) < 0) goto bad; if(sym->sym == nil) { werrstr("reloc of invalid sym %s idx=%d type=%d", sym->name, symindex, sym->type); goto bad; } rp->sym = sym->sym; rp->siz = 4; rp->off = rva; switch(type) { default: diag("%s: unknown relocation type %d;", pn, type); case IMAGE_REL_I386_REL32: case IMAGE_REL_AMD64_REL32: case IMAGE_REL_AMD64_ADDR32: // R_X86_64_PC32 case IMAGE_REL_AMD64_ADDR32NB: rp->type = D_PCREL; rp->add = le32(rsect->base+rp->off); break; case IMAGE_REL_I386_DIR32NB: case IMAGE_REL_I386_DIR32: rp->type = D_ADDR; // load addend from image rp->add = le32(rsect->base+rp->off); break; case IMAGE_REL_AMD64_ADDR64: // R_X86_64_64 rp->siz = 8; rp->type = D_ADDR; // load addend from image rp->add = le64(rsect->base+rp->off); break; } // ld -r could generate multiple section symbols for the // same section but with different values, we have to take // that into account if (obj->pesym[symindex].name[0] == '.') rp->add += obj->pesym[symindex].value; } qsort(r, rsect->sh.NumberOfRelocations, sizeof r[0], rbyoff); s = rsect->sym; s->r = r; s->nr = rsect->sh.NumberOfRelocations; } // enter sub-symbols into symbol table. for(i=0; inpesym; i++) { if(obj->pesym[i].name == 0) continue; if(obj->pesym[i].name[0] == '.') //skip section continue; if(obj->pesym[i].sectnum > 0) { sect = &obj->sect[obj->pesym[i].sectnum-1]; if(sect->sym == 0) continue; } if(readsym(obj, i, &sym) < 0) goto bad; s = sym->sym; if(sym->sectnum == 0) {// extern if(s->type == SDYNIMPORT) s->plt = -2; // flag for dynimport in PE object files. if (s->type == SXREF && sym->value > 0) {// global data s->type = SDATA; s->size = sym->value; } continue; } else if (sym->sectnum > 0) { sect = &obj->sect[sym->sectnum-1]; if(sect->sym == 0) diag("%s: %s sym == 0!", pn, s->name); } else { diag("%s: %s sectnum < 0!", pn, s->name); } if(sect == nil) return; s->sub = sect->sym->sub; sect->sym->sub = s; s->type = sect->sym->type | SSUB; s->value = sym->value; s->size = 4; 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 pe file: %r", pn); } static int map(PeObj *obj, PeSect *sect) { if(sect->base != nil) return 0; sect->base = mal(sect->sh.SizeOfRawData); if(sect->sh.PointerToRawData == 0) // .bss doesn't have data in object file return 0; werrstr("short read"); if(Bseek(obj->f, obj->base+sect->sh.PointerToRawData, 0) < 0 || Bread(obj->f, sect->base, sect->sh.SizeOfRawData) != sect->sh.SizeOfRawData) return -1; return 0; } static int readsym(PeObj *obj, int i, PeSym **y) { Sym *s; PeSym *sym; char *name, *p; if(i >= obj->npesym || i < 0) { werrstr("invalid pe symbol index"); return -1; } sym = &obj->pesym[i]; *y = sym; if(sym->name[0] == '.') // .section name = obj->sect[sym->sectnum-1].sym->name; else { name = sym->name; if(strncmp(name, "__imp_", 6) == 0) name = &name[6]; // __imp_Name => Name if(thechar == '8' && name[0] == '_') name = &name[1]; // _Name => Name } // remove last @XXX p = strchr(name, '@'); if(p) *p = 0; switch(sym->type) { default: werrstr("%s: invalid symbol type %d", sym->name, sym->type); return -1; case IMAGE_SYM_DTYPE_FUNCTION: case IMAGE_SYM_DTYPE_NULL: switch(sym->sclass) { case IMAGE_SYM_CLASS_EXTERNAL: //global s = lookup(name, 0); break; case IMAGE_SYM_CLASS_NULL: case IMAGE_SYM_CLASS_STATIC: s = lookup(name, version); break; default: werrstr("%s: invalid symbol binding %d", sym->name, sym->sclass); return -1; } break; } if(s != nil && s->type == 0 && !(sym->sclass == IMAGE_SYM_CLASS_STATIC && sym->value == 0)) s->type = SXREF; if(strncmp(sym->name, "__imp_", 6) == 0) s->got = -2; // flag for __imp_ sym->sym = s; return 0; }