diff options
-rw-r--r-- | quine.asm | 114 |
1 files changed, 65 insertions, 49 deletions
diff --git a/quine.asm b/quine.asm index 7e15a51..3b6df78 100644 --- a/quine.asm +++ b/quine.asm @@ -14,7 +14,7 @@ ;;; ;;; 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: $? +;;; rm quine2; fasmg quine.asm quine && ./quine > quine2; echo "exit code:" $?; echo; hexdump -C quine; echo; hexdump -C quine2; echo; cmp -l quine quine2 ; echo cmp: $? ;;; ;;; The reason this removes the old one first is that otherwise, there's a ;;; risk the error message will be scrolled off the top of the screen and @@ -73,7 +73,7 @@ end macro macro qwordreg result, register match =rax?, register result = 0 - else match =rcx?, regiser + else match =rcx?, register result = 1 else match =rdx?, register result = 2 @@ -265,9 +265,7 @@ end macro ; This uses opcode 0xC6, which has w = 0. Since we run in 64-bit mode, that ; makes the operand size 8 bits, regardless of the current operand-size ; attribute. [Intel] volume 2D, appendix B, section B-1.4.3, table B-6. -; -; TODO this should be mov.(something).disp8 -macro mov.rel.b target, offset, source +macro mov.qreg.disp8.bimm target, offset, source match =rsp, target db 0xC6 modrm 1, 0, 4 @@ -286,7 +284,7 @@ end macro ; us an operand size of 32 bits by default. [Intel] volume 1, section 3.6.1, ; table 3-4. We want a 16-bit operand, so we use the operand-size prefix, ; 0x66, and we leave REX.W unset. -macro mov.rel.w target, offset, source +macro mov.qreg.disp8.wimm target, offset, source match =rsp, target db 0x66 db 0xC7 @@ -305,7 +303,7 @@ end macro ; This uses opcode 0x67, which has w = 1. We run in 64-bit mode, so that gives ; us an operand size of 32 by default. [Intel] volume 2D, section B.1.43, ; table B-6. This is what we want, so we leave it. -macro mov.rel.d target, offset, source +macro mov.qreg.disp8.dimm target, offset, source match =rsp, target db 0xC7 modrm 1, 0, 4 @@ -321,7 +319,7 @@ end macro ; register, with an 8-bit displacement and no indexing. ; ; This uses opcode 0x89. -macro mov.rel.q target, offset, source +macro mov.qreg.disp8.qreg target, offset, source match =rsp, target qwordreg sreg, source rex.w @@ -343,7 +341,7 @@ end macro ; gives us an operand size of 32 by default. [Intel] volume 2D, ; section B.1.43, table B-6. We want a 64-bit operand, so we use the REX.W ; prefix, 0x48. -macro mov.rel.q.d target, offset, source +macro mov.qreg.disp8.dimm target, offset, source match =rsp, target rex.w db 0xC7 @@ -682,9 +680,32 @@ DOCOL: mov.qreg.qreg rsi, rax NEXT -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; Implementation strategy ;;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; Routine _start +;;; -------------- +;;; +;;; This is the entry point of the whole program, the very first code we +;;; actually execute. We go with the name linkers generally use for it, though +;;; it's not very Forth-y. The ELF header points to it. +;;; +;;; The kernel gives us most registers zeroed, and rsp pointing to the +;;; command-line stuff (argc, argv, envp), which is at an ASLR'd address with +;;; some stack space allocated for us, despite the fact we didn't request any. +;;; It also gives us all the flags clear except IF, but we don't rely on that. +;;; Lastly, of course, it loads our code segment and sets the instruction +;;; pointer where we asked; we don't need to check what those addresses are, +;;; because they're not randomized. +;;; +;;; This routine is really only responsible for one-time initialization. +;;; +_start: + cld ; clear the DF flag + ; If we wanted to save the initial stack pointer, we'd do that here. + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; (old) 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 @@ -704,45 +725,40 @@ DOCOL: ;;; ;;; * rsp points to the bottom of the buffer. ;;; -_start: - ;;; - ;;; Initialize registers - ;;; - cld ; clear the DF flag mov.dreg.dimm rdx, 0 ; store running file size here - sub.qreg.bimm rsp, 0xFF ; reserve stack space + ;sub.qreg.bimm rsp, 0xFF ; reserve stack space ;;; ;;; 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 - mov.rel.b rsp, 0x06, 1 ; ELF header format version 1 - mov.rel.b rsp, 0x07, 0 ; System-V ABI - mov.rel.q.d rsp, 0x08, 0 ; (padding) + mov.qreg.disp8.dimm rsp, 0x00, 0x7F bappend "ELF" ; magic number + mov.qreg.disp8.bimm rsp, 0x04, 2 ; 64-bit + mov.qreg.disp8.bimm rsp, 0x05, 1 ; little-endian + mov.qreg.disp8.bimm rsp, 0x06, 1 ; ELF header format version 1 + mov.qreg.disp8.bimm rsp, 0x07, 0 ; System-V ABI + mov.qreg.disp8.dimm rsp, 0x08, 0 ; (padding) - mov.rel.w rsp, 0x10, 2 ; executable - mov.rel.w rsp, 0x12, 0x3E ; Intel x86-64 - mov.rel.d rsp, 0x14, 1 ; ELF format version + mov.qreg.disp8.wimm rsp, 0x10, 2 ; executable + mov.qreg.disp8.wimm rsp, 0x12, 0x3E ; Intel x86-64 + mov.qreg.disp8.dimm rsp, 0x14, 1 ; ELF format version ; Compute the entry pointer. mov.qreg.qimm rax, $$ ; the memory origin add.qreg.bimm rax, 0x78 ; the size of the headers add.qreg.dimm rax, 155 ; the offset of _start - mov.rel.q rsp, 0x18, rax ; entry point + mov.qreg.disp8.qreg rsp, 0x18, rax ; entry point - mov.rel.q.d rsp, 0x20, 64 ; program header offset + mov.qreg.disp8.dimm rsp, 0x20, 64 ; program header offset ; We place the program header immediately after the ELF header. This ; offset is from the start of the file. - mov.rel.q.d rsp, 0x28, 0 ; section header offset - mov.rel.d rsp, 0x30, 0 ; processor flags - mov.rel.w rsp, 0x34, 64 ; ELF header size - mov.rel.w rsp, 0x36, 56 ; program header entry size - mov.rel.w rsp, 0x38, 1 ; number of program header entries - mov.rel.w rsp, 0x3a, 0 ; section header entry size - mov.rel.w rsp, 0x3c, 0 ; number of section header entries - mov.rel.w rsp, 0x3e, 0 ; section name string table index + mov.qreg.disp8.dimm rsp, 0x28, 0 ; section header offset + mov.qreg.disp8.dimm rsp, 0x30, 0 ; processor flags + mov.qreg.disp8.wimm rsp, 0x34, 64 ; ELF header size + mov.qreg.disp8.wimm rsp, 0x36, 56 ; program header entry size + mov.qreg.disp8.wimm rsp, 0x38, 1 ; number of program header entries + mov.qreg.disp8.wimm rsp, 0x3a, 0 ; section header entry size + mov.qreg.disp8.wimm rsp, 0x3c, 0 ; number of section header entries + mov.qreg.disp8.wimm rsp, 0x3e, 0 ; section name string table index ; Add the size of the ELF header to the running total mov.dreg.dimm rax, 0x40 @@ -751,19 +767,19 @@ _start: ;;; ;;; 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 - mov.rel.q.d rsp, 0x50, $$ ; virtual address + mov.qreg.disp8.dimm rsp, 0x40, 1 ; "loadable" segment type + mov.qreg.disp8.dimm rsp, 0x44, 0x05 ; read+execute permission + mov.qreg.disp8.dimm rsp, 0x48, 0 ; offset in file + mov.qreg.disp8.dimm rsp, 0x50, $$ ; virtual address ; required, but can be anything, subject to alignment - mov.rel.q.d rsp, 0x58, 0 ; physical address (ignored) + mov.qreg.disp8.dimm rsp, 0x58, 0 ; physical address (ignored) ; Fill in 0 as the file size for now, to avoid unitialized memory. - mov.rel.q.d rsp, 0x60, 0 ; size in file - mov.rel.q.d rsp, 0x68, 0 ; size in memory + mov.qreg.disp8.dimm rsp, 0x60, 0 ; size in file + mov.qreg.disp8.dimm rsp, 0x68, 0 ; size in memory - mov.rel.q.d rsp, 0x70, 0 ; segment alignment - ; for relocation - will we be ASLR'd? + mov.qreg.disp8.dimm rsp, 0x70, 0 ; segment alignment + ; for relocation, but this doesn't apply to us ; Add the size of the program header to the running total mov.dreg.dimm rax, 0x38 @@ -773,14 +789,14 @@ _start: ;;; way to generate it. ;;; ;;; TODO of course, really we want to for-real track this - mov.qreg.qimm rax, 0x200 + mov.qreg.qimm rax, 0x201 add.qreg.qreg rdx, rax ;;; ;;; 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 + mov.qreg.disp8.qreg rsp, 0x60, rdx ; size in file + mov.qreg.disp8.qreg rsp, 0x68, rdx ; size in memory ;;; ;;; The buffer is ready; output the file. |