about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--README.txt28
-rwxr-xr-xhexbin0 -> 480 bytes
-rw-r--r--hex.hex216
-rw-r--r--transform.e19
4 files changed, 244 insertions, 19 deletions
diff --git a/README.txt b/README.txt
index a6690e4..e3e12c1 100644
--- a/README.txt
+++ b/README.txt
@@ -193,22 +193,28 @@ than necessary; many of those bytes are used for error message strings, on the
 principle that it's very important that it be easy to distinguish a successful
 invocation of "hex" from a failed one.
 
-  When the hex transform is fully working, a copy of the compiled "hex" will
-be checked into source control so that it can serve as the root of trust for
-all Evocation builds. Meanwhile, you can compile it as follows:
+  Although the hex transform is not yet fully the compiled "hex" has proven
+quite stable, and the hex transform does work on it. So, a copy of the
+compiled "hex" is checked into source control so that it can serve as a root
+of trust for future Evocation builds. For ease of auditing, a commented hex
+dump version of this binary, produced via the hex transform, is also checked
+in, as "hex.hex" (We heard you liked metacircularity, so we put some
+metacircularity in your metacircularity so you can be metacircular while
+you're metacircular.)
+
+  If you need to compile "hex", you can do so as follows:
 
   $ cat labels.e elf.e hex.e | ./evoke > hex
   $ chmod 755 hex
 
-  Although the hex transform doesn't work, if you intend to play around with
-this you may wish to know how to attempt to run it on things. You can run it
-on hex.e as follows. (We heard you liked metacircularity, so we put some
-metacircularity in your metacircularity so you can be metacircular while
-you're metacircular.)
+  To produce the hex-dump version of it, do:
 
   $ (cat labels.e elf.e transform.e; echo 's" xyzzy" allocate-string dup 262144 read-to-buffer'; cat core.e linux.e output.e amd64.e execution-support.e log-load.e dynamic.e input.e interpret.e flow-control.e linux-dynamic.e labels.e elf.e hex.e; echo 'xyzzy s" hex-source" variable 1024 1024 * allocate s" hex-binary" variable 1024 1024 * allocate s" hex-metadata" variable hex-metadata hex-binary dup hex-source 5 roll hex-transform bye ' ) | ./evoke > hex.hex
 
-  There is not yet a recommended way to run the hex transform on evoke.e, but
-when there is it will look like appending most of the Evocation-in-Evocation
-build steps to the hex.hex build steps, swapping them in where hex.e is now.
+  Although the hex transform doesn't yet work on Forth programs (only programs
+written in Evocation-assembly), if you intend to play around with this you may
+wish to know how to attempt to run it on things. There is not yet a
+recommended way to run the hex transform on evoke.e, but when there is it will
+look like appending most of the Evocation-in-Evocation build steps to the
+hex.hex build steps, swapping them in where hex.e is now.
 
diff --git a/hex b/hex
new file mode 100755
index 0000000..4e3b7da
--- /dev/null
+++ b/hex
Binary files differdiff --git a/hex.hex b/hex.hex
new file mode 100644
index 0000000..1c30eae
--- /dev/null
+++ b/hex.hex
@@ -0,0 +1,216 @@
+~   This file is a commented hexadecimal representation of a compiled
+~ program, the output of Evocation's hex transform. The comments are
+~ intended to allow a human reader to audit it. You can convert it to a
+~ runnable executable by piping it through the program named 'hex' and
+~ chmodding the output 755. The difference between a source and a binary
+~ is comments! Enjoy. :)
+
+~   This particular program is 'hex' itself. You of course already need to
+~ have a binary copy of it to use this file, but you may find it helpful
+~ to have this hex dump anyway, to aid in auditing your binary.
+
+
+~ ELF file header
+  ~ * denotes mandatory fields according to breadbox
+  7f 45 4c 46                           ~ *magic number
+  02                                    ~ 64-bit
+  01                                    ~ little-endian
+  01                                    ~ ELF header format v1
+  00                                    ~ System-V ABI
+  00 00 00 00 00 00 00 00               ~ (padding)
+
+  02 00                                 ~ *executable
+  3e 00                                 ~ *Intel x86-64
+  01 00 00 00                           ~ ELF format version
+
+  78 00 00 08 00 00 00 00               ~ *entry point
+  40 00 00 00 00 00 00 00               ~ *program header offset
+  00 00 00 00 00 00 00 00               ~ section header offset
+  00 00 00 00                           ~ processor flags
+
+  40 00                                 ~ ELF header size
+  38 00                                 ~ *program header entry size
+  01 00                                 ~ *number of program header entries
+  00 00                                 ~ section header entry size
+  00 00                                 ~ number of section header entries
+  00 00                                 ~ section name string table index
+
+~ ELF program header
+  01 00 00 00                           ~ *"loadable" segment type
+  07 00 00 00                           ~ *read+write+execute permission
+  00 00 00 00 00 00 00 00               ~ *offset in file
+  00 00 00 08 00 00 00 00               ~ *virtual address
+  00 00 00 00 00 00 00 00               ~ physical address (ignored)
+
+  e0 01 00 00 00 00 00 00               ~ *size in file
+  e0 01 00 00 00 00 00 00               ~ *size in memory
+
+  00 00 00 00 00 00 00 00               ~ segment alignment
+
+
+~ This is the start routine, the first thing that runs when the ELF loads.
+  ~   The basic registers preserved across syscalls are rbx, rsp, rbp.
+  ~ To avoid redundant moves, we store the buffer pointer in rbx just once,
+  ~ and keep it there. We've made sure our load origin fits in 32 bits, so
+  ~ we can use imm32 for that. We're going to want to do an indirect load
+  ~ from it, so we can't use rbp for this.
+  48 c7 c3 d8 01 00 08                  ~ 0x080001d8 :rbx mov-reg64-imm32
+
+  e8 88 00 00 00                        ~ 0x00000088 call-rel-imm32
+
+  ~   If the length is 0, we got EOF. If it's less than zero, we got a read
+  ~ error. Either way, we exit. This is a signed comparison, as it needs to
+  ~ be.
+  48 83 f8 00                           ~ 0x00 :rax cmp-reg64-imm8
+  74 72                                 ~ 0x72 :cc-equal jmp-cc-rel-imm8
+  7c ee                                 ~ 0xee :cc-less jmp-cc-rel-imm8
+
+  ~ Now that the length is handled, retrieve the input byte.
+  48 8b 03                              ~ :rbx :rax mov-reg64-indirect-reg64
+
+  ~ If it's space or linefeed, skip it (go back to the loop start).
+  48 83 f8 20                           ~ 0x20 :rax cmp-reg64-imm8
+                                        ~ ASCII space
+  74 ea                                 ~ 0xffffffffffffffea :cc-equal jmp-cc-rel-imm8
+  48 83 f8 0a                           ~ 0x0a :rax cmp-reg64-imm8
+                                        ~ ASCII linefeed
+  74 e4                                 ~ 0xffffffffffffffe4 :cc-equal jmp-cc-rel-imm8
+  ~ If it's a comment, skip the whole thing.
+  48 83 f8 7e                           ~ 0x7e :rax cmp-reg64-imm8
+                                        ~ ASCII tilde
+  74 43                                 ~ 0x43 :cc-equal jmp-cc-rel-imm8
+
+  ~ Decode the value, or exit with an error.
+  e8 79 00 00 00                        ~ 0x00000079 call-rel-imm32
+
+  ~ We use rbp as a place to stash the high nibble.
+  48 89 c5                              ~ :rax :rbp mov-reg64-reg64
+  48 c1 c5 04                           ~ 0x04 :rbp rol-reg64-imm8
+
+  ~ Now we read another byte.
+  e8 5a 00 00 00                        ~ 0x0000005a call-rel-imm32
+
+  ~ Handle the length. A second hex digit is required here.
+  48 83 f8 00                           ~ 0x00 :rax cmp-reg64-imm8
+  74 ac                                 ~ 0xac :cc-equal jmp-cc-rel-imm8
+  7c c0                                 ~ 0xc0 :cc-less jmp-cc-rel-imm8
+
+  ~ Now that the length is handled, retrieve the input byte.
+  48 8b 03                              ~ :rbx :rax mov-reg64-indirect-reg64
+
+  ~ Decode the value, or exit with an error.
+  e8 5d 00 00 00                        ~ 0x0000005d call-rel-imm32
+
+  ~ We OR in the low nibble.
+  48 0b e8                              ~ :rax :rbp or-reg64-reg64
+
+  ~ Output the byte. We reuse the buffer as a place to store it.
+  48 89 2b                              ~ :rbx :rbp mov-indirect-reg64-reg64
+  48 89 de                              ~ :rbx :rsi mov-reg64-reg64
+                                        ~ buffer pointer
+  48 c7 c2 01 00 00 00                  ~ 0x00000001 :rdx mov-reg64-imm32
+                                        ~ buffer length
+  48 c7 c0 01 00 00 00                  ~ 0x00000001 :rax mov-reg64-imm32
+                                        ~ syscall number for sys-write
+  48 c7 c7 01 00 00 00                  ~ 0x00000001 :rdi mov-reg64-imm32
+                                        ~ file descriptor 1 is stdout
+  0f 05                                 ~ syscall
+
+  ~ Back to the start of the loop.
+  eb 9b                                 ~ 0xffffffffffffff9b jmp-rel-imm8
+
+  ~ Read a byte for the comment.
+  e8 23 00 00 00                        ~ 0x00000023 call-rel-imm32
+
+  ~ Handle the length. We're allowed to end in a comment.
+  48 83 f8 00                           ~ 0x00 :rax cmp-reg64-imm8
+  74 0d                                 ~ 0x0d :cc-equal jmp-cc-rel-imm8
+  7c 89                                 ~ 0x89 :cc-less jmp-cc-rel-imm8
+
+  ~ Now that the length is handled, retrieve the input byte.
+  48 8b 03                              ~ :rbx :rax mov-reg64-indirect-reg64
+
+  ~ If it's linefeed, the comment is over.
+  48 83 f8 0a                           ~ 0x0a :rax cmp-reg64-imm8
+                                        ~ ASCII linefeed
+  74 85                                 ~ 0xffffffffffffff85 :cc-equal jmp-cc-rel-imm8
+
+  ~ We're still in the comment, keep handling it.
+  eb e8                                 ~ 0xffffffffffffffe8 jmp-rel-imm8
+
+~ This is the routine named "exit".
+  48 c7 c0 3c 00 00 00                  ~ 0x0000003c :rax mov-reg64-imm32
+                                        ~ syscall number for sys-exit
+  48 c7 c7 00 00 00 00                  ~ 0x00000000 :rdi mov-reg64-imm32
+                                        ~ exit code
+  0f 05                                 ~ syscall
+
+~ This is the routine named "read-byte".
+  ~ We use self-xor as a concise way to set registers to zero.
+  48 33 c0                              ~ :rax :rax xor-reg64-reg64
+                                        ~ syscall number for sys-read
+  48 33 ff                              ~ :rdi :rdi xor-reg64-reg64
+                                        ~ file descriptor 0 is stdin
+  48 89 de                              ~ :rbx :rsi mov-reg64-reg64
+                                        ~ buffer pointer
+  ~ We read one byte at a time, because it makes the loop structure simple.
+  48 c7 c2 01 00 00 00                  ~ 0x00000001 :rdx mov-reg64-imm32
+                                        ~ buffer length
+  0f 05                                 ~ syscall
+  c3                                    ~ ret
+
+~ This is the routine named "decode-nibble".
+  48 83 e8 30                           ~ 0x30 :rax sub-reg64-imm8
+                                        ~ ASCII zero
+  7c 29                                 ~ 0x29 :cc-less jmp-cc-rel-imm8
+  48 83 f8 0a                           ~ 0x0a :rax cmp-reg64-imm8
+  72 22                                 ~ 0x22 :cc-below jmp-cc-rel-imm8
+  48 83 e8 11                           ~ 0x11 :rax sub-reg64-imm8
+                                        ~ ASCII capital A
+  7c 1d                                 ~ 0x1d :cc-less jmp-cc-rel-imm8
+  48 83 c0 0a                           ~ 0x0a :rax add-reg64-imm8
+  48 83 f8 10                           ~ 0x10 :rax cmp-reg64-imm8
+  72 12                                 ~ 0x12 :cc-below jmp-cc-rel-imm8
+  48 83 e8 2a                           ~ 0x2a :rax sub-reg64-imm8
+                                        ~ ASCII lowercase a
+  7c 0d                                 ~ 0x0d :cc-less jmp-cc-rel-imm8
+  48 83 c0 0a                           ~ 0x0a :rax add-reg64-imm8
+  48 83 f8 10                           ~ 0x10 :rax cmp-reg64-imm8
+  72 02                                 ~ 0x02 :cc-below jmp-cc-rel-imm8
+  eb 01                                 ~ 0x01 jmp-rel-imm8
+  c3                                    ~ ret
+
+
+~ These are the error handler routines.
+  48 be ae 01 00 08 00 00 00 00         ~ 0x00000000080001ae :rsi mov-reg64-imm64
+  48 ba 0e 00 00 00 00 00 00 00         ~ 0x000000000000000e :rdx mov-reg64-imm64
+  eb 2a                                 ~ 0x2a jmp-rel-imm8
+  48 be c8 01 00 08 00 00 00 00         ~ 0x00000000080001c8 :rsi mov-reg64-imm64
+  48 ba 10 00 00 00 00 00 00 00         ~ 0x0000000000000010 :rdx mov-reg64-imm64
+  eb 14                                 ~ 0x14 jmp-rel-imm8
+  48 be bc 01 00 08 00 00 00 00         ~ 0x00000000080001bc :rsi mov-reg64-imm64
+  48 ba 0c 00 00 00 00 00 00 00         ~ 0x000000000000000c :rdx mov-reg64-imm64
+  48 c7 c0 01 00 00 00                  ~ 0x00000001 :rax mov-reg64-imm32
+                                        ~ syscall number for sys-write
+  48 c7 c7 02 00 00 00                  ~ 0x00000002 :rdi mov-reg64-imm32
+                                        ~ file descriptor 2 is stderr
+  0f 05                                 ~ syscall
+  48 c7 c0 3c 00 00 00                  ~ 0x0000003c :rax mov-reg64-imm32
+                                        ~ syscall number for sys-exit
+  48 c7 c7 01 00 00 00                  ~ 0x00000001 :rdi mov-reg64-imm32
+                                        ~ exit code
+  0f 05                                 ~ syscall
+
+~ These are the message strings.
+  ~ String literal with null terminator: "Invalid byte."
+  49 6e 76 61 6c 69 64 20 62 79 74 65 2e 00
+  ~ String literal with null terminator: "Read error."
+  52 65 61 64 20 65 72 72 6f 72 2e 00
+  ~ String literal with null terminator: "Unexpected EOF."
+  55 6e 65 78 70 65 63 74 65 64 20 45 4f 46 2e 00
+
+~   This is a buffer that's zero in the ELF, but is written to at runtime
+~ and used as a variable.
+  00 00 00 00 00 00 00 00
+
+~ This is the end of the hex dump.
diff --git a/transform.e b/transform.e
index 84d43d0..30a9230 100644
--- a/transform.e
+++ b/transform.e
@@ -3437,23 +3437,23 @@ allocate-transformation-state s" transformation-state" variable
   0 has-non-space-this-input-line!
 
   over 0 hex-output-metadata-entry-type-line-comment
-  s"   This file is a commented hexadecimal representation of a compiled "
+  s"   This file is a commented hexadecimal representation of a compiled"
   pack-hex-output-metadata-entry
 
   over 0 hex-output-metadata-entry-type-line-comment
-  s" program, the output of Evocation's hex transform. The comments are "
+  s" program, the output of Evocation's hex transform. The comments are"
   pack-hex-output-metadata-entry
 
   over 0 hex-output-metadata-entry-type-line-comment
-  s" intended to allow a human reader to audit it. You can convert it to a "
+  s" intended to allow a human reader to audit it. You can convert it to a"
   pack-hex-output-metadata-entry
 
   over 0 hex-output-metadata-entry-type-line-comment
-  s" runnable executable by piping it through the program named 'hex' and "
+  s" runnable executable by piping it through the program named 'hex' and"
   pack-hex-output-metadata-entry
 
   over 0 hex-output-metadata-entry-type-line-comment
-  s" chmodding the output 755. The difference between a source and a binary "
+  s" chmodding the output 755. The difference between a source and a binary"
   pack-hex-output-metadata-entry
 
   over 0 hex-output-metadata-entry-type-line-comment
@@ -3930,11 +3930,14 @@ allocate-transformation-state s" transformation-state" variable
     over {
       is-fresh-line@ {
         indentation-depth@ dup indent advance-current-column
-      } if
+      } {
+        space
+        1 advance-current-column
+      } if-else
 
-      dup 8@ .hex8 space
+      dup 8@ .hex8
 
-      3 advance-current-column
+      2 advance-current-column
       0 is-fresh-line!
     } if