Repository: pwndbg/pwndbg

Issue:
16bit (i8086) support?
**Is your feature request related to a problem? Please describe.**

I would like to use pwndbg to debug a kernel from a QEMU debuggee (JOS, from my [OS course lab](https://pdos.csail.mit.edu/6.828/2018/labs/lab1/)), because gdb really sucks. You know.
Although the majority of that kernel is 32-bit, the bootloader crashes pwndbg (`KeyError: 'i8086'`), making it totally unable to use.

It would be great if pwndbg comes with i8086 support, saying that pwndbg targets "low-level software developers" and  "hardware hackers".
 At least it should not crash when debugging a bootloader :)

**Describe the solution you'd like**

I slightly tweaked the code to make it i8086-compatible:
```diff
diff --git a/pwndbg/arch.py b/pwndbg/arch.py
index 05f87a3..0c91bf7 100644
--- a/pwndbg/arch.py
+++ b/pwndbg/arch.py
@@ -33,4 +33,5 @@ def fix_arch(arch):
 
 @pwndbg.events.start
+@pwndbg.events.stop # arch may change
 @pwndbg.events.new_objfile
 def update():
diff --git a/pwndbg/disasm/__init__.py b/pwndbg/disasm/__init__.py
index 408da0c..1dab952 100644
--- a/pwndbg/disasm/__init__.py
+++ b/pwndbg/disasm/__init__.py
@@ -29,4 +29,5 @@ CapstoneArch = {
     'aarch64': CS_ARCH_ARM64,
     'i386': CS_ARCH_X86,
+    'i8086': CS_ARCH_X86,
     'x86-64': CS_ARCH_X86,
     'powerpc': CS_ARCH_PPC,
@@ -53,4 +54,5 @@ VariableInstructionSizeMax = {
     'i386':   16,
     'x86-64': 16,
+    'i8086':  16,
     'mips':   8,
 }
@@ -86,4 +88,8 @@ def get_disassembler(pc):
             # The ptrsize base modes cause capstone.CsError: Invalid mode (CS_ERR_MODE)
             extra = 0 
+            
+    elif pwndbg.arch.current == 'i8086':
+        extra = CS_MODE_16
+    
     else:
         extra = None
diff --git a/pwndbg/regs.py b/pwndbg/regs.py
index 2439252..405b7af 100644
--- a/pwndbg/regs.py
+++ b/pwndbg/regs.py
@@ -243,4 +242,5 @@ mips = RegisterSet( frame   = 'fp',
 arch_to_regs = {
     'i386': i386,
+    'i8086': i386,
     'x86-64': amd64,
     'mips': mips,
@@ -290,4 +290,6 @@ class module(ModuleType):
                 size = pwndbg.typeinfo.unsigned.get(value.type.sizeof, pwndbg.typeinfo.ulong)
                 value = value.cast(size)
+                if attr.lower() == 'pc' and pwndbg.arch.current == 'i8086':
+                    value += self.cs*16
 
             value = int(value)
```

After this patch, pwndbg can be used to debug JOS.

Note that more tweaks need to be done to make emulation works under i8086.

**Describe alternatives you've considered**

No. Just add the i8086 support please.

**Additional context**

[MIT's website](https://pdos.csail.mit.edu/6.828/2018/tools.html) provides a clear introduction to setting up a QEMU+JOS environment.


Discussion:
@xmcp can you send that patch in form of a pull request? It looks good enough to be included.
OK I will send a PR for this.

Some reminders:
- **`@pwndbg.events.stop` line in `arch.py` will force rechecking architecture once debugger stops, so this may cause some performance overhead** (maybe we can make this rechecking behavior configurable, as "changing architecture on the fly" is a rare thing)
- `pc` register is set to IP+CS*16 (in order to make `nearpc` happy) in i8086, which may confuse some people who assume it to be IP
- This patch is not fully tested (but at least works for JOS)

Resolving patch:
diff --git a/pwndbg/arch.py b/pwndbg/arch.py
--- a/pwndbg/arch.py
+++ b/pwndbg/arch.py
@@ -32,6 +32,7 @@ def fix_arch(arch):
     return arch
 
 @pwndbg.events.start
+@pwndbg.events.stop
 @pwndbg.events.new_objfile
 def update():
     m = sys.modules[__name__]
diff --git a/pwndbg/disasm/__init__.py b/pwndbg/disasm/__init__.py
--- a/pwndbg/disasm/__init__.py
+++ b/pwndbg/disasm/__init__.py
@@ -28,6 +28,7 @@
     'armcm': CS_ARCH_ARM,
     'aarch64': CS_ARCH_ARM64,
     'i386': CS_ARCH_X86,
+    'i8086': CS_ARCH_X86,
     'x86-64': CS_ARCH_X86,
     'powerpc': CS_ARCH_PPC,
     'mips': CS_ARCH_MIPS,
@@ -52,6 +53,7 @@
 VariableInstructionSizeMax = {
     'i386':   16,
     'x86-64': 16,
+    'i8086':  16,
     'mips':   8,
 }
 
@@ -85,6 +87,10 @@ def get_disassembler(pc):
         else:
             # The ptrsize base modes cause capstone.CsError: Invalid mode (CS_ERR_MODE)
             extra = 0 
+            
+    elif pwndbg.arch.current == 'i8086':
+        extra = CS_MODE_16
+    
     else:
         extra = None
 
diff --git a/pwndbg/regs.py b/pwndbg/regs.py
--- a/pwndbg/regs.py
+++ b/pwndbg/regs.py
@@ -162,7 +162,6 @@ def __iter__(self):
                                 'di','si','bp','sp','ip'),
                     retval  = 'eax')
 
-
 # http://math-atlas.sourceforge.net/devel/assembly/elfspec_ppc.pdf
 # r0      Volatile register which may be modified during function linkage
 # r1      Stack frame pointer, always valid
@@ -242,6 +241,7 @@ def __iter__(self):
 
 arch_to_regs = {
     'i386': i386,
+    'i8086': i386,
     'x86-64': amd64,
     'mips': mips,
     'sparc': sparc,
@@ -289,6 +289,8 @@ def __getattr__(self, attr):
                 value = get_register(attr)
                 size = pwndbg.typeinfo.unsigned.get(value.type.sizeof, pwndbg.typeinfo.ulong)
                 value = value.cast(size)
+                if attr.lower() == 'pc' and pwndbg.arch.current == 'i8086':
+                    value += self.cs * 16
 
             value = int(value)
             return value & pwndbg.arch.ptrmask


Test patch: