Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

297 lines
10 KiB

4 years ago
  1. /*++
  2. Copyright (c) 1990 Microsoft Corporation
  3. Module Name:
  4. chandler.c
  5. Abstract:
  6. This module implements the C specific exception handler that provides
  7. structured condition handling for the C language.
  8. Author:
  9. David N. Cutler (davec) 11-Sep-1990
  10. Environment:
  11. Any mode.
  12. Revision History:
  13. Thomas Van Baak (tvb) 29-Apr-1992
  14. Adapted for Alpha AXP.
  15. --*/
  16. #include "nt.h"
  17. //
  18. // Define procedure prototypes for exception filter and termination handler
  19. // execution routines defined in jmpuwind.s.
  20. //
  21. LONG
  22. __C_ExecuteExceptionFilter (
  23. PEXCEPTION_POINTERS ExceptionPointers,
  24. EXCEPTION_FILTER ExceptionFilter,
  25. ULONG EstablisherFrame
  26. );
  27. VOID
  28. __C_ExecuteTerminationHandler (
  29. BOOLEAN AbnormalTermination,
  30. TERMINATION_HANDLER TerminationHandler,
  31. ULONG EstablisherFrame
  32. );
  33. EXCEPTION_DISPOSITION
  34. __C_specific_handler (
  35. IN struct _EXCEPTION_RECORD *ExceptionRecord,
  36. IN void *EstablisherFrame,
  37. IN OUT struct _CONTEXT *ContextRecord,
  38. IN OUT struct _DISPATCHER_CONTEXT *DispatcherContext
  39. )
  40. /*++
  41. Routine Description:
  42. This function scans the scope tables associated with the specified
  43. procedure and calls exception and termination handlers as necessary.
  44. This language specific exception handler function is called on a
  45. per-frame basis and in two different cases:
  46. First, the IS_DISPATCHING case, it is called by the exception
  47. dispatcher, RtlDispatchException, via the short assembler routine,
  48. __C_ExecuteHandlerForException, when trying to locate exception
  49. filters within the given frame.
  50. Second, the IS_UNWINDING case, it is called by the frame unwinder,
  51. RtlUnwind, via the short assembler routine, __C_ExecuteHandlerForUnwind,
  52. when unwinding the stack and trying to locate termination handlers
  53. within the given frame.
  54. Arguments:
  55. ExceptionRecord - Supplies a pointer to an exception record.
  56. EstablisherFrame - Supplies a pointer to frame of the establisher function.
  57. ContextRecord - Supplies a pointer to a context record.
  58. DispatcherContext - Supplies a pointer to the exception dispatcher or
  59. unwind dispatcher context.
  60. Return Value:
  61. If the exception is handled by one of the exception filter routines, then
  62. there is no return from this routine and RtlUnwind is called. Otherwise,
  63. an exception disposition value of continue execution or continue search is
  64. returned.
  65. --*/
  66. {
  67. ULONG ControlPc;
  68. EXCEPTION_FILTER ExceptionFilter;
  69. EXCEPTION_POINTERS ExceptionPointers;
  70. PRUNTIME_FUNCTION FunctionEntry;
  71. ULONG Index;
  72. PSCOPE_TABLE ScopeTable;
  73. ULONG TargetPc;
  74. TERMINATION_HANDLER TerminationHandler;
  75. LONG Value;
  76. //
  77. // Get the address of where control left the establisher, the address of
  78. // the function table entry that describes the function, and the address of
  79. // the scope table.
  80. //
  81. ControlPc = DispatcherContext->ControlPc;
  82. FunctionEntry = DispatcherContext->FunctionEntry;
  83. ScopeTable = (PSCOPE_TABLE)(FunctionEntry->HandlerData);
  84. //
  85. // The scope table HandlerAddress is either the address of an exception
  86. // filter or a termination handler. The C compiler wraps the code in the
  87. // exception filter expression or the termination handler clause within
  88. // an internal C function. The value of the scope table JumpTarget field
  89. // is used to distinguish an exception filter function (JumpTarget non zero)
  90. // from a termination handler function (JumpTarget is zero).
  91. //
  92. //
  93. // If an unwind is not in progress, then scan the scope table and call
  94. // the appropriate exception filter routines. Otherwise, scan the scope
  95. // table and call the appropriate termination handlers using the target
  96. // PC obtained from the context record.
  97. //
  98. if (IS_DISPATCHING(ExceptionRecord->ExceptionFlags)) {
  99. //
  100. // Set up the ExceptionPointers structure that is passed as the argument
  101. // to the exception filter. It is used by the compiler to implement the
  102. // intrinsic functions exception_code() and exception_info().
  103. //
  104. ExceptionPointers.ExceptionRecord = ExceptionRecord;
  105. ExceptionPointers.ContextRecord = ContextRecord;
  106. //
  107. // Scan the scope table and call the appropriate exception filter
  108. // routines. The scope table entries are known to be sorted by
  109. // increasing EndAddress order. Thus a linear scan will naturally
  110. // hit inner scope exception clauses before outer scope clauses.
  111. //
  112. for (Index = 0; Index < ScopeTable->Count; Index += 1) {
  113. if ((ControlPc >= ScopeTable->ScopeRecord[Index].BeginAddress) &&
  114. (ControlPc < ScopeTable->ScopeRecord[Index].EndAddress) &&
  115. (ScopeTable->ScopeRecord[Index].JumpTarget != 0)) {
  116. //
  117. // Call the exception filter routine.
  118. //
  119. ExceptionFilter =
  120. (EXCEPTION_FILTER)ScopeTable->ScopeRecord[Index].HandlerAddress;
  121. Value = __C_ExecuteExceptionFilter(&ExceptionPointers,
  122. ExceptionFilter,
  123. (ULONG)EstablisherFrame);
  124. //
  125. // If the return value is less than zero, then dismiss the
  126. // exception. Otherwise, if the value is greater than zero,
  127. // then unwind to the target exception handler corresponding
  128. // to the exception filter. Otherwise, continue the search for
  129. // an exception filter.
  130. //
  131. //
  132. // Exception filters will usually return one of the following
  133. // defines, although the decision below is made only by sign:
  134. //
  135. // #define EXCEPTION_EXECUTE_HANDLER 1
  136. // #define EXCEPTION_CONTINUE_SEARCH 0
  137. // #define EXCEPTION_CONTINUE_EXECUTION -1
  138. //
  139. if (Value < 0) {
  140. return ExceptionContinueExecution;
  141. } else if (Value > 0) {
  142. //
  143. // Set the return value for the unwind to the exception
  144. // code so the exception handler clause can retrieve it
  145. // from v0. This is how GetExceptionCode() is implemented
  146. // in exception handler clauses.
  147. //
  148. RtlUnwind2(EstablisherFrame,
  149. (PVOID)ScopeTable->ScopeRecord[Index].JumpTarget,
  150. ExceptionRecord,
  151. (PVOID)ExceptionRecord->ExceptionCode,
  152. ContextRecord);
  153. }
  154. }
  155. }
  156. } else {
  157. //
  158. // Scan the scope table and call the appropriate termination handler
  159. // routines.
  160. //
  161. TargetPc = (ULONG)ContextRecord->Fir;
  162. for (Index = 0; Index < ScopeTable->Count; Index += 1) {
  163. if ((ControlPc >= ScopeTable->ScopeRecord[Index].BeginAddress) &&
  164. (ControlPc < ScopeTable->ScopeRecord[Index].EndAddress)) {
  165. //
  166. // If the target PC is within the same scope the control PC
  167. // is within, then this is an uplevel goto out of an inner try
  168. // scope or a long jump back into a try scope. Terminate the
  169. // scan for termination handlers - because any other handlers
  170. // will be outside the scope of both the goto and its label.
  171. //
  172. // N.B. The target PC can be just beyond the end of the scope,
  173. // in which case it is a leave from the scope.
  174. //
  175. if ((TargetPc >= ScopeTable->ScopeRecord[Index].BeginAddress) &&
  176. (TargetPc <= ScopeTable->ScopeRecord[Index].EndAddress)) {
  177. break;
  178. } else {
  179. //
  180. // If the scope table entry describes an exception filter
  181. // and the associated exception handler is the target of
  182. // the unwind, then terminate the scan for termination
  183. // handlers. Otherwise, if the scope table entry describes
  184. // a termination handler, then record the address of the
  185. // end of the scope as the new control PC address and call
  186. // the termination handler.
  187. //
  188. // Recording a new control PC is necessary to ensure that
  189. // termination handlers are called only once even when a
  190. // collided unwind occurs.
  191. //
  192. if (ScopeTable->ScopeRecord[Index].JumpTarget != 0) {
  193. //
  194. // try/except - exception filter (JumpTarget != 0).
  195. // After the exception filter is called, the exception
  196. // handler clause is executed by the call to unwind
  197. // above. Having reached this point in the scan of the
  198. // scope tables, any other termination handlers will
  199. // be outside the scope of the try/except.
  200. //
  201. if (TargetPc == ScopeTable->ScopeRecord[Index].JumpTarget) {
  202. break;
  203. }
  204. } else {
  205. //
  206. // try/finally - termination handler (JumpTarget == 0).
  207. //
  208. //
  209. // Unless the termination handler results in a long
  210. // jump, execution will resume at the instruction after
  211. // the exception handler clause.
  212. //
  213. // ## tvb - I'm still suspicious of the +4 below.
  214. DispatcherContext->ControlPc =
  215. ScopeTable->ScopeRecord[Index].EndAddress + 4;
  216. TerminationHandler =
  217. (TERMINATION_HANDLER)ScopeTable->ScopeRecord[Index].HandlerAddress;
  218. __C_ExecuteTerminationHandler(TRUE,
  219. TerminationHandler,
  220. (ULONG)EstablisherFrame);
  221. }
  222. }
  223. }
  224. }
  225. }
  226. //
  227. // Continue search for exception filters or termination handlers.
  228. //
  229. return ExceptionContinueSearch;
  230. }