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