1 | /* |
2 | ** LuaJIT VM builder: Assembler source code emitter. |
3 | ** Copyright (C) 2005-2021 Mike Pall. See Copyright Notice in luajit.h |
4 | */ |
5 | |
6 | #include "buildvm.h" |
7 | #include "lj_bc.h" |
8 | |
9 | /* ------------------------------------------------------------------------ */ |
10 | |
11 | #if LJ_TARGET_X86ORX64 |
12 | /* Emit bytes piecewise as assembler text. */ |
13 | static void emit_asm_bytes(BuildCtx *ctx, uint8_t *p, int n) |
14 | { |
15 | int i; |
16 | for (i = 0; i < n; i++) { |
17 | if ((i & 15) == 0) |
18 | fprintf(ctx->fp, "\t.byte %d" , p[i]); |
19 | else |
20 | fprintf(ctx->fp, ",%d" , p[i]); |
21 | if ((i & 15) == 15) putc('\n', ctx->fp); |
22 | } |
23 | if ((n & 15) != 0) putc('\n', ctx->fp); |
24 | } |
25 | |
26 | /* Emit relocation */ |
27 | static void emit_asm_reloc(BuildCtx *ctx, int type, const char *sym) |
28 | { |
29 | switch (ctx->mode) { |
30 | case BUILD_elfasm: |
31 | if (type) |
32 | fprintf(ctx->fp, "\t.long %s-.-4\n" , sym); |
33 | else |
34 | fprintf(ctx->fp, "\t.long %s\n" , sym); |
35 | break; |
36 | case BUILD_coffasm: |
37 | fprintf(ctx->fp, "\t.def %s; .scl 3; .type 32; .endef\n" , sym); |
38 | if (type) |
39 | fprintf(ctx->fp, "\t.long %s-.-4\n" , sym); |
40 | else |
41 | fprintf(ctx->fp, "\t.long %s\n" , sym); |
42 | break; |
43 | default: /* BUILD_machasm for relative relocations handled below. */ |
44 | fprintf(ctx->fp, "\t.long %s\n" , sym); |
45 | break; |
46 | } |
47 | } |
48 | |
49 | static const char *const jccnames[] = { |
50 | "jo" , "jno" , "jb" , "jnb" , "jz" , "jnz" , "jbe" , "ja" , |
51 | "js" , "jns" , "jpe" , "jpo" , "jl" , "jge" , "jle" , "jg" |
52 | }; |
53 | |
54 | /* Emit x86/x64 text relocations. */ |
55 | static void emit_asm_reloc_text(BuildCtx *ctx, uint8_t *cp, int n, |
56 | const char *sym) |
57 | { |
58 | const char *opname = NULL; |
59 | if (--n < 0) goto err; |
60 | if (cp[n] == 0xe8) { |
61 | opname = "call" ; |
62 | } else if (cp[n] == 0xe9) { |
63 | opname = "jmp" ; |
64 | } else if (cp[n] >= 0x80 && cp[n] <= 0x8f && n > 0 && cp[n-1] == 0x0f) { |
65 | opname = jccnames[cp[n]-0x80]; |
66 | n--; |
67 | } else { |
68 | err: |
69 | fprintf(stderr, "Error: unsupported opcode for %s symbol relocation.\n" , |
70 | sym); |
71 | exit(1); |
72 | } |
73 | emit_asm_bytes(ctx, cp, n); |
74 | if (strncmp(sym+(*sym == '_'), LABEL_PREFIX, sizeof(LABEL_PREFIX)-1)) { |
75 | /* Various fixups for external symbols outside of our binary. */ |
76 | if (ctx->mode == BUILD_elfasm) { |
77 | if (LJ_32) |
78 | fprintf(ctx->fp, "#if __PIC__\n\t%s lj_wrap_%s\n#else\n" , opname, sym); |
79 | fprintf(ctx->fp, "\t%s %s@PLT\n" , opname, sym); |
80 | if (LJ_32) |
81 | fprintf(ctx->fp, "#endif\n" ); |
82 | return; |
83 | } else if (LJ_32 && ctx->mode == BUILD_machasm) { |
84 | fprintf(ctx->fp, "\t%s L%s$stub\n" , opname, sym); |
85 | return; |
86 | } |
87 | } |
88 | fprintf(ctx->fp, "\t%s %s\n" , opname, sym); |
89 | } |
90 | #else |
91 | /* Emit words piecewise as assembler text. */ |
92 | static void emit_asm_words(BuildCtx *ctx, uint8_t *p, int n) |
93 | { |
94 | int i; |
95 | for (i = 0; i < n; i += 4) { |
96 | uint32_t ins = *(uint32_t *)(p+i); |
97 | #if LJ_TARGET_ARM64 && LJ_BE |
98 | ins = lj_bswap(ins); /* ARM64 instructions are always little-endian. */ |
99 | #endif |
100 | if ((i & 15) == 0) |
101 | fprintf(ctx->fp, "\t.long 0x%08x" , ins); |
102 | else |
103 | fprintf(ctx->fp, ",0x%08x" , ins); |
104 | if ((i & 15) == 12) putc('\n', ctx->fp); |
105 | } |
106 | if ((n & 15) != 0) putc('\n', ctx->fp); |
107 | } |
108 | |
109 | /* Emit relocation as part of an instruction. */ |
110 | static void emit_asm_wordreloc(BuildCtx *ctx, uint8_t *p, int n, |
111 | const char *sym) |
112 | { |
113 | uint32_t ins; |
114 | emit_asm_words(ctx, p, n-4); |
115 | ins = *(uint32_t *)(p+n-4); |
116 | #if LJ_TARGET_ARM |
117 | if ((ins & 0xff000000u) == 0xfa000000u) { |
118 | fprintf(ctx->fp, "\tblx %s\n" , sym); |
119 | } else if ((ins & 0x0e000000u) == 0x0a000000u) { |
120 | fprintf(ctx->fp, "\t%s%.2s %s\n" , (ins & 0x01000000u) ? "bl" : "b" , |
121 | &"eqnecsccmiplvsvchilsgeltgtle" [2*(ins >> 28)], sym); |
122 | } else { |
123 | fprintf(stderr, |
124 | "Error: unsupported opcode %08x for %s symbol relocation.\n" , |
125 | ins, sym); |
126 | exit(1); |
127 | } |
128 | #elif LJ_TARGET_ARM64 |
129 | if ((ins >> 26) == 0x25u) { |
130 | fprintf(ctx->fp, "\tbl %s\n" , sym); |
131 | } else { |
132 | fprintf(stderr, |
133 | "Error: unsupported opcode %08x for %s symbol relocation.\n" , |
134 | ins, sym); |
135 | exit(1); |
136 | } |
137 | #elif LJ_TARGET_PPC |
138 | #if LJ_TARGET_PS3 |
139 | #define TOCPREFIX "." |
140 | #else |
141 | #define TOCPREFIX "" |
142 | #endif |
143 | if ((ins >> 26) == 16) { |
144 | fprintf(ctx->fp, "\t%s %d, %d, " TOCPREFIX "%s\n" , |
145 | (ins & 1) ? "bcl" : "bc" , (ins >> 21) & 31, (ins >> 16) & 31, sym); |
146 | } else if ((ins >> 26) == 18) { |
147 | fprintf(ctx->fp, "\t%s " TOCPREFIX "%s\n" , (ins & 1) ? "bl" : "b" , sym); |
148 | } else { |
149 | fprintf(stderr, |
150 | "Error: unsupported opcode %08x for %s symbol relocation.\n" , |
151 | ins, sym); |
152 | exit(1); |
153 | } |
154 | #elif LJ_TARGET_MIPS |
155 | fprintf(stderr, |
156 | "Error: unsupported opcode %08x for %s symbol relocation.\n" , |
157 | ins, sym); |
158 | exit(1); |
159 | #else |
160 | #error "missing relocation support for this architecture" |
161 | #endif |
162 | } |
163 | #endif |
164 | |
165 | #if LJ_TARGET_ARM |
166 | #define ELFASM_PX "%%" |
167 | #else |
168 | #define ELFASM_PX "@" |
169 | #endif |
170 | |
171 | /* Emit an assembler label. */ |
172 | static void emit_asm_label(BuildCtx *ctx, const char *name, int size, int isfunc) |
173 | { |
174 | switch (ctx->mode) { |
175 | case BUILD_elfasm: |
176 | #if LJ_TARGET_PS3 |
177 | if (!strncmp(name, "lj_vm_" , 6) && |
178 | strcmp(name, ctx->beginsym) && |
179 | !strstr(name, "hook" )) { |
180 | fprintf(ctx->fp, |
181 | "\n\t.globl %s\n" |
182 | "\t.section \".opd\",\"aw\"\n" |
183 | "%s:\n" |
184 | "\t.long .%s,.TOC.@tocbase32\n" |
185 | "\t.size %s,8\n" |
186 | "\t.previous\n" |
187 | "\t.globl .%s\n" |
188 | "\t.hidden .%s\n" |
189 | "\t.type .%s, " ELFASM_PX "function\n" |
190 | "\t.size .%s, %d\n" |
191 | ".%s:\n" , |
192 | name, name, name, name, name, name, name, name, size, name); |
193 | break; |
194 | } |
195 | #endif |
196 | fprintf(ctx->fp, |
197 | "\n\t.globl %s\n" |
198 | "\t.hidden %s\n" |
199 | "\t.type %s, " ELFASM_PX "%s\n" |
200 | "\t.size %s, %d\n" |
201 | "%s:\n" , |
202 | name, name, name, isfunc ? "function" : "object" , name, size, name); |
203 | break; |
204 | case BUILD_coffasm: |
205 | fprintf(ctx->fp, "\n\t.globl %s\n" , name); |
206 | if (isfunc) |
207 | fprintf(ctx->fp, "\t.def %s; .scl 3; .type 32; .endef\n" , name); |
208 | fprintf(ctx->fp, "%s:\n" , name); |
209 | break; |
210 | case BUILD_machasm: |
211 | fprintf(ctx->fp, |
212 | "\n\t.private_extern %s\n" |
213 | "\t.no_dead_strip %s\n" |
214 | "%s:\n" , name, name, name); |
215 | break; |
216 | default: |
217 | break; |
218 | } |
219 | } |
220 | |
221 | /* Emit alignment. */ |
222 | static void emit_asm_align(BuildCtx *ctx, int bits) |
223 | { |
224 | switch (ctx->mode) { |
225 | case BUILD_elfasm: |
226 | case BUILD_coffasm: |
227 | fprintf(ctx->fp, "\t.p2align %d\n" , bits); |
228 | break; |
229 | case BUILD_machasm: |
230 | fprintf(ctx->fp, "\t.align %d\n" , bits); |
231 | break; |
232 | default: |
233 | break; |
234 | } |
235 | } |
236 | |
237 | /* ------------------------------------------------------------------------ */ |
238 | |
239 | /* Emit assembler source code. */ |
240 | void emit_asm(BuildCtx *ctx) |
241 | { |
242 | int i, rel; |
243 | |
244 | fprintf(ctx->fp, "\t.file \"buildvm_%s.dasc\"\n" , ctx->dasm_arch); |
245 | fprintf(ctx->fp, "\t.text\n" ); |
246 | emit_asm_align(ctx, 4); |
247 | |
248 | #if LJ_TARGET_PS3 |
249 | emit_asm_label(ctx, ctx->beginsym, ctx->codesz, 0); |
250 | #else |
251 | emit_asm_label(ctx, ctx->beginsym, 0, 0); |
252 | #endif |
253 | if (ctx->mode != BUILD_machasm) |
254 | fprintf(ctx->fp, ".Lbegin:\n" ); |
255 | |
256 | #if LJ_TARGET_ARM && defined(__GNUC__) && !LJ_NO_UNWIND |
257 | /* This should really be moved into buildvm_arm.dasc. */ |
258 | #if LJ_ARCH_HASFPU |
259 | fprintf(ctx->fp, |
260 | ".fnstart\n" |
261 | ".save {r5, r6, r7, r8, r9, r10, r11, lr}\n" |
262 | ".vsave {d8-d15}\n" |
263 | ".save {r4}\n" |
264 | ".pad #28\n" ); |
265 | #else |
266 | fprintf(ctx->fp, |
267 | ".fnstart\n" |
268 | ".save {r4, r5, r6, r7, r8, r9, r10, r11, lr}\n" |
269 | ".pad #28\n" ); |
270 | #endif |
271 | #endif |
272 | #if LJ_TARGET_MIPS |
273 | fprintf(ctx->fp, ".set nomips16\n.abicalls\n.set noreorder\n.set nomacro\n" ); |
274 | #endif |
275 | |
276 | for (i = rel = 0; i < ctx->nsym; i++) { |
277 | int32_t ofs = ctx->sym[i].ofs; |
278 | int32_t next = ctx->sym[i+1].ofs; |
279 | #if LJ_TARGET_ARM && defined(__GNUC__) && !LJ_NO_UNWIND && LJ_HASFFI |
280 | if (!strcmp(ctx->sym[i].name, "lj_vm_ffi_call" )) |
281 | fprintf(ctx->fp, |
282 | ".globl lj_err_unwind_arm\n" |
283 | ".personality lj_err_unwind_arm\n" |
284 | ".fnend\n" |
285 | ".fnstart\n" |
286 | ".save {r4, r5, r11, lr}\n" |
287 | ".setfp r11, sp\n" ); |
288 | #endif |
289 | emit_asm_label(ctx, ctx->sym[i].name, next - ofs, 1); |
290 | while (rel < ctx->nreloc && ctx->reloc[rel].ofs <= next) { |
291 | BuildReloc *r = &ctx->reloc[rel]; |
292 | int n = r->ofs - ofs; |
293 | #if LJ_TARGET_X86ORX64 |
294 | if (r->type != 0 && |
295 | (ctx->mode == BUILD_elfasm || ctx->mode == BUILD_machasm)) { |
296 | emit_asm_reloc_text(ctx, ctx->code+ofs, n, ctx->relocsym[r->sym]); |
297 | } else { |
298 | emit_asm_bytes(ctx, ctx->code+ofs, n); |
299 | emit_asm_reloc(ctx, r->type, ctx->relocsym[r->sym]); |
300 | } |
301 | ofs += n+4; |
302 | #else |
303 | emit_asm_wordreloc(ctx, ctx->code+ofs, n, ctx->relocsym[r->sym]); |
304 | ofs += n; |
305 | #endif |
306 | rel++; |
307 | } |
308 | #if LJ_TARGET_X86ORX64 |
309 | emit_asm_bytes(ctx, ctx->code+ofs, next-ofs); |
310 | #else |
311 | emit_asm_words(ctx, ctx->code+ofs, next-ofs); |
312 | #endif |
313 | } |
314 | |
315 | #if LJ_TARGET_ARM && defined(__GNUC__) && !LJ_NO_UNWIND |
316 | fprintf(ctx->fp, |
317 | #if !LJ_HASFFI |
318 | ".globl lj_err_unwind_arm\n" |
319 | ".personality lj_err_unwind_arm\n" |
320 | #endif |
321 | ".fnend\n" ); |
322 | #endif |
323 | |
324 | fprintf(ctx->fp, "\n" ); |
325 | switch (ctx->mode) { |
326 | case BUILD_elfasm: |
327 | #if !(LJ_TARGET_PS3 || LJ_TARGET_PSVITA) |
328 | fprintf(ctx->fp, "\t.section .note.GNU-stack,\"\"," ELFASM_PX "progbits\n" ); |
329 | #endif |
330 | #if LJ_TARGET_PPC && !LJ_TARGET_PS3 && !LJ_ABI_SOFTFP |
331 | /* Hard-float ABI. */ |
332 | fprintf(ctx->fp, "\t.gnu_attribute 4, 1\n" ); |
333 | #endif |
334 | /* fallthrough */ |
335 | case BUILD_coffasm: |
336 | fprintf(ctx->fp, "\t.ident \"%s\"\n" , ctx->dasm_ident); |
337 | break; |
338 | case BUILD_machasm: |
339 | fprintf(ctx->fp, |
340 | "\t.cstring\n" |
341 | "\t.ascii \"%s\\0\"\n" , ctx->dasm_ident); |
342 | break; |
343 | default: |
344 | break; |
345 | } |
346 | fprintf(ctx->fp, "\n" ); |
347 | } |
348 | |
349 | |