From e99038ea514a8703b170689d5a0730c8d2e542e7 Mon Sep 17 00:00:00 2001 From: Kartik Agaram Date: Mon, 18 Feb 2019 22:43:01 -0800 Subject: 4982 --- html/subx/059stop.subx.html | 350 ++++++++++++++++++++++---------------------- 1 file changed, 174 insertions(+), 176 deletions(-) (limited to 'html/subx/059stop.subx.html') diff --git a/html/subx/059stop.subx.html b/html/subx/059stop.subx.html index 72e26dda..cebc1975 100644 --- a/html/subx/059stop.subx.html +++ b/html/subx/059stop.subx.html @@ -24,7 +24,6 @@ a { color:inherit; } .subxMinorFunction { color: #875f5f; } .Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } .subxS2Comment { color: #8a8a8a; } -.SpecialChar { color: #d70000; } --> @@ -99,181 +98,180 @@ if ('onhashchange' in window) { 37 # . op subop mod rm32 base index scale r32 38 # . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes 39 - 40 # main: - 41 e8/call run-tests/disp32 # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'. - 42 #? e8/call test-stop-skips-returns-on-exit/disp32 - 43 # syscall(exit, Num-test-failures) - 44 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX - 45 b8/copy-to-EAX 1/imm32/exit - 46 cd/syscall 0x80/imm8 - 47 - 48 # Configure an exit-descriptor for a call pushing 'nbytes' bytes of args to - 49 # the stack. - 50 # Ugly that we need to know the size of args, but so it goes. - 51 tailor-exit-descriptor: # ed : (address exit-descriptor), nbytes : int -> <void> - 52 # . prolog - 53 55/push-EBP - 54 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 55 # . save registers - 56 50/push-EAX - 57 51/push-ECX - 58 # EAX = nbytes - 59 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 0/r32/EAX 0xc/disp8 . # copy *(EBP+12) to EAX - 60 # Let X be the value of ESP in the caller, before the call to tailor-exit-descriptor. - 61 # The return address for a call in the caller's body will be at: - 62 # X-8 if the caller takes 4 bytes of args for the exit-descriptor (add 4 bytes for the return address) - 63 # X-12 if the caller takes 8 bytes of args - 64 # ..and so on - 65 # That's the value we need to return: X-nbytes-4 - 66 # - 67 # However, we also need to account for the perturbance to ESP caused by the - 68 # call to tailor-exit-descriptor. It pushes 8 bytes of args followed by 4 - 69 # bytes for the return address and 4 bytes to push EBP above. - 70 # So EBP at this point is X-16. - 71 # - 72 # So the return address for the next call in the caller is: - 73 # EBP+8 if the caller takes 4 bytes of args - 74 # EBP+4 if the caller takes 8 bytes of args - 75 # EBP if the caller takes 12 bytes of args - 76 # EBP-4 if the caller takes 16 bytes of args - 77 # ..and so on - 78 # That's EBP+12-nbytes. - 79 # option 1: 6 + 3 bytes - 80 #? 2d/subtract 3/mod/direct 0/rm32/EAX . . . . . 8/imm32 # subtract from EAX - 81 #? 8d/copy-address 0/mod/indirect 4/rm32/sib 5/base/EBP 0/index/EAX . 0/r32/EAX . . # copy EBP+EAX to EAX - 82 # option 2: 2 + 4 bytes - 83 f7 3/subop/negate 3/mod/direct 0/rm32/EAX . . . . . . # negate EAX - 84 8d/copy-address 1/mod/*+disp8 4/rm32/sib 5/base/EBP 0/index/EAX . 0/r32/EAX 0xc/disp8 . # copy EBP+EAX+12 to EAX - 85 # copy EAX to ed->target - 86 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 1/r32/ECX 8/disp8 . # copy *(EBP+8) to ECX - 87 89/copy 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # copy EAX to *ECX - 88 # initialize ed->value - 89 c7 0/subop/copy 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 0/imm32 # copy to *(ECX+4) - 90 $tailor-exit-descriptor:end: - 91 # . restore registers - 92 59/pop-to-ECX - 93 58/pop-to-EAX - 94 # . epilog - 95 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 96 5d/pop-to-EBP - 97 c3/return - 98 - 99 stop: # ed : (address exit-descriptor), value : int -100 # no prolog; one way or another, we're going to clobber registers -101 # EAX = ed -102 8b/copy 1/mod/*+disp8 4/rm32/sib 4/base/ESP 4/index/none . 0/r32/EAX 4/disp8 . # copy *(ESP+4) to EAX -103 # if (ed->target == 0) really exit -104 81 7/subop/compare 0/mod/indirect 0/rm32/EAX . . . . . 0/imm32 # compare *EAX -105 75/jump-if-not-equal $stop:fake/disp8 -106 # . syscall(exit, value) -107 8b/copy 1/mod/*+disp8 4/rm32/sib 4/base/ESP 4/index/none . 3/r32/EBX 8/disp8 . # copy *(ESP+8) to EBX -108 b8/copy-to-EAX 1/imm32/exit -109 cd/syscall 0x80/imm8 -110 $stop:fake: -111 # otherwise: -112 # ed->value = value+1 -113 8b/copy 1/mod/*+disp8 4/rm32/sib 4/base/ESP 4/index/none . 1/r32/ECX 8/disp8 . # copy *(ESP+8) to ECX -114 41/inc-ECX -115 89/copy 1/mod/*+disp8 0/rm32/EAX . . . 1/r32/ECX 4/disp8 . # copy ECX to *(EAX+4) -116 # perform a non-local jump to ed->target -117 8b/copy 0/mod/indirect 0/rm32/EAX . . . 4/r32/ESP . . # copy *EAX to ESP -118 $stop:end: -119 c3/return # doesn't return to caller -120 -121 test-stop-skips-returns-on-exit: -122 # This looks like the standard prolog, but is here for different reasons. -123 # A function calling 'stop' can't rely on EBP persisting past the call. -124 # -125 # Use EBP here as a stable base to refer to locals and arguments from in the -126 # presence of push/pop/call instructions. -127 # *Don't* use EBP as a way to restore ESP. -128 55/push-EBP -129 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -130 # Make room for an exit descriptor on the stack. That's almost always the -131 # right place for it, available only as long as it's legal to use. Once this -132 # containing function returns we'll need a new exit descriptor. -133 # var ed/EAX : (address exit-descriptor) -134 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP -135 89/copy 3/mod/direct 0/rm32/EAX . . . 4/r32/ESP . . # copy ESP to EAX -136 # Size the exit-descriptor precisely for the next call below, to _test-stop-1. -137 # tailor-exit-descriptor(ed, 4) -138 # . . push args -139 68/push 4/imm32/nbytes-of-args-for-_test-stop-1 -140 50/push-EAX -141 # . . call -142 e8/call tailor-exit-descriptor/disp32 -143 # . . discard args -144 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -145 # . _test-stop-1(ed) -146 # . . push args -147 50/push-EAX -148 # . . call -149 e8/call _test-stop-1/disp32 -150 # registers except ESP may be clobbered at this point -151 # restore args -152 58/pop-to-EAX -153 # check that _test-stop-1 tried to call exit(1) -154 # check-ints-equal(ed->value, 2, msg) # i.e. stop was called with value 1 -155 # . . push args -156 68/push "F - test-stop-skips-returns-on-exit"/imm32 -157 68/push 2/imm32 -158 # . . push ed->value -159 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) -160 # . . call -161 e8/call check-ints-equal/disp32 -162 # . . discard args -163 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -164 # . epilog -165 # don't restore ESP from EBP; manually reclaim locals -166 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -167 5d/pop-to-EBP -168 c3/return -169 -170 _test-stop-1: # ed : (address exit-descriptor) -171 # . prolog -172 55/push-EBP -173 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -174 # _test-stop-2(ed) -175 # . . push args -176 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) -177 # . . call -178 e8/call _test-stop-2/disp32 -179 # should never get past this point -180 $_test-stop-1:dead-end: -181 # . . discard args -182 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -183 # signal test failed: check-ints-equal(1, 0, msg) -184 # . . push args -185 68/push "F - test-stop-skips-returns-on-exit"/imm32 -186 68/push 0/imm32 -187 68/push 1/imm32 -188 # . . call -189 e8/call check-ints-equal/disp32 -190 # . . discard args -191 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -192 # . epilog -193 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -194 5d/pop-to-EBP -195 c3/return -196 -197 _test-stop-2: # ed : (address exit-descriptor) -198 # . prolog -199 55/push-EBP -200 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -201 # . stop(ed, 1) -202 # . . push args -203 68/push 1/imm32 -204 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) -205 # . . call -206 e8/call stop/disp32 -207 # should never get past this point -208 $_test-stop-2:dead-end: -209 # . epilog -210 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -211 5d/pop-to-EBP -212 c3/return -213 -214 # . . vim:nowrap:textwidth=0 + 40 #? Entry: # run a single test, while debugging + 41 #? e8/call test-stop-skips-returns-on-exit/disp32 + 42 #? # syscall(exit, Num-test-failures) + 43 #? 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX + 44 #? b8/copy-to-EAX 1/imm32/exit + 45 #? cd/syscall 0x80/imm8 + 46 + 47 # Configure an exit-descriptor for a call pushing 'nbytes' bytes of args to + 48 # the stack. + 49 # Ugly that we need to know the size of args, but so it goes. + 50 tailor-exit-descriptor: # ed : (address exit-descriptor), nbytes : int -> <void> + 51 # . prolog + 52 55/push-EBP + 53 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 54 # . save registers + 55 50/push-EAX + 56 51/push-ECX + 57 # EAX = nbytes + 58 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 0/r32/EAX 0xc/disp8 . # copy *(EBP+12) to EAX + 59 # Let X be the value of ESP in the caller, before the call to tailor-exit-descriptor. + 60 # The return address for a call in the caller's body will be at: + 61 # X-8 if the caller takes 4 bytes of args for the exit-descriptor (add 4 bytes for the return address) + 62 # X-12 if the caller takes 8 bytes of args + 63 # ..and so on + 64 # That's the value we need to return: X-nbytes-4 + 65 # + 66 # However, we also need to account for the perturbance to ESP caused by the + 67 # call to tailor-exit-descriptor. It pushes 8 bytes of args followed by 4 + 68 # bytes for the return address and 4 bytes to push EBP above. + 69 # So EBP at this point is X-16. + 70 # + 71 # So the return address for the next call in the caller is: + 72 # EBP+8 if the caller takes 4 bytes of args + 73 # EBP+4 if the caller takes 8 bytes of args + 74 # EBP if the caller takes 12 bytes of args + 75 # EBP-4 if the caller takes 16 bytes of args + 76 # ..and so on + 77 # That's EBP+12-nbytes. + 78 # option 1: 6 + 3 bytes + 79 #? 2d/subtract 3/mod/direct 0/rm32/EAX . . . . . 8/imm32 # subtract from EAX + 80 #? 8d/copy-address 0/mod/indirect 4/rm32/sib 5/base/EBP 0/index/EAX . 0/r32/EAX . . # copy EBP+EAX to EAX + 81 # option 2: 2 + 4 bytes + 82 f7 3/subop/negate 3/mod/direct 0/rm32/EAX . . . . . . # negate EAX + 83 8d/copy-address 1/mod/*+disp8 4/rm32/sib 5/base/EBP 0/index/EAX . 0/r32/EAX 0xc/disp8 . # copy EBP+EAX+12 to EAX + 84 # copy EAX to ed->target + 85 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 1/r32/ECX 8/disp8 . # copy *(EBP+8) to ECX + 86 89/copy 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # copy EAX to *ECX + 87 # initialize ed->value + 88 c7 0/subop/copy 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 0/imm32 # copy to *(ECX+4) + 89 $tailor-exit-descriptor:end: + 90 # . restore registers + 91 59/pop-to-ECX + 92 58/pop-to-EAX + 93 # . epilog + 94 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 95 5d/pop-to-EBP + 96 c3/return + 97 + 98 stop: # ed : (address exit-descriptor), value : int + 99 # no prolog; one way or another, we're going to clobber registers +100 # EAX = ed +101 8b/copy 1/mod/*+disp8 4/rm32/sib 4/base/ESP 4/index/none . 0/r32/EAX 4/disp8 . # copy *(ESP+4) to EAX +102 # if (ed->target == 0) really exit +103 81 7/subop/compare 0/mod/indirect 0/rm32/EAX . . . . . 0/imm32 # compare *EAX +104 75/jump-if-not-equal $stop:fake/disp8 +105 # . syscall(exit, value) +106 8b/copy 1/mod/*+disp8 4/rm32/sib 4/base/ESP 4/index/none . 3/r32/EBX 8/disp8 . # copy *(ESP+8) to EBX +107 b8/copy-to-EAX 1/imm32/exit +108 cd/syscall 0x80/imm8 +109 $stop:fake: +110 # otherwise: +111 # ed->value = value+1 +112 8b/copy 1/mod/*+disp8 4/rm32/sib 4/base/ESP 4/index/none . 1/r32/ECX 8/disp8 . # copy *(ESP+8) to ECX +113 41/inc-ECX +114 89/copy 1/mod/*+disp8 0/rm32/EAX . . . 1/r32/ECX 4/disp8 . # copy ECX to *(EAX+4) +115 # perform a non-local jump to ed->target +116 8b/copy 0/mod/indirect 0/rm32/EAX . . . 4/r32/ESP . . # copy *EAX to ESP +117 $stop:end: +118 c3/return # doesn't return to caller +119 +120 test-stop-skips-returns-on-exit: +121 # This looks like the standard prolog, but is here for different reasons. +122 # A function calling 'stop' can't rely on EBP persisting past the call. +123 # +124 # Use EBP here as a stable base to refer to locals and arguments from in the +125 # presence of push/pop/call instructions. +126 # *Don't* use EBP as a way to restore ESP. +127 55/push-EBP +128 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +129 # Make room for an exit descriptor on the stack. That's almost always the +130 # right place for it, available only as long as it's legal to use. Once this +131 # containing function returns we'll need a new exit descriptor. +132 # var ed/EAX : (address exit-descriptor) +133 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP +134 89/copy 3/mod/direct 0/rm32/EAX . . . 4/r32/ESP . . # copy ESP to EAX +135 # Size the exit-descriptor precisely for the next call below, to _test-stop-1. +136 # tailor-exit-descriptor(ed, 4) +137 # . . push args +138 68/push 4/imm32/nbytes-of-args-for-_test-stop-1 +139 50/push-EAX +140 # . . call +141 e8/call tailor-exit-descriptor/disp32 +142 # . . discard args +143 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +144 # . _test-stop-1(ed) +145 # . . push args +146 50/push-EAX +147 # . . call +148 e8/call _test-stop-1/disp32 +149 # registers except ESP may be clobbered at this point +150 # restore args +151 58/pop-to-EAX +152 # check that _test-stop-1 tried to call exit(1) +153 # check-ints-equal(ed->value, 2, msg) # i.e. stop was called with value 1 +154 # . . push args +155 68/push "F - test-stop-skips-returns-on-exit"/imm32 +156 68/push 2/imm32 +157 # . . push ed->value +158 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) +159 # . . call +160 e8/call check-ints-equal/disp32 +161 # . . discard args +162 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +163 # . epilog +164 # don't restore ESP from EBP; manually reclaim locals +165 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +166 5d/pop-to-EBP +167 c3/return +168 +169 _test-stop-1: # ed : (address exit-descriptor) +170 # . prolog +171 55/push-EBP +172 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +173 # _test-stop-2(ed) +174 # . . push args +175 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) +176 # . . call +177 e8/call _test-stop-2/disp32 +178 # should never get past this point +179 $_test-stop-1:dead-end: +180 # . . discard args +181 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +182 # signal test failed: check-ints-equal(1, 0, msg) +183 # . . push args +184 68/push "F - test-stop-skips-returns-on-exit"/imm32 +185 68/push 0/imm32 +186 68/push 1/imm32 +187 # . . call +188 e8/call check-ints-equal/disp32 +189 # . . discard args +190 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +191 # . epilog +192 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +193 5d/pop-to-EBP +194 c3/return +195 +196 _test-stop-2: # ed : (address exit-descriptor) +197 # . prolog +198 55/push-EBP +199 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +200 # . stop(ed, 1) +201 # . . push args +202 68/push 1/imm32 +203 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) +204 # . . call +205 e8/call stop/disp32 +206 # should never get past this point +207 $_test-stop-2:dead-end: +208 # . epilog +209 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +210 5d/pop-to-EBP +211 c3/return +212 +213 # . . vim:nowrap:textwidth=0 -- cgit 1.4.1-2-gfad0