1/*
2** LuaJIT VM builder: Assembler source code emitter.
3** Copyright (C) 2005-2014 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 relocation for the incredibly stupid OSX assembler. */
55static void emit_asm_reloc_mach(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 fprintf(ctx->fp, "\t%s %s\n", opname, sym);
75}
76#else
77/* Emit words piecewise as assembler text. */
78static void emit_asm_words(BuildCtx *ctx, uint8_t *p, int n)
79{
80 int i;
81 for (i = 0; i < n; i += 4) {
82 if ((i & 15) == 0)
83 fprintf(ctx->fp, "\t.long 0x%08x", *(uint32_t *)(p+i));
84 else
85 fprintf(ctx->fp, ",0x%08x", *(uint32_t *)(p+i));
86 if ((i & 15) == 12) putc('\n', ctx->fp);
87 }
88 if ((n & 15) != 0) putc('\n', ctx->fp);
89}
90
91/* Emit relocation as part of an instruction. */
92static void emit_asm_wordreloc(BuildCtx *ctx, uint8_t *p, int n,
93 const char *sym)
94{
95 uint32_t ins;
96 emit_asm_words(ctx, p, n-4);
97 ins = *(uint32_t *)(p+n-4);
98#if LJ_TARGET_ARM
99 if ((ins & 0xff000000u) == 0xfa000000u) {
100 fprintf(ctx->fp, "\tblx %s\n", sym);
101 } else if ((ins & 0x0e000000u) == 0x0a000000u) {
102 fprintf(ctx->fp, "\t%s%.2s %s\n", (ins & 0x01000000u) ? "bl" : "b",
103 &"eqnecsccmiplvsvchilsgeltgtle"[2*(ins >> 28)], sym);
104 } else {
105 fprintf(stderr,
106 "Error: unsupported opcode %08x for %s symbol relocation.\n",
107 ins, sym);
108 exit(1);
109 }
110#elif LJ_TARGET_PPC || LJ_TARGET_PPCSPE
111#if LJ_TARGET_PS3
112#define TOCPREFIX "."
113#else
114#define TOCPREFIX ""
115#endif
116 if ((ins >> 26) == 16) {
117 fprintf(ctx->fp, "\t%s %d, %d, " TOCPREFIX "%s\n",
118 (ins & 1) ? "bcl" : "bc", (ins >> 21) & 31, (ins >> 16) & 31, sym);
119 } else if ((ins >> 26) == 18) {
120 fprintf(ctx->fp, "\t%s " TOCPREFIX "%s\n", (ins & 1) ? "bl" : "b", sym);
121 } else {
122 fprintf(stderr,
123 "Error: unsupported opcode %08x for %s symbol relocation.\n",
124 ins, sym);
125 exit(1);
126 }
127#elif LJ_TARGET_MIPS
128 fprintf(stderr,
129 "Error: unsupported opcode %08x for %s symbol relocation.\n",
130 ins, sym);
131 exit(1);
132#else
133#error "missing relocation support for this architecture"
134#endif
135}
136#endif
137
138#if LJ_TARGET_ARM
139#define ELFASM_PX "%%"
140#else
141#define ELFASM_PX "@"
142#endif
143
144/* Emit an assembler label. */
145static void emit_asm_label(BuildCtx *ctx, const char *name, int size, int isfunc)
146{
147 switch (ctx->mode) {
148 case BUILD_elfasm:
149#if LJ_TARGET_PS3
150 if (!strncmp(name, "lj_vm_", 6) &&
151 strcmp(name, ctx->beginsym) &&
152 !strstr(name, "hook")) {
153 fprintf(ctx->fp,
154 "\n\t.globl %s\n"
155 "\t.section \".opd\",\"aw\"\n"
156 "%s:\n"
157 "\t.long .%s,.TOC.@tocbase32\n"
158 "\t.size %s,8\n"
159 "\t.previous\n"
160 "\t.globl .%s\n"
161 "\t.hidden .%s\n"
162 "\t.type .%s, " ELFASM_PX "function\n"
163 "\t.size .%s, %d\n"
164 ".%s:\n",
165 name, name, name, name, name, name, name, name, size, name);
166 break;
167 }
168#endif
169 fprintf(ctx->fp,
170 "\n\t.globl %s\n"
171 "\t.hidden %s\n"
172 "\t.type %s, " ELFASM_PX "%s\n"
173 "\t.size %s, %d\n"
174 "%s:\n",
175 name, name, name, isfunc ? "function" : "object", name, size, name);
176 break;
177 case BUILD_coffasm:
178 fprintf(ctx->fp, "\n\t.globl %s\n", name);
179 if (isfunc)
180 fprintf(ctx->fp, "\t.def %s; .scl 3; .type 32; .endef\n", name);
181 fprintf(ctx->fp, "%s:\n", name);
182 break;
183 case BUILD_machasm:
184 fprintf(ctx->fp,
185 "\n\t.private_extern %s\n"
186 "%s:\n", name, name);
187 break;
188 default:
189 break;
190 }
191}
192
193/* Emit alignment. */
194static void emit_asm_align(BuildCtx *ctx, int bits)
195{
196 switch (ctx->mode) {
197 case BUILD_elfasm:
198 case BUILD_coffasm:
199 fprintf(ctx->fp, "\t.p2align %d\n", bits);
200 break;
201 case BUILD_machasm:
202 fprintf(ctx->fp, "\t.align %d\n", bits);
203 break;
204 default:
205 break;
206 }
207}
208
209/* ------------------------------------------------------------------------ */
210
211/* Emit assembler source code. */
212void emit_asm(BuildCtx *ctx)
213{
214 int i, rel;
215
216 fprintf(ctx->fp, "\t.file \"buildvm_%s.dasc\"\n", ctx->dasm_arch);
217 fprintf(ctx->fp, "\t.text\n");
218 emit_asm_align(ctx, 4);
219
220#if LJ_TARGET_PS3
221 emit_asm_label(ctx, ctx->beginsym, ctx->codesz, 0);
222#else
223 emit_asm_label(ctx, ctx->beginsym, 0, 0);
224#endif
225 if (ctx->mode != BUILD_machasm)
226 fprintf(ctx->fp, ".Lbegin:\n");
227
228#if LJ_TARGET_ARM && defined(__GNUC__) && !LJ_NO_UNWIND
229 /* This should really be moved into buildvm_arm.dasc. */
230 fprintf(ctx->fp,
231 ".fnstart\n"
232 ".save {r4, r5, r6, r7, r8, r9, r10, r11, lr}\n"
233 ".pad #28\n");
234#endif
235#if LJ_TARGET_MIPS
236 fprintf(ctx->fp, ".set nomips16\n.abicalls\n.set noreorder\n.set nomacro\n");
237#endif
238
239 for (i = rel = 0; i < ctx->nsym; i++) {
240 int32_t ofs = ctx->sym[i].ofs;
241 int32_t next = ctx->sym[i+1].ofs;
242#if LJ_TARGET_ARM && defined(__GNUC__) && !LJ_NO_UNWIND && LJ_HASFFI
243 if (!strcmp(ctx->sym[i].name, "lj_vm_ffi_call"))
244 fprintf(ctx->fp,
245 ".globl lj_err_unwind_arm\n"
246 ".personality lj_err_unwind_arm\n"
247 ".fnend\n"
248 ".fnstart\n"
249 ".save {r4, r5, r11, lr}\n"
250 ".setfp r11, sp\n");
251#endif
252 emit_asm_label(ctx, ctx->sym[i].name, next - ofs, 1);
253 while (rel < ctx->nreloc && ctx->reloc[rel].ofs <= next) {
254 BuildReloc *r = &ctx->reloc[rel];
255 int n = r->ofs - ofs;
256#if LJ_TARGET_X86ORX64
257 if (ctx->mode == BUILD_machasm && r->type != 0) {
258 emit_asm_reloc_mach(ctx, ctx->code+ofs, n, ctx->relocsym[r->sym]);
259 } else {
260 emit_asm_bytes(ctx, ctx->code+ofs, n);
261 emit_asm_reloc(ctx, r->type, ctx->relocsym[r->sym]);
262 }
263 ofs += n+4;
264#else
265 emit_asm_wordreloc(ctx, ctx->code+ofs, n, ctx->relocsym[r->sym]);
266 ofs += n;
267#endif
268 rel++;
269 }
270#if LJ_TARGET_X86ORX64
271 emit_asm_bytes(ctx, ctx->code+ofs, next-ofs);
272#else
273 emit_asm_words(ctx, ctx->code+ofs, next-ofs);
274#endif
275 }
276
277#if LJ_TARGET_ARM && defined(__GNUC__) && !LJ_NO_UNWIND
278 fprintf(ctx->fp,
279#if !LJ_HASFFI
280 ".globl lj_err_unwind_arm\n"
281 ".personality lj_err_unwind_arm\n"
282#endif
283 ".fnend\n");
284#endif
285
286 fprintf(ctx->fp, "\n");
287 switch (ctx->mode) {
288 case BUILD_elfasm:
289#if !LJ_TARGET_PS3
290 fprintf(ctx->fp, "\t.section .note.GNU-stack,\"\"," ELFASM_PX "progbits\n");
291#endif
292#if LJ_TARGET_PPCSPE
293 /* Soft-float ABI + SPE. */
294 fprintf(ctx->fp, "\t.gnu_attribute 4, 2\n\t.gnu_attribute 8, 3\n");
295#elif LJ_TARGET_PPC && !LJ_TARGET_PS3
296 /* Hard-float ABI. */
297 fprintf(ctx->fp, "\t.gnu_attribute 4, 1\n");
298#endif
299 /* fallthrough */
300 case BUILD_coffasm:
301 fprintf(ctx->fp, "\t.ident \"%s\"\n", ctx->dasm_ident);
302 break;
303 case BUILD_machasm:
304 fprintf(ctx->fp,
305 "\t.cstring\n"
306 "\t.ascii \"%s\\0\"\n", ctx->dasm_ident);
307 break;
308 default:
309 break;
310 }
311 fprintf(ctx->fp, "\n");
312}
313
314