<<>> <> <> <> <> <<>> DIRECTORY MIPSArchitecture USING [MIPSInstruction, MIPSContents], BreakWorldArchitecture USING [Address], Rope USING [ROPE]; MIPSManger: CEDAR DEFINITIONS ~ { <<>> <> <> <> <> <> <<>> <> <<>> <> << norm1 j patch norm1>> << norm2 noop norm2>> <> << noop>> <<>> <<-- This is the normal case, we just jump to the patch to exeucte the instructions.>> <<>> <> << bcc target j patch bcc target>> << delaySlot noop delaySlot>> <> << noop>> <<>> <<-- With a simple delay slot, we just jump to a copy (relocated) of the instruction and its delay slot. If the branch falls through, we just transfer back to the main code stream.>> << >> <> << jal(r) target jal patch j(r) target>> << delaySlot delaySlot noop>> <<>> <<-- The trick here is to call the patch, thus setting up the return address because the callee probably uses it (e.g. to return, or return aggregate results, etc.). We allow the instruction in the delay slot to execute (what if it messes with the return address? So be it, that's what it wanted to do.). In the manger, we jmpl to the callee without setting the return address.>> << >> <> << j(r) target j patch j(r) target>> << delaySlot delaySlot noop>> <<>> <<-- This is just a computed delayed transfer. We branch to a copy of the instruction. We allow the instruction in the delay slot to execute.>> <<>> <> <<>> <<>> <> Manger: TYPE ~ MACHINE DEPENDENT RECORD [ variants: SELECT COMPUTED MangerVariant FROM <> normal => [ <> sheep1: MIPSArchitecture.MIPSInstruction, sheep2: MIPSArchitecture.MIPSInstruction, normalContinue: MIPSArchitecture.MIPSInstruction, normalNoop: MIPSArchitecture.MIPSInstruction, tag: MIPSArchitecture.MIPSInstruction, breakpointPC: MIPSArchitecture.MIPSContents], branch => [ <> branchB: MIPSArchitecture.MIPSInstruction, branchDelay: MIPSArchitecture.MIPSInstruction, branchContinue: MIPSArchitecture.MIPSInstruction, branchNoop: MIPSArchitecture.MIPSInstruction, tag: MIPSArchitecture.MIPSInstruction, breakpointPC: MIPSArchitecture.MIPSContents], call => [ <> <> callJ: MIPSArchitecture.MIPSInstruction, callNoop: MIPSArchitecture.MIPSInstruction, dummy1: MIPSArchitecture.MIPSInstruction, dummy2: MIPSArchitecture.MIPSInstruction, tag: MIPSArchitecture.MIPSInstruction, breakpointPC: MIPSArchitecture.MIPSContents], jump => [ <> jumpJ: MIPSArchitecture.MIPSInstruction, jumpNoop: MIPSArchitecture.MIPSInstruction, dummy1: MIPSArchitecture.MIPSInstruction, dummy2: MIPSArchitecture.MIPSInstruction, tag: MIPSArchitecture.MIPSInstruction, breakpointPC: MIPSArchitecture.MIPSContents] ENDCASE ]; MangerVariant: TYPE ~ { noneOfTheAbove, normal, branch, call, jump }; NormalManger: TYPE ~ Manger.normal; BranchManger: TYPE ~ Manger.branch; CallManger: TYPE ~ Manger.call; JumpManger: TYPE ~ Manger.jump; <> Install: PROCEDURE [ address: BreakWorldArchitecture.Address, manger: BreakWorldArchitecture.Address, patchCode: BreakWorldArchitecture.Address] RETURNS []; Uninstall: PROCEDURE [ address: BreakWorldArchitecture.Address, manger: BreakWorldArchitecture.Address, patchCode: BreakWorldArchitecture.Address] RETURNS []; <> ErrorCode: TYPE ~ ATOM _ nullErrorCode; nullErrorCode: ErrorCode ~ NIL; ErrorMessage: TYPE ~ Rope.ROPE _ nullErrorMessage; nullErrorMessage: ErrorMessage ~ NIL; CantInstall: ERROR [code: ErrorCode, message: ErrorMessage]; CantUninstall: ERROR [code: ErrorCode, message: ErrorMessage]; }.