/* ** LuaJIT VM builder: PE object emitter. ** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h ** ** Only used for building on Windows, since we cannot assume the presence ** of a suitable assembler. The host and target byte order must match. */ #include "buildvm.h" #include "lj_bc.h" #if LJ_TARGET_X86ORX64 /* Context for PE object emitter. */ static char *strtab; static size_t strtabofs; /* -- PE object definitions ----------------------------------------------- */ /* PE header. */ typedef struct PEheader { uint16_t arch; uint16_t nsects; uint32_t time; uint32_t symtabofs; uint32_t nsyms; uint16_t opthdrsz; uint16_t flags; } PEheader; /* PE section. */ typedef struct PEsection { char name[8]; uint32_t vsize; uint32_t vaddr; uint32_t size; uint32_t ofs; uint32_t relocofs; uint32_t lineofs; uint16_t nreloc; uint16_t nline; uint32_t flags; } PEsection; /* PE relocation. */ typedef struct PEreloc { uint32_t vaddr; uint32_t symidx; uint16_t type; } PEreloc; /* Cannot use sizeof, because it pads up to the max. alignment. */ #define PEOBJ_RELOC_SIZE (4+4+2) /* PE symbol table entry. */ typedef struct PEsym { union { char name[8]; uint32_t nameref[2]; } n; uint32_t value; int16_t sect; uint16_t type; uint8_t scl; uint8_t naux; } PEsym; /* PE symbol table auxiliary entry for a section. */ typedef struct PEsymaux { uint32_t size; uint16_t nreloc; uint16_t nline; uint32_t cksum; uint16_t assoc; uint8_t comdatsel; uint8_t unused[3]; } PEsymaux; /* Cannot use sizeof, because it pads up to the max. alignment. */ #define PEOBJ_SYM_SIZE (8+4+2+2+1+1) /* PE object CPU specific defines. */ #if LJ_TARGET_X86 #define PEOBJ_ARCH_TARGET 0x014c #define PEOBJ_RELOC_REL32 0x14 /* MS: REL32, GNU: DISP32. */ #define PEOBJ_RELOC_DIR32 0x06 #define PEOBJ_SYM_PREFIX "_" #elif LJ_TARGET_X64 #define PEOBJ_ARCH_TARGET 0x8664 #define PEOBJ_RELOC_REL32 0x04 /* MS: REL32, GNU: DISP32. */ #define PEOBJ_RELOC_DIR32 0x02 #define PEOBJ_SYM_PREFIX "" #endif /* Section numbers (0-based). */ enum { PEOBJ_SECT_ABS = -2, PEOBJ_SECT_UNDEF = -1, PEOBJ_SECT_TEXT, /* TODO: add .pdata/.xdata for x64. */ PEOBJ_SECT_RDATA, PEOBJ_SECT_RDATA_Z, PEOBJ_NSECTIONS }; /* Symbol types. */ #define PEOBJ_TYPE_NULL 0 #define PEOBJ_TYPE_FUNC 0x20 /* Symbol storage class. */ #define PEOBJ_SCL_EXTERN 2 #define PEOBJ_SCL_STATIC 3 /* -- PE object emitter --------------------------------------------------- */ /* Emit PE object symbol. */ static void emit_peobj_sym(BuildCtx *ctx, const char *name, uint32_t value, int sect, int type, int scl) { PEsym sym; size_t len = strlen(name); if (!strtab) { /* Pass 1: only calculate string table length. */ if (len > 8) strtabofs += len+1; return; } if (len <= 8) { memcpy(sym.n.name, name, len); memset(sym.n.name+len, 0, 8-len); } else { sym.n.nameref[0] = 0; sym.n.nameref[1] = strtabofs; memcpy(strtab + strtabofs, name, len); strtab[strtabofs+len] = 0; strtabofs += len+1; } sym.value = value; sym.sect = (int16_t)(sect+1); /* 1-based section number. */ sym.type = (uint16_t)type; sym.scl = (uint8_t)scl; sym.naux = 0; owrite(ctx, &sym, PEOBJ_SYM_SIZE); } /* Emit PE object section symbol. */ static void emit_peobj_sym_sect(BuildCtx *ctx, PEsection *pesect, int sect) { PEsym sym; PEsymaux aux; if (!strtab) return; /* Pass 1: no output. */ memcpy(sym.n.name, pesect[sect].name, 8); sym.value = 0; sym.sect = (int16_t)(sect+1); /* 1-based section number. */ sym.type = PEOBJ_TYPE_NULL; sym.scl = PEOBJ_SCL_STATIC; sym.naux = 1; owrite(ctx, &sym, PEOBJ_SYM_SIZE); memset(&aux, 0, sizeof(PEsymaux)); aux.size = pesect[sect].size; aux.nreloc = pesect[sect].nreloc; owrite(ctx, &aux, PEOBJ_SYM_SIZE); } #define emit_peobj_sym_func(ctx, name, ofs) \ emit_peobj_sym(ctx, name, (uint32_t)(ofs), \ PEOBJ_SECT_TEXT, PEOBJ_TYPE_FUNC, PEOBJ_SCL_EXTERN) #define emit_peobj_sym_rdata(ctx, name, ofs) \ emit_peobj_sym(ctx, name, (uint32_t)(ofs), \ PEOBJ_SECT_RDATA, PEOBJ_TYPE_NULL, PEOBJ_SCL_EXTERN) /* Emit Windows PE object file. */ void emit_peobj(BuildCtx *ctx) { PEheader pehdr; PEsection pesect[PEOBJ_NSECTIONS]; int nzsym, relocsyms; uint32_t sofs; int i; union { uint8_t b; uint32_t u; } host_endian; host_endian.u = 1; if (host_endian.b != LJ_ENDIAN_SELECT(1, 0)) { fprintf(stderr, "Error: different byte order for host and target\n"); exit(1); } sofs = sizeof(PEheader) + PEOBJ_NSECTIONS*sizeof(PEsection); /* Fill in PE sections. */ memset(&pesect, 0, PEOBJ_NSECTIONS*sizeof(PEsection)); memcpy(pesect[PEOBJ_SECT_TEXT].name, ".text", sizeof(".text")-1); pesect[PEOBJ_SECT_TEXT].ofs = sofs; sofs += (pesect[PEOBJ_SECT_TEXT].size = (uint32_t)ctx->codesz); pesect[PEOBJ_SECT_TEXT].relocofs = sofs; sofs += (pesect[PEOBJ_SECT_TEXT].nreloc = (uint16_t)ctx->nreloc) * PEOBJ_RELOC_SIZE; /* Flags: 60 = read+execute, 50 = align16, 20 = code. */ pesect[PEOBJ_SECT_TEXT].flags = 0x60500020; memcpy(pesect[PEOBJ_SECT_RDATA].name, ".rdata", sizeof(".rdata")-1); pesect[PEOBJ_SECT_RDATA].ofs = sofs; sofs += (pesect[PEOBJ_SECT_RDATA].size = ctx->npc*sizeof(uint16_t)); /* Flags: 40 = read, 30 = align4, 40 = initialized data. */ pesect[PEOBJ_SECT_RDATA].flags = 0x40300040; memcpy(pesect[PEOBJ_SECT_RDATA_Z].name, ".rdata$Z", sizeof(".rdata$Z")-1); pesect[PEOBJ_SECT_RDATA_Z].ofs = sofs; sofs += (pesect[PEOBJ_SECT_RDATA_Z].size = (uint32_t)strlen(ctx->dasm_ident)+1); /* Flags: 40 = read, 30 = align4, 40 = initialized data. */ pesect[PEOBJ_SECT_RDATA_Z].flags = 0x40300040; /* Fill in PE header. */ pehdr.arch = PEOBJ_ARCH_TARGET; pehdr.nsects = PEOBJ_NSECTIONS; pehdr.time = 0; /* Timestamp is optional. */ pehdr.symtabofs = sofs; pehdr.opthdrsz = 0; pehdr.flags = 0; /* Compute the size of the symbol table: ** @feat.00 + nsections*2 ** + asm_start + (nsyms-nzsym) + op_ofs ** + relocsyms */ /* Skip _Z syms. */ for (nzsym = 0; ctx->sym_ofs[ctx->perm[nzsym]] < 0; nzsym++) ; for (relocsyms = 0; ctx->extnames[relocsyms]; relocsyms++) ; pehdr.nsyms = 1+PEOBJ_NSECTIONS*2 + 1+(ctx->nsym-nzsym)+1 + relocsyms; /* Write PE object header and all sections. */ owrite(ctx, &pehdr, sizeof(PEheader)); owrite(ctx, &pesect, sizeof(PEsection)*PEOBJ_NSECTIONS); /* Write .text section. */ owrite(ctx, ctx->code, ctx->codesz); for (i = 0; i < ctx->nreloc; i++) { PEreloc reloc; reloc.vaddr = (uint32_t)ctx->reloc[i].ofs; reloc.symidx = 1+2+ctx->reloc[i].sym; /* Reloc syms are after .text sym. */ reloc.type = ctx->reloc[i].type ? PEOBJ_RELOC_REL32 : PEOBJ_RELOC_DIR32; owrite(ctx, &reloc, PEOBJ_RELOC_SIZE); } /* Write .rdata section. */ for (i = 0; i < ctx->npc; i++) { uint16_t pcofs = (uint16_t)ctx->sym_ofs[i]; owrite(ctx, &pcofs, 2); } /* Write .rdata$Z section. */ owrite(ctx, ctx->dasm_ident, strlen(ctx->dasm_ident)+1); /* Write symbol table. */ strtab = NULL; /* 1st pass: collect string sizes. */ for (;;) { char name[80]; strtabofs = 4; /* Mark as SafeSEH compliant. */ emit_peobj_sym(ctx, "@feat.00", 1, PEOBJ_SECT_ABS, PEOBJ_TYPE_NULL, PEOBJ_SCL_STATIC); emit_peobj_sym_sect(ctx, pesect, PEOBJ_SECT_TEXT); for (i = 0; ctx->extnames[i]; i++) { sprintf(name, PEOBJ_SYM_PREFIX "%s", ctx->extnames[i]); emit_peobj_sym(ctx, name, 0, PEOBJ_SECT_UNDEF, PEOBJ_TYPE_FUNC, PEOBJ_SCL_EXTERN); } emit_peobj_sym(ctx, PEOBJ_SYM_PREFIX LABEL_ASM_BEGIN, 0, PEOBJ_SECT_TEXT, PEOBJ_TYPE_NULL, PEOBJ_SCL_EXTERN); for (i = nzsym; i < ctx->nsym; i++) { int pi = ctx->perm[i]; if (pi >= ctx->npc) { sprintf(name, PEOBJ_SYM_PREFIX LABEL_PREFIX "%s", ctx->globnames[pi-ctx->npc]); emit_peobj_sym_func(ctx, name, ctx->sym_ofs[pi]); #if LJ_HASJIT } else { #else } else if (!(pi == BC_JFORI || pi == BC_JFORL || pi == BC_JITERL || pi == BC_JLOOP || pi == BC_IFORL || pi == BC_IITERL || pi == BC_ILOOP)) { #endif sprintf(name, PEOBJ_SYM_PREFIX LABEL_PREFIX_BC "%s", bc_names[pi]); emit_peobj_sym_func(ctx, name, ctx->sym_ofs[pi]); } } emit_peobj_sym_sect(ctx, pesect, PEOBJ_SECT_RDATA); emit_peobj_sym_rdata(ctx, PEOBJ_SYM_PREFIX LABEL_OP_OFS, 0); emit_peobj_sym_sect(ctx, pesect, PEOBJ_SECT_RDATA_Z); if (strtab) break; /* 2nd pass: alloc strtab, write syms and copy strings. */ strtab = (char *)malloc(strtabofs); *(uint32_t *)strtab = strtabofs; } /* Write string table. */ owrite(ctx, strtab, strtabofs); } #endif