| 1 | /* |
| 2 | ** LuaJIT VM builder: PE object emitter. |
| 3 | ** Copyright (C) 2005-2014 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 { |
| 22 | uint16_t ; |
| 23 | uint16_t ; |
| 24 | uint32_t ; |
| 25 | uint32_t ; |
| 26 | uint32_t ; |
| 27 | uint16_t ; |
| 28 | uint16_t ; |
| 29 | } ; |
| 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 | #endif |
| 113 | PEOBJ_SECT_RDATA_Z, |
| 114 | PEOBJ_NSECTIONS |
| 115 | }; |
| 116 | |
| 117 | /* Symbol types. */ |
| 118 | #define PEOBJ_TYPE_NULL 0 |
| 119 | #define PEOBJ_TYPE_FUNC 0x20 |
| 120 | |
| 121 | /* Symbol storage class. */ |
| 122 | #define PEOBJ_SCL_EXTERN 2 |
| 123 | #define PEOBJ_SCL_STATIC 3 |
| 124 | |
| 125 | /* -- PE object emitter --------------------------------------------------- */ |
| 126 | |
| 127 | /* Emit PE object symbol. */ |
| 128 | static void emit_peobj_sym(BuildCtx *ctx, const char *name, uint32_t value, |
| 129 | int sect, int type, int scl) |
| 130 | { |
| 131 | PEsym sym; |
| 132 | size_t len = strlen(name); |
| 133 | if (!strtab) { /* Pass 1: only calculate string table length. */ |
| 134 | if (len > 8) strtabofs += len+1; |
| 135 | return; |
| 136 | } |
| 137 | if (len <= 8) { |
| 138 | memcpy(sym.n.name, name, len); |
| 139 | memset(sym.n.name+len, 0, 8-len); |
| 140 | } else { |
| 141 | sym.n.nameref[0] = 0; |
| 142 | sym.n.nameref[1] = (uint32_t)strtabofs; |
| 143 | memcpy(strtab + strtabofs, name, len); |
| 144 | strtab[strtabofs+len] = 0; |
| 145 | strtabofs += len+1; |
| 146 | } |
| 147 | sym.value = value; |
| 148 | sym.sect = (int16_t)(sect+1); /* 1-based section number. */ |
| 149 | sym.type = (uint16_t)type; |
| 150 | sym.scl = (uint8_t)scl; |
| 151 | sym.naux = 0; |
| 152 | owrite(ctx, &sym, PEOBJ_SYM_SIZE); |
| 153 | } |
| 154 | |
| 155 | /* Emit PE object section symbol. */ |
| 156 | static void emit_peobj_sym_sect(BuildCtx *ctx, PEsection *pesect, int sect) |
| 157 | { |
| 158 | PEsym sym; |
| 159 | PEsymaux aux; |
| 160 | if (!strtab) return; /* Pass 1: no output. */ |
| 161 | memcpy(sym.n.name, pesect[sect].name, 8); |
| 162 | sym.value = 0; |
| 163 | sym.sect = (int16_t)(sect+1); /* 1-based section number. */ |
| 164 | sym.type = PEOBJ_TYPE_NULL; |
| 165 | sym.scl = PEOBJ_SCL_STATIC; |
| 166 | sym.naux = 1; |
| 167 | owrite(ctx, &sym, PEOBJ_SYM_SIZE); |
| 168 | memset(&aux, 0, sizeof(PEsymaux)); |
| 169 | aux.size = pesect[sect].size; |
| 170 | aux.nreloc = pesect[sect].nreloc; |
| 171 | owrite(ctx, &aux, PEOBJ_SYM_SIZE); |
| 172 | } |
| 173 | |
| 174 | /* Emit Windows PE object file. */ |
| 175 | void emit_peobj(BuildCtx *ctx) |
| 176 | { |
| 177 | PEheader pehdr; |
| 178 | PEsection pesect[PEOBJ_NSECTIONS]; |
| 179 | uint32_t sofs; |
| 180 | int i, nrsym; |
| 181 | union { uint8_t b; uint32_t u; } host_endian; |
| 182 | |
| 183 | sofs = sizeof(PEheader) + PEOBJ_NSECTIONS*sizeof(PEsection); |
| 184 | |
| 185 | /* Fill in PE sections. */ |
| 186 | memset(&pesect, 0, PEOBJ_NSECTIONS*sizeof(PEsection)); |
| 187 | memcpy(pesect[PEOBJ_SECT_TEXT].name, ".text" , sizeof(".text" )-1); |
| 188 | pesect[PEOBJ_SECT_TEXT].ofs = sofs; |
| 189 | sofs += (pesect[PEOBJ_SECT_TEXT].size = (uint32_t)ctx->codesz); |
| 190 | pesect[PEOBJ_SECT_TEXT].relocofs = sofs; |
| 191 | sofs += (pesect[PEOBJ_SECT_TEXT].nreloc = (uint16_t)ctx->nreloc) * PEOBJ_RELOC_SIZE; |
| 192 | /* Flags: 60 = read+execute, 50 = align16, 20 = code. */ |
| 193 | pesect[PEOBJ_SECT_TEXT].flags = PEOBJ_TEXT_FLAGS; |
| 194 | |
| 195 | #if LJ_TARGET_X64 |
| 196 | memcpy(pesect[PEOBJ_SECT_PDATA].name, ".pdata" , sizeof(".pdata" )-1); |
| 197 | pesect[PEOBJ_SECT_PDATA].ofs = sofs; |
| 198 | sofs += (pesect[PEOBJ_SECT_PDATA].size = 6*4); |
| 199 | pesect[PEOBJ_SECT_PDATA].relocofs = sofs; |
| 200 | sofs += (pesect[PEOBJ_SECT_PDATA].nreloc = 6) * PEOBJ_RELOC_SIZE; |
| 201 | /* Flags: 40 = read, 30 = align4, 40 = initialized data. */ |
| 202 | pesect[PEOBJ_SECT_PDATA].flags = 0x40300040; |
| 203 | |
| 204 | memcpy(pesect[PEOBJ_SECT_XDATA].name, ".xdata" , sizeof(".xdata" )-1); |
| 205 | pesect[PEOBJ_SECT_XDATA].ofs = sofs; |
| 206 | sofs += (pesect[PEOBJ_SECT_XDATA].size = 8*2+4+6*2); /* See below. */ |
| 207 | pesect[PEOBJ_SECT_XDATA].relocofs = sofs; |
| 208 | sofs += (pesect[PEOBJ_SECT_XDATA].nreloc = 1) * PEOBJ_RELOC_SIZE; |
| 209 | /* Flags: 40 = read, 30 = align4, 40 = initialized data. */ |
| 210 | pesect[PEOBJ_SECT_XDATA].flags = 0x40300040; |
| 211 | #endif |
| 212 | |
| 213 | memcpy(pesect[PEOBJ_SECT_RDATA_Z].name, ".rdata$Z" , sizeof(".rdata$Z" )-1); |
| 214 | pesect[PEOBJ_SECT_RDATA_Z].ofs = sofs; |
| 215 | sofs += (pesect[PEOBJ_SECT_RDATA_Z].size = (uint32_t)strlen(ctx->dasm_ident)+1); |
| 216 | /* Flags: 40 = read, 30 = align4, 40 = initialized data. */ |
| 217 | pesect[PEOBJ_SECT_RDATA_Z].flags = 0x40300040; |
| 218 | |
| 219 | /* Fill in PE header. */ |
| 220 | pehdr.arch = PEOBJ_ARCH_TARGET; |
| 221 | pehdr.nsects = PEOBJ_NSECTIONS; |
| 222 | pehdr.time = 0; /* Timestamp is optional. */ |
| 223 | pehdr.symtabofs = sofs; |
| 224 | pehdr.opthdrsz = 0; |
| 225 | pehdr.flags = 0; |
| 226 | |
| 227 | /* Compute the size of the symbol table: |
| 228 | ** @feat.00 + nsections*2 |
| 229 | ** + asm_start + nsym |
| 230 | ** + nrsym |
| 231 | */ |
| 232 | nrsym = ctx->nrelocsym; |
| 233 | pehdr.nsyms = 1+PEOBJ_NSECTIONS*2 + 1+ctx->nsym + nrsym; |
| 234 | #if LJ_TARGET_X64 |
| 235 | pehdr.nsyms += 1; /* Symbol for lj_err_unwind_win64. */ |
| 236 | #endif |
| 237 | |
| 238 | /* Write PE object header and all sections. */ |
| 239 | owrite(ctx, &pehdr, sizeof(PEheader)); |
| 240 | owrite(ctx, &pesect, sizeof(PEsection)*PEOBJ_NSECTIONS); |
| 241 | |
| 242 | /* Write .text section. */ |
| 243 | host_endian.u = 1; |
| 244 | if (host_endian.b != LJ_ENDIAN_SELECT(1, 0)) { |
| 245 | #if LJ_TARGET_PPC |
| 246 | uint32_t *p = (uint32_t *)ctx->code; |
| 247 | int n = (int)(ctx->codesz >> 2); |
| 248 | for (i = 0; i < n; i++, p++) |
| 249 | *p = lj_bswap(*p); /* Byteswap .text section. */ |
| 250 | #else |
| 251 | fprintf(stderr, "Error: different byte order for host and target\n" ); |
| 252 | exit(1); |
| 253 | #endif |
| 254 | } |
| 255 | owrite(ctx, ctx->code, ctx->codesz); |
| 256 | for (i = 0; i < ctx->nreloc; i++) { |
| 257 | PEreloc reloc; |
| 258 | reloc.vaddr = (uint32_t)ctx->reloc[i].ofs + PEOBJ_RELOC_OFS; |
| 259 | reloc.symidx = 1+2+ctx->reloc[i].sym; /* Reloc syms are after .text sym. */ |
| 260 | reloc.type = ctx->reloc[i].type ? PEOBJ_RELOC_REL32 : PEOBJ_RELOC_DIR32; |
| 261 | owrite(ctx, &reloc, PEOBJ_RELOC_SIZE); |
| 262 | } |
| 263 | |
| 264 | #if LJ_TARGET_X64 |
| 265 | { /* Write .pdata section. */ |
| 266 | uint32_t fcofs = (uint32_t)ctx->sym[ctx->nsym-1].ofs; |
| 267 | uint32_t pdata[3]; /* Start of .text, end of .text and .xdata. */ |
| 268 | PEreloc reloc; |
| 269 | pdata[0] = 0; pdata[1] = fcofs; pdata[2] = 0; |
| 270 | owrite(ctx, &pdata, sizeof(pdata)); |
| 271 | pdata[0] = fcofs; pdata[1] = (uint32_t)ctx->codesz; pdata[2] = 20; |
| 272 | owrite(ctx, &pdata, sizeof(pdata)); |
| 273 | reloc.vaddr = 0; reloc.symidx = 1+2+nrsym+2+2+1; |
| 274 | reloc.type = PEOBJ_RELOC_ADDR32NB; |
| 275 | owrite(ctx, &reloc, PEOBJ_RELOC_SIZE); |
| 276 | reloc.vaddr = 4; reloc.symidx = 1+2+nrsym+2+2+1; |
| 277 | reloc.type = PEOBJ_RELOC_ADDR32NB; |
| 278 | owrite(ctx, &reloc, PEOBJ_RELOC_SIZE); |
| 279 | reloc.vaddr = 8; reloc.symidx = 1+2+nrsym+2; |
| 280 | reloc.type = PEOBJ_RELOC_ADDR32NB; |
| 281 | owrite(ctx, &reloc, PEOBJ_RELOC_SIZE); |
| 282 | reloc.vaddr = 12; reloc.symidx = 1+2+nrsym+2+2+1; |
| 283 | reloc.type = PEOBJ_RELOC_ADDR32NB; |
| 284 | owrite(ctx, &reloc, PEOBJ_RELOC_SIZE); |
| 285 | reloc.vaddr = 16; reloc.symidx = 1+2+nrsym+2+2+1; |
| 286 | reloc.type = PEOBJ_RELOC_ADDR32NB; |
| 287 | owrite(ctx, &reloc, PEOBJ_RELOC_SIZE); |
| 288 | reloc.vaddr = 20; reloc.symidx = 1+2+nrsym+2; |
| 289 | reloc.type = PEOBJ_RELOC_ADDR32NB; |
| 290 | owrite(ctx, &reloc, PEOBJ_RELOC_SIZE); |
| 291 | } |
| 292 | { /* Write .xdata section. */ |
| 293 | uint16_t xdata[8+2+6]; |
| 294 | PEreloc reloc; |
| 295 | xdata[0] = 0x01|0x08|0x10; /* Ver. 1, uhandler/ehandler, prolog size 0. */ |
| 296 | xdata[1] = 0x0005; /* Number of unwind codes, no frame pointer. */ |
| 297 | xdata[2] = 0x4200; /* Stack offset 4*8+8 = aword*5. */ |
| 298 | xdata[3] = 0x3000; /* Push rbx. */ |
| 299 | xdata[4] = 0x6000; /* Push rsi. */ |
| 300 | xdata[5] = 0x7000; /* Push rdi. */ |
| 301 | xdata[6] = 0x5000; /* Push rbp. */ |
| 302 | xdata[7] = 0; /* Alignment. */ |
| 303 | xdata[8] = xdata[9] = 0; /* Relocated address of exception handler. */ |
| 304 | xdata[10] = 0x01; /* Ver. 1, no handler, prolog size 0. */ |
| 305 | xdata[11] = 0x1504; /* Number of unwind codes, fp = rbp, fpofs = 16. */ |
| 306 | xdata[12] = 0x0300; /* set_fpreg. */ |
| 307 | xdata[13] = 0x0200; /* stack offset 0*8+8 = aword*1. */ |
| 308 | xdata[14] = 0x3000; /* Push rbx. */ |
| 309 | xdata[15] = 0x5000; /* Push rbp. */ |
| 310 | owrite(ctx, &xdata, sizeof(xdata)); |
| 311 | reloc.vaddr = 2*8; reloc.symidx = 1+2+nrsym+2+2; |
| 312 | reloc.type = PEOBJ_RELOC_ADDR32NB; |
| 313 | owrite(ctx, &reloc, PEOBJ_RELOC_SIZE); |
| 314 | } |
| 315 | #endif |
| 316 | |
| 317 | /* Write .rdata$Z section. */ |
| 318 | owrite(ctx, ctx->dasm_ident, strlen(ctx->dasm_ident)+1); |
| 319 | |
| 320 | /* Write symbol table. */ |
| 321 | strtab = NULL; /* 1st pass: collect string sizes. */ |
| 322 | for (;;) { |
| 323 | strtabofs = 4; |
| 324 | /* Mark as SafeSEH compliant. */ |
| 325 | emit_peobj_sym(ctx, "@feat.00" , 1, |
| 326 | PEOBJ_SECT_ABS, PEOBJ_TYPE_NULL, PEOBJ_SCL_STATIC); |
| 327 | |
| 328 | emit_peobj_sym_sect(ctx, pesect, PEOBJ_SECT_TEXT); |
| 329 | for (i = 0; i < nrsym; i++) |
| 330 | emit_peobj_sym(ctx, ctx->relocsym[i], 0, |
| 331 | PEOBJ_SECT_UNDEF, PEOBJ_TYPE_FUNC, PEOBJ_SCL_EXTERN); |
| 332 | |
| 333 | #if LJ_TARGET_X64 |
| 334 | emit_peobj_sym_sect(ctx, pesect, PEOBJ_SECT_PDATA); |
| 335 | emit_peobj_sym_sect(ctx, pesect, PEOBJ_SECT_XDATA); |
| 336 | emit_peobj_sym(ctx, "lj_err_unwind_win64" , 0, |
| 337 | PEOBJ_SECT_UNDEF, PEOBJ_TYPE_FUNC, PEOBJ_SCL_EXTERN); |
| 338 | #endif |
| 339 | |
| 340 | emit_peobj_sym(ctx, ctx->beginsym, 0, |
| 341 | PEOBJ_SECT_TEXT, PEOBJ_TYPE_NULL, PEOBJ_SCL_EXTERN); |
| 342 | for (i = 0; i < ctx->nsym; i++) |
| 343 | emit_peobj_sym(ctx, ctx->sym[i].name, (uint32_t)ctx->sym[i].ofs, |
| 344 | PEOBJ_SECT_TEXT, PEOBJ_TYPE_FUNC, PEOBJ_SCL_EXTERN); |
| 345 | |
| 346 | emit_peobj_sym_sect(ctx, pesect, PEOBJ_SECT_RDATA_Z); |
| 347 | |
| 348 | if (strtab) |
| 349 | break; |
| 350 | /* 2nd pass: alloc strtab, write syms and copy strings. */ |
| 351 | strtab = (char *)malloc(strtabofs); |
| 352 | *(uint32_t *)strtab = (uint32_t)strtabofs; |
| 353 | } |
| 354 | |
| 355 | /* Write string table. */ |
| 356 | owrite(ctx, strtab, strtabofs); |
| 357 | } |
| 358 | |
| 359 | #else |
| 360 | |
| 361 | void emit_peobj(BuildCtx *ctx) |
| 362 | { |
| 363 | UNUSED(ctx); |
| 364 | fprintf(stderr, "Error: no PE object support for this target\n" ); |
| 365 | exit(1); |
| 366 | } |
| 367 | |
| 368 | #endif |
| 369 | |