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. */ |
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 | #endif |
97 | |
98 | /* Section numbers (0-based). */ |
99 | enum { |
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. */ |
124 | static 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. */ |
152 | static 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. */ |
171 | void 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 | |
372 | void 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 | |