about summary refs log tree commit diff stats
path: root/subx
diff options
context:
space:
mode:
authorKartik Agaram <vc@akkartik.com>2018-09-21 22:25:00 -0700
committerKartik Agaram <vc@akkartik.com>2018-09-21 22:25:00 -0700
commitd47f3a82786c7d3abdb1001c2562780d0e1fab2e (patch)
treecd30cf4975ab70ceb427ee0f025f0804885fd8d8 /subx
parentef47911ff39c865aa2e65af442a03ddd8c2b1aa4 (diff)
downloadmu-d47f3a82786c7d3abdb1001c2562780d0e1fab2e.tar.gz
4584 - discrepancy between SubX and native x86
One of the more painful things I had to debug with machine code. Tricks
I used can be seen in ex10.subx:
- printing argv[1] in various places
- printing a single 'X' in various places to count how many times we get
  to different instructions
- exiting with the current value of EAX in various places

I repeatedly went down the wrong trail in several ways:
- forgetting that the problem lay in native runs, and accidentally switching
  to subx runs during debugging.
- forgetting to pass commandline args, because ex10 doesn't check its argv
- writing the wrong comment for an instruction, and then miscalculating
  the set of registers that need to be saved.
- forgetting that syscalls clobber EAX.

Debugging native runs is hard, because you have to write non-trivial code
to instrument the binary, and instrumentation can itself be buggy.

When we finally tracked it down, I recognized the problem immediately.
I'd meant to confirm the behavior of opcode 8a against bare metal, and
then forgot.
In any case, opcode 8a was inconsistent with 88. Sloppy.
Diffstat (limited to 'subx')
-rw-r--r--subx/014indirect_addressing.cc19
-rwxr-xr-xsubx/examples/ex10bin155 -> 276 bytes
-rw-r--r--subx/examples/ex10.subx77
3 files changed, 88 insertions, 8 deletions
diff --git a/subx/014indirect_addressing.cc b/subx/014indirect_addressing.cc
index 9142728a..ccbf994c 100644
--- a/subx/014indirect_addressing.cc
+++ b/subx/014indirect_addressing.cc
@@ -388,16 +388,18 @@ case 0x8b: {  // copy r32 to r/m32
 put(name, "88", "copy r8 (lowermost byte of r32) to r8/m8-at-r32");
 
 :(scenario copy_r8_to_mem_at_r32)
-% Reg[EBX].i = 0xafafafaf;
+% Reg[EBX].i = 0x224488ab;
 % Reg[EAX].i = 0x60;
 == 0x1
 # op  ModR/M  SIB   displacement  immediate
   88  18                                      # copy just the lowermost byte of EBX to the byte at *EAX
 # ModR/M in binary: 00 (indirect mode) 011 (src EBX) 000 (dest EAX)
+== 0x60
+f0 cc bb aa  # 0xf0 with more data in following bytes
 +run: copy lowermost byte of EBX to r8/m8-at-r32
 +run: effective address is 0x60 (EAX)
-+run: storing 0xaf
-% CHECK_EQ(0x000000af, read_mem_u32(0x60));
++run: storing 0xab
+% CHECK_EQ(0xaabbccab, read_mem_u32(0x60));
 
 :(before "End Single-Byte Opcodes")
 case 0x88: {  // copy r/m8 to r8
@@ -417,17 +419,19 @@ case 0x88: {  // copy r/m8 to r8
 put(name, "8a", "copy r8/m8-at-r32 to r8 (lowermost byte of r32)");
 
 :(scenario copy_mem_at_r32_to_r8)
-% Reg[EBX].i = 0xaf;
+% Reg[EBX].i = 0xaabbcc0f;  // one nibble each of lowest byte set to all 0s and all 1s, to maximize value of this test
 % Reg[EAX].i = 0x60;
 == 0x1
 # op  ModR/M  SIB   displacement  immediate
   8a  18                                      # copy just the byte at *EAX to lowermost byte of EBX (clearing remaining bytes)
 # ModR/M in binary: 00 (indirect mode) 011 (dest EBX) 000 (src EAX)
 == 0x60  # data segment
-af ff ff ff  # 0xaf with more data in following bytes
+ab ff ff ff  # 0xab with more data in following bytes
 +run: copy r8/m8-at-r32 to lowermost byte of EBX
 +run: effective address is 0x60 (EAX)
-+run: storing 0xaf
++run: storing 0xab
+# remaining bytes of EBX are *not* cleared
++run: EBX now contains 0xaabbccab
 
 :(before "End Single-Byte Opcodes")
 case 0x8a: {  // copy r/m8 to r8
@@ -436,8 +440,9 @@ case 0x8a: {  // copy r/m8 to r8
   trace(90, "run") << "copy r8/m8-at-r32 to lowermost byte of " << rname(reg1) << end();
   // use unsigned to zero-extend 8-bit value to 32 bits
   uint8_t* arg2 = reinterpret_cast<uint8_t*>(effective_address(modrm));
-  Reg[reg1].u = static_cast<uint32_t>(*arg2);
   trace(90, "run") << "storing 0x" << HEXBYTE << NUM(*arg2) << end();
+  *reinterpret_cast<uint8_t*>(&Reg[reg1].u) = *arg2;  // assumes host is little-endian
+  trace(90, "run") << rname(reg1) << " now contains 0x" << HEXWORD << Reg[reg1].u << end();
   break;
 }
 
diff --git a/subx/examples/ex10 b/subx/examples/ex10
index ddfd6c30..cd3de11e 100755
--- a/subx/examples/ex10
+++ b/subx/examples/ex10
Binary files differdiff --git a/subx/examples/ex10.subx b/subx/examples/ex10.subx
index 321cee64..1a426e28 100644
--- a/subx/examples/ex10.subx
+++ b/subx/examples/ex10.subx
@@ -26,9 +26,11 @@
     # push args
   50/push                         .               .             .           .             .           .           .               .                 # push EAX
   53/push                         .               .             .           .             .           .           .               .                 # push EBX
+#?   e8/call write_argv_3/disp32
     # call
-  e8/call  argv_equal/disp32
+  e8/call argv_equal/disp32
   # exit(EAX)
+$exit:
   89/copy                         3/mod/direct    3/rm32/EBX    .           .             .           0/r32/EAX   .               .                 # copy EAX to EBX
   b8/copy                         .               .             .           .             .           .           .               1/imm32           # copy 1 to EAX
   cd/syscall  0x80/imm8
@@ -39,11 +41,18 @@ argv_equal:  # (s1, s2) : null-terminated ascii strings -> EAX : boolean
   # initialize s1 (ECX) and s2 (EDX)
   8b/copy                         1/mod/*+disp8   4/rm32/sib    4/base/ESP  4/index/none  .           1/r32/ECX   8/disp8         .                 # copy *(ESP+8) to ECX
   8b/copy                         1/mod/*+disp8   4/rm32/sib    4/base/ESP  4/index/none  .           2/r32/EDX   4/disp8         .                 # copy *(ESP+4) to EDX
+#?     # i/ESI = 0
+#?   be/copy  0/imm32  # copy 0 to ESI
   # while (true)
 $argv_loop:
     # c1/EAX, c2/EBX = *s1, *s2
+  b8/copy  0/imm32  # clear EAX
   8a/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy byte at *ECX to lower byte of EAX
+  bb/copy  0/imm32  # clear EBX
   8a/copy                         0/mod/indirect  2/rm32/EDX    .           .             .           3/r32/EBX   .               .                 # copy byte at *EDX to lower byte of EBX
+#?     # if (i == _) exit(EAX)
+#?   81 7/subop  3/mod/direct  6/rm32/ESI  3/imm32  # compare ESI with _
+#?   74/jump-if-equal  $exit/disp8
     # if (c1 == 0) break
   3d/compare                      .               .             .           .             .           .           .               0/imm32           # compare EAX with 0
   74/jump-if-equal  $argv_break/disp8
@@ -53,7 +62,15 @@ $argv_loop:
     # ++s1, ++s2
   41/inc-ECX
   42/inc-EDX
+#?     # ++i
+#?   46/inc-ESI
   # end while
+#?   68/push  X/imm32
+#?   e8/call  write_stderr/disp32
+#?   81 0/subop/add  3/mod/direct  4/rm32/ESP  4/imm32  # add 4 to ESP
+#?   51/push                         .               .             .           .             .           .           .               .                 # push ECX
+#?   e8/call write_argv_3/disp32
+#?   81 0/subop/add  3/mod/direct  4/rm32/ESP  4/imm32  # add 4 to ESP
   eb/jump  $argv_loop/disp8
 $argv_break:
   # if (c2 == 0) return true
@@ -65,3 +82,61 @@ $argv_break:
 $argv_fail:
   b8/copy                         .               .             .           .             .           .           .               0/imm32           # copy 0 to EAX
   c3/return
+
+write_stderr:  # s : (address array byte) -> <void>
+  # save registers
+  50/push                         .               .             .           .             .           .           .               .                 # push EAX
+  51/push                         .               .             .           .             .           .           .               .                 # push ECX
+  52/push                         .               .             .           .             .           .           .               .                 # push EDX
+  53/push                         .               .             .           .             .           .           .               .                 # push EBX
+  # write(2/stderr, (data) s+4, (size) *s)
+    # fd = 2 (stderr)
+  bb/copy                         .               .             .           .             .           .           .               2/imm32           # copy 2 to EBX
+    # x = s+4
+  8b/copy                         1/mod/*+disp8   4/rm32/SIB    4/base/ESP  4/index/none  .           1/r32/ECX   0x14/disp8      .                 # copy *(ESP+20) to ECX
+  81          0/subop/add         3/mod/direct    1/rm32/ECX    .           .             .           .           .               4/imm32           # add 4 to ECX
+    # size = *s
+  8b/copy                         1/mod/*+disp8   4/rm32/SIB    4/base/ESP  4/index/none  .           2/r32/EDX   0x14/disp8      .                 # copy *(ESP+20) to EDX
+  8b/copy                         0/mod/indirect  2/rm32/EDX    .           .             .           2/r32/EDX   .               .                 # copy *EDX to EDX
+    # call write()
+  b8/copy                         .               .             .           .             .           .           .               4/imm32/write     # copy 1 to EAX
+  cd/syscall  0x80/imm8
+  # restore registers
+  5b/pop                          .               .             .           .             .           .           .               .                 # pop EBX
+  5a/pop                          .               .             .           .             .           .           .               .                 # pop EDX
+  59/pop                          .               .             .           .             .           .           .               .                 # pop ECX
+  58/pop                          .               .             .           .             .           .           .               .                 # pop EAX
+  # end
+  c3/return
+
+write_argv_3:  # s : null-terminated ascii string of size exactly 3 -> <void>
+  # save registers
+  50/push                         .               .             .           .             .           .           .               .                 # push EAX
+  51/push                         .               .             .           .             .           .           .               .                 # push ECX
+  52/push                         .               .             .           .             .           .           .               .                 # push EDX
+  53/push                         .               .             .           .             .           .           .               .                 # push EBX
+  # write(2/stderr, (data) s+4, (size) *s)
+    # fd = 2 (stderr)
+  bb/copy                         .               .             .           .             .           .           .               2/imm32           # copy 2 to EBX
+    # x = s
+  8b/copy                         1/mod/*+disp8   4/rm32/SIB    4/base/ESP  4/index/none  .           1/r32/ECX   0x14/disp8      .                 # copy *(ESP+20) to ECX
+    # size = 3
+  ba/copy                         .               .             .           .             .           .           .               3/imm32           # copy 3 to EDX (hardcoded)
+    # call write()
+  b8/copy                         .               .             .           .             .           .           .               4/imm32/write     # copy 1 to EAX
+  cd/syscall  0x80/imm8
+  # restore registers
+  5b/pop                          .               .             .           .             .           .           .               .                 # pop EBX
+  5a/pop                          .               .             .           .             .           .           .               .                 # pop EDX
+  59/pop                          .               .             .           .             .           .           .               .                 # pop ECX
+  58/pop                          .               .             .           .             .           .           .               .                 # pop EAX
+  # end
+  c3/return
+
+== data
+X:
+  02 00 00 00
+  58/X 0a/newline
+Y:
+  02 00 00 00
+  59/Y 0a/newline