text: /*
   * Author: Landon Fuller <landonf@plausiblelabs.com>
   *
   * Copyright (c) 2008-2013 Plausible Labs Cooperative, Inc.
   * All rights reserved.
   *
   * Permission is hereby granted, free of charge, to any person
   * obtaining a copy of this software and associated documentation
   * files (the "Software"), to deal in the Software without
   * restriction, including without limitation the rights to use,
   * copy, modify, merge, publish, distribute, sublicense, and/or sell
   * copies of the Software, and to permit persons to whom the
   * Software is furnished to do so, subject to the following
   * conditions:
   *
   * The above copyright notice and this permission notice shall be
   * included in all copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
   * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
   * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
   * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
   * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
   * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
   * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
   * OTHER DEALINGS IN THE SOFTWARE.
   */
  
  #include "PLCrashAsyncThread.h"
  #include "PLCrashAsync.h"
  
  #include <signal.h>
  #include <assert.h>
  #include <stdlib.h>
  
  #define RETGEN(name, type, ts) {\
      return (ts->x86_state. type . __ ## name); \
  }
  
  #define SETGEN(name, type, ts, regnum, value) {\
      ts->valid_regs |= 1ULL<<regnum; \
      (ts->x86_state. type . __ ## name) = value; \
      break; \
  }
  
  #if defined(__i386__) || defined(__x86_64__)
  
  /* Mapping of DWARF register numbers to PLCrashReporter register numbers. */
  struct dwarf_register_table {
      /** Standard register number. */
      plcrash_regnum_t regnum;
  
      /** DWARF register number. */
      uint64_t dwarf_value;
  };
  
  
  /*
   * i386 GP registers defined as callee-preserved, as per Apple's Mac OS X IA-32
   * Function Call Guide
   */
  static const plcrash_regnum_t x86_32_nonvolatile_registers[] = {
      PLCRASH_X86_EBX,
      PLCRASH_X86_EBP,
      PLCRASH_X86_ESI,
      PLCRASH_X86_EDI,
      PLCRASH_X86_ESP
  };
  
  /*
   * x86-64 GP registers defined as callee-preserved, as per System V Application Binary Interface,
   * AMD64 Architecture Processor Supplement - Draft Version 0.99.6
   */
  static const plcrash_regnum_t x86_64_nonvolatile_registers[] = {
      PLCRASH_X86_64_RBX,
      PLCRASH_X86_64_RSP,
      PLCRASH_X86_64_RBP,
      PLCRASH_X86_64_R12,
      PLCRASH_X86_64_R13,
      PLCRASH_X86_64_R14,
      PLCRASH_X86_64_R15
  };
  
  /*
   * i386 GCC eh_frame register mappings as defined by GCC and LLVM/clang. These mappings
   * appear to have originally been defined by the SVR4 reference port C compiler,
   * and then later implemented by GCC for its 80386 target.
   *
   * This set of registers defines the common intersection of the gcc/llvm implementations.
   * It appears that LLVM implements a strict subset of the GCC-defined register set, but
   * further investigation is warranted prior to expanding the set of defined DWARF registers.
   *
   * Note that not all registers defined by gcc/LLVM are currently supported by our
   * thread-state API, and are not mapped.
   *
   * @warning These mappings are not accurate for use in DWARF debug_frame.
   */
  static const struct dwarf_register_table x86_32_dwarf_table [] = {
      { PLCRASH_X86_EAX, 0 },
      { PLCRASH_X86_ECX, 1 },
      { PLCRASH_X86_EDX, 2 },
      { PLCRASH_X86_EBX, 3 },
      { PLCRASH_X86_EBP, 4 },
      { PLCRASH_X86_ESP, 5 },
      { PLCRASH_X86_ESI, 6 },
      { PLCRASH_X86_EDI, 7 },
      { PLCRASH_X86_EIP, 8 }
  };
  
  /*
   * x86-64 DWARF register mappings as defined in the System V Application Binary Interface,
   * AMD64 Architecture Processor Supplement - Draft Version 0.99.6
   *
   * Note that not all registers defined the AMD64 ABI are currently supported by our
   * thread-state API, and are not mapped.
   */
  static const struct dwarf_register_table x86_64_dwarf_table [] = {
      { PLCRASH_X86_64_RAX,  0 },
      { PLCRASH_X86_64_RDX,  1 },
      { PLCRASH_X86_64_RCX,  2 },
      { PLCRASH_X86_64_RBX,  3 },
      { PLCRASH_X86_64_RSI,  4 },
      { PLCRASH_X86_64_RDI,  5 },
      { PLCRASH_X86_64_RBP,  6 },
      { PLCRASH_X86_64_RSP,  7 },
      
      { PLCRASH_X86_64_R8,   8 },
      { PLCRASH_X86_64_R9,   9 },
      { PLCRASH_X86_64_R10, 10 },
      { PLCRASH_X86_64_R11, 11 },
      { PLCRASH_X86_64_R12, 12 },
      { PLCRASH_X86_64_R13, 13 },
      { PLCRASH_X86_64_R14, 14 },
      { PLCRASH_X86_64_R15, 15 },
      
      { PLCRASH_X86_64_RFLAGS, 49 },
      
      { PLCRASH_X86_64_CS, 51 },
      { PLCRASH_X86_64_FS, 54 },
      { PLCRASH_X86_64_GS, 55 }
  };
  
  static plcrash_greg_t plcrash_async_thread_state_get_reg_32 (const plcrash_async_thread_state_t *thread_state, plcrash_regnum_t regnum);
  static plcrash_greg_t plcrash_async_thread_state_get_reg_64 (const plcrash_async_thread_state_t *thread_state, plcrash_regnum_t regnum);
  
  static void plcrash_async_thread_state_set_reg_32 (plcrash_async_thread_state_t *cursor, plcrash_regnum_t regnum, plcrash_greg_t reg);
  static void plcrash_async_thread_state_set_reg_64 (plcrash_async_thread_state_t *cursor, plcrash_regnum_t regnum, plcrash_greg_t reg);
  
  static const char *plcrash_async_thread_state_get_regname_32 (plcrash_regnum_t regnum);
  static const char *plcrash_async_thread_state_get_regname_64 (plcrash_regnum_t regnum);
  
  plcrash_regnum_t plcrash_async_thread_state_map_dwarf_reg_32 (uint64_t dwarf_reg, plcrash_regnum_t *regnum);
  plcrash_regnum_t plcrash_async_thread_state_map_dwarf_reg_64 (uint64_t dwarf_reg, plcrash_regnum_t *regnum);
  
  // PLCrashAsyncThread API
  plcrash_greg_t plcrash_async_thread_state_get_reg (const plcrash_async_thread_state_t *thread_state, plcrash_regnum_t regnum) {
      if (thread_state->x86_state.thread.tsh.flavor == x86_THREAD_STATE32) {
          return plcrash_async_thread_state_get_reg_32(thread_state, regnum);
      } else {
          return plcrash_async_thread_state_get_reg_64(thread_state, regnum);
      }
  }
  
  // PLCrashAsyncThread API
  void plcrash_async_thread_state_set_reg (plcrash_async_thread_state_t *thread_state, plcrash_regnum_t regnum, plcrash_greg_t reg) {
      if (thread_state->x86_state.thread.tsh.flavor == x86_THREAD_STATE32) {
          return plcrash_async_thread_state_set_reg_32(thread_state, regnum, reg);
      } else {
          return plcrash_async_thread_state_set_reg_64(thread_state, regnum, reg);
      }
  }
  
  // PLCrashAsyncThread API
  char const *plcrash_async_thread_state_get_reg_name (const plcrash_async_thread_state_t *thread_state, plcrash_regnum_t regnum) {
      if (thread_state->x86_state.thread.tsh.flavor == x86_THREAD_STATE32) {
          return plcrash_async_thread_state_get_regname_32(regnum);
      } else {
          return plcrash_async_thread_state_get_regname_64(regnum);
      }
  }
  
  // PLCrashAsyncThread API
  size_t plcrash_async_thread_state_get_reg_count (const plcrash_async_thread_state_t *thread_state) {
      /* Last is an index value, so increment to get the count */
      if (thread_state->x86_state.thread.tsh.flavor == x86_THREAD_STATE32) {
          return PLCRASH_X86_LAST_REG+1;
      } else {
          return PLCRASH_X86_64_LAST_REG+1;
      }
  }
  
  // PLCrashAsyncThread API
  void plcrash_async_thread_state_clear_volatile_regs (plcrash_async_thread_state_t *thread_state) {
      const plcrash_regnum_t *table;
      size_t table_count = 0;
      
      if (thread_state->x86_state.thread.tsh.flavor == x86_THREAD_STATE32) {
          table = x86_32_nonvolatile_registers;
          table_count = sizeof(x86_32_nonvolatile_registers) / sizeof(x86_32_nonvolatile_registers[0]);
      } else {
          table = x86_64_nonvolatile_registers;
          table_count = sizeof(x86_64_nonvolatile_registers) / sizeof(x86_64_nonvolatile_registers[0]);
      }
      
      size_t reg_count = plcrash_async_thread_state_get_reg_count(thread_state);
      for (size_t reg = 0; reg < reg_count; reg++) {
          /* Skip unset registers */
          if (!plcrash_async_thread_state_has_reg(thread_state, reg))
              continue;
          
          /* Check for the register in the preservation table */
          bool preserved = false;
          for (size_t i = 0; i < table_count; i++) {
              if (table[i] == reg) {
                  preserved = true;
                  break;
              }
          }
  
          /* If not preserved, clear */
          if (!preserved)
              plcrash_async_thread_state_clear_reg(thread_state, reg);
      }
  }
  
  // PLCrashAsyncThread API
  bool plcrash_async_thread_state_map_dwarf_to_reg (const plcrash_async_thread_state_t *thread_state, uint64_t dwarf_reg, plcrash_regnum_t *regnum) {
      const struct dwarf_register_table *table;
      size_t table_count = 0;
  
      if (thread_state->x86_state.thread.tsh.flavor == x86_THREAD_STATE32) {
          table = x86_32_dwarf_table;
          table_count = sizeof(x86_32_dwarf_table) / sizeof(x86_32_dwarf_table[0]);
      } else {
          table = x86_64_dwarf_table;
          table_count = sizeof(x86_64_dwarf_table) / sizeof(x86_64_dwarf_table[0]);
      }
      
      for (size_t i = 0; i < table_count; i++) {
          if (table[i].dwarf_value == dwarf_reg) {
              *regnum = table[i].regnum;
              return true;
          }
      }
  
      /* Unknown DWARF register.  */
      return false;
  }
  
  // PLCrashAsyncThread API
  bool plcrash_async_thread_state_map_reg_to_dwarf (plcrash_async_thread_state_t *thread_state, plcrash_regnum_t regnum, uint64_t *dwarf_reg) {
      const struct dwarf_register_table *table;
      size_t table_count = 0;
      
      if (thread_state->x86_state.thread.tsh.flavor == x86_THREAD_STATE32) {
          table = x86_32_dwarf_table;
          table_count = sizeof(x86_32_dwarf_table) / sizeof(x86_32_dwarf_table[0]);
      } else {
          table = x86_64_dwarf_table;
          table_count = sizeof(x86_64_dwarf_table) / sizeof(x86_64_dwarf_table[0]);
      }
      
      for (size_t i = 0; i < table_count; i++) {
          if (table[i].regnum == regnum) {
              *dwarf_reg = table[i].dwarf_value;
              return true;
          }
      }
  
      /* Unknown register.  */
      return false;
  }
  
  /**
   * @internal
   * 32-bit implementation of plcrash_async_thread_state_get_reg()
   */
  static plcrash_greg_t plcrash_async_thread_state_get_reg_32 (const plcrash_async_thread_state_t *thread_state, plcrash_regnum_t regnum) {
      const plcrash_async_thread_state_t *ts = thread_state;
  
      /* All word-sized registers */
      switch (regnum) {
          case PLCRASH_X86_EAX:
              RETGEN(eax, thread.uts.ts32, ts);
              
          case PLCRASH_X86_EDX:
              RETGEN(edx, thread.uts.ts32, ts);
              
          case PLCRASH_X86_ECX:
              RETGEN(ecx, thread.uts.ts32, ts);
              
          case PLCRASH_X86_EBX:
              RETGEN(ebx, thread.uts.ts32, ts);
              
          case PLCRASH_X86_EBP:
              RETGEN(ebp, thread.uts.ts32, ts);
              
          case PLCRASH_X86_ESI:
              RETGEN(esi, thread.uts.ts32, ts);
              
          case PLCRASH_X86_EDI:
              RETGEN(edi, thread.uts.ts32, ts);
              
          case PLCRASH_X86_ESP:
              RETGEN(esp, thread.uts.ts32, ts);
              
          case PLCRASH_X86_EIP:
              RETGEN(eip, thread.uts.ts32, ts);
              
          case PLCRASH_X86_EFLAGS:
              RETGEN(eflags, thread.uts.ts32, ts);
              
          case PLCRASH_X86_TRAPNO:
              RETGEN(trapno, exception.ues.es32, ts);
              
          case PLCRASH_X86_CS:
              RETGEN(cs, thread.uts.ts32, ts);
              
          case PLCRASH_X86_DS:
              RETGEN(ds, thread.uts.ts32, ts);
              
          case PLCRASH_X86_ES:
              RETGEN(es, thread.uts.ts32, ts);
              
          case PLCRASH_X86_FS:
              RETGEN(fs, thread.uts.ts32, ts);
              
          case PLCRASH_X86_GS:
              RETGEN(gs, thread.uts.ts32, ts);
              
          default:
              // Unsupported register
              __builtin_trap();
      }
      
      /* Shouldn't be reachable */
      return 0;
  }
  
  /**
   * @internal
   * 64-bit implementation of plcrash_async_thread_state_get_reg()
   */
  static plcrash_greg_t plcrash_async_thread_state_get_reg_64 (const plcrash_async_thread_state_t *thread_state, plcrash_regnum_t regnum) {
      const plcrash_async_thread_state_t *ts = thread_state;
  
      switch (regnum) {
          case PLCRASH_X86_64_RAX:
              RETGEN(rax, thread.uts.ts64, ts);
              
          case PLCRASH_X86_64_RBX:
              RETGEN(rbx, thread.uts.ts64, ts);
              
          case PLCRASH_X86_64_RCX:
              RETGEN(rcx, thread.uts.ts64, ts);
              
          case PLCRASH_X86_64_RDX:
              RETGEN(rdx, thread.uts.ts64, ts);
              
          case PLCRASH_X86_64_RDI:
              RETGEN(rdi, thread.uts.ts64, ts);
              
          case PLCRASH_X86_64_RSI:
              RETGEN(rsi, thread.uts.ts64, ts);
              
          case PLCRASH_X86_64_RBP:
              RETGEN(rbp, thread.uts.ts64, ts);
              
          case PLCRASH_X86_64_RSP:
              RETGEN(rsp, thread.uts.ts64, ts);
              
          case PLCRASH_X86_64_R8:
              RETGEN(r8, thread.uts.ts64, ts);
              
          case PLCRASH_X86_64_R9:
              RETGEN(r9, thread.uts.ts64, ts);
              
          case PLCRASH_X86_64_R10:
              RETGEN(r10, thread.uts.ts64, ts);
              
          case PLCRASH_X86_64_R11:
              RETGEN(r11, thread.uts.ts64, ts);
              
          case PLCRASH_X86_64_R12:
              RETGEN(r12, thread.uts.ts64, ts);
              
          case PLCRASH_X86_64_R13:
              RETGEN(r13, thread.uts.ts64, ts);
              
          case PLCRASH_X86_64_R14:
              RETGEN(r14, thread.uts.ts64, ts);
              
          case PLCRASH_X86_64_R15:
              RETGEN(r15, thread.uts.ts64, ts);
              
          case PLCRASH_X86_64_RIP:
              RETGEN(rip, thread.uts.ts64, ts);
              
          case PLCRASH_X86_64_RFLAGS:
              RETGEN(rflags, thread.uts.ts64, ts);
              
          case PLCRASH_X86_64_CS:
              RETGEN(cs, thread.uts.ts64, ts);
              
          case PLCRASH_X86_64_FS:
              RETGEN(fs, thread.uts.ts64, ts);
              
          case PLCRASH_X86_64_GS:
              RETGEN(gs, thread.uts.ts64, ts);
              
          default:
              // Unsupported register
              __builtin_trap();
      }
  
      /* Should not be reachable */
      return 0;
  }
  
  /**
   * @internal
   * 32-bit implementation of plcrash_async_thread_state_set_reg()
   */
  static void plcrash_async_thread_state_set_reg_32 (plcrash_async_thread_state_t *thread_state, plcrash_regnum_t regnum, plcrash_greg_t reg) {
      plcrash_async_thread_state_t *ts = thread_state;
      
      /* All word-sized registers */
      switch (regnum) {
          case PLCRASH_X86_EAX:
              SETGEN(eax, thread.uts.ts32, ts, regnum, reg);
              
          case PLCRASH_X86_EDX:
              SETGEN(edx, thread.uts.ts32, ts, regnum, reg);
              
          case PLCRASH_X86_ECX:
              SETGEN(ecx, thread.uts.ts32, ts, regnum, reg);
              
          case PLCRASH_X86_EBX:
              SETGEN(ebx, thread.uts.ts32, ts, regnum, reg);
              
          case PLCRASH_X86_EBP:
              SETGEN(ebp, thread.uts.ts32, ts, regnum, reg);
              
          case PLCRASH_X86_ESI:
              SETGEN(esi, thread.uts.ts32, ts, regnum, reg);
              
          case PLCRASH_X86_EDI:
              SETGEN(edi, thread.uts.ts32, ts, regnum, reg);
              
          case PLCRASH_X86_ESP:
              SETGEN(esp, thread.uts.ts32, ts, regnum, reg);
              
          case PLCRASH_X86_EIP:
              SETGEN(eip, thread.uts.ts32, ts, regnum, reg);
              
          case PLCRASH_X86_EFLAGS:
              SETGEN(eflags, thread.uts.ts32, ts, regnum, reg);
              
          case PLCRASH_X86_TRAPNO:
              SETGEN(trapno, exception.ues.es32, ts, regnum, reg);
              
          case PLCRASH_X86_CS:
              SETGEN(cs, thread.uts.ts32, ts, regnum, reg);
              
          case PLCRASH_X86_DS:
              SETGEN(ds, thread.uts.ts32, ts, regnum, reg);
              
          case PLCRASH_X86_ES:
              SETGEN(es, thread.uts.ts32, ts, regnum, reg);
              
          case PLCRASH_X86_FS:
              SETGEN(fs, thread.uts.ts32, ts, regnum, reg);
              
          case PLCRASH_X86_GS:
              SETGEN(gs, thread.uts.ts32, ts, regnum, reg);
              
          default:
              // Unsupported register
              __builtin_trap();
      }
  }
  
  /**
   * @internal
   * 64-bit implementation of plcrash_async_thread_state_set_reg()
   */
  static void plcrash_async_thread_state_set_reg_64 (plcrash_async_thread_state_t *thread_state, plcrash_regnum_t regnum, plcrash_greg_t reg) {
      plcrash_async_thread_state_t *ts = thread_state;
      
      switch (regnum) {
          case PLCRASH_X86_64_RAX:
              SETGEN(rax, thread.uts.ts64, ts, regnum, reg);
              
          case PLCRASH_X86_64_RBX:
              SETGEN(rbx, thread.uts.ts64, ts, regnum, reg);
              
          case PLCRASH_X86_64_RCX:
              SETGEN(rcx, thread.uts.ts64, ts, regnum, reg);
              
          case PLCRASH_X86_64_RDX:
              SETGEN(rdx, thread.uts.ts64, ts, regnum, reg);
              
          case PLCRASH_X86_64_RDI:
              SETGEN(rdi, thread.uts.ts64, ts, regnum, reg);
              
          case PLCRASH_X86_64_RSI:
              SETGEN(rsi, thread.uts.ts64, ts, regnum, reg);
              
          case PLCRASH_X86_64_RBP:
              SETGEN(rbp, thread.uts.ts64, ts, regnum, reg);
              
          case PLCRASH_X86_64_RSP:
              SETGEN(rsp, thread.uts.ts64, ts, regnum, reg);
              
          case PLCRASH_X86_64_R8:
              SETGEN(r8, thread.uts.ts64, ts, regnum, reg);
              
          case PLCRASH_X86_64_R9:
              SETGEN(r9, thread.uts.ts64, ts, regnum, reg);
              
          case PLCRASH_X86_64_R10:
              SETGEN(r10, thread.uts.ts64, ts, regnum, reg);
              
          case PLCRASH_X86_64_R11:
              SETGEN(r11, thread.uts.ts64, ts, regnum, reg);
              
          case PLCRASH_X86_64_R12:
              SETGEN(r12, thread.uts.ts64, ts, regnum, reg);
              
          case PLCRASH_X86_64_R13:
              SETGEN(r13, thread.uts.ts64, ts, regnum, reg);
              
          case PLCRASH_X86_64_R14:
              SETGEN(r14, thread.uts.ts64, ts, regnum, reg);
              
          case PLCRASH_X86_64_R15:
              SETGEN(r15, thread.uts.ts64, ts, regnum, reg);
              
          case PLCRASH_X86_64_RIP:
              SETGEN(rip, thread.uts.ts64, ts, regnum, reg);
              
          case PLCRASH_X86_64_RFLAGS:
              SETGEN(rflags, thread.uts.ts64, ts, regnum, reg);
              
          case PLCRASH_X86_64_CS:
              SETGEN(cs, thread.uts.ts64, ts, regnum, reg);
              
          case PLCRASH_X86_64_FS:
              SETGEN(fs, thread.uts.ts64, ts, regnum, reg);
              
          case PLCRASH_X86_64_GS:
              SETGEN(gs, thread.uts.ts64, ts, regnum, reg);
              
          default:
              // Unsupported register
              __builtin_trap();
      }    
  }
  
  /**
   * @internal
   * 32-bit implementation of plcrash_async_thread_state_get_regname()
   */
  static char const *plcrash_async_thread_state_get_regname_32 (plcrash_regnum_t regnum) {
      /* All word-sized registers */
      switch (regnum) {
          case PLCRASH_X86_EAX:
              return "eax";
              
          case PLCRASH_X86_EDX:
              return "edx";
              
          case PLCRASH_X86_ECX:
              return "ecx";
              
          case PLCRASH_X86_EBX:
              return "ebx";
              
          case PLCRASH_X86_EBP:
              return "ebp";
              
          case PLCRASH_X86_ESI:
              return "esi";
              
          case PLCRASH_X86_EDI:
              return "edi";
              
          case PLCRASH_X86_ESP:
              return "esp";
              
          case PLCRASH_X86_EIP:
              return "eip";
              
          case PLCRASH_X86_EFLAGS:
              return "eflags";
              
          case PLCRASH_X86_TRAPNO:
              return "trapno";
              
          case PLCRASH_X86_CS:
              return "cs";
              
          case PLCRASH_X86_DS:
              return "ds";
              
          case PLCRASH_X86_ES:
              return "es";
              
          case PLCRASH_X86_FS:
              return "fs";
              
          case PLCRASH_X86_GS:
              return "gs";
              
          default:
              // Unsupported register
              break;
      }
      
      /* Unsupported register is an implementation error (checked in unit tests) */
      PLCF_DEBUG("Missing register name for register id: %d", regnum);
      abort();
  }
  
  /**
   * @internal
   * 64-bit implementation of plcrash_async_thread_state_get_regname()
   */
  static const char *plcrash_async_thread_state_get_regname_64 (plcrash_regnum_t regnum) {
      switch (regnum) {
          case PLCRASH_X86_64_RAX:
              return "rax";
              
          case PLCRASH_X86_64_RBX:
              return "rbx";
              
          case PLCRASH_X86_64_RCX:
              return "rcx";
              
          case PLCRASH_X86_64_RDX:
              return "rdx";
              
          case PLCRASH_X86_64_RDI:
              return "rdi";
              
          case PLCRASH_X86_64_RSI:
              return "rsi";
              
          case PLCRASH_X86_64_RBP:
              return "rbp";
              
          case PLCRASH_X86_64_RSP:
              return "rsp";
              
          case PLCRASH_X86_64_R8:
              return "r8";
              
          case PLCRASH_X86_64_R9:
              return "r9";
              
          case PLCRASH_X86_64_R10:
              return "r10";
              
          case PLCRASH_X86_64_R11:
              return "r11";
              
          case PLCRASH_X86_64_R12:
              return "r12";
              
          case PLCRASH_X86_64_R13:
              return "r13";
              
          case PLCRASH_X86_64_R14:
              return "r14";
              
          case PLCRASH_X86_64_R15:
              return "r15";
              
          case PLCRASH_X86_64_RIP:
              return "rip";
              
          case PLCRASH_X86_64_RFLAGS:
              return "rflags";
              
          case PLCRASH_X86_64_CS:
              return "cs";
              
          case PLCRASH_X86_64_FS:
              return "fs";
              
          case PLCRASH_X86_64_GS:
              return "gs";
              
          default:
              // Unsupported register
              break;
      }
      
      /* Unsupported register is an implementation error (checked in unit tests) */
      PLCF_DEBUG("Missing register name for register id: %d", regnum);
      abort();
  }
  
  #endif /* defined(__i386__) || defined(__x86_64__) */
  