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. */
13static 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 */
27static 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
49static 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. */
55static 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 {
68err:
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. */
92static 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. */
110static 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. */
172static 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. */
222static 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. */
240void 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