2 ** LuaJIT VM builder: PE object emitter.
3 ** Copyright (C) 2005-2012 Mike Pall. See Copyright Notice in luajit.h
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.
12 #if LJ_TARGET_X86ORX64
14 /* Context for PE object emitter. */
16 static size_t strtabofs
;
18 /* -- PE object definitions ----------------------------------------------- */
21 typedef struct PEheader
{
32 typedef struct PEsection
{
46 typedef struct 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
{
68 /* PE symbol table auxiliary entry for a section. */
69 typedef struct 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. */
84 #define PEOBJ_ARCH_TARGET 0x014c
85 #define PEOBJ_RELOC_REL32 0x14 /* MS: REL32, GNU: DISP32. */
86 #define PEOBJ_RELOC_DIR32 0x06
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
94 /* Section numbers (0-based). */
97 PEOBJ_SECT_UNDEF
= -1,
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
)
122 size_t len
= strlen(name
);
123 if (!strtab
) { /* Pass 1: only calculate string table length. */
124 if (len
> 8) strtabofs
+= len
+1;
128 memcpy(sym
.n
.name
, name
, len
);
129 memset(sym
.n
.name
+len
, 0, 8-len
);
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;
138 sym
.sect
= (int16_t)(sect
+1); /* 1-based section number. */
139 sym
.type
= (uint16_t)type
;
140 sym
.scl
= (uint8_t)scl
;
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
)
150 if (!strtab
) return; /* Pass 1: no output. */
151 memcpy(sym
.n
.name
, pesect
[sect
].name
, 8);
153 sym
.sect
= (int16_t)(sect
+1); /* 1-based section number. */
154 sym
.type
= PEOBJ_TYPE_NULL
;
155 sym
.scl
= PEOBJ_SCL_STATIC
;
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
)
168 PEsection pesect
[PEOBJ_NSECTIONS
];
171 union { uint8_t b
; uint32_t u
; } host_endian
;
174 if (host_endian
.b
!= LJ_ENDIAN_SELECT(1, 0)) {
175 fprintf(stderr
, "Error: different byte order for host and target\n");
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;
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;
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
;
223 /* Compute the size of the symbol table:
224 ** @feat.00 + nsections*2
225 ** + asm_start + nsym
228 nrsym
= ctx
->nrelocsym
;
229 pehdr
.nsyms
= 1+PEOBJ_NSECTIONS
*2 + 1+ctx
->nsym
+ nrsym
;
231 pehdr
.nsyms
+= 1; /* Symbol for lj_err_unwind_win64. */
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
++) {
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
);
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. */
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];
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
);
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. */
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
);
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
);
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
);
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
);
345 void emit_peobj(BuildCtx
*ctx
)
348 fprintf(stderr
, "Error: no PE object support for this target\n");