text: /*
   * Copyright (C) 2019 Intel Corporation.  All rights reserved.
   * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
   */
  
  #include "bh_common.h"
  #include "bh_log.h"
  #include "wasm_export.h"
  #include "../interpreter/wasm.h"
  
  void
  wasm_runtime_set_exception(wasm_module_inst_t module, const char *exception);
  
  uint32
  wasm_runtime_get_temp_ret(wasm_module_inst_t module);
  
  void
  wasm_runtime_set_temp_ret(wasm_module_inst_t module, uint32 temp_ret);
  
  uint32
  wasm_runtime_get_llvm_stack(wasm_module_inst_t module);
  
  void
  wasm_runtime_set_llvm_stack(wasm_module_inst_t module, uint32 llvm_stack);
  
  #define get_module_inst(exec_env) \
      wasm_runtime_get_module_inst(exec_env)
  
  #define validate_app_addr(offset, size) \
      wasm_runtime_validate_app_addr(module_inst, offset, size)
  
  #define validate_app_str_addr(offset) \
      wasm_runtime_validate_app_str_addr(module_inst, offset)
  
  #define validate_native_addr(addr, size) \
      wasm_runtime_validate_native_addr(module_inst, addr, size)
  
  #define addr_app_to_native(offset) \
      wasm_runtime_addr_app_to_native(module_inst, offset)
  
  #define addr_native_to_app(ptr) \
      wasm_runtime_addr_native_to_app(module_inst, ptr)
  
  #define module_malloc(size, p_native_addr) \
      wasm_runtime_module_malloc(module_inst, size, p_native_addr)
  
  #define module_free(offset) \
      wasm_runtime_module_free(module_inst, offset)
  
  typedef int (*out_func_t)(int c, void *ctx);
  
  enum pad_type {
      PAD_NONE,
      PAD_ZERO_BEFORE,
      PAD_SPACE_BEFORE,
      PAD_SPACE_AFTER,
  };
  
  typedef char *_va_list;
  #define _INTSIZEOF(n)       \
      (((uint32)sizeof(n) +  3) & (uint32)~3)
  #define _va_arg(ap, t)      \
      (*(t*)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)))
  
  #define CHECK_VA_ARG(ap, t) do {                        \
      if ((uint8*)ap + _INTSIZEOF(t) > native_end_addr)   \
          goto fail;                                      \
  } while (0)
  
  /**
   * @brief Output an unsigned int in hex format
   *
   * Output an unsigned int on output installed by platform at init time. Should
   * be able to handle an unsigned int of any size, 32 or 64 bit.
   * @param num Number to output
   *
   * @return N/A
   */
  static void
  _printf_hex_uint(out_func_t out, void *ctx,
                   const uint64 num, bool is_u64,
                   enum pad_type padding,
                   int min_width)
  {
      int shift = sizeof(num) * 8;
      int found_largest_digit = 0;
      int remaining = 16; /* 16 digits max */
      int digits = 0;
      char nibble;
  
       while (shift >= 4) {
           shift -= 4;
           nibble = (num >> shift) & 0xf;
  
          if (nibble || found_largest_digit || shift == 0) {
              found_largest_digit = 1;
              nibble = (char)(nibble + (nibble > 9 ? 87 : 48));
              out((int) nibble, ctx);
              digits++;
              continue;
          }
  
          if (remaining-- <= min_width) {
              if (padding == PAD_ZERO_BEFORE) {
                  out('0', ctx);
              } else if (padding == PAD_SPACE_BEFORE) {
                  out(' ', ctx);
              }
          }
      }
  
      if (padding == PAD_SPACE_AFTER) {
          remaining = min_width * 2 - digits;
          while (remaining-- > 0) {
              out(' ', ctx);
          }
      }
  }
  
  /**
   * @brief Output an unsigned int in decimal format
   *
   * Output an unsigned int on output installed by platform at init time. Only
   * works with 32-bit values.
   * @param num Number to output
   *
   * @return N/A
   */
  static void
  _printf_dec_uint(out_func_t out, void *ctx,
                   const uint32 num,
                   enum pad_type padding,
                   int min_width)
  {
      uint32 pos = 999999999;
      uint32 remainder = num;
      int found_largest_digit = 0;
      int remaining = 10; /* 10 digits max */
      int digits = 1;
  
      /* make sure we don't skip if value is zero */
      if (min_width <= 0) {
          min_width = 1;
      }
  
      while (pos >= 9) {
          if (found_largest_digit || remainder > pos) {
              found_largest_digit = 1;
              out((int) ((remainder / (pos + 1)) + 48), ctx);
              digits++;
          } else if (remaining <= min_width && padding < PAD_SPACE_AFTER) {
              out((int) (padding == PAD_ZERO_BEFORE ? '0' : ' '), ctx);
              digits++;
          }
          remaining--;
          remainder %= (pos + 1);
          pos /= 10;
      }
      out((int) (remainder + 48), ctx);
  
      if (padding == PAD_SPACE_AFTER) {
          remaining = min_width - digits;
          while (remaining-- > 0) {
              out(' ', ctx);
          }
      }
  }
  
  static void
  print_err(out_func_t out, void *ctx)
  {
      out('E', ctx);
      out('R', ctx);
      out('R', ctx);
  }
  
  static bool
  _vprintf_wa(out_func_t out, void *ctx, const char *fmt, _va_list ap,
              wasm_module_inst_t module_inst)
  {
      int might_format = 0; /* 1 if encountered a '%' */
      enum pad_type padding = PAD_NONE;
      int min_width = -1;
      int long_ctr = 0;
      uint8 *native_end_addr;
  
      if (!wasm_runtime_get_native_addr_range(module_inst, (uint8*)ap,
                                              NULL, &native_end_addr))
          goto fail;
  
      /* fmt has already been adjusted if needed */
  
      while (*fmt) {
          if (!might_format) {
              if (*fmt != '%') {
                  out((int) *fmt, ctx);
              }
              else {
                  might_format = 1;
                  min_width = -1;
                  padding = PAD_NONE;
                  long_ctr = 0;
              }
          }
          else {
              switch (*fmt) {
              case '-':
                  padding = PAD_SPACE_AFTER;
                  goto still_might_format;
  
              case '0':
                  if (min_width < 0 && padding == PAD_NONE) {
                      padding = PAD_ZERO_BEFORE;
                      goto still_might_format;
                  }
                  /* Fall through */
              case '1' ... '9':
                  if (min_width < 0) {
                      min_width = *fmt - '0';
                  } else {
                      min_width = 10 * min_width + *fmt - '0';
                  }
  
                  if (padding == PAD_NONE) {
                      padding = PAD_SPACE_BEFORE;
                  }
                  goto still_might_format;
  
              case 'l':
                  long_ctr++;
                  /* Fall through */
              case 'z':
              case 'h':
                  /* FIXME: do nothing for these modifiers */
                  goto still_might_format;
  
              case 'd':
              case 'i': {
                  int32 d;
  
                  if (long_ctr < 2) {
                      CHECK_VA_ARG(ap, int32);
                      d = _va_arg(ap, int32);
                  }
                  else {
                      int64 lld;
                      CHECK_VA_ARG(ap, int64);
                      lld = _va_arg(ap, int64);
                      if (lld > INT32_MAX || lld < INT32_MIN) {
                          print_err(out, ctx);
                          break;
                      }
                      d = (int32)lld;
                  }
  
                  if (d < 0) {
                      out((int)'-', ctx);
                      d = -d;
                      min_width--;
                  }
                  _printf_dec_uint(out, ctx, (uint32)d, padding, min_width);
                  break;
              }
              case 'u': {
                  uint32 u;
  
                  if (long_ctr < 2) {
                      CHECK_VA_ARG(ap, uint32);
                      u = _va_arg(ap, uint32);
                  }
                  else {
                      uint64 llu;
                      CHECK_VA_ARG(ap, uint64);
                      llu = _va_arg(ap, uint64);
                      if (llu > INT32_MAX) {
                          print_err(out, ctx);
                          break;
                      }
                      u = (uint32)llu;
                  }
                  _printf_dec_uint(out, ctx, u, padding, min_width);
                  break;
              }
              case 'p':
                  out('0', ctx);
                  out('x', ctx);
                  /* left-pad pointers with zeros */
                  padding = PAD_ZERO_BEFORE;
                  min_width = 8;
                  /* Fall through */
              case 'x':
              case 'X': {
                  uint64 x;
                  bool is_ptr = (*fmt == 'p') ? true : false;
  
                  if (long_ctr < 2) {
                      CHECK_VA_ARG(ap, uint32);
                      x = _va_arg(ap, uint32);
                  } else {
                      CHECK_VA_ARG(ap, uint64);
                      x = _va_arg(ap, uint64);
                  }
                  _printf_hex_uint(out, ctx, x, !is_ptr, padding, min_width);
                  break;
              }
  
              case 's': {
                  char *s;
                  char *start;
                  int32 s_offset;
  
                  CHECK_VA_ARG(ap, int32);
                  s_offset = _va_arg(ap, int32);
  
                  if (!validate_app_str_addr(s_offset)) {
                      return false;
                  }
  
                  s = start = addr_app_to_native(s_offset);
  
                  while (*s)
                      out((int) (*s++), ctx);
  
                  if (padding == PAD_SPACE_AFTER) {
                      int remaining = min_width - (int32)(s - start);
                      while (remaining-- > 0) {
                          out(' ', ctx);
                      }
                  }
                  break;
              }
  
              case 'c': {
                  int c;
                  CHECK_VA_ARG(ap, int);
                  c = _va_arg(ap, int);
                  out(c, ctx);
                  break;
              }
  
              case '%': {
                  out((int) '%', ctx);
                  break;
              }
  
              default:
                  out((int) '%', ctx);
                  out((int) *fmt, ctx);
                  break;
              }
  
              might_format = 0;
          }
  
  still_might_format:
          ++fmt;
      }
      return true;
  
  fail:
      wasm_runtime_set_exception(module_inst, "out of bounds memory access");
      return false;
  }
  
  struct str_context {
      char *str;
      uint32 max;
      uint32 count;
  };
  
  static int
  sprintf_out(int c, struct str_context *ctx)
  {
      if (!ctx->str || ctx->count >= ctx->max) {
          ctx->count++;
          return c;
      }
  
      if (ctx->count == ctx->max - 1) {
          ctx->str[ctx->count++] = '\0';
      } else {
          ctx->str[ctx->count++] = (char)c;
      }
  
      return c;
  }
  
  static int
  printf_out(int c, struct str_context *ctx)
  {
      bh_printf("%c", c);
      ctx->count++;
      return c;
  }
  
  static int
  printf_wrapper(wasm_exec_env_t exec_env,
                 const char * format, _va_list va_args)
  {
      wasm_module_inst_t module_inst = get_module_inst(exec_env);
      struct str_context ctx = { NULL, 0, 0 };
  
      /* format has been checked by runtime */
      if (!validate_native_addr(va_args, sizeof(int32)))
          return 0;
  
      if (!_vprintf_wa((out_func_t)printf_out, &ctx, format, va_args, module_inst))
          return 0;
  
      return (int)ctx.count;
  }
  
  static int
  sprintf_wrapper(wasm_exec_env_t exec_env,
                  char *str, const char *format, _va_list va_args)
  {
      wasm_module_inst_t module_inst = get_module_inst(exec_env);
      uint8 *native_end_offset;
      struct str_context ctx;
  
      /* str and format have been checked by runtime */
      if (!validate_native_addr(va_args, sizeof(uint32)))
          return 0;
  
      if (!wasm_runtime_get_native_addr_range(module_inst, (uint8*)str,
                                              NULL, &native_end_offset)) {
          wasm_runtime_set_exception(module_inst, "out of bounds memory access");
          return false;
      }
  
      ctx.str = str;
      ctx.max = (uint32)(native_end_offset - (uint8*)str);
      ctx.count = 0;
  
      if (!_vprintf_wa((out_func_t)sprintf_out, &ctx, format, va_args, module_inst))
          return 0;
  
      if (ctx.count < ctx.max) {
          str[ctx.count] = '\0';
      }
  
      return (int)ctx.count;
  }
  
  static int
  snprintf_wrapper(wasm_exec_env_t exec_env, char *str, uint32 size,
                   const char *format, _va_list va_args)
  {
      wasm_module_inst_t module_inst = get_module_inst(exec_env);
      struct str_context ctx;
  
      /* str and format have been checked by runtime */
      if (!validate_native_addr(va_args, sizeof(uint32)))
          return 0;
  
      ctx.str = str;
      ctx.max = size;
      ctx.count = 0;
  
      if (!_vprintf_wa((out_func_t)sprintf_out, &ctx, format, va_args, module_inst))
          return 0;
  
      if (ctx.count < ctx.max) {
          str[ctx.count] = '\0';
      }
  
      return (int)ctx.count;
  }
  
  static int
  puts_wrapper(wasm_exec_env_t exec_env, const char *str)
  {
      return bh_printf("%s\n", str);
  }
  
  static int
  putchar_wrapper(wasm_exec_env_t exec_env, int c)
  {
      bh_printf("%c", c);
      return 1;
  }
  
  static int32
  strdup_wrapper(wasm_exec_env_t exec_env, const char *str)
  {
      wasm_module_inst_t module_inst = get_module_inst(exec_env);
      char *str_ret;
      uint32 len;
      int32 str_ret_offset = 0;
  
      /* str has been checked by runtime */
      if (str) {
          len = (uint32)strlen(str) + 1;
  
          str_ret_offset = module_malloc(len, (void**)&str_ret);
          if (str_ret_offset) {
              bh_memcpy_s(str_ret, len, str, len);
          }
      }
  
      return str_ret_offset;
  }
  
  static int32
  _strdup_wrapper(wasm_exec_env_t exec_env, const char *str)
  {
      return strdup_wrapper(exec_env, str);
  }
  
  static int32
  memcmp_wrapper(wasm_exec_env_t exec_env,
                 const void *s1, const void *s2, uint32 size)
  {
      wasm_module_inst_t module_inst = get_module_inst(exec_env);
  
      /* s2 has been checked by runtime */
      if (!validate_native_addr((void*)s1, size))
          return 0;
  
      return memcmp(s1, s2, size);
  }
  
  static int32
  memcpy_wrapper(wasm_exec_env_t exec_env,
                 void *dst, const void *src, uint32 size)
  {
      wasm_module_inst_t module_inst = get_module_inst(exec_env);
      int32 dst_offset = addr_native_to_app(dst);
  
      if (size == 0)
          return dst_offset;
  
      /* src has been checked by runtime */
      if (!validate_native_addr(dst, size))
          return dst_offset;
  
      bh_memcpy_s(dst, size, src, size);
      return dst_offset;
  }
  
  static int32
  memmove_wrapper(wasm_exec_env_t exec_env,
                  void *dst, void *src, uint32 size)
  {
      wasm_module_inst_t module_inst = get_module_inst(exec_env);
      int32 dst_offset = addr_native_to_app(dst);
  
      if (size == 0)
          return dst_offset;
  
      /* src has been checked by runtime */
      if (!validate_native_addr(dst, size))
          return dst_offset;
  
      memmove(dst, src, size);
      return dst_offset;
  }
  
  static int32
  memset_wrapper(wasm_exec_env_t exec_env,
                 void *s, int32 c, uint32 size)
  {
      wasm_module_inst_t module_inst = get_module_inst(exec_env);
      int32 s_offset = addr_native_to_app(s);
  
      if (!validate_native_addr(s, size))
          return s_offset;
  
      memset(s, c, size);
      return s_offset;
  }
  
  static int32
  strchr_wrapper(wasm_exec_env_t exec_env,
                 const char *s, int32 c)
  {
      wasm_module_inst_t module_inst = get_module_inst(exec_env);
      char *ret;
  
      /* s has been checked by runtime */
      ret = strchr(s, c);
      return ret ? addr_native_to_app(ret) : 0;
  }
  
  static int32
  strcmp_wrapper(wasm_exec_env_t exec_env,
                 const char *s1, const char *s2)
  {
      /* s1 and s2 have been checked by runtime */
      return strcmp(s1, s2);
  }
  
  static int32
  strncmp_wrapper(wasm_exec_env_t exec_env,
                  const char *s1, const char *s2, uint32 size)
  {
      wasm_module_inst_t module_inst = get_module_inst(exec_env);
  
      /* s2 has been checked by runtime */
      if (!validate_native_addr((void*)s1, size))
          return 0;
  
      return strncmp(s1, s2, size);
  }
  
  static int32
  strcpy_wrapper(wasm_exec_env_t exec_env, char *dst, const char *src)
  {
      wasm_module_inst_t module_inst = get_module_inst(exec_env);
      uint32 len = strlen(src) + 1;
  
      /* src has been checked by runtime */
      if (!validate_native_addr(dst, len))
          return 0;
  
      strncpy(dst, src, len);
      return addr_native_to_app(dst);
  }
  
  static int32
  strncpy_wrapper(wasm_exec_env_t exec_env,
                  char *dst, const char *src, uint32 size)
  {
      wasm_module_inst_t module_inst = get_module_inst(exec_env);
  
      /* src has been checked by runtime */
      if (!validate_native_addr(dst, size))
          return 0;
  
      strncpy(dst, src, size);
      return addr_native_to_app(dst);
  }
  
  static uint32
  strlen_wrapper(wasm_exec_env_t exec_env, const char *s)
  {
      /* s has been checked by runtime */
      return (uint32)strlen(s);
  }
  
  static int32
  malloc_wrapper(wasm_exec_env_t exec_env, uint32 size)
  {
      wasm_module_inst_t module_inst = get_module_inst(exec_env);
      return module_malloc(size, NULL);
  }
  
  static int32
  calloc_wrapper(wasm_exec_env_t exec_env, uint32 nmemb, uint32 size)
  {
      wasm_module_inst_t module_inst = get_module_inst(exec_env);
      uint64 total_size = (uint64) nmemb * (uint64) size;
      int32 ret_offset = 0;
      uint8 *ret_ptr;
  
      if (total_size >= UINT32_MAX)
          return 0;
  
      ret_offset = module_malloc((uint32)total_size, (void**)&ret_ptr);
      if (ret_offset) {
          memset(ret_ptr, 0, (uint32) total_size);
      }
  
      return ret_offset;
  }
  
  static void
  free_wrapper(wasm_exec_env_t exec_env, void *ptr)
  {
      wasm_module_inst_t module_inst = get_module_inst(exec_env);
  
      if (!validate_native_addr(ptr, sizeof(uint32)))
          return;
  
      return module_free(addr_native_to_app(ptr));
  }
  
  static int32
  atoi_wrapper(wasm_exec_env_t exec_env, const char *s)
  {
      /* s has been checked by runtime */
      return atoi(s);
  }
  
  static void
  exit_wrapper(wasm_exec_env_t exec_env, int32 status)
  {
      wasm_module_inst_t module_inst = get_module_inst(exec_env);
      char buf[32];
      snprintf(buf, sizeof(buf), "env.exit(%i)", status);
      wasm_runtime_set_exception(module_inst, buf);
  }
  
  static int32
  strtol_wrapper(wasm_exec_env_t exec_env,
                 const char *nptr, char **endptr, int32 base)
  {
      wasm_module_inst_t module_inst = get_module_inst(exec_env);
      int32 num = 0;
  
      /* nptr has been checked by runtime */
      if (!validate_native_addr(endptr, sizeof(uint32)))
          return 0;
  
      num = (int32)strtol(nptr, endptr, base);
      *(int32*)endptr = addr_native_to_app(*endptr);
  
      return num;
  }
  
  static uint32
  strtoul_wrapper(wasm_exec_env_t exec_env,
                  const char *nptr, char **endptr, int32 base)
  {
      wasm_module_inst_t module_inst = get_module_inst(exec_env);
      uint32 num = 0;
  
      /* nptr has been checked by runtime */
      if (!validate_native_addr(endptr, sizeof(uint32)))
          return 0;
  
      num = (uint32)strtoul(nptr, endptr, base);
      *(int32 *)endptr = addr_native_to_app(*endptr);
  
      return num;
  }
  
  static int32
  memchr_wrapper(wasm_exec_env_t exec_env,
                 const void *s, int32 c, uint32 n)
  {
      wasm_module_inst_t module_inst = get_module_inst(exec_env);
      void *res;
  
      if (!validate_native_addr((void*)s, n))
          return 0;
  
      res = memchr(s, c, n);
      return addr_native_to_app(res);
  }
  
  static int32
  strncasecmp_wrapper(wasm_exec_env_t exec_env,
                      const char *s1, const char *s2, int32 n)
  {
      /* s1 and s2 have been checked by runtime */
      return strncasecmp(s1, s2, n);
  }
  
  static uint32
  strspn_wrapper(wasm_exec_env_t exec_env,
                 const char *s, const char *accept)
  {
      /* s and accept have been checked by runtime */
      return (uint32)strspn(s, accept);
  }
  
  static uint32
  strcspn_wrapper(wasm_exec_env_t exec_env,
                  const char *s, const char *reject)
  {
      /* s and reject have been checked by runtime */
      return (uint32)strcspn(s, reject);
  }
  
  static int32
  strstr_wrapper(wasm_exec_env_t exec_env,
                 const char *s, const char *find)
  {
      wasm_module_inst_t module_inst = get_module_inst(exec_env);
      /* s and find have been checked by runtime */
      char *res = strstr(s, find);
      return addr_native_to_app(res);
  }
  
  static int32
  isupper_wrapper(wasm_exec_env_t exec_env, int32 c)
  {
      return isupper(c);
  }
  
  static int32
  isalpha_wrapper(wasm_exec_env_t exec_env, int32 c)
  {
      return isalpha(c);
  }
  
  static int32
  isspace_wrapper(wasm_exec_env_t exec_env, int32 c)
  {
      return isspace(c);
  }
  
  static int32
  isgraph_wrapper(wasm_exec_env_t exec_env, int32 c)
  {
      return isgraph(c);
  }
  
  static int32
  isprint_wrapper(wasm_exec_env_t exec_env, int32 c)
  {
      return isprint(c);
  }
  
  static int32
  isdigit_wrapper(wasm_exec_env_t exec_env, int32 c)
  {
      return isdigit(c);
  }
  
  static int32
  isxdigit_wrapper(wasm_exec_env_t exec_env, int32 c)
  {
      return isxdigit(c);
  }
  
  static int32
  tolower_wrapper(wasm_exec_env_t exec_env, int32 c)
  {
      return tolower(c);
  }
  
  static int32
  toupper_wrapper(wasm_exec_env_t exec_env, int32 c)
  {
      return toupper(c);
  }
  
  static int32
  isalnum_wrapper(wasm_exec_env_t exec_env, int32 c)
  {
      return isalnum(c);
  }
  
  static void
  setTempRet0_wrapper(wasm_exec_env_t exec_env, uint32 temp_ret)
  {
      wasm_module_inst_t module_inst = get_module_inst(exec_env);
      wasm_runtime_set_temp_ret(module_inst, temp_ret);
  }
  
  static uint32
  getTempRet0_wrapper(wasm_exec_env_t exec_env)
  {
      wasm_module_inst_t module_inst = get_module_inst(exec_env);
      return wasm_runtime_get_temp_ret(module_inst);
  }
  
  static uint32
  llvm_bswap_i16_wrapper(wasm_exec_env_t exec_env, uint32 data)
  {
      return (data & 0xFFFF0000)
             | ((data & 0xFF) << 8)
             | ((data & 0xFF00) >> 8);
  }
  
  static uint32
  llvm_bswap_i32_wrapper(wasm_exec_env_t exec_env, uint32 data)
  {
      return ((data & 0xFF) << 24)
             | ((data & 0xFF00) << 8)
             | ((data & 0xFF0000) >> 8)
             | ((data & 0xFF000000) >> 24);
  }
  
  static uint32
  bitshift64Lshr_wrapper(wasm_exec_env_t exec_env,
                         uint32 uint64_part0, uint32 uint64_part1,
                         uint32 bits)
  {
      wasm_module_inst_t module_inst = get_module_inst(exec_env);
      union {
          uint64 value;
          uint32 parts[2];
      } u;
  
      u.parts[0] = uint64_part0;
      u.parts[1] = uint64_part1;
  
      u.value >>= bits;
      /* return low 32bit and save high 32bit to temp ret */
      wasm_runtime_set_temp_ret(module_inst, (uint32) (u.value >> 32));
      return (uint32) u.value;
  }
  
  static uint32
  bitshift64Shl_wrapper(wasm_exec_env_t exec_env,
                        uint32 int64_part0, uint32 int64_part1,
                        uint32 bits)
  {
      wasm_module_inst_t module_inst = get_module_inst(exec_env);
      union {
          int64 value;
          uint32 parts[2];
      } u;
  
      u.parts[0] = int64_part0;
      u.parts[1] = int64_part1;
  
      u.value <<= bits;
      /* return low 32bit and save high 32bit to temp ret */
      wasm_runtime_set_temp_ret(module_inst, (uint32) (u.value >> 32));
      return (uint32) u.value;
  }
  
  static void
  llvm_stackrestore_wrapper(wasm_exec_env_t exec_env, uint32 llvm_stack)
  {
      wasm_module_inst_t module_inst = get_module_inst(exec_env);
      bh_printf("_llvm_stackrestore called!\n");
      wasm_runtime_set_llvm_stack(module_inst, llvm_stack);
  }
  
  static uint32
  llvm_stacksave_wrapper(wasm_exec_env_t exec_env)
  {
      wasm_module_inst_t module_inst = get_module_inst(exec_env);
      bh_printf("_llvm_stacksave called!\n");
      return wasm_runtime_get_llvm_stack(module_inst);
  }
  
  static int32
  emscripten_memcpy_big_wrapper(wasm_exec_env_t exec_env,
                                void *dst, const void *src, uint32 size)
  {
      wasm_module_inst_t module_inst = get_module_inst(exec_env);
      int32 dst_offset = addr_native_to_app(dst);
  
      /* src has been checked by runtime */
      if (!validate_native_addr(dst, size))
          return dst_offset;
  
      bh_memcpy_s(dst, size, src, size);
      return dst_offset;
  }
  
  static void
  abort_wrapper(wasm_exec_env_t exec_env, int32 code)
  {
      wasm_module_inst_t module_inst = get_module_inst(exec_env);
      char buf[32];
      snprintf(buf, sizeof(buf), "env.abort(%i)", code);
      wasm_runtime_set_exception(module_inst, buf);
  }
  
  static void
  abortStackOverflow_wrapper(wasm_exec_env_t exec_env, int32 code)
  {
      wasm_module_inst_t module_inst = get_module_inst(exec_env);
      char buf[32];
      snprintf(buf, sizeof(buf), "env.abortStackOverflow(%i)", code);
      wasm_runtime_set_exception(module_inst, buf);
  }
  
  static void
  nullFunc_X_wrapper(wasm_exec_env_t exec_env, int32 code)
  {
      wasm_module_inst_t module_inst = get_module_inst(exec_env);
      char buf[32];
      snprintf(buf, sizeof(buf), "env.nullFunc_X(%i)", code);
      wasm_runtime_set_exception(module_inst, buf);
  }
  
  static int32
  __cxa_allocate_exception_wrapper(wasm_exec_env_t exec_env,
                                   uint32 thrown_size)
  {
      wasm_module_inst_t module_inst = get_module_inst(exec_env);
      int32 exception = module_malloc(thrown_size, NULL);
      if (!exception)
          return 0;
  
      return exception;
  }
  
  static void
  __cxa_begin_catch_wrapper(wasm_exec_env_t exec_env,
                            void *exception_object)
  {
  }
  
  static void
  __cxa_throw_wrapper(wasm_exec_env_t exec_env,
                      void *thrown_exception,
                      void *tinfo,
                      uint32 table_elem_idx)
  {
      wasm_module_inst_t module_inst = get_module_inst(exec_env);
      char buf[32];
  
      snprintf(buf, sizeof(buf), "%s", "exception thrown by stdc++");
      wasm_runtime_set_exception(module_inst, buf);
  }
  
  static void
  print_i32_wrapper(wasm_exec_env_t exec_env, int32 i32)
  {
      bh_printf("%d\n", i32);
  }
  
  #define REG_NATIVE_FUNC(func_name, signature)  \
      { #func_name, func_name##_wrapper, signature }
  
  static NativeSymbol native_symbols_libc_builtin[] = {
      REG_NATIVE_FUNC(printf, "($*)i"),
      REG_NATIVE_FUNC(sprintf, "($$*)i"),
      REG_NATIVE_FUNC(snprintf, "(*~$*)i"),
      REG_NATIVE_FUNC(puts, "($)i"),
      REG_NATIVE_FUNC(putchar, "(i)i"),
      REG_NATIVE_FUNC(memcmp, "(**~)i"),
      REG_NATIVE_FUNC(memcpy, "(**~)i"),
      REG_NATIVE_FUNC(memmove, "(**~)i"),
      REG_NATIVE_FUNC(memset, "(*ii)i"),
      REG_NATIVE_FUNC(strchr, "($i)i"),
      REG_NATIVE_FUNC(strcmp, "($$)i"),
      REG_NATIVE_FUNC(strcpy, "(*$)i"),
      REG_NATIVE_FUNC(strlen, "($)i"),
      REG_NATIVE_FUNC(strncmp, "(**~)i"),
      REG_NATIVE_FUNC(strncpy, "(**~)i"),
      REG_NATIVE_FUNC(malloc, "(i)i"),
      REG_NATIVE_FUNC(calloc, "(ii)i"),
      REG_NATIVE_FUNC(strdup, "($)i"),
      /* clang may introduce __strdup */
      REG_NATIVE_FUNC(_strdup, "($)i"),
      REG_NATIVE_FUNC(free, "(*)"),
      REG_NATIVE_FUNC(atoi, "($)i"),
      REG_NATIVE_FUNC(exit, "(i)"),
      REG_NATIVE_FUNC(strtol, "($*i)i"),
      REG_NATIVE_FUNC(strtoul, "($*i)i"),
      REG_NATIVE_FUNC(memchr, "(*ii)"),
      REG_NATIVE_FUNC(strncasecmp, "($$i)"),
      REG_NATIVE_FUNC(strspn, "($$)i"),
      REG_NATIVE_FUNC(strcspn, "($$)i"),
      REG_NATIVE_FUNC(strstr, "($$)i"),
      REG_NATIVE_FUNC(isupper, "(i)i"),
      REG_NATIVE_FUNC(isalpha, "(i)i"),
      REG_NATIVE_FUNC(isspace, "(i)i"),
      REG_NATIVE_FUNC(isgraph, "(i)i"),
      REG_NATIVE_FUNC(isprint, "(i)i"),
      REG_NATIVE_FUNC(isdigit, "(i)i"),
      REG_NATIVE_FUNC(isxdigit, "(i)i"),
      REG_NATIVE_FUNC(tolower, "(i)i"),
      REG_NATIVE_FUNC(toupper, "(i)i"),
      REG_NATIVE_FUNC(isalnum, "(i)i"),
      REG_NATIVE_FUNC(setTempRet0, "(i)"),
      REG_NATIVE_FUNC(getTempRet0, "()i"),
      REG_NATIVE_FUNC(llvm_bswap_i16, "(i)i"),
      REG_NATIVE_FUNC(llvm_bswap_i32, "(i)i"),
      REG_NATIVE_FUNC(bitshift64Lshr, "(iii)i"),
      REG_NATIVE_FUNC(bitshift64Shl, "(iii)i"),
      REG_NATIVE_FUNC(llvm_stackrestore, "(i)"),
      REG_NATIVE_FUNC(llvm_stacksave, "()i"),
      REG_NATIVE_FUNC(emscripten_memcpy_big, "(**~)i"),
      REG_NATIVE_FUNC(abort, "(i)"),
      REG_NATIVE_FUNC(abortStackOverflow, "(i)"),
      REG_NATIVE_FUNC(nullFunc_X, "(i)"),
      REG_NATIVE_FUNC(__cxa_allocate_exception, "(i)i"),
      REG_NATIVE_FUNC(__cxa_begin_catch, "(*)"),
      REG_NATIVE_FUNC(__cxa_throw, "(**i)")
  };
  
  static NativeSymbol native_symbols_spectest[] = {
      REG_NATIVE_FUNC(print_i32, "(i)")
  };
  
  uint32
  get_libc_builtin_export_apis(NativeSymbol **p_libc_builtin_apis)
  {
      *p_libc_builtin_apis = native_symbols_libc_builtin;
      return sizeof(native_symbols_libc_builtin) / sizeof(NativeSymbol);
  }
  
  uint32
  get_spectest_export_apis(NativeSymbol **p_libc_builtin_apis)
  {
      *p_libc_builtin_apis = native_symbols_spectest;
      return sizeof(native_symbols_spectest) / sizeof(NativeSymbol);
  }
  
  /*************************************
   * Global Variables                  *
   *************************************/
  
  typedef struct WASMNativeGlobalDef {
      const char *module_name;
      const char *global_name;
      WASMValue global_data;
  } WASMNativeGlobalDef;
  
  static WASMNativeGlobalDef native_global_defs[] = {
      { "spectest", "global_i32", .global_data.i32 = 666 },
      { "spectest", "global_f32", .global_data.f32 = 0 },
      { "spectest", "global_f64", .global_data.f64 = 0 },
      { "test", "global-i32", .global_data.i32 = 0 },
      { "test", "global-f32", .global_data.f32 = 0 },
      { "env", "STACKTOP", .global_data.u32 = 0 },
      { "env", "STACK_MAX", .global_data.u32 = 0 },
      { "env", "ABORT", .global_data.u32 = 0 },
      { "env", "memoryBase", .global_data.u32 = 0 },
      { "env", "__memory_base", .global_data.u32 = 0 },
      { "env", "tableBase", .global_data.u32 = 0 },
      { "env", "__table_base", .global_data.u32 = 0 },
      { "env", "DYNAMICTOP_PTR", .global_data.addr = 0 },
      { "env", "tempDoublePtr", .global_data.addr = 0 },
      { "global", "NaN", .global_data.u64 = 0x7FF8000000000000LL },
      { "global", "Infinity", .global_data.u64 = 0x7FF0000000000000LL }
  };
  
  bool
  wasm_native_lookup_libc_builtin_global(const char *module_name,
                                         const char *global_name,
                                         WASMGlobalImport *global)
  {
      uint32 size = sizeof(native_global_defs) / sizeof(WASMNativeGlobalDef);
      WASMNativeGlobalDef *global_def = native_global_defs;
      WASMNativeGlobalDef *global_def_end = global_def + size;
  
      if (!module_name || !global_name || !global)
          return false;
  
      /* Lookup constant globals which can be defined by table */
      while (global_def < global_def_end) {
          if (!strcmp(global_def->module_name, module_name)
              && !strcmp(global_def->global_name, global_name)) {
              global->global_data_linked = global_def->global_data;
              return true;
          }
          global_def++;
      }
  
      return false;
  }
  
  