1 # stop: dependency-injected wrapper around the exit() syscall 2 # 3 # We'd like to be able to write tests for functions that call exit(), and to 4 # make assertions about whether they exit() or not in a given situation. To 5 # achieve this we'll call exit() via a smarter wrapper called 'stop'. 6 # 7 # In the context of a test, calling a function X that calls 'stop' (directly 8 # or through further intervening calls) will unwind the stack until X returns, 9 # so that we can say check any further assertions after the execution of X. To 10 # achieve this end, we'll pass the return address of X as a 'target' argument 11 # into X, plumbing it through to 'stop'. When 'stop' gets a non-null target it 12 # unwinds the stack until the target. If it gets a null target it calls 13 # exit(). 14 # 15 # We'd also like to get the exit status out of 'stop', so we'll combine the 16 # input target with an output status parameter into a type called 'exit-descriptor'. 17 # 18 # So the exit-descriptor looks like this: 19 # target: address # return address for 'stop' to unwind to 20 # value: int # exit status stop was called with 21 # 22 # 'stop' thus takes two parameters: an exit-descriptor and the exit status. 23 # 24 # 'stop' won't bother cleaning up any other processor state besides the stack, 25 # such as registers. Only esp will have a well-defined value after 'stop' 26 # returns. (This is a poor man's setjmp/longjmp, if you know what that is.) 27 # 28 # Before you can call any function that may call 'stop', you need to pass in an 29 # exit-descriptor to it. To create an exit-descriptor use 'tailor-exit-descriptor' 30 # below. It's not the most pleasant abstraction in the world. 31 # 32 # An exit-descriptor's target is its input, computed during 'tailor-exit-descriptor'. 33 # Its value is its output, computed during stop and available to the test. 34 35 == code 36 # instruction effective address register displacement immediate 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 # Configure an exit-descriptor for a call pushing 'nbytes' bytes of args to 41 # the stack. 42 # Ugly that we need to know the size of args. Don't allocate variables between 43 # tailor-exit-descriptor and the call it's for. 44 tailor-exit-descriptor: # ed: (addr exit-descriptor), nbytes: int 45 # . prologue 46 55/push-ebp 47 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 48 # . save registers 49 50/push-eax 50 51/push-ecx 51 # eax = nbytes 52 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 0/r32/eax 0xc/disp8 . # copy *(ebp+12) to eax 53 # Let X be the value of esp in the caller, before the call to tailor-exit-descriptor. 54 # The return address for a call in the caller's body will be at: 55 # X-8 if the caller takes 4 bytes of args for the exit-descriptor (add 4 bytes for the return address) 56 # X-12 if the caller takes 8 bytes of args 57 # ..and so on 58 # That's the value we need to return: X-nbytes-4 59 # 60 # However, we also need to account for the perturbance to esp caused by the 61 # call to tailor-exit-descriptor. It pushes 8 bytes of args followed by 4 62 # bytes for the return address and 4 bytes to push ebp above. 63 # So ebp at this point is X-16. 64 # 65 # So the return address for the next call in the caller is: 66 # ebp+8 if the caller takes 4 bytes of args<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html> <head> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <title>Mu - 000organization.cc</title> <meta name="Generator" content="Vim/8.1"> <meta name="plugin-version" content="vim8.1_v1"> <meta name="syntax" content="cpp"> <meta name="settings" content="number_lines,use_css,pre_wrap,no_foldcolumn,expand_tabs,line_ids,prevent_copy="> <meta name="colorscheme" content="minimal-light"> <style type="text/css"> <!-- pre { white-space: pre-wrap; font-family: monospace; color: #000000; background-color: #c6c6c6; } body { font-size:12pt; font-family: monospace; color: #000000; background-color: #c6c6c6; } a { color:inherit; } * { font-size:12pt; font-size: 1em; } .Delimiter { color: #c000c0; } .LineNr { } .Comment { color: #005faf; } .Constant { color: #008787; } .Identifier { color: #af5f00; } .Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } .PreProc { color: #c000c0; } --> </style> <script type='text/javascript'> <!-- /* function to open any folds containing a jumped-to line before jumping to it */ function JumpToLine() { var lineNum; lineNum = window.location.hash; lineNum = lineNum.substr(1); /* strip off '#' */ if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ if (lineElem) { lineElem.scrollIntoView(true); } return true; } if ('onhashchange' in window) { window.onhashchange = JumpToLine; } --> </script> </head> <body onload='JumpToLine();'> <a href='https://github.com/akkartik/mu/blob/master/000organization.cc'>https://github.com/akkartik/mu/blob/master/000organization.cc</a> <pre id='vimCodeElement'> <span id="L1" class="LineNr"> 1 </span><span class="Comment">//: You guessed right: the '000' prefix means you should start reading here.</span> <span id="L2" class="LineNr"> 2 </span><span class="Comment">//:</span> <span id="L3" class="LineNr"> 3 </span><span class="Comment">//: This project is set up to load all files with a numeric prefix. Just</span> <span id="L4" class="LineNr"> 4 </span><span class="Comment">//: create a new file and start hacking.</span> <span id="L5" class="LineNr"> 5 </span><span class="Comment">//:</span> <span id="L6" class="LineNr"> 6 </span><span class="Comment">//: The first few files (00*) are independent of what this program does, an</span> <span id="L7" class="LineNr"> 7 </span><span class="Comment">//: experimental skeleton that will hopefully make it both easier for others to</span<