ljx

FORK: LuaJIT with native 5.2 and 5.3 support
git clone https://git.neptards.moe/neptards/ljx.git
Log | Files | Refs | README

buildvm_peobj.c (12154B)


      1 /*
      2 ** LuaJIT VM builder: PE object emitter.
      3 ** Copyright (C) 2005-2016 Mike Pall. See Copyright Notice in luajit.h
      4 **
      5 ** Only used for building on Windows, since we cannot assume the presence
      6 ** of a suitable assembler. The host and target byte order must match.
      7 */
      8 
      9 #include "buildvm.h"
     10 #include "lj_bc.h"
     11 
     12 #if LJ_TARGET_X86ORX64 || LJ_TARGET_PPC
     13 
     14 /* Context for PE object emitter. */
     15 static char *strtab;
     16 static size_t strtabofs;
     17 
     18 /* -- PE object definitions ----------------------------------------------- */
     19 
     20 /* PE header. */
     21 typedef struct PEheader {
     22   uint16_t arch;
     23   uint16_t nsects;
     24   uint32_t time;
     25   uint32_t symtabofs;
     26   uint32_t nsyms;
     27   uint16_t opthdrsz;
     28   uint16_t flags;
     29 } PEheader;
     30 
     31 /* PE section. */
     32 typedef struct PEsection {
     33   char name[8];
     34   uint32_t vsize;
     35   uint32_t vaddr;
     36   uint32_t size;
     37   uint32_t ofs;
     38   uint32_t relocofs;
     39   uint32_t lineofs;
     40   uint16_t nreloc;
     41   uint16_t nline;
     42   uint32_t flags;
     43 } PEsection;
     44 
     45 /* PE relocation. */
     46 typedef struct PEreloc {
     47   uint32_t vaddr;
     48   uint32_t symidx;
     49   uint16_t type;
     50 } PEreloc;
     51 
     52 /* Cannot use sizeof, because it pads up to the max. alignment. */
     53 #define PEOBJ_RELOC_SIZE	(4+4+2)
     54 
     55 /* PE symbol table entry. */
     56 typedef struct PEsym {
     57   union {
     58     char name[8];
     59     uint32_t nameref[2];
     60   } n;
     61   uint32_t value;
     62   int16_t sect;
     63   uint16_t type;
     64   uint8_t scl;
     65   uint8_t naux;
     66 } PEsym;
     67 
     68 /* PE symbol table auxiliary entry for a section. */
     69 typedef struct PEsymaux {
     70   uint32_t size;
     71   uint16_t nreloc;
     72   uint16_t nline;
     73   uint32_t cksum;
     74   uint16_t assoc;
     75   uint8_t comdatsel;
     76   uint8_t unused[3];
     77 } PEsymaux;
     78 
     79 /* Cannot use sizeof, because it pads up to the max. alignment. */
     80 #define PEOBJ_SYM_SIZE	(8+4+2+2+1+1)
     81 
     82 /* PE object CPU specific defines. */
     83 #if LJ_TARGET_X86
     84 #define PEOBJ_ARCH_TARGET	0x014c
     85 #define PEOBJ_RELOC_REL32	0x14  /* MS: REL32, GNU: DISP32. */
     86 #define PEOBJ_RELOC_DIR32	0x06
     87 #define PEOBJ_RELOC_OFS		0
     88 #define PEOBJ_TEXT_FLAGS	0x60500020  /* 60=r+x, 50=align16, 20=code. */
     89 #elif LJ_TARGET_X64
     90 #define PEOBJ_ARCH_TARGET	0x8664
     91 #define PEOBJ_RELOC_REL32	0x04  /* MS: REL32, GNU: DISP32. */
     92 #define PEOBJ_RELOC_DIR32	0x02
     93 #define PEOBJ_RELOC_ADDR32NB	0x03
     94 #define PEOBJ_RELOC_OFS		0
     95 #define PEOBJ_TEXT_FLAGS	0x60500020  /* 60=r+x, 50=align16, 20=code. */
     96 #elif LJ_TARGET_PPC
     97 #define PEOBJ_ARCH_TARGET	0x01f2
     98 #define PEOBJ_RELOC_REL32	0x06
     99 #define PEOBJ_RELOC_DIR32	0x02
    100 #define PEOBJ_RELOC_OFS		(-4)
    101 #define PEOBJ_TEXT_FLAGS	0x60400020  /* 60=r+x, 40=align8, 20=code. */
    102 #endif
    103 
    104 /* Section numbers (0-based). */
    105 enum {
    106   PEOBJ_SECT_ABS = -2,
    107   PEOBJ_SECT_UNDEF = -1,
    108   PEOBJ_SECT_TEXT,
    109 #if LJ_TARGET_X64
    110   PEOBJ_SECT_PDATA,
    111   PEOBJ_SECT_XDATA,
    112 #elif LJ_TARGET_X86
    113   PEOBJ_SECT_SXDATA,
    114 #endif
    115   PEOBJ_SECT_RDATA_Z,
    116   PEOBJ_NSECTIONS
    117 };
    118 
    119 /* Symbol types. */
    120 #define PEOBJ_TYPE_NULL		0
    121 #define PEOBJ_TYPE_FUNC		0x20
    122 
    123 /* Symbol storage class. */
    124 #define PEOBJ_SCL_EXTERN	2
    125 #define PEOBJ_SCL_STATIC	3
    126 
    127 /* -- PE object emitter --------------------------------------------------- */
    128 
    129 /* Emit PE object symbol. */
    130 static void emit_peobj_sym(BuildCtx *ctx, const char *name, uint32_t value,
    131 			   int sect, int type, int scl)
    132 {
    133   PEsym sym;
    134   size_t len = strlen(name);
    135   if (!strtab) {  /* Pass 1: only calculate string table length. */
    136     if (len > 8) strtabofs += len+1;
    137     return;
    138   }
    139   if (len <= 8) {
    140     memcpy(sym.n.name, name, len);
    141     memset(sym.n.name+len, 0, 8-len);
    142   } else {
    143     sym.n.nameref[0] = 0;
    144     sym.n.nameref[1] = (uint32_t)strtabofs;
    145     memcpy(strtab + strtabofs, name, len);
    146     strtab[strtabofs+len] = 0;
    147     strtabofs += len+1;
    148   }
    149   sym.value = value;
    150   sym.sect = (int16_t)(sect+1);  /* 1-based section number. */
    151   sym.type = (uint16_t)type;
    152   sym.scl = (uint8_t)scl;
    153   sym.naux = 0;
    154   owrite(ctx, &sym, PEOBJ_SYM_SIZE);
    155 }
    156 
    157 /* Emit PE object section symbol. */
    158 static void emit_peobj_sym_sect(BuildCtx *ctx, PEsection *pesect, int sect)
    159 {
    160   PEsym sym;
    161   PEsymaux aux;
    162   if (!strtab) return;  /* Pass 1: no output. */
    163   memcpy(sym.n.name, pesect[sect].name, 8);
    164   sym.value = 0;
    165   sym.sect = (int16_t)(sect+1);  /* 1-based section number. */
    166   sym.type = PEOBJ_TYPE_NULL;
    167   sym.scl = PEOBJ_SCL_STATIC;
    168   sym.naux = 1;
    169   owrite(ctx, &sym, PEOBJ_SYM_SIZE);
    170   memset(&aux, 0, sizeof(PEsymaux));
    171   aux.size = pesect[sect].size;
    172   aux.nreloc = pesect[sect].nreloc;
    173   owrite(ctx, &aux, PEOBJ_SYM_SIZE);
    174 }
    175 
    176 /* Emit Windows PE object file. */
    177 void emit_peobj(BuildCtx *ctx)
    178 {
    179   PEheader pehdr;
    180   PEsection pesect[PEOBJ_NSECTIONS];
    181   uint32_t sofs;
    182   int i, nrsym;
    183   union { uint8_t b; uint32_t u; } host_endian;
    184 
    185   sofs = sizeof(PEheader) + PEOBJ_NSECTIONS*sizeof(PEsection);
    186 
    187   /* Fill in PE sections. */
    188   memset(&pesect, 0, PEOBJ_NSECTIONS*sizeof(PEsection));
    189   memcpy(pesect[PEOBJ_SECT_TEXT].name, ".text", sizeof(".text")-1);
    190   pesect[PEOBJ_SECT_TEXT].ofs = sofs;
    191   sofs += (pesect[PEOBJ_SECT_TEXT].size = (uint32_t)ctx->codesz);
    192   pesect[PEOBJ_SECT_TEXT].relocofs = sofs;
    193   sofs += (pesect[PEOBJ_SECT_TEXT].nreloc = (uint16_t)ctx->nreloc) * PEOBJ_RELOC_SIZE;
    194   /* Flags: 60 = read+execute, 50 = align16, 20 = code. */
    195   pesect[PEOBJ_SECT_TEXT].flags = PEOBJ_TEXT_FLAGS;
    196 
    197 #if LJ_TARGET_X64
    198   memcpy(pesect[PEOBJ_SECT_PDATA].name, ".pdata", sizeof(".pdata")-1);
    199   pesect[PEOBJ_SECT_PDATA].ofs = sofs;
    200   sofs += (pesect[PEOBJ_SECT_PDATA].size = 6*4);
    201   pesect[PEOBJ_SECT_PDATA].relocofs = sofs;
    202   sofs += (pesect[PEOBJ_SECT_PDATA].nreloc = 6) * PEOBJ_RELOC_SIZE;
    203   /* Flags: 40 = read, 30 = align4, 40 = initialized data. */
    204   pesect[PEOBJ_SECT_PDATA].flags = 0x40300040;
    205 
    206   memcpy(pesect[PEOBJ_SECT_XDATA].name, ".xdata", sizeof(".xdata")-1);
    207   pesect[PEOBJ_SECT_XDATA].ofs = sofs;
    208   sofs += (pesect[PEOBJ_SECT_XDATA].size = 8*2+4+6*2);  /* See below. */
    209   pesect[PEOBJ_SECT_XDATA].relocofs = sofs;
    210   sofs += (pesect[PEOBJ_SECT_XDATA].nreloc = 1) * PEOBJ_RELOC_SIZE;
    211   /* Flags: 40 = read, 30 = align4, 40 = initialized data. */
    212   pesect[PEOBJ_SECT_XDATA].flags = 0x40300040;
    213 #elif LJ_TARGET_X86
    214   memcpy(pesect[PEOBJ_SECT_SXDATA].name, ".sxdata", sizeof(".sxdata")-1);
    215   pesect[PEOBJ_SECT_SXDATA].ofs = sofs;
    216   sofs += (pesect[PEOBJ_SECT_SXDATA].size = 4);
    217   pesect[PEOBJ_SECT_SXDATA].relocofs = sofs;
    218   /* Flags: 40 = read, 30 = align4, 02 = lnk_info, 40 = initialized data. */
    219   pesect[PEOBJ_SECT_SXDATA].flags = 0x40300240;
    220 #endif
    221 
    222   memcpy(pesect[PEOBJ_SECT_RDATA_Z].name, ".rdata$Z", sizeof(".rdata$Z")-1);
    223   pesect[PEOBJ_SECT_RDATA_Z].ofs = sofs;
    224   sofs += (pesect[PEOBJ_SECT_RDATA_Z].size = (uint32_t)strlen(ctx->dasm_ident)+1);
    225   /* Flags: 40 = read, 30 = align4, 40 = initialized data. */
    226   pesect[PEOBJ_SECT_RDATA_Z].flags = 0x40300040;
    227 
    228   /* Fill in PE header. */
    229   pehdr.arch = PEOBJ_ARCH_TARGET;
    230   pehdr.nsects = PEOBJ_NSECTIONS;
    231   pehdr.time = 0;  /* Timestamp is optional. */
    232   pehdr.symtabofs = sofs;
    233   pehdr.opthdrsz = 0;
    234   pehdr.flags = 0;
    235 
    236   /* Compute the size of the symbol table:
    237   ** @feat.00 + nsections*2
    238   ** + asm_start + nsym
    239   ** + nrsym
    240   */
    241   nrsym = ctx->nrelocsym;
    242   pehdr.nsyms = 1+PEOBJ_NSECTIONS*2 + 1+ctx->nsym + nrsym;
    243 #if LJ_TARGET_X64
    244   pehdr.nsyms += 1;  /* Symbol for lj_err_unwind_win. */
    245 #endif
    246 
    247   /* Write PE object header and all sections. */
    248   owrite(ctx, &pehdr, sizeof(PEheader));
    249   owrite(ctx, &pesect, sizeof(PEsection)*PEOBJ_NSECTIONS);
    250 
    251   /* Write .text section. */
    252   host_endian.u = 1;
    253   if (host_endian.b != LJ_ENDIAN_SELECT(1, 0)) {
    254 #if LJ_TARGET_PPC
    255     uint32_t *p = (uint32_t *)ctx->code;
    256     int n = (int)(ctx->codesz >> 2);
    257     for (i = 0; i < n; i++, p++)
    258       *p = lj_bswap(*p);  /* Byteswap .text section. */
    259 #else
    260     fprintf(stderr, "Error: different byte order for host and target\n");
    261     exit(1);
    262 #endif
    263   }
    264   owrite(ctx, ctx->code, ctx->codesz);
    265   for (i = 0; i < ctx->nreloc; i++) {
    266     PEreloc reloc;
    267     reloc.vaddr = (uint32_t)ctx->reloc[i].ofs + PEOBJ_RELOC_OFS;
    268     reloc.symidx = 1+2+ctx->reloc[i].sym;  /* Reloc syms are after .text sym. */
    269     reloc.type = ctx->reloc[i].type ? PEOBJ_RELOC_REL32 : PEOBJ_RELOC_DIR32;
    270     owrite(ctx, &reloc, PEOBJ_RELOC_SIZE);
    271   }
    272 
    273 #if LJ_TARGET_X64
    274   { /* Write .pdata section. */
    275     uint32_t fcofs = (uint32_t)ctx->sym[ctx->nsym-1].ofs;
    276     uint32_t pdata[3];  /* Start of .text, end of .text and .xdata. */
    277     PEreloc reloc;
    278     pdata[0] = 0; pdata[1] = fcofs; pdata[2] = 0;
    279     owrite(ctx, &pdata, sizeof(pdata));
    280     pdata[0] = fcofs; pdata[1] = (uint32_t)ctx->codesz; pdata[2] = 20;
    281     owrite(ctx, &pdata, sizeof(pdata));
    282     reloc.vaddr = 0; reloc.symidx = 1+2+nrsym+2+2+1;
    283     reloc.type = PEOBJ_RELOC_ADDR32NB;
    284     owrite(ctx, &reloc, PEOBJ_RELOC_SIZE);
    285     reloc.vaddr = 4; reloc.symidx = 1+2+nrsym+2+2+1;
    286     reloc.type = PEOBJ_RELOC_ADDR32NB;
    287     owrite(ctx, &reloc, PEOBJ_RELOC_SIZE);
    288     reloc.vaddr = 8; reloc.symidx = 1+2+nrsym+2;
    289     reloc.type = PEOBJ_RELOC_ADDR32NB;
    290     owrite(ctx, &reloc, PEOBJ_RELOC_SIZE);
    291     reloc.vaddr = 12; reloc.symidx = 1+2+nrsym+2+2+1;
    292     reloc.type = PEOBJ_RELOC_ADDR32NB;
    293     owrite(ctx, &reloc, PEOBJ_RELOC_SIZE);
    294     reloc.vaddr = 16; reloc.symidx = 1+2+nrsym+2+2+1;
    295     reloc.type = PEOBJ_RELOC_ADDR32NB;
    296     owrite(ctx, &reloc, PEOBJ_RELOC_SIZE);
    297     reloc.vaddr = 20; reloc.symidx = 1+2+nrsym+2;
    298     reloc.type = PEOBJ_RELOC_ADDR32NB;
    299     owrite(ctx, &reloc, PEOBJ_RELOC_SIZE);
    300   }
    301   { /* Write .xdata section. */
    302     uint16_t xdata[8+2+6];
    303     PEreloc reloc;
    304     xdata[0] = 0x01|0x08|0x10;  /* Ver. 1, uhandler/ehandler, prolog size 0. */
    305     xdata[1] = 0x0005;  /* Number of unwind codes, no frame pointer. */
    306     xdata[2] = 0x4200;  /* Stack offset 4*8+8 = aword*5. */
    307     xdata[3] = 0x3000;  /* Push rbx. */
    308     xdata[4] = 0x6000;  /* Push rsi. */
    309     xdata[5] = 0x7000;  /* Push rdi. */
    310     xdata[6] = 0x5000;  /* Push rbp. */
    311     xdata[7] = 0;  /* Alignment. */
    312     xdata[8] = xdata[9] = 0;  /* Relocated address of exception handler. */
    313     xdata[10] = 0x01;  /* Ver. 1, no handler, prolog size 0. */
    314     xdata[11] = 0x1504;  /* Number of unwind codes, fp = rbp, fpofs = 16. */
    315     xdata[12] = 0x0300;  /* set_fpreg. */
    316     xdata[13] = 0x0200;  /* stack offset 0*8+8 = aword*1. */
    317     xdata[14] = 0x3000;  /* Push rbx. */
    318     xdata[15] = 0x5000;  /* Push rbp. */
    319     owrite(ctx, &xdata, sizeof(xdata));
    320     reloc.vaddr = 2*8; reloc.symidx = 1+2+nrsym+2+2;
    321     reloc.type = PEOBJ_RELOC_ADDR32NB;
    322     owrite(ctx, &reloc, PEOBJ_RELOC_SIZE);
    323   }
    324 #elif LJ_TARGET_X86
    325   /* Write .sxdata section. */
    326   for (i = 0; i < nrsym; i++) {
    327     if (!strcmp(ctx->relocsym[i], "_lj_err_unwind_win")) {
    328       uint32_t symidx = 1+2+i;
    329       owrite(ctx, &symidx, 4);
    330       break;
    331     }
    332   }
    333   if (i == nrsym) {
    334     fprintf(stderr, "Error: extern lj_err_unwind_win not used\n");
    335     exit(1);
    336   }
    337 #endif
    338 
    339   /* Write .rdata$Z section. */
    340   owrite(ctx, ctx->dasm_ident, strlen(ctx->dasm_ident)+1);
    341 
    342   /* Write symbol table. */
    343   strtab = NULL;  /* 1st pass: collect string sizes. */
    344   for (;;) {
    345     strtabofs = 4;
    346     /* Mark as SafeSEH compliant. */
    347     emit_peobj_sym(ctx, "@feat.00", 1,
    348 		   PEOBJ_SECT_ABS, PEOBJ_TYPE_NULL, PEOBJ_SCL_STATIC);
    349 
    350     emit_peobj_sym_sect(ctx, pesect, PEOBJ_SECT_TEXT);
    351     for (i = 0; i < nrsym; i++)
    352       emit_peobj_sym(ctx, ctx->relocsym[i], 0,
    353 		     PEOBJ_SECT_UNDEF, PEOBJ_TYPE_FUNC, PEOBJ_SCL_EXTERN);
    354 
    355 #if LJ_TARGET_X64
    356     emit_peobj_sym_sect(ctx, pesect, PEOBJ_SECT_PDATA);
    357     emit_peobj_sym_sect(ctx, pesect, PEOBJ_SECT_XDATA);
    358     emit_peobj_sym(ctx, "lj_err_unwind_win", 0,
    359 		   PEOBJ_SECT_UNDEF, PEOBJ_TYPE_FUNC, PEOBJ_SCL_EXTERN);
    360 #elif LJ_TARGET_X86
    361     emit_peobj_sym_sect(ctx, pesect, PEOBJ_SECT_SXDATA);
    362 #endif
    363 
    364     emit_peobj_sym(ctx, ctx->beginsym, 0,
    365 		   PEOBJ_SECT_TEXT, PEOBJ_TYPE_NULL, PEOBJ_SCL_EXTERN);
    366     for (i = 0; i < ctx->nsym; i++)
    367       emit_peobj_sym(ctx, ctx->sym[i].name, (uint32_t)ctx->sym[i].ofs,
    368 		     PEOBJ_SECT_TEXT, PEOBJ_TYPE_FUNC, PEOBJ_SCL_EXTERN);
    369 
    370     emit_peobj_sym_sect(ctx, pesect, PEOBJ_SECT_RDATA_Z);
    371 
    372     if (strtab)
    373       break;
    374     /* 2nd pass: alloc strtab, write syms and copy strings. */
    375     strtab = (char *)malloc(strtabofs);
    376     *(uint32_t *)strtab = (uint32_t)strtabofs;
    377   }
    378 
    379   /* Write string table. */
    380   owrite(ctx, strtab, strtabofs);
    381 }
    382 
    383 #else
    384 
    385 void emit_peobj(BuildCtx *ctx)
    386 {
    387   UNUSED(ctx);
    388   fprintf(stderr, "Error: no PE object support for this target\n");
    389   exit(1);
    390 }
    391 
    392 #endif