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