;------------------------------------------------------------------------------- .TITLE EXCEPTION_INTERCEPTOR .IDENT /V1.0/ ; This utility intercepts exceptions that pass through specified stack ; frames. The arrays that describe the exceptions are saved in dynami- ; cally-allocated memory. The handling of the exceptions (or lack of han- ; dling) is not affected in any way. ; Four routines are provided. ENABLE_EXCEPTION_INTERCEPTOR, a function ; that takes no arguments, enables interception for the stack frame from ; which it is called. The return value is a longword that identifies the ; frame; this value is required by other routines in this utility. Once ; interception has been enabled for a particular frame, the first excep- ; tion to pass through is intercepted and the signal and mechanism arrays ; that describe the exception are saved. Interception for the frame is ; then disabled. Finally, if the frame has an associated condition han- ; dler, control is transferred to it. Otherwise, the exception is re- ; signaled. ; DISABLE_EXCEPTION_INTERCEPTOR, a procedure that takes no arguments, dis- ; ables interception for the stack frame from which it is called. Inter- ; ception for the frame must be enabled at the time of the call. This ; procedure should be called if the frame has not encountered an exception ; and is about to return, so that dynamically-allocated memory related to ; the frame is freed. ; RETRIEVE_EXCEPTION_ARRAYS returns the exception arrays for a specified ; frame. It is a procedure that takes one read-only argument by value, ; the identifier of the frame as returned by ENABLE_EXCEPTION_INTERCEPTOR, ; and two write-only arguments by reference, the addresses of the signal ; and mechanism arrays, respectively. Note that the frame in question ; need not be active or even exist. If the frame has not yet encountered ; or never did encounter an exception, zeros are returned. ; Finally, FREE_INTERCEPTOR_MEMORY frees the dynamically-allocated memory ; related to a frame, including any exception arrays. It is a procedure ; that takes one read-only argument by value, the identifier of the frame. ; Note that the frame in question need not be active or even exist. This ; procedure may be called only if the frame has encountered an exception ; or if the frame no longer exists. ; None of the routines in this utility perform error checking on their in- ; puts or on the conditions under which they're called. Given that the ; inputs and conditions are valid, the only error that can arise from ; calling these routines is LIB$_INSVIRMEM; this error is signaled. ; Greg Janée ; General Research Corporation ; 5383 Hollister Ave. ; Santa Barbara, CA 93111 ; 805-964-7724 ;------------------------------------------------------------------------------- $CHFDEF $SFDEF $SSDEF .EXTRN LIB$FREE_VM .EXTRN LIB$GET_VM .EXTRN LIB$STOP .EXTRN OTS$MOVE3_R5 ; Our approach is to replace the specified frame's condition handler (if ; any) with our own. When our handler is called, we simply copy the ex- ; ception arrays and then either transfer control to the frame's original ; handler (if there is one) or resignal the exception (if not). ; A problem is that we must associate data (e.g., the address of the orig- ; inal condition handler) with the frame, but without modifying the frame. ; Furthermore, this data must be accessible by our condition handler. To ; solve this problem we create (for each frame enabled for interception) a ; block that contains both code and data. The first part of this block is ; a condition handler that replaces the frame's original condition han- ; dler. This handler's code is: ; ; ; CALLG (AP),@# ; RET ; ; Note that the frame's pointer to this condition handler is effectively ; a pointer to the block. Three longwords comprise the remainder of the ; block. The first longword is the address of the original condition han- ; dler. The second and third longwords are the addresses of the copies of ; the signal and mechanism arrays, respectively. When our condition han- ; dler is called by the above condition handler, it uses SF$L_SAVE_PC(FP) ; to locate the RET instruction in the block, and from there the other ; data in the block. BLOCK_SIZE = 22 CODE = 0 ORIGINAL_HANDLER = 10 SIGNAL_ARRAY = 14 MECHANISM_ARRAY = 18 ;------------------------------------------------------------------------------- ;------------------------------------------------------------------------------- ; ENABLE_EXCEPTION_INTERCEPTOR creates and initializes one of the above ; blocks, sets the calling frame's condition handler to the handler within ; the block, and returns the address of the block. If the block can't be ; allocated an exception is signaled. The signaling takes place before ; the calling frame's condition handler is modified. .PSECT $CODE,PIC,USR,CON,REL,LCL,SHR,EXE,RD,NOWRT,NOVEC,BYTE .ENTRY ENABLE_EXCEPTION_INTERCEPTOR,^M<> MOVQ #BLOCK_SIZE,-(SP) PUSHAL 4(SP) PUSHAL 4(SP) CALLS #2,G^LIB$GET_VM BLBC R0,1200$ ; We fill in the block starting with the handler's entry mask. Due to the ; way our condition handler operates, the calling frame's original handler ; runs in the context of the handler we are now creating. In particular, ; this means the handler we are now creating must have the same entry mask ; as the calling frame's original handler. In the following code we as- ; sume SF$A_HANDLER is 0. MOVL 4(SP),R0 MOVL @SF$L_SAVE_FP(FP),R1 BEQL 1000$ MOVW (R1),(R0)+ ; BRB 1100$ 1000$: CLRW (R0)+ ; 1100$: MOVB #^XFA,(R0)+ ; CALLG MOVB #^X6C,(R0)+ ; (AP) MOVB #^X9F,(R0)+ ; @# MOVAB B^REPLACEMENT_HANDLER,(R0)+ ; MOVB #^X04,(R0)+ ; RET ; The handler is complete. We finish up by initializing the remaining ; three longwords in the block and replacing the calling frame's condition ; handler. Again, we assume SF$A_HANDLER is 0. MOVL R1,(R0)+ CLRQ (R0)+ SUBL2 #BLOCK_SIZE,R0 MOVL R0,@SF$L_SAVE_FP(FP) RET 1200$: PUSHL R0 CALLS #1,G^LIB$STOP ;------------------------------------------------------------------------------- ;------------------------------------------------------------------------------- ; Our condition handler, REPLACEMENT_HANDLER, is called by the condition ; handler in one of our blocks, which in turn is called by VMS on behalf ; of the frame associated with the block (the "handling frame"). REPLACE- ; MENT_HANDLER resets the handling frame's condition handler to the origi- ; nal condition handler, saves the signal and mechanism arrays, and then ; transfers control to the original condition handler, if any. If the ; memory needed to hold the exception arrays can't be allocated, an excep- ; tion is signaled. Note that the signaling takes place after the han- ; dling frame's condition handler has been reset. REPLACEMENT_HANDLER: .WORD ^M ; SF$L_SAVE_PC(FP) points to the RET instruction in the block's handler; ; one byte beyond this is the block's ORIGINAL_HANDLER field. We reset ; the handling frame's condition handler first so that we will not be ; called during any unwind operations. In the following code we assume ; SF$A_HANDLER is 0. SUBL3 #,SF$L_SAVE_PC(FP),R6 MOVL CHF$L_MCHARGLST(AP),R2 MOVL ORIGINAL_HANDLER(R6),@CHF$L_MCH_FRAME(R2) ; We use SAVE_EXCEPTION_ARRAY_R5, defined below, to copy the signal and ; mechanism arrays. This subroutine accepts the address of an exception ; array in R2 and returns the address of the copy in R0. R1-5 are modi- ; fied on return. BSBB SAVE_EXCEPTION_ARRAY_R5 MOVL R0,MECHANISM_ARRAY(R6) MOVL CHF$L_SIGARGLST(AP),R2 BSBB SAVE_EXCEPTION_ARRAY_R5 MOVL R0,SIGNAL_ARRAY(R6) ; If there is an original condition handler we transfer control to it by ; modifying our frame's return PC and returning. Recall that the block's ; handler has the same entry mask as the handling frame's original condi- ; tion handler. We use this method in case the original condition handler ; assumes it is called directly by VMS. TSTL ORIGINAL_HANDLER(R6) BEQL 1300$ ADDL3 #2,ORIGINAL_HANDLER(R6),SF$L_SAVE_PC(FP) RET 1300$: MOVZWL #SS$_RESIGNAL,R0 RET ; The code for SAVE_EXCEPTION_ARRAY_R5 is straight-forward. SAVE_EXCEPTION_ARRAY_R5: PUSHL #0 ASHL #2,(R2),-(SP) ADDL2 #4,(SP) PUSHAL 4(SP) PUSHAL 4(SP) CALLS #2,G^LIB$GET_VM BLBC R0,1400$ MOVL (SP)+,R0 MOVL R2,R1 MOVL (SP),R2 JSB G^OTS$MOVE3_R5 MOVL (SP)+,R0 RSB 1400$: PUSHL R0 CALLS #1,G^LIB$STOP ;------------------------------------------------------------------------------- ;------------------------------------------------------------------------------- ; DISABLE_EXCEPTION_INTERCEPTOR locates the block associated with the ; calling frame via the frame's condition handler, resets the frame's con- ; dition handler to the original condition handler, and deallocates the ; block. In the following code we assume SF$A_HANDLER is 0. .ENTRY DISABLE_EXCEPTION_INTERCEPTOR,^M<> MOVL @SF$L_SAVE_FP(FP),R0 MOVL ORIGINAL_HANDLER(R0),@SF$L_SAVE_FP(FP) PUSHL R0 CALLS #1,B^FREE_INTERCEPTOR_MEMORY RET ;------------------------------------------------------------------------------- ;------------------------------------------------------------------------------- ; RETRIEVE_EXCEPTION_ARRAYS simply returns the addresses of the copied ar- ; rays for a specified frame. The frame is specified by its associated ; block. .ENTRY RETRIEVE_EXCEPTION_ARRAYS,^M<> MOVL 4(AP),R0 MOVL SIGNAL_ARRAY(R0),@8(AP) MOVL MECHANISM_ARRAY(R0),@12(AP) RET ;------------------------------------------------------------------------------- ;------------------------------------------------------------------------------- ; FREE_INTERCEPTOR_MEMORY deallocates a specified block and the attached ; exception array copies, if any. .ENTRY FREE_INTERCEPTOR_MEMORY,^M MOVL 4(AP),R2 ; We use FREE_EXCEPTION_ARRAY_R1, defined below, to deallocate the excep- ; tion array copies. This subroutine accepts the address of an exception ; array in R0. R0 and R1 are modified on return. The block may or may ; not have attached exception arrays since FREE_INTERCEPTOR_MEMORY may be ; called after the associated frame has returned without encountering an ; exception. MOVL SIGNAL_ARRAY(R2),R0 BEQL 1500$ BSBB FREE_EXCEPTION_ARRAY_R1 MOVL MECHANISM_ARRAY(R2),R0 BSBB FREE_EXCEPTION_ARRAY_R1 ; In any case we deallocate the block itself. 1500$: PUSHL R2 PUSHL #BLOCK_SIZE PUSHAL 4(SP) PUSHAL 4(SP) CALLS #2,G^LIB$FREE_VM RET ; The code for FREE_EXCEPTION_ARRAY_R1 is straight-forward. FREE_EXCEPTION_ARRAY_R1: PUSHL R0 ASHL #2,(R0),-(SP) ADDL2 #4,(SP) PUSHAL 4(SP) PUSHAL 4(SP) CALLS #2,G^LIB$FREE_VM ADDL2 #8,SP RSB ;------------------------------------------------------------------------------- .END