diff options
Diffstat (limited to 'src/bin')
| -rw-r--r-- | src/bin/fspec-dump.c | 540 | ||||
| -rw-r--r-- | src/bin/utils.h | 81 | 
2 files changed, 621 insertions, 0 deletions
| diff --git a/src/bin/fspec-dump.c b/src/bin/fspec-dump.c new file mode 100644 index 0000000..195674f --- /dev/null +++ b/src/bin/fspec-dump.c @@ -0,0 +1,540 @@ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <stdint.h> +#include <stdbool.h> +#include <limits.h> +#include <ctype.h> +#include <assert.h> +#include <err.h> + +#include "utils.h" + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) +#define DIV_ROUND_UP(a, b) (1 + ((a - 1) / b)) + +#define MAX(x, y) ((x) > (y) ? (x) : (y)) +#define MIN(x, y) ((x) < (y) ? (x) : (y)) + +static const int INDSTP = 3; + +static size_t +to_hex(const uint8_t *buf, const size_t buf_sz, char *out, const size_t out_sz, const bool reverse) +{ +   assert(out); +   const char nibble[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; +   const uint8_t nbs = sizeof(nibble) - 1; + +   size_t w = 0, last_non_zero = w; +   for (size_t i = 0; i < buf_sz && out_sz > 2 && w < out_sz - 2; ++i) { +      for (uint8_t c = 0; c < CHAR_BIT / 8 && w < out_sz; ++c) { +         const size_t idx = (reverse ? (buf_sz - 1) - i : i); +         const uint8_t hi = (buf[idx] >> (4 * (c + 1))) & nbs; +         const uint8_t lo = (buf[idx] >> (8 * c)) & nbs; + +         if (w || hi || lo) { +            out[w++] = nibble[hi]; +            out[w++] = nibble[lo]; +            last_non_zero = (hi || lo ? w : last_non_zero); +         } +      } +   } + +   if (!w) { +      out[w++] = nibble[0]; +      out[w++] = nibble[0]; +   } else { +      w = last_non_zero; +   } + +   assert(w < out_sz); +   out[w] = 0; +   return w; +} + +static void +print_dec(const uint8_t *buf, const size_t size, const bool is_signed) +{ +   char hex[2 * sizeof(uint64_t) + 1]; +   to_hex(buf, size, hex, sizeof(hex), true); + +   if (is_signed) { +      printf("%ld", (int64_t)strtoll(hex, NULL, 16)); +   } else { +      printf("%lu", (uint64_t)strtoull(hex, NULL, 16)); +   } +} + +static void +print_udec(const uint8_t *buf, const size_t size) +{ +   print_dec(buf, size, false); +} + +static void +print_sdec(const uint8_t *buf, const size_t size) +{ +   print_dec(buf, size, true); +} + +static void +print_hex(const uint8_t *buf, const size_t size) +{ +   char hex[2 * sizeof(uint64_t) + 1]; +   to_hex(buf, size, hex, sizeof(hex), true); +   printf("0x%s", hex); +} + +static void +print_array(const uint8_t *buf, const size_t size, const size_t nmemb, void (*fun)(const uint8_t *buf, const size_t size), const int sindent) +{ +   const int indent = sindent + INDSTP; +   if (nmemb > 8) { +      printf("{\n%*s", indent, ""); +   } else if (nmemb > 1) { +      printf("{ "); +   } + +   for (size_t n = 0; n < nmemb; ++n) { +      fun(buf + n * size, size); +      printf("%s", (nmemb > 1 && n + 1 < nmemb ? ", " : "")); + +      if (n + 1 < nmemb && !((n + 1) % 8)) +         printf("\n%*s", indent, ""); +   } + +   if (nmemb > 8) { +      printf("\n%*s}\n", sindent, ""); +   } else { +      printf("%s\n", (nmemb > 1 ? " }" : "")); +   } +} + +static void +print_str(const char *buf, const size_t size, const size_t nmemb) +{ +   const bool has_nl = memchr(buf, '\n', size * nmemb); +   if (has_nl) +      puts("```"); + +   for (size_t n = 0; n < size * nmemb && buf[n] != 0; ++n) +      printf("%c", buf[n]); + +   puts((has_nl ? "```" : "")); +} + +enum fspec_visual { +   VISUAL_NUL, +   VISUAL_DEC, +   VISUAL_HEX, +   VISUAL_STR, +   VISUAL_FLT +}; + +static void +fspec_print(const uint8_t *buf, const size_t size, const size_t nmemb, const bool sign, const enum fspec_visual visual, const int indent) +{ +   const uint64_t szb = DIV_ROUND_UP(size, CHAR_BIT); +   switch (visual) { +      case VISUAL_STR: +         print_str((char*)buf, szb, nmemb); +         break; + +      case VISUAL_HEX: +         print_array(buf, szb, nmemb, print_hex, indent); +         break; + +      case VISUAL_DEC: +         print_array(buf, szb, nmemb, (sign ? print_sdec : print_udec), indent); +         break; + +      case VISUAL_FLT: +         // TODO +         break; + +      case VISUAL_NUL: +         break; +   } +} + +enum fspec_instruction { +   INS_VERSION, +   INS_REG, +   INS_PUSH, +   INS_PUSHR, +   INS_STORE, +   INS_OP, +   INS_QUEUE, +   INS_IO, +   INS_EXEC, +   INS_CALL, +   INS_JMP, +   INS_JMPIF, +}; + +enum fspec_operation { +   OP_UNM, +   OP_LNOT, +   OP_BNOT, +   OP_MUL, +   OP_DIV, +   OP_MOD, +   OP_ADD, +   OP_SUB, +   OP_SHIFTL, +   OP_SHIFTR, +   OP_LESS, +   OP_LESSEQ, +   OP_EQ, +   OP_NOTEQ, +   OP_BAND, +   OP_BOR, +   OP_BXOR, +   OP_LAND, +   OP_LOR, +   OP_CTERNARY, +   OP_SUBSCRIPT +}; + +struct fspec_register { +   uint64_t off, len; +   uint8_t shift[2]; +}; + +struct fspec_stack { +   struct fspec_register value[64]; +   uint8_t numv; +}; + +struct fspec_istream { +   size_t (*read)(void *ctx, void *buf, const size_t size); +}; + +struct fspec_buffer { +   uint8_t *data; +   uint64_t ptr, size; +}; + +static void +fspec_buffer_write(struct fspec_buffer *buf, const void *data, const size_t size) +{ +   assert(buf->ptr + size <= buf->size); +   memcpy(buf->data + buf->ptr, data, size); +   buf->ptr += size; +} + +struct fspec_ctx { +   struct fspec_buffer mem; +   struct fspec_stack S, R; +   struct fspec_istream ir, binary; +}; + +static void +stack_push_num(struct fspec_stack *stack, struct fspec_buffer *buf, const uint64_t v) +{ +   assert(stack->numv < ARRAY_SIZE(stack->value)); +   const uint8_t bsz = DIV_ROUND_UP(__builtin_ctzl((v ? v : 1)), CHAR_BIT); +   stack->value[stack->numv++] = (struct fspec_register){ .off = buf->ptr, .len = bsz }; +   const union { uint8_t u8[sizeof(v)]; uint64_t v; } u = { .v = v }; +   fspec_buffer_write(buf, u.u8, bsz); +} + +static void +stack_push(struct fspec_stack *stack, struct fspec_register *value) +{ +   assert(stack->numv < ARRAY_SIZE(stack->value)); +   stack->value[stack->numv++] = *value; +} + +static void +stack_pop(struct fspec_stack *stack, struct fspec_register *out_value) +{ +   assert(stack->numv > 0); +   *out_value = stack->value[--stack->numv]; +} + +static uint64_t +stack_pop_num(struct fspec_stack *stack, const struct fspec_buffer *buf) +{ +   assert(stack->numv > 0); +   const struct fspec_register v = stack->value[--stack->numv]; +   union { uint8_t u8[sizeof(uint64_t)]; uint64_t v; } u = {0}; +   memcpy(u.u8, buf->data + v.off, MIN(v.len, sizeof(u.u8))); +   return (u.v << v.shift[0]) >> v.shift[1]; +} + +static bool +is_binary(const uint8_t *data, const uint64_t len) +{ +   for (uint64_t i = 0; i < len; ++i) +      if (!isprint(data[i])) +         return true; +   return false; +} + +static void +fspec_seek(struct fspec_ctx *ctx) +{ +   const uint64_t off = stack_pop_num(&ctx->S, &ctx->mem); +   // fseek(ctx->input, off, SEEK_SET); +} + +static uint64_t +math(const enum fspec_operation op, const uint64_t r[3]) +{ +   switch (op) { +      case OP_UNM: return -r[0]; +      case OP_LNOT: return !r[0]; +      case OP_BNOT: return ~r[0]; +      case OP_MUL: return r[0] * r[1]; +      case OP_DIV: return r[0] / r[1]; +      case OP_MOD: return r[0] % r[1]; +      case OP_ADD: return r[0] + r[1]; +      case OP_SUB: return r[0] - r[1]; +      case OP_SHIFTL: return r[0] << r[1]; +      case OP_SHIFTR: return r[0] >> r[1]; +      case OP_LESS: return r[0] < r[1]; +      case OP_LESSEQ: return r[0] <= r[1]; +      case OP_EQ: return r[0] == r[1]; +      case OP_NOTEQ: return r[0] != r[1]; +      case OP_BAND: return r[0] & r[1]; +      case OP_BOR: return r[0] | r[1]; +      case OP_BXOR: return r[0] ^ r[1]; +      case OP_LAND: return r[0] && r[1]; +      case OP_LOR: return r[0] || r[1]; +      case OP_CTERNARY: return r[0] ? r[1] : r[2]; +      case OP_SUBSCRIPT: assert(0 && "should not happen"); +   } +   return 0; +} + +static void +do_op(struct fspec_ctx *ctx, const enum fspec_operation op) +{ +   const struct { +      char *name; +      uint8_t args; +   } map[] = { +      { "UNM", 1 }, { "LNOT", 1 }, { "BNOT", 1 }, // unary +      { "MUL", 2 }, { "DIV", 2 }, { "MOD", 2 }, { "ADD", 2 }, { "SUB", 2 }, // binary math +      { "SHIFTL", 2 }, { "SHIFTR", 2 }, // bitshifts +      { "LESS", 2 }, { "LESSEQ", 2 }, { "EQ", 2 }, { "NOTEQ", 2 }, // logical comparison +      { "BAND", 2 }, { "BOR", 2 }, { "BXOR", 2 }, // bitwise operations +      { "LAND", 2 }, { "LOR", 2 }, // logical and && or +      { "CTERNARY", 3 }, // ternary +      { "SUBSCRIPT", 2 }, // subscript +   }; + +   assert(op < ARRAY_SIZE(map)); + +   uint64_t r[3]; +   fprintf(stderr, "%s: ", map[op].name); +   for (uint8_t i = 0; i < map[op].args; ++i) { +      r[i] = stack_pop_num(&ctx->S, &ctx->mem); +      fprintf(stderr, "%lu%s", r[i], (i + 1 < map[op].args ? ", " : "\n")); +   } + +   stack_push_num(&ctx->S, &ctx->mem, math(op, r)); +} + +static bool +fspec_execute(struct fspec_ctx *ctx, const uint8_t *ir, const uint64_t irlen, const int ind) +{ +   const struct filter { +      const char *name, **unpacking, **packing; +   } filters[] = { +      { "encoding", (const char*[]){ "iconv", "-f", NULL }, (const char*[]){ "iconv", "-t", NULL } } +   }; + +   const struct function { +      const char *name; +      void (*fun)(struct fspec_ctx *ctx); +   } functions[] = { +      { "seek", fspec_seek } +   }; + +   for (const uint8_t *pc = ir; pc < ir + irlen;) { +      union { +         struct { unsigned name:5; unsigned n:2; uint64_t v:57; } ins; +         uint8_t v[16]; +      } u = {0}; + +      memcpy(u.v, pc, sizeof(u.v[0])); +      const uint8_t insw = sizeof(uint16_t) * (1 << u.ins.n); +      memcpy(u.v, pc, insw); +      pc += insw; + +      const uint64_t insv = u.ins.v; +      switch (u.ins.name) { +         case INS_VERSION: +            fprintf(stderr, "VERSION: %lu\n", insv); +            break; +         case INS_REG: +            stack_push(&ctx->R, (struct fspec_register[]){{ .off = pc - ctx->mem.data, .len = insv }}); +            if (is_binary(pc, insv)) { +               fprintf(stderr, "REG len: %lu, [binary data]\n", insv); +            } else { +               fprintf(stderr, "REG len: %lu, %.*s\n", insv, (int)insv, (char*)pc); +            } +            pc += insv; +            break; +         case INS_PUSH: +            fprintf(stderr, "PUSH v: %lu\n", insv); +            stack_push(&ctx->S, (struct fspec_register[]){{ .off = pc - ctx->mem.data - insw, .len = insw, .shift = {0,7} }}); +            break; +         case INS_PUSHR: +            fprintf(stderr, "PUSHR R: %lu\n", insv); +            stack_push(&ctx->S, &ctx->R.value[insv]); +            break; +         case INS_STORE: +            fprintf(stderr, "STORE R: %lu\n", insv); +            stack_pop(&ctx->S, &ctx->R.value[insv]); +            break; +         case INS_OP: +            fprintf(stderr, "OP op: %lu\n", insv); +            do_op(ctx, insv); +            break; +         case INS_QUEUE: +            fprintf(stderr, "QUEUE len: %lu\n", insv); +            break; +         case INS_IO: { +               const uint64_t R = stack_pop_num(&ctx->S, &ctx->mem); +               fprintf(stderr, "IO: sz: %lu, R: %lu\n", insv, R); +               ctx->R.value[R].off = ctx->mem.ptr; +               const uint64_t szb = DIV_ROUND_UP(insv, CHAR_BIT), bpe = (szb * CHAR_BIT) / insv; +               uint64_t nmemb = 1; +               do { +                  nmemb *= (ctx->S.numv ? stack_pop_num(&ctx->S, &ctx->mem) : 1) / bpe; +                  assert(ctx->mem.ptr + szb * nmemb <= ctx->mem.size); +                  ctx->mem.ptr += ctx->binary.read(ctx, ctx->mem.data + ctx->mem.ptr, szb * nmemb); +               } while (ctx->S.numv); +               if (ctx->mem.ptr == ctx->R.value[R].off) +                  return true; +               ctx->R.value[R].len = ctx->mem.ptr - ctx->R.value[R].off; +            } +            break; +         case INS_EXEC: { +               fprintf(stderr, "EXEC R: %lu\n", insv); +               uint64_t nmemb = 1; +               do { +                  nmemb *= (ctx->S.numv ? stack_pop_num(&ctx->S, &ctx->mem) : 1); +                  for (uint64_t i = 0; i < nmemb; ++i) +                     if (fspec_execute(ctx, ctx->mem.data + ctx->R.value[insv].off, ctx->R.value[insv].len, ind + INDSTP)) +                        return true; +               } while (ctx->S.numv); +            } +            break; +         case INS_CALL: { +               fprintf(stderr, "CALL R: %lu\n", insv); +               ctx->S.numv = 0; +#if 0 +               const struct filter *filter = NULL; +               const struct fspec_data *name = &ctx->D[num - 1]; +               for (size_t i = 0; i < ARRAY_SIZE(filters); ++i) { +                  if (strlen(filters[i].name) != name->len || memcmp(filters[i].name, (char*)ctx->code + name->off, name->len)) +                     continue; + +                  filter = &filters[i]; +                  break; +               } + +               if (filter) { +                  size_t i; +                  const char *args[32]; +                  for (i = 0; filters->unpacking[i]; ++i) { +                     args[i] = filters->unpacking[i]; +                     fprintf(stderr, "%zu: %s\n", i, args[i]); +                  } + +                  size_t aw = 0; +                  char additional[1024]; +                  memset(additional, 0, sizeof(additional)); +                  for (; ctx->S.written; ++i) { +                     const struct fspec_value v = stack_pop(&ctx->S); +                     if (v.type == FSPEC_VALUE_NUMBER) { +                        aw += snprintf(additional, sizeof(additional) - aw, "%lu", v.u.num) + 1; +                     } else if (v.type == FSPEC_VALUE_DATA) { +                        args[i] = additional + aw; +                        memcpy(additional + aw, (char*)ctx->code + v.u.data.off, v.u.data.len); +                        aw += v.u.data.len + 1; +                     } else if (v.type == FSPEC_VALUE_FIELD) { +                        args[i] = additional + aw; +                        memcpy(additional + aw, (char*)ctx->binary + v.u.data.off, v.u.data.len); +                        aw += v.u.data.len + 1; +                     } +                     fprintf(stderr, "%zu: %s\n", i, args[i]); +                  } +                  args[i] = NULL; + +                  struct proc p; +                  if (proc_open(args[0], (char*const*)args, &p)) { +                     ctx->bsz -= write(p.fds[0], ctx->binary + last->u.primitive.data.off, ctx->bsz - last->u.primitive.data.off); +                     close_fd(&p.fds[0]); +                     assert(ctx->bsz == last->u.primitive.data.off); +                     ssize_t rd; +                     for (; (rd = read(p.fds[1], ctx->binary + last->u.primitive.data.off, 1024)) == 1024; ctx->bsz += rd); +                     ctx->bsz += rd; +                     proc_close(&p); +                  } else { +                     warn("failed to spawn: %s", args[0]); +                  } +               } else { +                  ctx->S.numv = 0; +               } +#endif +            } +            break; +         case INS_JMP: +            fprintf(stderr, "JMP off: %lu\n", insv); +            pc = ir + insv; +            break; +         case INS_JMPIF: +            fprintf(stderr, "JMPIF off: %lu\n", insv); +            const uint64_t r1 = stack_pop_num(&ctx->S, &ctx->mem); +            pc = (r1 ? ir + insv : pc); +            break; +         default: +            errx(EXIT_FAILURE, "unknown instruction: %u\n", u.ins.name); +      } +   } +   return false; +} + +static FILE *input; + +static size_t +read_binary(void *ctx, void *ptr, const size_t size) +{ +   return fread(ptr, 1, size, input); +} + +static size_t +read_ir(void *ctx, void *ptr, const size_t size) +{ +   return fread(ptr, 1, size, stdin); +} + +int +main(int argc, char *argv[]) +{ +   input = fopen(argv[1], "rb"); + + +   struct fspec_ctx ctx = { +      .mem = { .data = calloc(4096, 4096), .size = 4096 * 4096 }, +      .ir.read = read_ir, +      .binary.read = read_binary, +      .R.numv = 1 +   }; + +   ctx.mem.ptr += ctx.ir.read(&ctx, ctx.mem.data, ctx.mem.size); +   fspec_execute(&ctx, ctx.mem.data, ctx.mem.ptr, 0); +   fspec_execute(&ctx, ctx.mem.data + ctx.R.value[ctx.R.numv - 1].off, ctx.R.value[ctx.R.numv - 1].len, 0); + +   for (uint64_t i = 0; i < ctx.R.numv; ++i) { +      printf("REG%lu: ", i); +      fspec_print(ctx.mem.data + ctx.R.value[i].off, 1, ctx.R.value[i].len, false, VISUAL_HEX, 0); +   } + +   return EXIT_SUCCESS; +} diff --git a/src/bin/utils.h b/src/bin/utils.h new file mode 100644 index 0000000..3baa1b1 --- /dev/null +++ b/src/bin/utils.h @@ -0,0 +1,81 @@ +#pragma once + +// #include <spawn.h> +#include <sys/wait.h> +#include <unistd.h> + +struct proc { +   pid_t pid; +   int fds[2]; +}; + +static inline void +close_fd(int *fd) +{ +   assert(fd); +   if (*fd >= 0) +      close(*fd); +} + +static inline bool +proc_open(const char *file, char *const argv[], struct proc *out_proc) +{ +   assert(file && argv && out_proc); +   *out_proc = (struct proc){0}; + +   int pipes[4]; +   pipe(&pipes[0]); /* parent */ +   pipe(&pipes[2]); /* child */ + +#if 0 +   // Doesn't work, no idea why +   posix_spawn_file_actions_t fa; +   if (posix_spawn_file_actions_init(&fa) != 0 || +       posix_spawn_file_actions_addclose(&fa, pipes[0]) != 0 || +       posix_spawn_file_actions_addclose(&fa, pipes[3]) != 0 || +       posix_spawn_file_actions_adddup2(&fa, pipes[2], 0) != 0 || +       posix_spawn_file_actions_adddup2(&fa, pipes[1], 1) != 0 || +       posix_spawn_file_actions_addclose(&fa, pipes[2]) != 0 || +       posix_spawn_file_actions_addclose(&fa, pipes[1]) != 0 || +       posix_spawnp(&out_proc->pid, file, &fa, NULL, argv, NULL) != 0) { +      posix_spawn_file_actions_destroy(&fa); +      for (uint8_t i = 0; i < ARRAY_SIZE(pipes); ++i) +         close(pipes[i]); +      return false; +   } +   posix_spawn_file_actions_destroy(&fa); +#else +   if ((out_proc->pid = fork()) > 0) { +      out_proc->fds[0] = pipes[3]; +      out_proc->fds[1] = pipes[0]; +      close(pipes[1]); +      close(pipes[2]); +      return true; +   } else { +      close(pipes[0]); +      close(pipes[3]); +      dup2(pipes[2], 0); +      dup2(pipes[1], 1); +      close(pipes[2]); +      close(pipes[1]); +      execvp(file, argv); +      _exit(0); +   } +#endif + +   out_proc->fds[0] = pipes[3]; +   out_proc->fds[1] = pipes[0]; +   close(pipes[1]); +   close(pipes[2]); +   return true; +} + +static inline void +proc_close(struct proc *proc) +{ +   assert(proc); +   waitpid(proc->pid, NULL, 0); +   close_fd(&proc->fds[0]); +   close_fd(&proc->fds[1]); +   *proc = (struct proc){0}; +} | 
