summary refs log tree commit diff
diff options
context:
space:
mode:
authorIrene Knapp <ireneista@irenes.space>2025-10-11 18:11:34 -0700
committerIrene Knapp <ireneista@irenes.space>2025-10-11 18:11:34 -0700
commit4ccde781d12f3aa416aaa2f0f57de29d6e2f062b (patch)
treea12d1927a4ef05b8e3a2e05dc9c9829ecff531dd
parent825ae329d7106de1c457f91e4861d0164376a41a (diff)
comment the code in preparation for restructuring it
Force-Push: yes
Change-Id: I561d5fbb35db97b9d236a159c6c888b0651248c3
-rw-r--r--quine.asm97
1 files changed, 73 insertions, 24 deletions
diff --git a/quine.asm b/quine.asm
index ab4f13a..b7904da 100644
--- a/quine.asm
+++ b/quine.asm
@@ -1,12 +1,25 @@
-; fasmg hello.asm hello && chmod 755 hello && ./hello; echo $?
+; Currently, this is not yet fully self-hosting; it is based on
+; flatassembler[1]. A minimal command to build and run it is:
+;
+; fasmg quine.asm quine && chmod 755 quine && ./quine; echo $?
 ;
 ; ***WARNING*** this version leaves the error message scrolled off the top of
 ; the screen and you see stale output
 ; fasmg quine.asm quine && ./quine > quine2; echo "exit code:" $?; echo; hexdump -C quine; echo; hexdump -C quine2; cmp quine quine2; echo; echo "compare:" $?
 ; ZydisDisasm -64 quine
 ;
-; rm quine2; fasmg quine.asm quine && ./quine > quine2; echo "exit code:" $?; echo; hexdump -C quine; echo; hexdump -C quine2; echo; cmp quine quine2 ; echo $?
-
+; A workflow you may wish to use for debugging is:
+;
+; rm quine2; fasmg quine.asm quine && ./quine > quine2; echo "exit code:" $?; echo; hexdump -C quine; echo; hexdump -C quine2; echo; cmp quine quine2 ; echo cmp: $?
+;
+; You may also wish to do:
+;
+; objdump --disassemble quine
+; ZydisDisasm -64 quine
+;
+; This relies on GNU binutils, and on zydis, respectively.
+;
+; [1] https://flatassembler.net/
 
 
 macro rex.0
@@ -251,6 +264,7 @@ end macro
 
 org 0x08000000
 
+
 elf_header:
   ; * denotes mandatory fields according to breadbox
   db 0x7F, "ELF"                 ; *magic number
@@ -292,11 +306,37 @@ program_header_entry_size = $ - program_header
 
 load_origin = 0x08000000
 
+
+;;; Implementation strategy:
+;;;
+;;;   We assemble the entire file contents in a stack-allocated buffer.
+;;; We avoid using the stack for any other purpose. When the file is fully
+;;; assembled, we output it.
+;;;
+;;;   The assembly proceeds in several chunks - ELF header, program header,
+;;; etc. Each chunk extends the buffer as per its own needs, by adjusting
+;;; the stack pointer. All chunks also update a running total file size,
+;;; which refers to how many bytes have actually been populated, not to the
+;;; size of the buffer.
+;;;
+;;; Register usage:
+;;;
+;;; * rdx holds the total used file size so far. During hand-off between
+;;;   chunks, this size must be equal to the buffer size; within a chunk it
+;;;   may be less.
+;;;
+;;; * rsp points to the bottom of the buffer.
+;;;
 _start:
+  ;;;
+  ;;; Initialize registers
+  ;;;
   mov.dreg.dimm rdx, 0                     ; store running file size here
   sub.b rsp, 0xFF                          ; reserve stack space
 
-  ; ELF header
+  ;;;
+  ;;; ELF header
+  ;;;
   mov.rel.d rsp, 0x00, 0x7F bappend "ELF"  ; magic number
   mov.rel.b rsp, 0x04, 2                   ; 64-bit
   mov.rel.b rsp, 0x05, 1                   ; little-endian
@@ -325,7 +365,13 @@ _start:
   mov.rel.w rsp, 0x3c, 0                   ; number of section header entries
   mov.rel.w rsp, 0x3e, 0                   ; section name string table index
 
-  ; Program header
+  ; Add the size of the ELF header to the running total
+  mov.dreg.dimm rax, 0x40
+  add.q rdx, rax
+
+  ;;;
+  ;;; Program header
+  ;;;
   mov.rel.d rsp, 0x40, 1                   ; "loadable" segment type
   mov.rel.d rsp, 0x44, 0x05                ; read+execute permission
   mov.rel.q.d rsp, 0x48, 0                 ; offset in file
@@ -340,21 +386,29 @@ _start:
   mov.rel.q.d rsp, 0x70, 0                 ; segment alignment
     ;   for relocation - will we be ASLR'd?
 
-  ; Add the size of the ELF header to the running total
-  mov.dreg.dimm rax, 0x78
+  ; Add the size of the program header to the running total
+  mov.dreg.dimm rax, 0x38
   add.q rdx, rax
 
-  ; Go back and fill in the file size now that we know it.
-  ; so notionally, let's pretend rdx is a variable that keeps track of file
-  ; size. we would do something to populate it, then we would add it to rax...
-  mov.qreg.qimm rax, 0x185
+  ; Add the guessed, wrong size of the program
+
+  ;;; Hardcode the size of the actual code chunk, since we don't yet have a
+  ;;; way to generate it.
+  ;;;
+  ;;; TODO of course, really we want to for-real track this
+  mov.qreg.qimm rax, 0x15a
   add.q rdx, rax
-    ; TODO of course, really we want to for-real track these
+
+  ;;;
+  ;;; Go back and fill in the file size now that we know it.
+  ;;;
   mov.rel.q rsp, 0x60, rdx                 ; size in file
   mov.rel.q rsp, 0x68, rdx                 ; size in memory
     ; TODO it works for rax source but not yet for rdx
 
-  ;;; after the file size is populated, that's the entire ELF header. yay!
+  ;;;
+  ;;; The buffer is ready; output the file.
+  ;;;
 
   ; write() from stack-allocated buffer
   mov.b rax,1
@@ -363,28 +417,23 @@ _start:
   mov.qreg.qimm rdx, 0x78
   syscall
 
-  ; write() hardcoded header
+  ; write() the machine code by using self-reference
+  ;
+  ; TODO do this in a "real" quine way
   mov.b rax, 1
   mov.qreg.qimm rdi, 1
   mov.qreg.qimm rsi, elf_header + 0x78
   mov.qreg.qimm rdx, file_size - 0x78
   syscall
 
-  ; write() greeting
-  mov.b rax, 1
-  mov.qreg.qimm rdi, 2
-  mov.qreg.qimm rsi, greeting
-  mov.qreg.qimm rdx, greeting_size
-  syscall
+  ;;;
+  ;;; Clean up.
+  ;;;
 
   ; exit()
   mov.b rax, 60
   mov.b rdi, 0
   syscall
 
-greeting:
-  db "Hello, Irenes!", 0x0A
-greeting_size = $ - greeting
-
 file_size = $ - $$