From bda3f24bb893d79c8d772025c45eb79495d0ad58 Mon Sep 17 00:00:00 2001 From: Irene Knapp Date: Wed, 9 Sep 2026 02:16:04 -0700 Subject: check in the hex binary and commented hex-dump yes, wow, right? big milestone! doesn't work for evoke itself quite yet, but getting really close Force-Push: yes Change-Id: I1e836b85cf185db4aa835d2aa494a67585e60744 --- README.txt | 28 ++++---- hex | Bin 0 -> 480 bytes hex.hex | 216 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ transform.e | 19 +++--- 4 files changed, 244 insertions(+), 19 deletions(-) create mode 100755 hex create mode 100644 hex.hex 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 Binary files /dev/null and b/hex differ 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. 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 -- cgit 1.4.1