summaryrefslogtreecommitdiff
path: root/src/buildvm_peobj.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/buildvm_peobj.c')
-rw-r--r--src/buildvm_peobj.c303
1 files changed, 303 insertions, 0 deletions
diff --git a/src/buildvm_peobj.c b/src/buildvm_peobj.c
new file mode 100644
index 00000000..9acf6b76
--- /dev/null
+++ b/src/buildvm_peobj.c
@@ -0,0 +1,303 @@
1/*
2** LuaJIT VM builder: PE object emitter.
3** Copyright (C) 2005-2009 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_SYM_PREFIX "_"
88#elif LJ_TARGET_X64
89#define PEOBJ_ARCH_TARGET 0x8664
90#define PEOBJ_RELOC_REL32 0x04 /* MS: REL32, GNU: DISP32. */
91#define PEOBJ_RELOC_DIR32 0x02
92#define PEOBJ_SYM_PREFIX ""
93#endif
94
95/* Section numbers (0-based). */
96enum {
97 PEOBJ_SECT_ABS = -2,
98 PEOBJ_SECT_UNDEF = -1,
99 PEOBJ_SECT_TEXT,
100 /* TODO: add .pdata/.xdata for x64. */
101 PEOBJ_SECT_RDATA,
102 PEOBJ_SECT_RDATA_Z,
103 PEOBJ_NSECTIONS
104};
105
106/* Symbol types. */
107#define PEOBJ_TYPE_NULL 0
108#define PEOBJ_TYPE_FUNC 0x20
109
110/* Symbol storage class. */
111#define PEOBJ_SCL_EXTERN 2
112#define PEOBJ_SCL_STATIC 3
113
114/* -- PE object emitter --------------------------------------------------- */
115
116/* Emit PE object symbol. */
117static void emit_peobj_sym(BuildCtx *ctx, const char *name, uint32_t value,
118 int sect, int type, int scl)
119{
120 PEsym sym;
121 size_t len = strlen(name);
122 if (!strtab) { /* Pass 1: only calculate string table length. */
123 if (len > 8) strtabofs += len+1;
124 return;
125 }
126 if (len <= 8) {
127 memcpy(sym.n.name, name, len);
128 memset(sym.n.name+len, 0, 8-len);
129 } else {
130 sym.n.nameref[0] = 0;
131 sym.n.nameref[1] = strtabofs;
132 memcpy(strtab + strtabofs, name, len);
133 strtab[strtabofs+len] = 0;
134 strtabofs += len+1;
135 }
136 sym.value = value;
137 sym.sect = (int16_t)(sect+1); /* 1-based section number. */
138 sym.type = (uint16_t)type;
139 sym.scl = (uint8_t)scl;
140 sym.naux = 0;
141 owrite(ctx, &sym, PEOBJ_SYM_SIZE);
142}
143
144/* Emit PE object section symbol. */
145static void emit_peobj_sym_sect(BuildCtx *ctx, PEsection *pesect, int sect)
146{
147 PEsym sym;
148 PEsymaux aux;
149 if (!strtab) return; /* Pass 1: no output. */
150 memcpy(sym.n.name, pesect[sect].name, 8);
151 sym.value = 0;
152 sym.sect = (int16_t)(sect+1); /* 1-based section number. */
153 sym.type = PEOBJ_TYPE_NULL;
154 sym.scl = PEOBJ_SCL_STATIC;
155 sym.naux = 1;
156 owrite(ctx, &sym, PEOBJ_SYM_SIZE);
157 memset(&aux, 0, sizeof(PEsymaux));
158 aux.size = pesect[sect].size;
159 aux.nreloc = pesect[sect].nreloc;
160 owrite(ctx, &aux, PEOBJ_SYM_SIZE);
161}
162
163#define emit_peobj_sym_func(ctx, name, ofs) \
164 emit_peobj_sym(ctx, name, (uint32_t)(ofs), \
165 PEOBJ_SECT_TEXT, PEOBJ_TYPE_FUNC, PEOBJ_SCL_EXTERN)
166#define emit_peobj_sym_rdata(ctx, name, ofs) \
167 emit_peobj_sym(ctx, name, (uint32_t)(ofs), \
168 PEOBJ_SECT_RDATA, PEOBJ_TYPE_NULL, PEOBJ_SCL_EXTERN)
169
170/* Emit Windows PE object file. */
171void emit_peobj(BuildCtx *ctx)
172{
173 PEheader pehdr;
174 PEsection pesect[PEOBJ_NSECTIONS];
175 int nzsym, relocsyms;
176 uint32_t sofs;
177 int i;
178 union { uint8_t b; uint32_t u; } host_endian;
179
180 host_endian.u = 1;
181 if (host_endian.b != LJ_ENDIAN_SELECT(1, 0)) {
182 fprintf(stderr, "Error: different byte order for host and target\n");
183 exit(1);
184 }
185
186 sofs = sizeof(PEheader) + PEOBJ_NSECTIONS*sizeof(PEsection);
187
188 /* Fill in PE sections. */
189 memset(&pesect, 0, PEOBJ_NSECTIONS*sizeof(PEsection));
190 memcpy(pesect[PEOBJ_SECT_TEXT].name, ".text", sizeof(".text")-1);
191 pesect[PEOBJ_SECT_TEXT].ofs = sofs;
192 sofs += (pesect[PEOBJ_SECT_TEXT].size = (uint32_t)ctx->codesz);
193 pesect[PEOBJ_SECT_TEXT].relocofs = sofs;
194 sofs += (pesect[PEOBJ_SECT_TEXT].nreloc = (uint16_t)ctx->nreloc) * PEOBJ_RELOC_SIZE;
195 /* Flags: 60 = read+execute, 50 = align16, 20 = code. */
196 pesect[PEOBJ_SECT_TEXT].flags = 0x60500020;
197
198 memcpy(pesect[PEOBJ_SECT_RDATA].name, ".rdata", sizeof(".rdata")-1);
199 pesect[PEOBJ_SECT_RDATA].ofs = sofs;
200 sofs += (pesect[PEOBJ_SECT_RDATA].size = ctx->npc*sizeof(uint16_t));
201 /* Flags: 40 = read, 30 = align4, 40 = initialized data. */
202 pesect[PEOBJ_SECT_RDATA].flags = 0x40300040;
203
204 memcpy(pesect[PEOBJ_SECT_RDATA_Z].name, ".rdata$Z", sizeof(".rdata$Z")-1);
205 pesect[PEOBJ_SECT_RDATA_Z].ofs = sofs;
206 sofs += (pesect[PEOBJ_SECT_RDATA_Z].size = (uint32_t)strlen(ctx->dasm_ident)+1);
207 /* Flags: 40 = read, 30 = align4, 40 = initialized data. */
208 pesect[PEOBJ_SECT_RDATA_Z].flags = 0x40300040;
209
210 /* Fill in PE header. */
211 pehdr.arch = PEOBJ_ARCH_TARGET;
212 pehdr.nsects = PEOBJ_NSECTIONS;
213 pehdr.time = 0; /* Timestamp is optional. */
214 pehdr.symtabofs = sofs;
215 pehdr.opthdrsz = 0;
216 pehdr.flags = 0;
217
218 /* Compute the size of the symbol table:
219 ** @feat.00 + nsections*2
220 ** + asm_start + (nsyms-nzsym) + op_ofs
221 ** + relocsyms
222 */
223 /* Skip _Z syms. */
224 for (nzsym = 0; ctx->sym_ofs[ctx->perm[nzsym]] < 0; nzsym++) ;
225 for (relocsyms = 0; ctx->extnames[relocsyms]; relocsyms++) ;
226 pehdr.nsyms = 1+PEOBJ_NSECTIONS*2 + 1+(ctx->nsym-nzsym)+1 + relocsyms;
227
228 /* Write PE object header and all sections. */
229 owrite(ctx, &pehdr, sizeof(PEheader));
230 owrite(ctx, &pesect, sizeof(PEsection)*PEOBJ_NSECTIONS);
231
232 /* Write .text section. */
233 owrite(ctx, ctx->code, ctx->codesz);
234 for (i = 0; i < ctx->nreloc; i++) {
235 PEreloc reloc;
236 reloc.vaddr = (uint32_t)ctx->reloc[i].ofs;
237 reloc.symidx = 1+2+ctx->reloc[i].sym; /* Reloc syms are after .text sym. */
238 reloc.type = ctx->reloc[i].type ? PEOBJ_RELOC_REL32 : PEOBJ_RELOC_DIR32;
239 owrite(ctx, &reloc, PEOBJ_RELOC_SIZE);
240 }
241
242 /* Write .rdata section. */
243 for (i = 0; i < ctx->npc; i++) {
244 uint16_t pcofs = (uint16_t)ctx->sym_ofs[i];
245 owrite(ctx, &pcofs, 2);
246 }
247
248 /* Write .rdata$Z section. */
249 owrite(ctx, ctx->dasm_ident, strlen(ctx->dasm_ident)+1);
250
251 /* Write symbol table. */
252 strtab = NULL; /* 1st pass: collect string sizes. */
253 for (;;) {
254 char name[80];
255
256 strtabofs = 4;
257 /* Mark as SafeSEH compliant. */
258 emit_peobj_sym(ctx, "@feat.00", 1,
259 PEOBJ_SECT_ABS, PEOBJ_TYPE_NULL, PEOBJ_SCL_STATIC);
260
261 emit_peobj_sym_sect(ctx, pesect, PEOBJ_SECT_TEXT);
262 for (i = 0; ctx->extnames[i]; i++) {
263 sprintf(name, PEOBJ_SYM_PREFIX "%s", ctx->extnames[i]);
264 emit_peobj_sym(ctx, name, 0,
265 PEOBJ_SECT_UNDEF, PEOBJ_TYPE_FUNC, PEOBJ_SCL_EXTERN);
266 }
267 emit_peobj_sym_func(ctx, PEOBJ_SYM_PREFIX LABEL_ASM_BEGIN, 0);
268 for (i = nzsym; i < ctx->nsym; i++) {
269 int pi = ctx->perm[i];
270 if (pi >= ctx->npc) {
271 sprintf(name, PEOBJ_SYM_PREFIX LABEL_PREFIX "%s",
272 ctx->globnames[pi-ctx->npc]);
273 emit_peobj_sym_func(ctx, name, ctx->sym_ofs[pi]);
274#if LJ_HASJIT
275 } else {
276#else
277 } else if (!(pi == BC_JFORI || pi == BC_JFORL || pi == BC_JITERL ||
278 pi == BC_JLOOP || pi == BC_IFORL || pi == BC_IITERL ||
279 pi == BC_ILOOP)) {
280#endif
281 sprintf(name, PEOBJ_SYM_PREFIX LABEL_PREFIX_BC "%s",
282 bc_names[pi]);
283 emit_peobj_sym_func(ctx, name, ctx->sym_ofs[pi]);
284 }
285 }
286
287 emit_peobj_sym_sect(ctx, pesect, PEOBJ_SECT_RDATA);
288 emit_peobj_sym_rdata(ctx, PEOBJ_SYM_PREFIX LABEL_OP_OFS, 0);
289
290 emit_peobj_sym_sect(ctx, pesect, PEOBJ_SECT_RDATA_Z);
291
292 if (strtab)
293 break;
294 /* 2nd pass: alloc strtab, write syms and copy strings. */
295 strtab = (char *)malloc(strtabofs);
296 *(uint32_t *)strtab = strtabofs;
297 }
298
299 /* Write string table. */
300 owrite(ctx, strtab, strtabofs);
301}
302
303#endif