about summary refs log tree commit diff
path: root/hex.hex
diff options
context:
space:
mode:
Diffstat (limited to 'hex.hex')
-rw-r--r--hex.hex216
1 files changed, 216 insertions, 0 deletions
diff --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.