about summary refs log tree commit diff stats
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
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.
-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