diff options
| -rw-r--r-- | hex.e | 3 | ||||
| -rw-r--r-- | hex.hex | 277 |
2 files changed, 150 insertions, 130 deletions
diff --git a/hex.e b/hex.e index 25e6893..42c204f 100644 --- a/hex.e +++ b/hex.e @@ -220,16 +220,19 @@ ~ : indent current-offset L!' invalid-byte L@' origin L@' invalid-byte-message + :rsi mov-reg64-imm64 + L' invalid-byte-message-size set-label-non-offset L@' invalid-byte-message-size :rdx mov-reg64-imm64 L@' exit-with-error jmp-rel-imm8-from-here current-offset L!' unexpected-eof L@' origin L@' unexpected-eof-message + :rsi mov-reg64-imm64 + L' unexpected-eof-message-size set-label-non-offset L@' unexpected-eof-message-size :rdx mov-reg64-imm64 L@' exit-with-error jmp-rel-imm8-from-here current-offset L!' read-error L@' origin L@' read-error-message + :rsi mov-reg64-imm64 + L' read-error-message-size set-label-non-offset L@' read-error-message-size :rdx mov-reg64-imm64 ~ Fall through. diff --git a/hex.hex b/hex.hex index 28a5ed2..6e627e9 100644 --- a/hex.hex +++ b/hex.hex @@ -12,205 +12,222 @@ ~ 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 + 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) + ~ label elf-program-header at offset 0x0000000000000040 is here. + 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 + 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 + 00 00 00 00 00 00 00 00 ~ segment alignment ~ This is the start routine, the first thing that runs when the ELF loads. + ~ label cold-start at offset 0x0000000000000078 is here. ~ 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 ~ 0x80001d8 :rbx mov-reg64-imm32 + 48 c7 c3 d8 01 00 08 ~ 0x80001d8 :rbx mov-reg64-imm32 - e8 88 00 00 00 ~ 0x88 call-rel-imm32 + ~ label input-loop-start at offset 0x000000000000007f is here. + e8 88 00 00 00 ~ 0x88 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 ~ 0x0 :rax cmp-reg64-imm8 - 74 72 ~ 0x72 :cc-equal jmp-cc-rel-imm8 - 7c ee ~ 0xee :cc-less jmp-cc-rel-imm8 + 48 83 f8 00 ~ 0x0 :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 + 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 ~ 0xa :rax cmp-reg64-imm8 - ~ ASCII linefeed - 74 e4 ~ 0xffffffffffffffe4 :cc-equal jmp-cc-rel-imm8 + 48 83 f8 20 ~ 0x20 :rax cmp-reg64-imm8 + ~ ASCII space + 74 ea ~ 0xffffffffffffffea :cc-equal jmp-cc-rel-imm8 + 48 83 f8 0a ~ 0xa :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 + 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 ~ 0x79 call-rel-imm32 + e8 79 00 00 00 ~ 0x79 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 ~ 0x4 :rbp rol-reg64-imm8 + 48 89 c5 ~ :rax :rbp mov-reg64-reg64 + 48 c1 c5 04 ~ 0x4 :rbp rol-reg64-imm8 ~ Now we read another byte. - e8 5a 00 00 00 ~ 0x5a call-rel-imm32 + e8 5a 00 00 00 ~ 0x5a call-rel-imm32 ~ Handle the length. A second hex digit is required here. - 48 83 f8 00 ~ 0x0 :rax cmp-reg64-imm8 - 74 ac ~ 0xac :cc-equal jmp-cc-rel-imm8 - 7c c0 ~ 0xc0 :cc-less jmp-cc-rel-imm8 + 48 83 f8 00 ~ 0x0 :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 + 48 8b 03 ~ :rbx :rax mov-reg64-indirect-reg64 ~ Decode the value, or exit with an error. - e8 5d 00 00 00 ~ 0x5d call-rel-imm32 + e8 5d 00 00 00 ~ 0x5d call-rel-imm32 ~ We OR in the low nibble. - 48 0b e8 ~ :rax :rbp or-reg64-reg64 + 48 0b e8 ~ :rax :rbp or-reg64-reg64 ~ Output the byte. We reuse the buffer as a place to store it. - 48 89 2b ~ :rbp :rbx mov-indirect-reg64-reg64 - 48 89 de ~ :rbx :rsi mov-reg64-reg64 - ~ buffer pointer - 48 c7 c2 01 00 00 00 ~ 0x1 :rdx mov-reg64-imm32 - ~ buffer length - 48 c7 c0 01 00 00 00 ~ 0x1 :rax mov-reg64-imm32 - ~ syscall number for sys-write - 48 c7 c7 01 00 00 00 ~ 0x1 :rdi mov-reg64-imm32 - ~ file descriptor 1 is stdout - 0f 05 ~ syscall + 48 89 2b ~ :rbp :rbx mov-indirect-reg64-reg64 + 48 89 de ~ :rbx :rsi mov-reg64-reg64 + ~ buffer pointer + 48 c7 c2 01 00 00 00 ~ 0x1 :rdx mov-reg64-imm32 + ~ buffer length + 48 c7 c0 01 00 00 00 ~ 0x1 :rax mov-reg64-imm32 + ~ syscall number for sys-write + 48 c7 c7 01 00 00 00 ~ 0x1 :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 + eb 9b ~ 0xffffffffffffff9b jmp-rel-imm8 + ~ label skip-comment at offset 0x00000000000000e4 is here. ~ Read a byte for the comment. - e8 23 00 00 00 ~ 0x23 call-rel-imm32 + e8 23 00 00 00 ~ 0x23 call-rel-imm32 ~ Handle the length. We're allowed to end in a comment. - 48 83 f8 00 ~ 0x0 :rax cmp-reg64-imm8 - 74 0d ~ 0xd :cc-equal jmp-cc-rel-imm8 - 7c 89 ~ 0x89 :cc-less jmp-cc-rel-imm8 + 48 83 f8 00 ~ 0x0 :rax cmp-reg64-imm8 + 74 0d ~ 0xd :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 + 48 8b 03 ~ :rbx :rax mov-reg64-indirect-reg64 ~ If it's linefeed, the comment is over. - 48 83 f8 0a ~ 0xa :rax cmp-reg64-imm8 - ~ ASCII linefeed - 74 85 ~ 0xffffffffffffff85 :cc-equal jmp-cc-rel-imm8 + 48 83 f8 0a ~ 0xa :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 + eb e8 ~ 0xffffffffffffffe8 jmp-rel-imm8 ~ This is the routine named "exit". - 48 c7 c0 3c 00 00 00 ~ 0x3c :rax mov-reg64-imm32 - ~ syscall number for sys-exit - 48 c7 c7 00 00 00 00 ~ 0x0 :rdi mov-reg64-imm32 - ~ exit code - 0f 05 ~ syscall + ~ label exit at offset 0x00000000000000fc is here. + 48 c7 c0 3c 00 00 00 ~ 0x3c :rax mov-reg64-imm32 + ~ syscall number for sys-exit + 48 c7 c7 00 00 00 00 ~ 0x0 :rdi mov-reg64-imm32 + ~ exit code + 0f 05 ~ syscall ~ This is the routine named "read-byte". + ~ label read-byte at offset 0x000000000000010c is here. ~ 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 + 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 ~ 0x1 :rdx mov-reg64-imm32 - ~ buffer length - 0f 05 ~ syscall - c3 ~ ret + 48 c7 c2 01 00 00 00 ~ 0x1 :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 ~ 0xa :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 ~ 0xa :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 ~ 0xd :cc-less jmp-cc-rel-imm8 - 48 83 c0 0a ~ 0xa :rax add-reg64-imm8 - 48 83 f8 10 ~ 0x10 :rax cmp-reg64-imm8 - 72 02 ~ 0x2 :cc-below jmp-cc-rel-imm8 - eb 01 ~ 0x1 jmp-rel-imm8 - c3 ~ ret + ~ label decode-nibble at offset 0x000000000000011f is here. + 48 83 e8 30 ~ 0x30 :rax sub-reg64-imm8 + ~ ASCII zero + 7c 29 ~ 0x29 :cc-less jmp-cc-rel-imm8 + 48 83 f8 0a ~ 0xa :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 ~ 0xa :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 ~ 0xd :cc-less jmp-cc-rel-imm8 + 48 83 c0 0a ~ 0xa :rax add-reg64-imm8 + 48 83 f8 10 ~ 0x10 :rax cmp-reg64-imm8 + 72 02 ~ 0x2 :cc-below jmp-cc-rel-imm8 + eb 01 ~ 0x1 jmp-rel-imm8 + ~ label got-nibble at offset 0x000000000000014d is here. + c3 ~ ret ~ These are the error handler routines. - 48 be ae 01 00 08 00 00 00 00 ~ 0x80001ae :rsi mov-reg64-imm64 - 48 ba 0e 00 00 00 00 00 00 00 ~ 0xe :rdx mov-reg64-imm64 - eb 2a ~ 0x2a jmp-rel-imm8 - 48 be c8 01 00 08 00 00 00 00 ~ 0x80001c8 :rsi mov-reg64-imm64 - 48 ba 10 00 00 00 00 00 00 00 ~ 0x10 :rdx mov-reg64-imm64 - eb 14 ~ 0x14 jmp-rel-imm8 - 48 be bc 01 00 08 00 00 00 00 ~ 0x80001bc :rsi mov-reg64-imm64 - 48 ba 0c 00 00 00 00 00 00 00 ~ 0xc :rdx mov-reg64-imm64 - 48 c7 c0 01 00 00 00 ~ 0x1 :rax mov-reg64-imm32 - ~ syscall number for sys-write - 48 c7 c7 02 00 00 00 ~ 0x2 :rdi mov-reg64-imm32 - ~ file descriptor 2 is stderr - 0f 05 ~ syscall - 48 c7 c0 3c 00 00 00 ~ 0x3c :rax mov-reg64-imm32 - ~ syscall number for sys-exit - 48 c7 c7 01 00 00 00 ~ 0x1 :rdi mov-reg64-imm32 - ~ exit code - 0f 05 ~ syscall + ~ label invalid-byte at offset 0x000000000000014e is here. + 48 be ae 01 00 08 00 00 00 00 ~ 0x80001ae :rsi mov-reg64-imm64 + 48 ba 0e 00 00 00 00 00 00 00 ~ 0xe :rdx mov-reg64-imm64 + eb 2a ~ 0x2a jmp-rel-imm8 + ~ label unexpected-eof at offset 0x0000000000000164 is here. + 48 be c8 01 00 08 00 00 00 00 ~ 0x80001c8 :rsi mov-reg64-imm64 + 48 ba 10 00 00 00 00 00 00 00 ~ 0x10 :rdx mov-reg64-imm64 + eb 14 ~ 0x14 jmp-rel-imm8 + ~ label read-error at offset 0x000000000000017a is here. + 48 be bc 01 00 08 00 00 00 00 ~ 0x80001bc :rsi mov-reg64-imm64 + 48 ba 0c 00 00 00 00 00 00 00 ~ 0xc :rdx mov-reg64-imm64 + ~ label exit-with-error at offset 0x000000000000018e is here. + 48 c7 c0 01 00 00 00 ~ 0x1 :rax mov-reg64-imm32 + ~ syscall number for sys-write + 48 c7 c7 02 00 00 00 ~ 0x2 :rdi mov-reg64-imm32 + ~ file descriptor 2 is stderr + 0f 05 ~ syscall + 48 c7 c0 3c 00 00 00 ~ 0x3c :rax mov-reg64-imm32 + ~ syscall number for sys-exit + 48 c7 c7 01 00 00 00 ~ 0x1 :rdi mov-reg64-imm32 + ~ exit code + 0f 05 ~ syscall ~ These are the message strings. - ~ String literal with null terminator: "Invalid byte." + ~ label invalid-byte-message at offset 0x00000000000001ae is here. + ~ "Invalid byte." (string literal with null) 49 6e 76 61 6c 69 64 20 62 79 74 65 2e 00 - ~ String literal with null terminator: "Read error." + ~ label read-error-message at offset 0x00000000000001bc is here. + ~ "Read error." (string literal with null) 52 65 61 64 20 65 72 72 6f 72 2e 00 - ~ String literal with null terminator: "Unexpected EOF." + ~ label unexpected-eof-message at offset 0x00000000000001c8 is here. + ~ "Unexpected EOF." (string literal with null) 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. + ~ label buffer at offset 0x00000000000001d8 is here. 00 00 00 00 00 00 00 00 +~ label total-size at offset 0x00000000000001e0 is here. ~ This is the end of the hex dump. |