diff options
Diffstat (limited to 'src/buildvm_peobj.c')
-rw-r--r-- | src/buildvm_peobj.c | 303 |
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. */ | ||
15 | static char *strtab; | ||
16 | static size_t strtabofs; | ||
17 | |||
18 | /* -- PE object definitions ----------------------------------------------- */ | ||
19 | |||
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; | ||
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_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). */ | ||
96 | enum { | ||
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. */ | ||
117 | static 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. */ | ||
145 | static 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. */ | ||
171 | void 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 | ||