from collections import defaultdict
 
 from .constants import *
-from .dwarf_expr import ExprDumper
+from .dwarf_expr import GenericExprVisitor
 from .die import DIE
 from ..common.utils import preserve_stream_pos
 
 
+def set_global_machine_arch(machine_arch):
+    global _MACHINE_ARCH
+    _MACHINE_ARCH = machine_arch
+
+
 def describe_attr_value(attr, die, section_offset):
     """ Given an attribute attr, return the textual representation of its
         value, suitable for tools like readelf.
     return str(val_description) + '\t' + extra_info    
 
 
+def describe_reg_name(regnum, machine_arch):
+    """ Provide a textual description for a register name, given its serial
+        number. The number is expected to be valid.
+    """
+    if machine_arch == 'x86':
+        return _REG_NAMES_x86[regnum]
+    elif machine_arch == 'x64':
+        return _REG_NAMES_x64[regnum]
+    else:
+        return '<none>'
+
 #-------------------------------------------------------------------------------
 
+# The machine architecture. Set globally via set_global_machine_arch
+#
+_MACHINE_ARCH = None
+
+
 def _describe_attr_ref(attr, die, section_offset):
     return '<0x%x>' % (attr.value + die.cu.cu_offset)
 
     DW_AT_import=_import_extra,
 )
 
+# 8 in a line, for easier counting
+_REG_NAMES_x86 = [
+    'eax', 'ecx', 'edx', 'ebx', 'esp', 'ebp', 'esi', 'edi',
+    'eip', 'eflags', '<none>', 'st0', 'st1', 'st2', 'st3', 'st4',
+    'st5', 'st6', 'st7', '<none>', '<none>', 'xmm0', 'xmm1', 'xmm2',
+    'xmm3', 'xmm4', 'xmm5', 'xmm6', 'xmm7', 'mm0', 'mm1', 'mm2',
+    'mm3', 'mm4', 'mm5', 'mm6', 'mm7', 'fcw', 'fsw', 'mxcsr',
+    'es', 'cs', 'ss', 'ds', 'fs', 'gs', '<none>', '<none>', 'tr', 'ldtr'
+]
+
+_REG_NAMES_x64 = [
+    'rax', 'rdx', 'rcx', 'rbx', 'rsi', 'rdi', 'rbp', 'rsp',
+    'r8',  'r9',  'r10', 'r11', 'r12', 'r13', 'r14', 'r15',
+    'rip', 'xmm0',  'xmm1',  'xmm2',  'xmm3', 'xmm4', 'xmm5', 'xmm6',
+    'xmm7', 'xmm8', 'xmm9', 'xmm10', 'xmm11', 'xmm12', 'xmm13', 'xmm14',
+    'xmm15', 'st0', 'st1', 'st2', 'st3', 'st4', 'st5', 'st6',
+    'st7', 'mm0', 'mm1', 'mm2', 'mm3', 'mm4', 'mm5', 'mm6',
+    'mm7', 'rflags', 'es', 'cs', 'ss', 'ds', 'fs', 'gs',
+    '<none>', '<none>', 'fs.base', 'gs.base', '<none>', '<none>', 'tr', 'ldtr',
+    'mxcsr', 'fcw', 'fsw'
+]
+
+
+class ExprDumper(GenericExprVisitor):
+    """ A concrete visitor for DWARF expressions that dumps a textual
+        representation of the complete expression.
+        
+        Usage: after creation, call process_expr, and then get_str for a
+        semicolon-delimited string representation of the decoded expression.
+    """
+    def __init__(self, structs):
+        super(ExprDumper, self).__init__(structs)
+        self._init_lookups()
+        self._str_parts = []
+
+    def clear(self):
+        self._str_parts = []
+
+    def get_str(self):
+        return '; '.join(self._str_parts)
+
+    def _init_lookups(self):
+        self._ops_with_decimal_arg = set([
+            'DW_OP_const1u', 'DW_OP_const1s', 'DW_OP_const2u', 'DW_OP_const2s',
+            'DW_OP_const4u', 'DW_OP_const4s', 'DW_OP_constu', 'DW_OP_consts',
+            'DW_OP_pick', 'DW_OP_plus_uconst', 'DW_OP_bra', 'DW_OP_skip',
+            'DW_OP_fbreg', 'DW_OP_piece', 'DW_OP_deref_size',
+            'DW_OP_xderef_size', 'DW_OP_regx',])
+        
+        for n in range(0, 32):
+            self._ops_with_decimal_arg.add('DW_OP_breg%s' % n)
+        
+        self._ops_with_two_decimal_args = set([
+            'DW_OP_const8u', 'DW_OP_const8s', 'DW_OP_bregx', 'DW_OP_bit_piece'])
+
+        self._ops_with_hex_arg = set(
+            ['DW_OP_addr', 'DW_OP_call2', 'DW_OP_call4', 'DW_OP_call_ref'])
+
+    def _after_visit(self, opcode, opcode_name, args):
+        self._str_parts.append(self._dump_to_string(opcode, opcode_name, args))
+
+    def _dump_to_string(self, opcode, opcode_name, args):
+        if len(args) == 0:
+            if opcode_name.startswith('DW_OP_reg'):
+                regnum = int(opcode_name[9:])
+                return '%s (%s)' % (
+                    opcode_name,
+                    describe_reg_name(regnum, _MACHINE_ARCH))
+            else:
+                return opcode_name
+        elif opcode_name in self._ops_with_decimal_arg:
+            if opcode_name.startswith('DW_OP_breg'):
+                regnum = int(opcode_name[10:])
+                return '%s (%s): %s' % (
+                    opcode_name,
+                    describe_reg_name(regnum, _MACHINE_ARCH),
+                    args[0])
+            elif opcode_name.endswith('regx'):
+                # applies to both regx and bregx
+                return '%s: %s (%s)' % (
+                    opcode_name,
+                    args[0],
+                    describe_reg_name(args[0], _MACHINE_ARCH))
+            else:
+                return '%s: %s' % (opcode_name, args[0])
+        elif opcode_name in self._ops_with_hex_arg:
+            return '%s: %x' % (opcode_name, args[0])
+        elif opcode_name in self._ops_with_two_decimal_args:
+            return '%s: %s %s' % (opcode_name, args[0], args[1])
+        else:
+            return '<unknown %s>' % opcode_name
+
+
 
 
         the visitor then just execute process_expr. The subclass can keep
         its own internal information updated in _after_visit and provide
         methods to extract it. For a good example of this usage, see the
-        ExprDumper class in this module.
+        ExprDumper class in the descriptions module.
 
         A more complex usage could be to override visiting methods for
         specific instructions, by placing them into the dispatch table.
             self._make_visitor_arg_struct(self.structs.Dwarf_offset('')))
 
 
-class ExprDumper(GenericExprVisitor):
-    """ A concrete visitor for DWARF expressions that dumps a textual
-        representation of the complete expression.
-        
-        Usage: after creation, call process_expr, and then get_str for a
-        semicolon-delimited string representation of the decoded expression.
-    """
-    def __init__(self, structs):
-        super(ExprDumper, self).__init__(structs)
-        self._init_lookups()
-        self._str_parts = []
-
-    def clear(self):
-        self._str_parts = []
-
-    def get_str(self):
-        return '; '.join(self._str_parts)
-
-    def _init_lookups(self):
-        self._ops_with_decimal_arg = set([
-            'DW_OP_const1u', 'DW_OP_const1s', 'DW_OP_const2u', 'DW_OP_const2s',
-            'DW_OP_const4u', 'DW_OP_const4s', 'DW_OP_constu', 'DW_OP_consts',
-            'DW_OP_pick', 'DW_OP_plus_uconst', 'DW_OP_bra', 'DW_OP_skip',
-            'DW_OP_fbreg', 'DW_OP_piece', 'DW_OP_deref_size',
-            'DW_OP_xderef_size', 'DW_OP_regx',])
-        
-        for n in range(0, 32):
-            self._ops_with_decimal_arg.add('DW_OP_breg%s' % n)
-        
-        self._ops_with_two_decimal_args = set([
-            'DW_OP_const8u', 'DW_OP_const8s', 'DW_OP_bregx', 'DW_OP_bit_piece'])
-
-        self._ops_with_hex_arg = set(
-            ['DW_OP_addr', 'DW_OP_call2', 'DW_OP_call4', 'DW_OP_call_ref'])
-
-    def _after_visit(self, opcode, opcode_name, args):
-        self._str_parts.append(self._dump_to_string(opcode, opcode_name, args))
-
-    def _dump_to_string(self, opcode, opcode_name, args):
-        if len(args) == 0:
-            return opcode_name
-        elif opcode_name in self._ops_with_decimal_arg:
-            return '%s: %s' % (opcode_name, args[0])
-        elif opcode_name in self._ops_with_hex_arg:
-            return '%s: %x' % (opcode_name, args[0])
-        elif opcode_name in self._ops_with_two_decimal_args:
-            return '%s: %s %s' % (opcode_name, args[0], args[1])
-        else:
-            return '<unknown %s>' % opcode_name
-
-
-