Fix compilation on OpenBSD.
[luajit-2.0.git] / src / buildvm_peobj.c
blob17b3293a4ad807b6f57a0849e704fcd0309031aa
1 /*
2 ** LuaJIT VM builder: PE object emitter.
3 ** Copyright (C) 2005-2012 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 */
9 #include "buildvm.h"
10 #include "lj_bc.h"
12 #if LJ_TARGET_X86ORX64
14 /* Context for PE object emitter. */
15 static char *strtab;
16 static size_t strtabofs;
18 /* -- PE object definitions ----------------------------------------------- */
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;
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;
45 /* PE relocation. */
46 typedef struct PEreloc {
47 uint32_t vaddr;
48 uint32_t symidx;
49 uint16_t type;
50 } PEreloc;
52 /* Cannot use sizeof, because it pads up to the max. alignment. */
53 #define PEOBJ_RELOC_SIZE (4+4+2)
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;
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;
79 /* Cannot use sizeof, because it pads up to the max. alignment. */
80 #define PEOBJ_SYM_SIZE (8+4+2+2+1+1)
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 #elif LJ_TARGET_X64
88 #define PEOBJ_ARCH_TARGET 0x8664
89 #define PEOBJ_RELOC_REL32 0x04 /* MS: REL32, GNU: DISP32. */
90 #define PEOBJ_RELOC_DIR32 0x02
91 #define PEOBJ_RELOC_ADDR32NB 0x03
92 #endif
94 /* Section numbers (0-based). */
95 enum {
96 PEOBJ_SECT_ABS = -2,
97 PEOBJ_SECT_UNDEF = -1,
98 PEOBJ_SECT_TEXT,
99 #if LJ_TARGET_X64
100 PEOBJ_SECT_PDATA,
101 PEOBJ_SECT_XDATA,
102 #endif
103 PEOBJ_SECT_RDATA_Z,
104 PEOBJ_NSECTIONS
107 /* Symbol types. */
108 #define PEOBJ_TYPE_NULL 0
109 #define PEOBJ_TYPE_FUNC 0x20
111 /* Symbol storage class. */
112 #define PEOBJ_SCL_EXTERN 2
113 #define PEOBJ_SCL_STATIC 3
115 /* -- PE object emitter --------------------------------------------------- */
117 /* Emit PE object symbol. */
118 static void emit_peobj_sym(BuildCtx *ctx, const char *name, uint32_t value,
119 int sect, int type, int scl)
121 PEsym sym;
122 size_t len = strlen(name);
123 if (!strtab) { /* Pass 1: only calculate string table length. */
124 if (len > 8) strtabofs += len+1;
125 return;
127 if (len <= 8) {
128 memcpy(sym.n.name, name, len);
129 memset(sym.n.name+len, 0, 8-len);
130 } else {
131 sym.n.nameref[0] = 0;
132 sym.n.nameref[1] = (uint32_t)strtabofs;
133 memcpy(strtab + strtabofs, name, len);
134 strtab[strtabofs+len] = 0;
135 strtabofs += len+1;
137 sym.value = value;
138 sym.sect = (int16_t)(sect+1); /* 1-based section number. */
139 sym.type = (uint16_t)type;
140 sym.scl = (uint8_t)scl;
141 sym.naux = 0;
142 owrite(ctx, &sym, PEOBJ_SYM_SIZE);
145 /* Emit PE object section symbol. */
146 static void emit_peobj_sym_sect(BuildCtx *ctx, PEsection *pesect, int sect)
148 PEsym sym;
149 PEsymaux aux;
150 if (!strtab) return; /* Pass 1: no output. */
151 memcpy(sym.n.name, pesect[sect].name, 8);
152 sym.value = 0;
153 sym.sect = (int16_t)(sect+1); /* 1-based section number. */
154 sym.type = PEOBJ_TYPE_NULL;
155 sym.scl = PEOBJ_SCL_STATIC;
156 sym.naux = 1;
157 owrite(ctx, &sym, PEOBJ_SYM_SIZE);
158 memset(&aux, 0, sizeof(PEsymaux));
159 aux.size = pesect[sect].size;
160 aux.nreloc = pesect[sect].nreloc;
161 owrite(ctx, &aux, PEOBJ_SYM_SIZE);
164 /* Emit Windows PE object file. */
165 void emit_peobj(BuildCtx *ctx)
167 PEheader pehdr;
168 PEsection pesect[PEOBJ_NSECTIONS];
169 uint32_t sofs;
170 int i, nrsym;
171 union { uint8_t b; uint32_t u; } host_endian;
173 host_endian.u = 1;
174 if (host_endian.b != LJ_ENDIAN_SELECT(1, 0)) {
175 fprintf(stderr, "Error: different byte order for host and target\n");
176 exit(1);
179 sofs = sizeof(PEheader) + PEOBJ_NSECTIONS*sizeof(PEsection);
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 = 0x60500020;
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;
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 #endif
209 memcpy(pesect[PEOBJ_SECT_RDATA_Z].name, ".rdata$Z", sizeof(".rdata$Z")-1);
210 pesect[PEOBJ_SECT_RDATA_Z].ofs = sofs;
211 sofs += (pesect[PEOBJ_SECT_RDATA_Z].size = (uint32_t)strlen(ctx->dasm_ident)+1);
212 /* Flags: 40 = read, 30 = align4, 40 = initialized data. */
213 pesect[PEOBJ_SECT_RDATA_Z].flags = 0x40300040;
215 /* Fill in PE header. */
216 pehdr.arch = PEOBJ_ARCH_TARGET;
217 pehdr.nsects = PEOBJ_NSECTIONS;
218 pehdr.time = 0; /* Timestamp is optional. */
219 pehdr.symtabofs = sofs;
220 pehdr.opthdrsz = 0;
221 pehdr.flags = 0;
223 /* Compute the size of the symbol table:
224 ** @feat.00 + nsections*2
225 ** + asm_start + nsym
226 ** + nrsym
228 nrsym = ctx->nrelocsym;
229 pehdr.nsyms = 1+PEOBJ_NSECTIONS*2 + 1+ctx->nsym + nrsym;
230 #if LJ_TARGET_X64
231 pehdr.nsyms += 1; /* Symbol for lj_err_unwind_win64. */
232 #endif
234 /* Write PE object header and all sections. */
235 owrite(ctx, &pehdr, sizeof(PEheader));
236 owrite(ctx, &pesect, sizeof(PEsection)*PEOBJ_NSECTIONS);
238 /* Write .text section. */
239 owrite(ctx, ctx->code, ctx->codesz);
240 for (i = 0; i < ctx->nreloc; i++) {
241 PEreloc reloc;
242 reloc.vaddr = (uint32_t)ctx->reloc[i].ofs;
243 reloc.symidx = 1+2+ctx->reloc[i].sym; /* Reloc syms are after .text sym. */
244 reloc.type = ctx->reloc[i].type ? PEOBJ_RELOC_REL32 : PEOBJ_RELOC_DIR32;
245 owrite(ctx, &reloc, PEOBJ_RELOC_SIZE);
248 #if LJ_TARGET_X64
249 { /* Write .pdata section. */
250 uint32_t fcofs = (uint32_t)ctx->sym[ctx->nsym-1].ofs;
251 uint32_t pdata[3]; /* Start of .text, end of .text and .xdata. */
252 PEreloc reloc;
253 pdata[0] = 0; pdata[1] = fcofs; pdata[2] = 0;
254 owrite(ctx, &pdata, sizeof(pdata));
255 pdata[0] = fcofs; pdata[1] = (uint32_t)ctx->codesz; pdata[2] = 20;
256 owrite(ctx, &pdata, sizeof(pdata));
257 reloc.vaddr = 0; reloc.symidx = 1+2+nrsym+2+2+1;
258 reloc.type = PEOBJ_RELOC_ADDR32NB;
259 owrite(ctx, &reloc, PEOBJ_RELOC_SIZE);
260 reloc.vaddr = 4; reloc.symidx = 1+2+nrsym+2+2+1;
261 reloc.type = PEOBJ_RELOC_ADDR32NB;
262 owrite(ctx, &reloc, PEOBJ_RELOC_SIZE);
263 reloc.vaddr = 8; reloc.symidx = 1+2+nrsym+2;
264 reloc.type = PEOBJ_RELOC_ADDR32NB;
265 owrite(ctx, &reloc, PEOBJ_RELOC_SIZE);
266 reloc.vaddr = 12; reloc.symidx = 1+2+nrsym+2+2+1;
267 reloc.type = PEOBJ_RELOC_ADDR32NB;
268 owrite(ctx, &reloc, PEOBJ_RELOC_SIZE);
269 reloc.vaddr = 16; reloc.symidx = 1+2+nrsym+2+2+1;
270 reloc.type = PEOBJ_RELOC_ADDR32NB;
271 owrite(ctx, &reloc, PEOBJ_RELOC_SIZE);
272 reloc.vaddr = 20; reloc.symidx = 1+2+nrsym+2;
273 reloc.type = PEOBJ_RELOC_ADDR32NB;
274 owrite(ctx, &reloc, PEOBJ_RELOC_SIZE);
276 { /* Write .xdata section. */
277 uint16_t xdata[8+2+6];
278 PEreloc reloc;
279 xdata[0] = 0x01|0x08|0x10; /* Ver. 1, uhandler/ehandler, prolog size 0. */
280 xdata[1] = 0x0005; /* Number of unwind codes, no frame pointer. */
281 xdata[2] = 0x4200; /* Stack offset 4*8+8 = aword*5. */
282 xdata[3] = 0x3000; /* Push rbx. */
283 xdata[4] = 0x6000; /* Push rsi. */
284 xdata[5] = 0x7000; /* Push rdi. */
285 xdata[6] = 0x5000; /* Push rbp. */
286 xdata[7] = 0; /* Alignment. */
287 xdata[8] = xdata[9] = 0; /* Relocated address of exception handler. */
288 xdata[10] = 0x01; /* Ver. 1, no handler, prolog size 0. */
289 xdata[11] = 0x1504; /* Number of unwind codes, fp = rbp, fpofs = 16. */
290 xdata[12] = 0x0300; /* set_fpreg. */
291 xdata[13] = 0x0200; /* stack offset 0*8+8 = aword*1. */
292 xdata[14] = 0x3000; /* Push rbx. */
293 xdata[15] = 0x5000; /* Push rbp. */
294 owrite(ctx, &xdata, sizeof(xdata));
295 reloc.vaddr = 2*8; reloc.symidx = 1+2+nrsym+2+2;
296 reloc.type = PEOBJ_RELOC_ADDR32NB;
297 owrite(ctx, &reloc, PEOBJ_RELOC_SIZE);
299 #endif
301 /* Write .rdata$Z section. */
302 owrite(ctx, ctx->dasm_ident, strlen(ctx->dasm_ident)+1);
304 /* Write symbol table. */
305 strtab = NULL; /* 1st pass: collect string sizes. */
306 for (;;) {
307 strtabofs = 4;
308 /* Mark as SafeSEH compliant. */
309 emit_peobj_sym(ctx, "@feat.00", 1,
310 PEOBJ_SECT_ABS, PEOBJ_TYPE_NULL, PEOBJ_SCL_STATIC);
312 emit_peobj_sym_sect(ctx, pesect, PEOBJ_SECT_TEXT);
313 for (i = 0; i < nrsym; i++)
314 emit_peobj_sym(ctx, ctx->relocsym[i], 0,
315 PEOBJ_SECT_UNDEF, PEOBJ_TYPE_FUNC, PEOBJ_SCL_EXTERN);
317 #if LJ_TARGET_X64
318 emit_peobj_sym_sect(ctx, pesect, PEOBJ_SECT_PDATA);
319 emit_peobj_sym_sect(ctx, pesect, PEOBJ_SECT_XDATA);
320 emit_peobj_sym(ctx, "lj_err_unwind_win64", 0,
321 PEOBJ_SECT_UNDEF, PEOBJ_TYPE_FUNC, PEOBJ_SCL_EXTERN);
322 #endif
324 emit_peobj_sym(ctx, ctx->beginsym, 0,
325 PEOBJ_SECT_TEXT, PEOBJ_TYPE_NULL, PEOBJ_SCL_EXTERN);
326 for (i = 0; i < ctx->nsym; i++)
327 emit_peobj_sym(ctx, ctx->sym[i].name, (uint32_t)ctx->sym[i].ofs,
328 PEOBJ_SECT_TEXT, PEOBJ_TYPE_FUNC, PEOBJ_SCL_EXTERN);
330 emit_peobj_sym_sect(ctx, pesect, PEOBJ_SECT_RDATA_Z);
332 if (strtab)
333 break;
334 /* 2nd pass: alloc strtab, write syms and copy strings. */
335 strtab = (char *)malloc(strtabofs);
336 *(uint32_t *)strtab = (uint32_t)strtabofs;
339 /* Write string table. */
340 owrite(ctx, strtab, strtabofs);
343 #else
345 void emit_peobj(BuildCtx *ctx)
347 UNUSED(ctx);
348 fprintf(stderr, "Error: no PE object support for this target\n");
349 exit(1);
352 #endif