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 | |