src/host/buildvm_asm.c - luajit-2.0-src

Global variables defined

Functions defined

Macros defined

Source code

  1. /*
  2. ** LuaJIT VM builder: Assembler source code emitter.
  3. ** Copyright (C) 2005-2015 Mike Pall. See Copyright Notice in luajit.h
  4. */

  5. #include "buildvm.h"
  6. #include "lj_bc.h"

  7. /* ------------------------------------------------------------------------ */

  8. #if LJ_TARGET_X86ORX64
  9. /* Emit bytes piecewise as assembler text. */
  10. static void emit_asm_bytes(BuildCtx *ctx, uint8_t *p, int n)
  11. {
  12.   int i;
  13.   for (i = 0; i < n; i++) {
  14.     if ((i & 15) == 0)
  15.       fprintf(ctx->fp, "\t.byte %d", p[i]);
  16.     else
  17.       fprintf(ctx->fp, ",%d", p[i]);
  18.     if ((i & 15) == 15) putc('\n', ctx->fp);
  19.   }
  20.   if ((n & 15) != 0) putc('\n', ctx->fp);
  21. }

  22. /* Emit relocation */
  23. static void emit_asm_reloc(BuildCtx *ctx, int type, const char *sym)
  24. {
  25.   switch (ctx->mode) {
  26.   case BUILD_elfasm:
  27.     if (type)
  28.       fprintf(ctx->fp, "\t.long %s-.-4\n", sym);
  29.     else
  30.       fprintf(ctx->fp, "\t.long %s\n", sym);
  31.     break;
  32.   case BUILD_coffasm:
  33.     fprintf(ctx->fp, "\t.def %s; .scl 3; .type 32; .endef\n", sym);
  34.     if (type)
  35.       fprintf(ctx->fp, "\t.long %s-.-4\n", sym);
  36.     else
  37.       fprintf(ctx->fp, "\t.long %s\n", sym);
  38.     break;
  39.   default:  /* BUILD_machasm for relative relocations handled below. */
  40.     fprintf(ctx->fp, "\t.long %s\n", sym);
  41.     break;
  42.   }
  43. }

  44. static const char *const jccnames[] = {
  45.   "jo", "jno", "jb", "jnb", "jz", "jnz", "jbe", "ja",
  46.   "js", "jns", "jpe", "jpo", "jl", "jge", "jle", "jg"
  47. };

  48. /* Emit x86/x64 text relocations. */
  49. static void emit_asm_reloc_text(BuildCtx *ctx, uint8_t *cp, int n,
  50.                                 const char *sym)
  51. {
  52.   const char *opname = NULL;
  53.   if (--n < 0) goto err;
  54.   if (cp[n] == 0xe8) {
  55.     opname = "call";
  56.   } else if (cp[n] == 0xe9) {
  57.     opname = "jmp";
  58.   } else if (cp[n] >= 0x80 && cp[n] <= 0x8f && n > 0 && cp[n-1] == 0x0f) {
  59.     opname = jccnames[cp[n]-0x80];
  60.     n--;
  61.   } else {
  62. err:
  63.     fprintf(stderr, "Error: unsupported opcode for %s symbol relocation.\n",
  64.             sym);
  65.     exit(1);
  66.   }
  67.   emit_asm_bytes(ctx, cp, n);
  68.   if (strncmp(sym+(*sym == '_'), LABEL_PREFIX, sizeof(LABEL_PREFIX)-1)) {
  69.     /* Various fixups for external symbols outside of our binary. */
  70.     if (ctx->mode == BUILD_elfasm) {
  71.       if (LJ_32)
  72.         fprintf(ctx->fp, "#if __PIC__\n\t%s lj_wrap_%s\n#else\n", opname, sym);
  73.       fprintf(ctx->fp, "\t%s %s@PLT\n", opname, sym);
  74.       if (LJ_32)
  75.         fprintf(ctx->fp, "#endif\n");
  76.       return;
  77.     } else if (LJ_32 && ctx->mode == BUILD_machasm) {
  78.       fprintf(ctx->fp, "\t%s L%s$stub\n", opname, sym);
  79.       return;
  80.     }
  81.   }
  82.   fprintf(ctx->fp, "\t%s %s\n", opname, sym);
  83. }
  84. #else
  85. /* Emit words piecewise as assembler text. */
  86. static void emit_asm_words(BuildCtx *ctx, uint8_t *p, int n)
  87. {
  88.   int i;
  89.   for (i = 0; i < n; i += 4) {
  90.     if ((i & 15) == 0)
  91.       fprintf(ctx->fp, "\t.long 0x%08x", *(uint32_t *)(p+i));
  92.     else
  93.       fprintf(ctx->fp, ",0x%08x", *(uint32_t *)(p+i));
  94.     if ((i & 15) == 12) putc('\n', ctx->fp);
  95.   }
  96.   if ((n & 15) != 0) putc('\n', ctx->fp);
  97. }

  98. /* Emit relocation as part of an instruction. */
  99. static void emit_asm_wordreloc(BuildCtx *ctx, uint8_t *p, int n,
  100.                                const char *sym)
  101. {
  102.   uint32_t ins;
  103.   emit_asm_words(ctx, p, n-4);
  104.   ins = *(uint32_t *)(p+n-4);
  105. #if LJ_TARGET_ARM
  106.   if ((ins & 0xff000000u) == 0xfa000000u) {
  107.     fprintf(ctx->fp, "\tblx %s\n", sym);
  108.   } else if ((ins & 0x0e000000u) == 0x0a000000u) {
  109.     fprintf(ctx->fp, "\t%s%.2s %s\n", (ins & 0x01000000u) ? "bl" : "b",
  110.             &"eqnecsccmiplvsvchilsgeltgtle"[2*(ins >> 28)], sym);
  111.   } else {
  112.     fprintf(stderr,
  113.             "Error: unsupported opcode %08x for %s symbol relocation.\n",
  114.             ins, sym);
  115.     exit(1);
  116.   }
  117. #elif LJ_TARGET_ARM64
  118.   if ((ins >> 26) == 0x25u) {
  119.     fprintf(ctx->fp, "\tbl %s\n", sym);
  120.   } else {
  121.     fprintf(stderr,
  122.             "Error: unsupported opcode %08x for %s symbol relocation.\n",
  123.             ins, sym);
  124.     exit(1);
  125.   }
  126. #elif LJ_TARGET_PPC
  127. #if LJ_TARGET_PS3
  128. #define TOCPREFIX "."
  129. #else
  130. #define TOCPREFIX ""
  131. #endif
  132.   if ((ins >> 26) == 16) {
  133.     fprintf(ctx->fp, "\t%s %d, %d, " TOCPREFIX "%s\n",
  134.             (ins & 1) ? "bcl" : "bc", (ins >> 21) & 31, (ins >> 16) & 31, sym);
  135.   } else if ((ins >> 26) == 18) {
  136. #if LJ_ARCH_PPC64
  137.     const char *suffix = strchr(sym, '@');
  138.     if (suffix && suffix[1] == 'h') {
  139.       fprintf(ctx->fp, "\taddis 11, 2, %s\n", sym);
  140.     } else if (suffix && suffix[1] == 'l') {
  141.       fprintf(ctx->fp, "\tld 12, %s\n", sym);
  142.     } else
  143. #endif
  144.     fprintf(ctx->fp, "\t%s " TOCPREFIX "%s\n", (ins & 1) ? "bl" : "b", sym);
  145.   } else {
  146.     fprintf(stderr,
  147.             "Error: unsupported opcode %08x for %s symbol relocation.\n",
  148.             ins, sym);
  149.     exit(1);
  150.   }
  151. #elif LJ_TARGET_MIPS
  152.   fprintf(stderr,
  153.           "Error: unsupported opcode %08x for %s symbol relocation.\n",
  154.           ins, sym);
  155.   exit(1);
  156. #else
  157. #error "missing relocation support for this architecture"
  158. #endif
  159. }
  160. #endif

  161. #if LJ_TARGET_ARM
  162. #define ELFASM_PX        "%%"
  163. #else
  164. #define ELFASM_PX        "@"
  165. #endif

  166. /* Emit an assembler label. */
  167. static void emit_asm_label(BuildCtx *ctx, const char *name, int size, int isfunc)
  168. {
  169.   switch (ctx->mode) {
  170.   case BUILD_elfasm:
  171. #if LJ_TARGET_PS3
  172.     if (!strncmp(name, "lj_vm_", 6) &&
  173.         strcmp(name, ctx->beginsym) &&
  174.         !strstr(name, "hook")) {
  175.       fprintf(ctx->fp,
  176.         "\n\t.globl %s\n"
  177.         "\t.section \".opd\",\"aw\"\n"
  178.         "%s:\n"
  179.         "\t.long .%s,.TOC.@tocbase32\n"
  180.         "\t.size %s,8\n"
  181.         "\t.previous\n"
  182.         "\t.globl .%s\n"
  183.         "\t.hidden .%s\n"
  184.         "\t.type .%s, " ELFASM_PX "function\n"
  185.         "\t.size .%s, %d\n"
  186.         ".%s:\n",
  187.         name, name, name, name, name, name, name, name, size, name);
  188.       break;
  189.     }
  190. #endif
  191.     fprintf(ctx->fp,
  192.       "\n\t.globl %s\n"
  193.       "\t.hidden %s\n"
  194.       "\t.type %s, " ELFASM_PX "%s\n"
  195.       "\t.size %s, %d\n"
  196.       "%s:\n",
  197.       name, name, name, isfunc ? "function" : "object", name, size, name);
  198.     break;
  199.   case BUILD_coffasm:
  200.     fprintf(ctx->fp, "\n\t.globl %s\n", name);
  201.     if (isfunc)
  202.       fprintf(ctx->fp, "\t.def %s; .scl 3; .type 32; .endef\n", name);
  203.     fprintf(ctx->fp, "%s:\n", name);
  204.     break;
  205.   case BUILD_machasm:
  206.     fprintf(ctx->fp,
  207.       "\n\t.private_extern %s\n"
  208.       "%s:\n", name, name);
  209.     break;
  210.   default:
  211.     break;
  212.   }
  213. }

  214. /* Emit alignment. */
  215. static void emit_asm_align(BuildCtx *ctx, int bits)
  216. {
  217.   switch (ctx->mode) {
  218.   case BUILD_elfasm:
  219.   case BUILD_coffasm:
  220.     fprintf(ctx->fp, "\t.p2align %d\n", bits);
  221.     break;
  222.   case BUILD_machasm:
  223.     fprintf(ctx->fp, "\t.align %d\n", bits);
  224.     break;
  225.   default:
  226.     break;
  227.   }
  228. }

  229. /* ------------------------------------------------------------------------ */

  230. /* Emit assembler source code. */
  231. void emit_asm(BuildCtx *ctx)
  232. {
  233.   int i, rel;

  234.   fprintf(ctx->fp, "\t.file \"buildvm_%s.dasc\"\n", ctx->dasm_arch);
  235. #if LJ_ARCH_PPC64
  236.   fprintf(ctx->fp, "\t.abiversion 2\n");
  237. #endif
  238.   fprintf(ctx->fp, "\t.text\n");
  239.   emit_asm_align(ctx, 4);

  240. #if LJ_TARGET_PS3
  241.   emit_asm_label(ctx, ctx->beginsym, ctx->codesz, 0);
  242. #else
  243.   emit_asm_label(ctx, ctx->beginsym, 0, 0);
  244. #endif
  245.   if (ctx->mode != BUILD_machasm)
  246.     fprintf(ctx->fp, ".Lbegin:\n");

  247. #if LJ_TARGET_ARM && defined(__GNUC__) && !LJ_NO_UNWIND
  248.   /* This should really be moved into buildvm_arm.dasc. */
  249.   fprintf(ctx->fp,
  250.           ".fnstart\n"
  251.           ".save {r4, r5, r6, r7, r8, r9, r10, r11, lr}\n"
  252.           ".pad #28\n");
  253. #endif
  254. #if LJ_TARGET_MIPS
  255.   fprintf(ctx->fp, ".set nomips16\n.abicalls\n.set noreorder\n.set nomacro\n");
  256. #endif

  257.   for (i = rel = 0; i < ctx->nsym; i++) {
  258.     int32_t ofs = ctx->sym[i].ofs;
  259.     int32_t next = ctx->sym[i+1].ofs;
  260. #if LJ_TARGET_ARM && defined(__GNUC__) && !LJ_NO_UNWIND && LJ_HASFFI
  261.     if (!strcmp(ctx->sym[i].name, "lj_vm_ffi_call"))
  262.       fprintf(ctx->fp,
  263.               ".globl lj_err_unwind_arm\n"
  264.               ".personality lj_err_unwind_arm\n"
  265.               ".fnend\n"
  266.               ".fnstart\n"
  267.               ".save {r4, r5, r11, lr}\n"
  268.               ".setfp r11, sp\n");
  269. #endif
  270.     emit_asm_label(ctx, ctx->sym[i].name, next - ofs, 1);
  271.     while (rel < ctx->nreloc && ctx->reloc[rel].ofs <= next) {
  272.       BuildReloc *r = &ctx->reloc[rel];
  273.       int n = r->ofs - ofs;
  274. #if LJ_TARGET_X86ORX64
  275.       if (r->type != 0 &&
  276.           (ctx->mode == BUILD_elfasm || ctx->mode == BUILD_machasm)) {
  277.         emit_asm_reloc_text(ctx, ctx->code+ofs, n, ctx->relocsym[r->sym]);
  278.       } else {
  279.         emit_asm_bytes(ctx, ctx->code+ofs, n);
  280.         emit_asm_reloc(ctx, r->type, ctx->relocsym[r->sym]);
  281.       }
  282.       ofs += n+4;
  283. #else
  284.       emit_asm_wordreloc(ctx, ctx->code+ofs, n, ctx->relocsym[r->sym]);
  285.       ofs += n;
  286. #endif
  287.       rel++;
  288.     }
  289. #if LJ_TARGET_X86ORX64
  290.     emit_asm_bytes(ctx, ctx->code+ofs, next-ofs);
  291. #else
  292.     emit_asm_words(ctx, ctx->code+ofs, next-ofs);
  293. #endif
  294.   }

  295. #if LJ_TARGET_ARM && defined(__GNUC__) && !LJ_NO_UNWIND
  296.   fprintf(ctx->fp,
  297. #if !LJ_HASFFI
  298.           ".globl lj_err_unwind_arm\n"
  299.           ".personality lj_err_unwind_arm\n"
  300. #endif
  301.           ".fnend\n");
  302. #endif

  303.   fprintf(ctx->fp, "\n");
  304.   switch (ctx->mode) {
  305.   case BUILD_elfasm:
  306. #if !(LJ_TARGET_PS3 || LJ_TARGET_PSVITA)
  307.     fprintf(ctx->fp, "\t.section .note.GNU-stack,\"\"," ELFASM_PX "progbits\n");
  308. #endif
  309. #if LJ_TARGET_PPC && !LJ_TARGET_PS3
  310.     /* Hard-float ABI. */
  311.     fprintf(ctx->fp, "\t.gnu_attribute 4, 1\n");
  312. #endif
  313.     /* fallthrough */
  314.   case BUILD_coffasm:
  315.     fprintf(ctx->fp, "\t.ident \"%s\"\n", ctx->dasm_ident);
  316.     break;
  317.   case BUILD_machasm:
  318.     fprintf(ctx->fp,
  319.       "\t.cstring\n"
  320.       "\t.ascii \"%s\\0\"\n", ctx->dasm_ident);
  321.     break;
  322.   default:
  323.     break;
  324.   }
  325.   fprintf(ctx->fp, "\n");
  326. }