📄 except.c
字号:
/* Copyright (C) 1986 Sun Microsystems Inc. *//* * This source code is a product of Sun Microsystems, Inc. and is provided * for unrestricted use provided that this legend is included on all tape * media and as a part of the software program in whole or part. Users * may copy or modify this source code without charge, but are not authorized * to license or distribute it to anyone else except as part of a product or * program developed by the user. * * THIS PROGRAM CONTAINS SOURCE CODE COPYRIGHTED BY SUN MICROSYSTEMS, INC. * AND IS LICENSED TO SUNSOFT, INC., A SUBSIDIARY OF SUN MICROSYSTEMS, INC. * SUN MICROSYSTEMS, INC., MAKES NO REPRESENTATIONS ABOUT THE SUITABLITY * OF SUCH SOURCE CODE FOR ANY PURPOSE. IT IS PROVIDED "AS IS" WITHOUT * EXPRESS OR IMPLIED WARRANTY OF ANY KIND. SUN MICROSYSTEMS, INC. DISCLAIMS * ALL WARRANTIES WITH REGARD TO SUCH SOURCE CODE, INCLUDING ALL IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN * NO EVENT SHALL SUN MICROSYSTEMS, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT, * INCIDENTAL, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING * FROM USE OF SUCH SOURCE CODE, REGARDLESS OF THE THEORY OF LIABILITY. * * This source code is provided with no support and without any obligation on * the part of Sun Microsystems, Inc. to assist in its use, correction, * modification or enhancement. * * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS * SOURCE CODE OR ANY PART THEREOF. * * Sun Microsystems, Inc. * 2550 Garcia Avenue * Mountain View, California 94043 */#include "common.h"#include "queue.h"#include "asynch.h"#include "machsig.h"#include "machdep.h"#include "cntxt.h"#include "lwperror.h"#include "message.h"#include "process.h"#include "schedule.h"#include "alloc.h"#include "except.h"#include "monitor.h"#ifndef lintSCCSID(@(#) except.c 1.1 92/07/30 Copyr 1987 Sun Micro);#endif lint/* * PRIMITIVES contained herein: * exc_handle(pattern, func, env) * exc_unhandle() * (*exc_bound(pattern, env))() * exc_notify(pattern) * exc_raise(pattern) * exc_on_exit(func, arg) * exc_uniqpatt() *//* Globals to facilitate asm stuff. Must not be static. */exccontext_t *__Cntxt;caddr_t *__ExcRegs;int *__ProcRet;int __Argptr;STATIC int ExcType; /* cookie for exception context caches *//* * exc_uniqpatt() -- PRIMITIVE. * return a unique pattern that doesn't conflict with any * preassigned patterns. BUGS: wraparound voids uniqueness guarantee. * We know that 0 (EXITPATTERN), -1 (error) and -2 (CATCHALL) are * reserved. Also, special meaning is reserved for 1..NUMSIG-1 * for synchronous traps. * If -1 is returned, a non-unique pattern was returned. * Subsequently, all patterns returned may be duplicates. */intexc_uniqpatt(){ static int newpat = NUMSIG - 1; newpat++; if (newpat == CATCHALL) { newpat = NUMSIG - 1; SET_ERROR(__Curproc, LE_REUSE); return (-1); } return (newpat);}/* * exc_handle() -- PRIMITIVE. * Establish an exception handler. A procedure calling * exc_handle will be set up to return to a special * cleanup procedure that pops all exception contexts * before returning control to the original return point of the procedure. * The context saved by the handler will allow exc_raise() * to act as a non-local return to the point just after * the call to exc_handle(). exc_raise() actually accomplishes * control transfer via a context switch to the exc_handled context. * Special context (e.g., fpa regs) is NOT saved by exc_handle. * Only non-volatile regs are saved/restored by exc_handle/exc_raise. */intexc_handle(pattern, func, env) int pattern; /* pattern to match on excraise */ caddr_t (*func)(); /* func bound to exception for NOTIFY semantics */ caddr_t env; /* environment argument passed to func */{ /* no locals to simplify things (i.e., registers are unchanged) */ LWPINIT(); CLR_ERROR(); LOCK(); ERROR(((pattern == EXITPATTERN) || (pattern == -1)), LE_INVALIDARG); /* don't use local registers (proc call ok cause regs restored on ret) */ NR_GETCHUNK((exccontext_t *), __Cntxt, ExcType); __ExcRegs = (caddr_t *)__Cntxt->exc_regs; /* * assume procedure foo() calls procedure bar() and bar() contains * a call to exc_handle. Then the stack looks like: * * args to bar * return address back to foo <== __ProcRet * |-->a6 valid in foo <== ArgPtr * | locals in foo * | args to exc_handle() <== sp in foo (foo pops the args) * | ret addr just after call to exc_raise in bar() <== SP+4 * |<--a6 valid in bar <== SP, A6 */ /* save all volatile registers at point of handle call */ asm ("movl ___ExcRegs, a0"); asm ("moveml #0xfcfc, a0@"); asm ("movl a6@(0), ___Argptr"); /* caller's value of a6 */ __Cntxt->exc_regs[A6] = __Argptr; /* get return address (to return from exc_handle call) */ __Cntxt->exc_pc = *(int *) (__Cntxt->exc_regs[SP] + 4); /* * Set saved stack to point before call to exc_handle(). * When exc_handle returns, the sp will be here. */ __Cntxt->exc_regs[SP] += 2 * sizeof (int); /* * foo calls bar which calls exc_handle(). * __ProcRet points to the address where foo will return to * when bar returns. * Note that it's important that create_lwp take a procedure, * not just a starting address, because a6 must be set up correctly * for the handler to be accessible via a6 in the initial procedure. */ __ProcRet = (int *)(__Argptr + 4); /* addr of where return address is */ __Cntxt->exc_pattern = pattern; __Cntxt->exc_func = func; __Cntxt->exc_env = env; /* * If the exit routine (which is installed like a breakpoint) * isn't already established, do so by saving * the correct return address and installing the exit routine * exccleanup(). Otherwise, just bump the ref. count of * exception handlers set within the current procedure. * A ref count of 0 indicates the first handler in a procedure. * Thus, the stack of handlers will look like (for example): * 4 3 2 1 0 3 2 1 0 .... 1 0 * The ref count is only used to conveniently find the * initial exception handler in a procedure. */ if (*__ProcRet != (int) __exccleanup) { __Cntxt->exc_return = *__ProcRet; __Cntxt->exc_retaddr = (int *)__ProcRet; __Cntxt->exc_refcnt = 0; *__ProcRet = (int) __exccleanup; } else { __Cntxt->exc_refcnt = 1 + ((exccontext_t *)FIRSTQ(&__Curproc->lwp_exchandles))->exc_refcnt; } FRONT_QUEUE(&__Curproc->lwp_exchandles, __Cntxt); UNLOCK(); return (0);}/* * exc_on_exit -- PRIMITIVE. * Establish an exit handler for a procedure. * Each procedure may establish at most one exit handler * which will be invoked when the procedure exits (either * via a return or as a result of an exception). * The exit handler may not contain exc_raise's. * Further, the argument may not contain the address of a local variable * in the scope of the procedure. * Note that all local context (stack) is gone when the exit handler * is invoked. Also, any results returned by an exit handler are ignored: * the value returned by the returning procedure is preserved. */intexc_on_exit(func, arg) void (*func)(); caddr_t arg;{ register exccontext_t *cntxt; register exccontext_t *newcntxt; LWPINIT(); CLR_ERROR(); LOCK(); GETCHUNK((exccontext_t *), newcntxt, ExcType); newcntxt->exc_pattern = EXITPATTERN; newcntxt->exc_func = (caddr_t (*)())func; newcntxt->exc_env = arg; newcntxt->exc_refcnt = 0; asm ("movl a6@, ___ProcRet"); __ProcRet++; if (*__ProcRet == (int) __exccleanup) { /* * make this the first entry in the stack of handlers * for this process by adjusting ref counts. */ for (cntxt = (exccontext_t *)FIRSTQ(&__Curproc->lwp_exchandles); cntxt != EXCNULL; cntxt = cntxt->exc_next) { if (cntxt->exc_refcnt++ == 0) { newcntxt->exc_next = cntxt->exc_next; cntxt->exc_next = newcntxt; newcntxt->exc_return = cntxt->exc_return; newcntxt->exc_retaddr = cntxt->exc_retaddr; break; } } } else { newcntxt->exc_return = *__ProcRet; newcntxt->exc_retaddr = (int *)__ProcRet; *__ProcRet = (int) __exccleanup; FRONT_QUEUE(&__Curproc->lwp_exchandles, newcntxt); } UNLOCK(); return (0);}int __RetAddr; /* asm help *//* * exc_unhandle() -- PRIMITIVE. * Pop the most recent handler from handler stack and remove * __exccleanup if no more handlers remain in the procedure. * It is illegal to remove an exit handler this way. */intexc_unhandle(){ register exccontext_t *cntxt; CLR_ERROR(); LOCK(); /* * check that there is at least one handler left in * the procedure by looking at the return address. */ asm ("movl a6@, a0"); asm ("movl a0@(4), ___RetAddr"); ERROR((__RetAddr != (int)__exccleanup), LE_NONEXIST); REM_QUEUE((exccontext_t *), &__Curproc->lwp_exchandles, cntxt); if (cntxt == EXCNULL) __panic("garbage exception context"); if (cntxt->exc_pattern == EXITPATTERN) { FRONT_QUEUE(&__Curproc->lwp_exchandles, cntxt); TERROR(LE_NONEXIST); } /* * if last one, (note that no exit handler can be set here) * restore normal return address (replacing __exccleanup). */ if (cntxt->exc_refcnt == 0) { *cntxt->exc_retaddr = cntxt->exc_return; } FREECHUNK(ExcType, cntxt); UNLOCK(); return (0);}/* * exc_bound() -- PRIMITIVE. * returns function bound to handler and sets the reference * param "env" to the vaalue bound by the handler. * env is useful for indicating context of local variables. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -