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.

2335 lines
82 KiB

4 years ago
  1. /*++
  2. Copyright (c) 1990 Microsoft Corporation
  3. Copyright (c) 1993 Digital Equipment Corporation
  4. Module Name:
  5. exdsptch.c
  6. Abstract:
  7. This module implements the dispatching of exceptions and the unwinding of
  8. procedure call frames.
  9. Author:
  10. David N. Cutler (davec) 11-Sep-1990
  11. Environment:
  12. Any mode.
  13. Revision History:
  14. Thomas Van Baak (tvb) 13-May-1992
  15. Adapted for Alpha AXP.
  16. --*/
  17. #include "ntrtlp.h"
  18. //
  19. // Define local macros.
  20. //
  21. // Raise noncontinuable exception with associated exception record.
  22. //
  23. #define RAISE_EXCEPTION(Status, ExceptionRecordt) { \
  24. EXCEPTION_RECORD ExceptionRecordn; \
  25. \
  26. ExceptionRecordn.ExceptionCode = Status; \
  27. ExceptionRecordn.ExceptionFlags = EXCEPTION_NONCONTINUABLE; \
  28. ExceptionRecordn.ExceptionRecord = ExceptionRecordt; \
  29. ExceptionRecordn.NumberParameters = 0; \
  30. RtlRaiseException(&ExceptionRecordn); \
  31. }
  32. //
  33. // The low 2 bits of ExceptionHandler are flags bits and not part of the
  34. // exception handler address.
  35. //
  36. #define IS_HANDLER_DEFINED(FunctionEntry) \
  37. (((ULONG)FunctionEntry->ExceptionHandler & ~0x3) != 0)
  38. #if DBG
  39. //
  40. // Maintain a short history of PC's for malformed function table errors.
  41. //
  42. #define PC_HISTORY_DEPTH 4
  43. //
  44. // Definition of global flag to debug/validate exception handling.
  45. // See ntrtlalp.h for the bit definitions in this flag word.
  46. //
  47. ULONG RtlDebugFlags = 0;
  48. #endif
  49. #define Virtual VirtualFramePointer
  50. #define Real RealFramePointer
  51. //
  52. // Define private function prototypes.
  53. //
  54. VOID
  55. RtlpRaiseException (
  56. IN PEXCEPTION_RECORD ExceptionRecord
  57. );
  58. VOID
  59. RtlpRaiseStatus (
  60. IN NTSTATUS Status
  61. );
  62. ULONG
  63. RtlpVirtualUnwind (
  64. IN ULONG ControlPc,
  65. IN PRUNTIME_FUNCTION FunctionEntry,
  66. IN PCONTEXT ContextRecord,
  67. OUT PBOOLEAN InFunction,
  68. OUT PULONG EstablisherFrame
  69. );
  70. BOOLEAN
  71. RtlDispatchException (
  72. IN PEXCEPTION_RECORD ExceptionRecord,
  73. IN PCONTEXT ContextRecord
  74. )
  75. /*++
  76. Routine Description:
  77. This function attempts to dispatch an exception to a frame based
  78. handler by searching backwards through the stack based call frames.
  79. The search begins with the frame specified in the context record and
  80. continues backward until either a handler is found that handles the
  81. exception, the stack is found to be invalid (i.e., out of limits or
  82. unaligned), or the end of the call hierarchy is reached.
  83. As each frame is encountered, the PC where control left the corresponding
  84. function is determined and used to lookup exception handler information
  85. in the runtime function table built by the linker. If the respective
  86. routine has an exception handler, then the handler is called. If the
  87. handler does not handle the exception, then the prologue of the routine
  88. is executed backwards to "unwind" the effect of the prologue and then
  89. the next frame is examined.
  90. Arguments:
  91. ExceptionRecord - Supplies a pointer to an exception record.
  92. ContextRecord - Supplies a pointer to a context record.
  93. Return Value:
  94. If the exception is handled by one of the frame based handlers, then
  95. a value of TRUE is returned. Otherwise a value of FALSE is returned.
  96. --*/
  97. {
  98. CONTEXT ContextRecord1;
  99. ULONG ControlPc;
  100. #if DBG
  101. ULONG ControlPcHistory[PC_HISTORY_DEPTH];
  102. ULONG ControlPcHistoryIndex = 0;
  103. #endif
  104. DISPATCHER_CONTEXT DispatcherContext;
  105. EXCEPTION_DISPOSITION Disposition;
  106. FRAME_POINTERS EstablisherFrame;
  107. ULONG ExceptionFlags;
  108. #if DBG
  109. LONG FrameDepth = 0;
  110. #endif
  111. PRUNTIME_FUNCTION FunctionEntry;
  112. ULONG HighLimit;
  113. BOOLEAN InFunction;
  114. ULONG LowLimit;
  115. ULONG NestedFrame;
  116. ULONG NextPc;
  117. //
  118. // Get current stack limits, copy the context record, get the initial
  119. // PC value, capture the exception flags, and set the nested exception
  120. // frame pointer.
  121. //
  122. // The initial PC value is obtained from ExceptionAddress rather than
  123. // from ContextRecord.Fir since some Alpha exceptions are asynchronous.
  124. //
  125. RtlpGetStackLimits(&LowLimit, &HighLimit);
  126. RtlMoveMemory(&ContextRecord1, ContextRecord, sizeof(CONTEXT));
  127. ControlPc = (ULONG)ExceptionRecord->ExceptionAddress;
  128. #if DBG
  129. if ((ULONG)ExceptionRecord->ExceptionAddress != (ULONG)ContextRecord->Fir) {
  130. DbgPrint("RtlDispatchException: ExceptionAddress = %lx, Fir = %lx\n",
  131. (ULONG)ExceptionRecord->ExceptionAddress, (ULONG)ContextRecord->Fir);
  132. }
  133. #endif
  134. ExceptionFlags = ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE;
  135. NestedFrame = 0;
  136. #if DBG
  137. if (RtlDebugFlags & RTL_DBG_DISPATCH_EXCEPTION) {
  138. DbgPrint("\nRtlDispatchException(ExceptionRecord = %lx, ContextRecord = %lx)\n",
  139. ExceptionRecord, ContextRecord);
  140. DbgPrint("RtlDispatchException: ControlPc = %lx, ExceptionRecord->ExceptionCode = %lx\n",
  141. ControlPc, ExceptionRecord->ExceptionCode);
  142. }
  143. #endif
  144. //
  145. // Start with the frame specified by the context record and search
  146. // backwards through the call frame hierarchy attempting to find an
  147. // exception handler that will handle the exception.
  148. //
  149. do {
  150. #if DBG
  151. if (RtlDebugFlags & RTL_DBG_DISPATCH_EXCEPTION_DETAIL) {
  152. DbgPrint("RtlDispatchException: Loop: FrameDepth = %d, sp = %lx, ControlPc = %lx\n",
  153. FrameDepth, ContextRecord1.IntSp, ControlPc);
  154. FrameDepth -= 1;
  155. }
  156. #endif
  157. //
  158. // Lookup the function table entry using the point at which control
  159. // left the procedure.
  160. //
  161. FunctionEntry = RtlLookupFunctionEntry(ControlPc);
  162. //
  163. // If there is a function table entry for the routine, then virtually
  164. // unwind to the caller of the current routine to obtain the virtual
  165. // frame pointer of the establisher and check if there is an exception
  166. // handler for the frame.
  167. //
  168. if (FunctionEntry != NULL) {
  169. NextPc = RtlVirtualUnwind(ControlPc,
  170. FunctionEntry,
  171. &ContextRecord1,
  172. &InFunction,
  173. &EstablisherFrame,
  174. NULL);
  175. //
  176. // If the virtual frame pointer is not within the specified stack
  177. // limits or the virtual frame pointer is unaligned, then set the
  178. // stack invalid flag in the exception record and return exception
  179. // not handled. Otherwise, check if the current routine has an
  180. // exception handler.
  181. //
  182. if ((EstablisherFrame.Virtual < LowLimit) ||
  183. (EstablisherFrame.Virtual > HighLimit) ||
  184. ((EstablisherFrame.Virtual & 0xF) != 0)) {
  185. #if DBG
  186. DbgPrint("\n****** Warning - stack invalid (exception).\n");
  187. DbgPrint(" EstablisherFrame.Virtual = %08lx, EstablisherFrame.Real = %08lx\n",
  188. EstablisherFrame.Virtual, EstablisherFrame.Real);
  189. DbgPrint(" LowLimit = %08lx, HighLimit = %08lx\n",
  190. LowLimit, HighLimit);
  191. DbgPrint(" NextPc = %08lx, ControlPc = %08lx\n",
  192. NextPc, ControlPc);
  193. DbgPrint(" Now setting EXCEPTION_STACK_INVALID flag.\n");
  194. #endif
  195. ExceptionFlags |= EXCEPTION_STACK_INVALID;
  196. break;
  197. } else if (IS_HANDLER_DEFINED(FunctionEntry) && InFunction) {
  198. ULONG Index;
  199. #if DBG
  200. if (RtlDebugFlags & RTL_DBG_DISPATCH_EXCEPTION_DETAIL) {
  201. DbgPrint("RtlDispatchException: ExceptionHandler = %lx, HandlerData = %lx\n",
  202. FunctionEntry->ExceptionHandler, FunctionEntry->HandlerData);
  203. }
  204. #endif
  205. //
  206. // The frame has an exception handler. The handler must be
  207. // executed by calling another routine that is written in
  208. // assembler. This is required because up level addressing
  209. // of the handler information is required when a nested
  210. // exception is encountered.
  211. //
  212. DispatcherContext.ControlPc = ControlPc;
  213. DispatcherContext.FunctionEntry = FunctionEntry;
  214. DispatcherContext.EstablisherFrame = EstablisherFrame.Virtual;
  215. DispatcherContext.ContextRecord = ContextRecord;
  216. ExceptionRecord->ExceptionFlags = ExceptionFlags;
  217. if (NtGlobalFlag & FLG_ENABLE_EXCEPTION_LOGGING) {
  218. Index = RtlpLogExceptionHandler(
  219. ExceptionRecord,
  220. ContextRecord,
  221. ControlPc,
  222. FunctionEntry,
  223. sizeof(RUNTIME_FUNCTION));
  224. }
  225. #if DBG
  226. if (RtlDebugFlags & RTL_DBG_DISPATCH_EXCEPTION_DETAIL) {
  227. DbgPrint("RtlDispatchException: calling RtlpExecuteHandlerForException, ControlPc = %lx\n", ControlPc);
  228. }
  229. #endif
  230. Disposition =
  231. RtlpExecuteHandlerForException(ExceptionRecord,
  232. EstablisherFrame.Virtual,
  233. ContextRecord,
  234. &DispatcherContext,
  235. FunctionEntry->ExceptionHandler);
  236. #if DBG
  237. if (RtlDebugFlags & RTL_DBG_DISPATCH_EXCEPTION_DETAIL) {
  238. DbgPrint("RtlDispatchException: RtlpExecuteHandlerForException returned Disposition = %lx\n", Disposition);
  239. }
  240. #endif
  241. if (NtGlobalFlag & FLG_ENABLE_EXCEPTION_LOGGING) {
  242. RtlpLogLastExceptionDisposition(Index, Disposition);
  243. }
  244. ExceptionFlags |=
  245. (ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE);
  246. //
  247. // If the current scan is within a nested context and the frame
  248. // just examined is the end of the nested region, then clear
  249. // the nested context frame and the nested exception flag in
  250. // the exception flags.
  251. //
  252. if (NestedFrame == EstablisherFrame.Virtual) {
  253. ExceptionFlags &= (~EXCEPTION_NESTED_CALL);
  254. NestedFrame = 0;
  255. }
  256. //
  257. // Case on the handler disposition.
  258. //
  259. switch (Disposition) {
  260. //
  261. // The disposition is to continue execution.
  262. //
  263. // If the exception is not continuable, then raise the
  264. // exception STATUS_NONCONTINUABLE_EXCEPTION. Otherwise
  265. // return exception handled.
  266. //
  267. case ExceptionContinueExecution :
  268. if ((ExceptionFlags & EXCEPTION_NONCONTINUABLE) != 0) {
  269. RAISE_EXCEPTION(STATUS_NONCONTINUABLE_EXCEPTION, ExceptionRecord);
  270. } else {
  271. #if DBG
  272. if (RtlDebugFlags & RTL_DBG_DISPATCH_EXCEPTION) {
  273. DbgPrint("RtlDispatchException: returning TRUE\n");
  274. }
  275. #endif
  276. return TRUE;
  277. }
  278. //
  279. // The disposition is to continue the search.
  280. //
  281. // Get next frame address and continue the search.
  282. //
  283. case ExceptionContinueSearch :
  284. break;
  285. //
  286. // The disposition is nested exception.
  287. //
  288. // Set the nested context frame to the establisher frame
  289. // address and set the nested exception flag in the
  290. // exception flags.
  291. //
  292. case ExceptionNestedException :
  293. ExceptionFlags |= EXCEPTION_NESTED_CALL;
  294. if (DispatcherContext.EstablisherFrame > NestedFrame) {
  295. NestedFrame = DispatcherContext.EstablisherFrame;
  296. }
  297. break;
  298. //
  299. // All other disposition values are invalid.
  300. //
  301. // Raise invalid disposition exception.
  302. //
  303. default :
  304. RAISE_EXCEPTION(STATUS_INVALID_DISPOSITION, ExceptionRecord);
  305. }
  306. }
  307. } else {
  308. //
  309. // Set point at which control left the previous routine.
  310. //
  311. NextPc = (ULONG)ContextRecord1.IntRa - 4;
  312. //
  313. // If the next control PC is the same as the old control PC, then
  314. // the function table is not correctly formed.
  315. //
  316. if (NextPc == ControlPc) {
  317. #if DBG
  318. ULONG Count;
  319. DbgPrint("\n****** Warning - malformed function table (exception).\n");
  320. DbgPrint("ControlPc = %08lx, %08lx", NextPc, ControlPc);
  321. for (Count = 0; Count < PC_HISTORY_DEPTH; Count += 1) {
  322. if (ControlPcHistoryIndex > 0) {
  323. ControlPcHistoryIndex -= 1;
  324. ControlPc = ControlPcHistory[ControlPcHistoryIndex % PC_HISTORY_DEPTH];
  325. DbgPrint(", %08lx", ControlPc);
  326. }
  327. }
  328. DbgPrint(ControlPcHistoryIndex == 0 ? ".\n" : ", ...\n");
  329. #endif
  330. break;
  331. }
  332. }
  333. //
  334. // Set point at which control left the previous routine.
  335. //
  336. #if DBG
  337. ControlPcHistory[ControlPcHistoryIndex % PC_HISTORY_DEPTH] = ControlPc;
  338. ControlPcHistoryIndex += 1;
  339. #endif
  340. ControlPc = NextPc;
  341. } while ((ULONG)ContextRecord1.IntSp < HighLimit);
  342. //
  343. // Set final exception flags and return exception not handled.
  344. //
  345. ExceptionRecord->ExceptionFlags = ExceptionFlags;
  346. #if DBG
  347. if (RtlDebugFlags & RTL_DBG_DISPATCH_EXCEPTION) {
  348. DbgPrint("RtlDispatchException: returning FALSE\n");
  349. }
  350. #endif
  351. return FALSE;
  352. }
  353. PRUNTIME_FUNCTION
  354. RtlLookupFunctionEntry (
  355. IN ULONG ControlPc
  356. )
  357. /*++
  358. Routine Description:
  359. This function searches the currently active function tables for an entry
  360. that corresponds to the specified PC value.
  361. Arguments:
  362. ControlPc - Supplies the address of an instruction within the specified
  363. function.
  364. Return Value:
  365. If there is no entry in the function table for the specified PC, then
  366. NULL is returned. Otherwise, the address of the function table entry
  367. that corresponds to the specified PC is returned.
  368. --*/
  369. {
  370. PRUNTIME_FUNCTION FunctionEntry;
  371. PRUNTIME_FUNCTION FunctionTable;
  372. ULONG SizeOfExceptionTable;
  373. LONG High;
  374. PVOID ImageBase;
  375. LONG Low;
  376. LONG Middle;
  377. //
  378. // Search for the image that includes the specified PC value.
  379. //
  380. ImageBase = RtlPcToFileHeader((PVOID)ControlPc, &ImageBase);
  381. #if DBG
  382. if (RtlDebugFlags & RTL_DBG_FUNCTION_ENTRY) {
  383. DbgPrint("RtlLookupFunctionEntry(ControlPc = %lx) ImageBase = %lx\n",
  384. ControlPc, ImageBase);
  385. }
  386. #endif
  387. //
  388. // If an image is found that includes the specified PC, then locate the
  389. // function table for the image.
  390. //
  391. if (ImageBase != NULL) {
  392. FunctionTable = (PRUNTIME_FUNCTION)RtlImageDirectoryEntryToData(
  393. ImageBase, TRUE, IMAGE_DIRECTORY_ENTRY_EXCEPTION,
  394. &SizeOfExceptionTable);
  395. #if DBG
  396. if (RtlDebugFlags & RTL_DBG_FUNCTION_ENTRY_DETAIL) {
  397. DbgPrint("RtlLookupFunctionEntry: FunctionTable = %lx, SizeOfExceptionTable = %lx\n",
  398. FunctionTable, SizeOfExceptionTable);
  399. }
  400. #endif
  401. //
  402. // If a function table is located, then search the function table
  403. // for a function table entry for the specified PC.
  404. //
  405. if (FunctionTable != NULL) {
  406. //
  407. // Initialize search indicies.
  408. //
  409. Low = 0;
  410. High = (SizeOfExceptionTable / sizeof(RUNTIME_FUNCTION)) - 1;
  411. //
  412. // Perform binary search on the function table for a function table
  413. // entry that subsumes the specified PC.
  414. //
  415. while (High >= Low) {
  416. //
  417. // Compute next probe index and test entry. If the specified PC
  418. // is greater than of equal to the beginning address and less
  419. // than the ending address of the function table entry, then
  420. // return the address of the function table entry. Otherwise,
  421. // continue the search.
  422. //
  423. Middle = (Low + High) >> 1;
  424. FunctionEntry = &FunctionTable[Middle];
  425. if (ControlPc < FunctionEntry->BeginAddress) {
  426. High = Middle - 1;
  427. } else if (ControlPc >= FunctionEntry->EndAddress) {
  428. Low = Middle + 1;
  429. } else {
  430. #if DBG
  431. if (RtlDebugFlags & RTL_DBG_FUNCTION_ENTRY_DETAIL) {
  432. DbgPrint(" BeginAddress = %lx\n", FunctionEntry->BeginAddress);
  433. DbgPrint(" EndAddress = %lx\n", FunctionEntry->EndAddress);
  434. DbgPrint(" PrologEndAddress = %lx\n", FunctionEntry->PrologEndAddress);
  435. DbgPrint(" ExceptionHandler = %lx\n", FunctionEntry->ExceptionHandler);
  436. DbgPrint(" HandlerData = %lx\n", FunctionEntry->HandlerData);
  437. }
  438. #endif
  439. //
  440. // The capability exists for more than one function entry
  441. // to map to the same function. This permits a function to
  442. // have (within reason) discontiguous code segment(s). If
  443. // PrologEndAddress is out of range, it is re-interpreted
  444. // as a pointer to the primary function table entry for
  445. // that function.
  446. //
  447. if ((FunctionEntry->PrologEndAddress < FunctionEntry->BeginAddress) ||
  448. (FunctionEntry->PrologEndAddress > FunctionEntry->EndAddress)) {
  449. FunctionEntry = (PRUNTIME_FUNCTION)FunctionEntry->PrologEndAddress;
  450. #if DBG
  451. if (RtlDebugFlags & RTL_DBG_FUNCTION_ENTRY_DETAIL) {
  452. DbgPrint("RtlLookupFunctionEntry: ** indirect function entry **\n");
  453. DbgPrint(" BeginAddress = %lx\n", FunctionEntry->BeginAddress);
  454. DbgPrint(" EndAddress = %lx\n", FunctionEntry->EndAddress);
  455. DbgPrint(" PrologEndAddress = %lx\n", FunctionEntry->PrologEndAddress);
  456. DbgPrint(" ExceptionHandler = %lx\n", FunctionEntry->ExceptionHandler);
  457. DbgPrint(" HandlerData = %lx\n", FunctionEntry->HandlerData);
  458. }
  459. #endif
  460. }
  461. #if DBG
  462. if (RtlDebugFlags & RTL_DBG_FUNCTION_ENTRY_DETAIL) {
  463. DbgPrint("RtlLookupFunctionEntry: returning FunctionEntry = %lx\n",
  464. FunctionEntry);
  465. }
  466. #endif
  467. return FunctionEntry;
  468. }
  469. }
  470. }
  471. }
  472. //
  473. // A function table entry for the specified PC was not found.
  474. //
  475. #if DBG
  476. if (RtlDebugFlags & RTL_DBG_FUNCTION_ENTRY) {
  477. DbgPrint("RtlLookupFunctionEntry: returning FunctionEntry = NULL\n");
  478. }
  479. #endif
  480. return NULL;
  481. }
  482. VOID
  483. RtlRaiseException (
  484. IN PEXCEPTION_RECORD ExceptionRecord
  485. )
  486. /*++
  487. Routine Description:
  488. This function raises a software exception by building a context record
  489. and calling the raise exception system service.
  490. N.B. This routine is a shell routine that simply calls another routine
  491. to do the real work. The reason this is done is to avoid a problem
  492. in try/finally scopes where the last statement in the scope is a
  493. call to raise an exception.
  494. Arguments:
  495. ExceptionRecord - Supplies a pointer to an exception record.
  496. Return Value:
  497. None.
  498. --*/
  499. {
  500. #if DBG
  501. if (RtlDebugFlags & RTL_DBG_RAISE_EXCEPTION) {
  502. DbgPrint("RtlRaiseException(ExceptionRecord = %lx) Status = %lx\n",
  503. ExceptionRecord, ExceptionRecord->ExceptionCode);
  504. }
  505. #endif
  506. RtlpRaiseException(ExceptionRecord);
  507. return;
  508. }
  509. VOID
  510. RtlpRaiseException (
  511. IN PEXCEPTION_RECORD ExceptionRecord
  512. )
  513. /*++
  514. Routine Description:
  515. This function raises a software exception by building a context record
  516. and calling the raise exception system service.
  517. Arguments:
  518. ExceptionRecord - Supplies a pointer to an exception record.
  519. Return Value:
  520. None.
  521. --*/
  522. {
  523. ULONG ControlPc;
  524. CONTEXT ContextRecord;
  525. FRAME_POINTERS EstablisherFrame;
  526. PRUNTIME_FUNCTION FunctionEntry;
  527. BOOLEAN InFunction;
  528. ULONG NextPc;
  529. NTSTATUS Status;
  530. //
  531. // Capture the current context, virtually unwind to the caller of this
  532. // routine, set the fault instruction address to that of the caller, and
  533. // call the raise exception system service.
  534. //
  535. RtlCaptureContext(&ContextRecord);
  536. ControlPc = (ULONG)ContextRecord.IntRa - 4;
  537. FunctionEntry = RtlLookupFunctionEntry(ControlPc);
  538. NextPc = RtlVirtualUnwind(ControlPc,
  539. FunctionEntry,
  540. &ContextRecord,
  541. &InFunction,
  542. &EstablisherFrame,
  543. NULL);
  544. ContextRecord.Fir = (ULONGLONG)(LONG)NextPc + 4;
  545. ExceptionRecord->ExceptionAddress = (PVOID)ContextRecord.Fir;
  546. Status = ZwRaiseException(ExceptionRecord, &ContextRecord, TRUE);
  547. //
  548. // There should never be a return from this system service unless
  549. // there is a problem with the argument list itself. Raise another
  550. // exception specifying the status value returned.
  551. //
  552. RtlRaiseStatus(Status);
  553. return;
  554. }
  555. VOID
  556. RtlRaiseStatus (
  557. IN NTSTATUS Status
  558. )
  559. /*++
  560. Routine Description:
  561. This function raises an exception with the specified status value. The
  562. exception is marked as noncontinuable with no parameters.
  563. N.B. This routine is a shell routine that simply calls another routine
  564. to do the real work. The reason this is done is to avoid a problem
  565. in try/finally scopes where the last statement in the scope is a
  566. call to raise an exception.
  567. Arguments:
  568. Status - Supplies the status value to be used as the exception code
  569. for the exception that is to be raised.
  570. Return Value:
  571. None.
  572. --*/
  573. {
  574. #if DBG
  575. if (RtlDebugFlags & RTL_DBG_RAISE_EXCEPTION) {
  576. DbgPrint("RtlRaiseStatus(Status = %lx)\n", Status);
  577. }
  578. #endif
  579. RtlpRaiseStatus(Status);
  580. return;
  581. }
  582. VOID
  583. RtlpRaiseStatus (
  584. IN NTSTATUS Status
  585. )
  586. /*++
  587. Routine Description:
  588. This function raises an exception with the specified status value. The
  589. exception is marked as noncontinuable with no parameters.
  590. Arguments:
  591. Status - Supplies the status value to be used as the exception code
  592. for the exception that is to be raised.
  593. Return Value:
  594. None.
  595. --*/
  596. {
  597. ULONG ControlPc;
  598. CONTEXT ContextRecord;
  599. FRAME_POINTERS EstablisherFrame;
  600. EXCEPTION_RECORD ExceptionRecord;
  601. PRUNTIME_FUNCTION FunctionEntry;
  602. BOOLEAN InFunction;
  603. ULONG NextPc;
  604. //
  605. // Construct an exception record.
  606. //
  607. ExceptionRecord.ExceptionCode = Status;
  608. ExceptionRecord.ExceptionRecord = (PEXCEPTION_RECORD)NULL;
  609. ExceptionRecord.NumberParameters = 0;
  610. ExceptionRecord.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
  611. //
  612. // Capture the current context, virtually unwind to the caller of this
  613. // routine, set the fault instruction address to that of the caller, and
  614. // call the raise exception system service.
  615. //
  616. RtlCaptureContext(&ContextRecord);
  617. ControlPc = (ULONG)ContextRecord.IntRa - 4;
  618. FunctionEntry = RtlLookupFunctionEntry(ControlPc);
  619. NextPc = RtlVirtualUnwind(ControlPc,
  620. FunctionEntry,
  621. &ContextRecord,
  622. &InFunction,
  623. &EstablisherFrame,
  624. NULL);
  625. ContextRecord.Fir = (ULONGLONG)(LONG)NextPc + 4;
  626. ExceptionRecord.ExceptionAddress = (PVOID)ContextRecord.Fir;
  627. Status = ZwRaiseException(&ExceptionRecord, &ContextRecord, TRUE);
  628. //
  629. // There should never be a return from this system service unless
  630. // there is a problem with the argument list itself. Raise another
  631. // exception specifying the status value returned.
  632. //
  633. RtlRaiseStatus(Status);
  634. return;
  635. }
  636. VOID
  637. RtlUnwind (
  638. IN PVOID TargetFrame OPTIONAL,
  639. IN PVOID TargetIp OPTIONAL,
  640. IN PEXCEPTION_RECORD ExceptionRecord OPTIONAL,
  641. IN PVOID ReturnValue
  642. )
  643. /*++
  644. Routine Description:
  645. This function initiates an unwind of procedure call frames. The machine
  646. state at the time of the call to unwind is captured in a context record
  647. and the unwinding flag is set in the exception flags of the exception
  648. record. If the TargetFrame parameter is not specified, then the exit unwind
  649. flag is also set in the exception flags of the exception record. A backward
  650. scan through the procedure call frames is then performed to find the target
  651. of the unwind operation.
  652. As each frame is encounter, the PC where control left the corresponding
  653. function is determined and used to lookup exception handler information
  654. in the runtime function table built by the linker. If the respective
  655. routine has an exception handler, then the handler is called.
  656. N.B. This routine is provided for backward compatibility with release 1.
  657. Arguments:
  658. TargetFrame - Supplies an optional pointer to the call frame that is the
  659. target of the unwind. If this parameter is not specified, then an exit
  660. unwind is performed.
  661. TargetIp - Supplies an optional instruction address that specifies the
  662. continuation address of the unwind. This address is ignored if the
  663. target frame parameter is not specified.
  664. ExceptionRecord - Supplies an optional pointer to an exception record.
  665. ReturnValue - Supplies a value that is to be placed in the integer
  666. function return register just before continuing execution.
  667. Return Value:
  668. None.
  669. --*/
  670. {
  671. CONTEXT ContextRecord;
  672. //
  673. // Call real unwind routine specifying a context record as an
  674. // extra argument.
  675. //
  676. RtlUnwind2(TargetFrame,
  677. TargetIp,
  678. ExceptionRecord,
  679. ReturnValue,
  680. &ContextRecord);
  681. return;
  682. }
  683. VOID
  684. RtlUnwind2 (
  685. IN PVOID TargetFrame OPTIONAL,
  686. IN PVOID TargetIp OPTIONAL,
  687. IN PEXCEPTION_RECORD ExceptionRecord OPTIONAL,
  688. IN PVOID ReturnValue,
  689. IN PCONTEXT ContextRecord
  690. )
  691. /*++
  692. Routine Description:
  693. This function initiates an unwind of procedure call frames. The machine
  694. state at the time of the call to unwind is captured in a context record
  695. and the unwinding flag is set in the exception flags of the exception
  696. record. If the TargetFrame parameter is not specified, then the exit unwind
  697. flag is also set in the exception flags of the exception record. A backward
  698. scan through the procedure call frames is then performed to find the target
  699. of the unwind operation.
  700. As each frame is encounter, the PC where control left the corresponding
  701. function is determined and used to lookup exception handler information
  702. in the runtime function table built by the linker. If the respective
  703. routine has an exception handler, then the handler is called.
  704. N.B. This routine is provided for backward compatibility with release 1.
  705. Arguments:
  706. TargetFrame - Supplies an optional pointer to the call frame that is the
  707. target of the unwind. If this parameter is not specified, then an exit
  708. unwind is performed.
  709. TargetIp - Supplies an optional instruction address that specifies the
  710. continuation address of the unwind. This address is ignored if the
  711. target frame parameter is not specified.
  712. ExceptionRecord - Supplies an optional pointer to an exception record.
  713. ReturnValue - Supplies a value that is to be placed in the integer
  714. function return register just before continuing execution.
  715. Return Value:
  716. None.
  717. --*/
  718. {
  719. ULONG ControlPc;
  720. #if DBG
  721. ULONG ControlPcHistory[PC_HISTORY_DEPTH];
  722. ULONG ControlPcHistoryIndex = 0;
  723. #endif
  724. DISPATCHER_CONTEXT DispatcherContext;
  725. EXCEPTION_DISPOSITION Disposition;
  726. FRAME_POINTERS EstablisherFrame;
  727. ULONG ExceptionFlags;
  728. EXCEPTION_RECORD ExceptionRecord1;
  729. #if DBG
  730. LONG FrameDepth = 0;
  731. #endif
  732. PRUNTIME_FUNCTION FunctionEntry;
  733. ULONG HighLimit;
  734. BOOLEAN InFunction;
  735. ULONG LowLimit;
  736. ULONG NextPc;
  737. #if DBG
  738. if (RtlDebugFlags & RTL_DBG_UNWIND) {
  739. DbgPrint("\nRtlUnwind(TargetFrame = %lx, TargetIp = %lx,, ReturnValue = %lx)\n",
  740. TargetFrame, TargetIp, ReturnValue);
  741. }
  742. #endif
  743. //
  744. // Get current stack limits, capture the current context, virtually
  745. // unwind to the caller of this routine, get the initial PC value, and
  746. // set the unwind target address.
  747. //
  748. RtlpGetStackLimits(&LowLimit, &HighLimit);
  749. RtlCaptureContext(ContextRecord);
  750. ControlPc = (ULONG)ContextRecord->IntRa - 4;
  751. FunctionEntry = RtlLookupFunctionEntry(ControlPc);
  752. NextPc = RtlVirtualUnwind(ControlPc,
  753. FunctionEntry,
  754. ContextRecord,
  755. &InFunction,
  756. &EstablisherFrame,
  757. NULL);
  758. ControlPc = NextPc;
  759. ContextRecord->Fir = (ULONGLONG)(LONG)TargetIp;
  760. //
  761. // If an exception record is not specified, then build a local exception
  762. // record for use in calling exception handlers during the unwind operation.
  763. //
  764. if (ARGUMENT_PRESENT(ExceptionRecord) == FALSE) {
  765. ExceptionRecord = &ExceptionRecord1;
  766. ExceptionRecord1.ExceptionCode = STATUS_UNWIND;
  767. ExceptionRecord1.ExceptionRecord = NULL;
  768. ExceptionRecord1.ExceptionAddress = (PVOID)ControlPc;
  769. ExceptionRecord1.NumberParameters = 0;
  770. }
  771. //
  772. // If the target frame of the unwind is specified, then a normal unwind
  773. // is being performed. Otherwise, an exit unwind is being performed.
  774. //
  775. ExceptionFlags = EXCEPTION_UNWINDING;
  776. if (ARGUMENT_PRESENT(TargetFrame) == FALSE) {
  777. ExceptionRecord->ExceptionFlags |= EXCEPTION_EXIT_UNWIND;
  778. }
  779. //
  780. // Scan backward through the call frame hierarchy and call exception
  781. // handlers until the target frame of the unwind is reached.
  782. //
  783. do {
  784. #if DBG
  785. if (RtlDebugFlags & RTL_DBG_UNWIND_DETAIL) {
  786. DbgPrint("RtlUnwind: Loop: FrameDepth = %d, sp = %lx, ControlPc = %lx\n",
  787. FrameDepth, ContextRecord->IntSp, ControlPc);
  788. FrameDepth -= 1;
  789. }
  790. #endif
  791. //
  792. // Lookup the function table entry using the point at which control
  793. // left the procedure.
  794. //
  795. FunctionEntry = RtlLookupFunctionEntry(ControlPc);
  796. //
  797. // If there is a function table entry for the routine, then virtually
  798. // unwind to the caller of the routine to obtain the virtual frame
  799. // pointer of the establisher, but don't update the context record.
  800. //
  801. if (FunctionEntry != NULL) {
  802. NextPc = RtlpVirtualUnwind(ControlPc,
  803. FunctionEntry,
  804. ContextRecord,
  805. &InFunction,
  806. (PULONG)&EstablisherFrame);
  807. //
  808. // If the virtual frame pointer is not within the specified stack
  809. // limits, the virtual frame pointer is unaligned, or the target
  810. // frame is below the virtual frame and an exit unwind is not being
  811. // performed, then raise the exception STATUS_BAD_STACK. Otherwise,
  812. // check to determine if the current routine has an exception
  813. // handler.
  814. //
  815. if ((EstablisherFrame.Virtual < LowLimit) ||
  816. (EstablisherFrame.Virtual > HighLimit) ||
  817. ((ARGUMENT_PRESENT(TargetFrame) != FALSE) &&
  818. ((ULONG)TargetFrame < EstablisherFrame.Virtual)) ||
  819. ((EstablisherFrame.Virtual & 0xF) != 0)) {
  820. #if DBG
  821. DbgPrint("\n****** Warning - bad stack or target frame (unwind).\n");
  822. DbgPrint(" EstablisherFrame Virtual = %08lx, Real = %08lx\n",
  823. EstablisherFrame.Virtual, EstablisherFrame.Real);
  824. DbgPrint(" TargetFrame = %08lx\n", TargetFrame);
  825. if ((ARGUMENT_PRESENT(TargetFrame) != FALSE) &&
  826. ((ULONG)TargetFrame < EstablisherFrame.Virtual)) {
  827. DbgPrint(" TargetFrame is below EstablisherFrame!\n");
  828. }
  829. DbgPrint(" Previous EstablisherFrame (sp) = %08lx\n",
  830. (ULONG)ContextRecord->IntSp);
  831. DbgPrint(" LowLimit = %08lx, HighLimit = %08lx\n",
  832. LowLimit, HighLimit);
  833. DbgPrint(" NextPc = %08lx, ControlPc = %08lx\n",
  834. NextPc, ControlPc);
  835. DbgPrint(" Now raising STATUS_BAD_STACK exception.\n");
  836. #endif
  837. RAISE_EXCEPTION(STATUS_BAD_STACK, ExceptionRecord);
  838. } else if (IS_HANDLER_DEFINED(FunctionEntry) && InFunction) {
  839. #if DBG
  840. if (RtlDebugFlags & RTL_DBG_DISPATCH_EXCEPTION_DETAIL) {
  841. DbgPrint("RtlUnwind: ExceptionHandler = %lx, HandlerData = %lx\n",
  842. FunctionEntry->ExceptionHandler, FunctionEntry->HandlerData);
  843. }
  844. #endif
  845. //
  846. // The frame has an exception handler.
  847. //
  848. // The control PC, establisher frame pointer, the address
  849. // of the function table entry, and the address of the
  850. // context record are all stored in the dispatcher context.
  851. // This information is used by the unwind linkage routine
  852. // and can be used by the exception handler itself.
  853. //
  854. // A linkage routine written in assembler is used to actually
  855. // call the actual exception handler. This is required by the
  856. // exception handler that is associated with the linkage
  857. // routine so it can have access to two sets of dispatcher
  858. // context when it is called.
  859. //
  860. DispatcherContext.ControlPc = ControlPc;
  861. DispatcherContext.FunctionEntry = FunctionEntry;
  862. DispatcherContext.EstablisherFrame = EstablisherFrame.Virtual;
  863. DispatcherContext.ContextRecord = ContextRecord;
  864. //
  865. // Call the exception handler.
  866. //
  867. do {
  868. //
  869. // If the establisher frame is the target of the unwind
  870. // operation, then set the target unwind flag.
  871. //
  872. if ((ULONG)TargetFrame == EstablisherFrame.Virtual) {
  873. ExceptionFlags |= EXCEPTION_TARGET_UNWIND;
  874. }
  875. ExceptionRecord->ExceptionFlags = ExceptionFlags;
  876. //
  877. // Set the specified return value in case the exception
  878. // handler directly continues execution.
  879. //
  880. ContextRecord->IntV0 = (ULONGLONG)(LONG)ReturnValue;
  881. #if DBG
  882. if (RtlDebugFlags & RTL_DBG_UNWIND_DETAIL) {
  883. DbgPrint("RtlUnwind: calling RtlpExecuteHandlerForUnwind, ControlPc = %lx\n", ControlPc);
  884. }
  885. #endif
  886. Disposition =
  887. RtlpExecuteHandlerForUnwind(ExceptionRecord,
  888. EstablisherFrame.Virtual,
  889. ContextRecord,
  890. &DispatcherContext,
  891. FunctionEntry->ExceptionHandler);
  892. #if DBG
  893. if (RtlDebugFlags & RTL_DBG_UNWIND_DETAIL) {
  894. DbgPrint("RtlUnwind: RtlpExecuteHandlerForUnwind returned Disposition = %lx\n", Disposition);
  895. }
  896. #endif
  897. //
  898. // Clear target unwind and collided unwind flags.
  899. //
  900. ExceptionFlags &= ~(EXCEPTION_COLLIDED_UNWIND |
  901. EXCEPTION_TARGET_UNWIND);
  902. //
  903. // Case on the handler disposition.
  904. //
  905. switch (Disposition) {
  906. //
  907. // The disposition is to continue the search.
  908. //
  909. // If the target frame has not been reached, then
  910. // virtually unwind to the caller of the current
  911. // routine, update the context record, and continue
  912. // the search for a handler.
  913. //
  914. case ExceptionContinueSearch :
  915. if (EstablisherFrame.Virtual != (ULONG)TargetFrame) {
  916. NextPc = RtlVirtualUnwind(ControlPc,
  917. FunctionEntry,
  918. ContextRecord,
  919. &InFunction,
  920. &EstablisherFrame,
  921. NULL);
  922. }
  923. break;
  924. //
  925. // The disposition is collided unwind.
  926. //
  927. // Set the target of the current unwind to the context
  928. // record of the previous unwind, and reexecute the
  929. // exception handler from the collided frame with the
  930. // collided unwind flag set in the exception record.
  931. //
  932. case ExceptionCollidedUnwind :
  933. ControlPc = DispatcherContext.ControlPc;
  934. FunctionEntry = DispatcherContext.FunctionEntry;
  935. ContextRecord = DispatcherContext.ContextRecord;
  936. ContextRecord->Fir = (ULONGLONG)(LONG)TargetIp;
  937. ExceptionFlags |= EXCEPTION_COLLIDED_UNWIND;
  938. EstablisherFrame.Virtual = DispatcherContext.EstablisherFrame;
  939. break;
  940. //
  941. // All other disposition values are invalid.
  942. //
  943. // Raise invalid disposition exception.
  944. //
  945. default :
  946. RAISE_EXCEPTION(STATUS_INVALID_DISPOSITION, ExceptionRecord);
  947. }
  948. } while ((ExceptionFlags & EXCEPTION_COLLIDED_UNWIND) != 0);
  949. } else {
  950. //
  951. // Virtually unwind to the caller of the current routine and
  952. // update the context record.
  953. //
  954. if (EstablisherFrame.Virtual != (ULONG)TargetFrame) {
  955. NextPc = RtlVirtualUnwind(ControlPc,
  956. FunctionEntry,
  957. ContextRecord,
  958. &InFunction,
  959. &EstablisherFrame,
  960. NULL);
  961. }
  962. }
  963. } else {
  964. //
  965. // Set point at which control left the previous routine.
  966. //
  967. NextPc = (ULONG)ContextRecord->IntRa - 4;
  968. //
  969. // If the next control PC is the same as the old control PC, then
  970. // the function table is not correctly formed.
  971. //
  972. if (NextPc == ControlPc) {
  973. #if DBG
  974. ULONG Count;
  975. DbgPrint("\n****** Warning - malformed function table (unwind).\n");
  976. DbgPrint("ControlPc = %08lx, %08lx", NextPc, ControlPc);
  977. for (Count = 0; Count < PC_HISTORY_DEPTH; Count += 1) {
  978. if (ControlPcHistoryIndex > 0) {
  979. ControlPcHistoryIndex -= 1;
  980. ControlPc = ControlPcHistory[ControlPcHistoryIndex % PC_HISTORY_DEPTH];
  981. DbgPrint(", %08lx", ControlPc);
  982. }
  983. }
  984. DbgPrint(ControlPcHistoryIndex == 0 ? ".\n" : ", ...\n");
  985. DbgPrint(" Now raising STATUS_BAD_FUNCTION_TABLE exception.\n");
  986. #endif
  987. RtlRaiseStatus(STATUS_BAD_FUNCTION_TABLE);
  988. }
  989. }
  990. //
  991. // Set point at which control left the previous routine.
  992. //
  993. #if DBG
  994. ControlPcHistory[ControlPcHistoryIndex % PC_HISTORY_DEPTH] = ControlPc;
  995. ControlPcHistoryIndex += 1;
  996. #endif
  997. ControlPc = NextPc;
  998. } while ((EstablisherFrame.Virtual < HighLimit) &&
  999. (EstablisherFrame.Virtual != (ULONG)TargetFrame));
  1000. //
  1001. // If the establisher stack pointer is equal to the target frame
  1002. // pointer, then continue execution. Otherwise, an exit unwind was
  1003. // performed or the target of the unwind did not exist and the
  1004. // debugger and subsystem are given a second chance to handle the
  1005. // unwind.
  1006. //
  1007. if (EstablisherFrame.Virtual == (ULONG)TargetFrame) {
  1008. ContextRecord->IntV0 = (ULONGLONG)(LONG)ReturnValue;
  1009. #if DBG
  1010. if (RtlDebugFlags & RTL_DBG_UNWIND) {
  1011. DbgPrint("RtlUnwind: finished unwinding, and calling RtlpRestoreContext(%lx)\n",ContextRecord);
  1012. }
  1013. #endif
  1014. RtlpRestoreContext(ContextRecord);
  1015. } else {
  1016. #if DBG
  1017. if (RtlDebugFlags & RTL_DBG_UNWIND) {
  1018. DbgPrint("RtlUnwind: finished unwinding, but calling ZwRaiseException\n");
  1019. }
  1020. #endif
  1021. ZwRaiseException(ExceptionRecord, ContextRecord, FALSE);
  1022. }
  1023. }
  1024. #if DBG
  1025. //
  1026. // Define an array of symbolic names for the integer registers.
  1027. //
  1028. PCHAR RtlpIntegerRegisterNames[32] = {
  1029. "v0", "t0", "t1", "t2", "t3", "t4", "t5", "t6", // 0 - 7
  1030. "t7", "s0", "s1", "s2", "s3", "s4", "s5", "fp", // 8 - 15
  1031. "a0", "a1", "a2", "a3", "a4", "a5", "t8", "t9", // 16 - 23
  1032. "t10", "t11", "ra", "t12", "at", "gp", "sp", "zero", // 24 - 31
  1033. };
  1034. //
  1035. // This function disassembles the instruction at the given address. It is
  1036. // only used for debugging and recognizes only those few instructions that
  1037. // are relevant during reverse execution of the prologue by virtual unwind.
  1038. //
  1039. VOID
  1040. _RtlpDebugDisassemble (
  1041. IN ULONG ControlPc,
  1042. IN PCONTEXT ContextRecord
  1043. )
  1044. {
  1045. UCHAR Comments[50];
  1046. PULONGLONG FloatingRegister;
  1047. ULONG Function;
  1048. ULONG Hint;
  1049. ULONG Literal8;
  1050. ALPHA_INSTRUCTION Instruction;
  1051. PULONGLONG IntegerRegister;
  1052. LONG Offset16;
  1053. UCHAR Operands[20];
  1054. ULONG Opcode;
  1055. PCHAR OpName;
  1056. ULONG Ra;
  1057. ULONG Rb;
  1058. ULONG Rc;
  1059. PCHAR RaName;
  1060. PCHAR RbName;
  1061. PCHAR RcName;
  1062. if (RtlDebugFlags & RTL_DBG_VIRTUAL_UNWIND_DETAIL) {
  1063. Instruction.Long = *((PULONG)ControlPc);
  1064. Hint = Instruction.Jump.Hint;
  1065. Literal8 = Instruction.OpLit.Literal;
  1066. Offset16 = Instruction.Memory.MemDisp;
  1067. Opcode = Instruction.Memory.Opcode;
  1068. Ra = Instruction.OpReg.Ra;
  1069. RaName = RtlpIntegerRegisterNames[Ra];
  1070. Rb = Instruction.OpReg.Rb;
  1071. RbName = RtlpIntegerRegisterNames[Rb];
  1072. Rc = Instruction.OpReg.Rc;
  1073. RcName = RtlpIntegerRegisterNames[Rc];
  1074. IntegerRegister = &ContextRecord->IntV0;
  1075. FloatingRegister = &ContextRecord->FltF0;
  1076. OpName = NULL;
  1077. switch (Opcode) {
  1078. case JMP_OP :
  1079. if (Instruction.Jump.Function == RET_FUNC) {
  1080. OpName = "ret";
  1081. sprintf(Operands, "%s, (%s), %04lx", RaName, RbName, Hint);
  1082. sprintf(Comments, "%s = %Lx", RbName, IntegerRegister[Rb]);
  1083. }
  1084. break;
  1085. case LDAH_OP :
  1086. case LDA_OP :
  1087. case STQ_OP :
  1088. if (Opcode == LDA_OP) {
  1089. OpName = "lda";
  1090. } else if (Opcode == LDAH_OP) {
  1091. OpName = "ldah";
  1092. } else if (Opcode == STQ_OP) {
  1093. OpName = "stq";
  1094. }
  1095. sprintf(Operands, "%s, $%d(%s)", RaName, Offset16, RbName);
  1096. sprintf(Comments, "%s = %Lx", RaName, IntegerRegister[Ra]);
  1097. break;
  1098. case ARITH_OP :
  1099. case BIT_OP :
  1100. Function = Instruction.OpReg.Function;
  1101. if ((Opcode == ARITH_OP) && (Function == ADDQ_FUNC)) {
  1102. OpName = "addq";
  1103. } else if ((Opcode == ARITH_OP) && (Function == SUBQ_FUNC)) {
  1104. OpName = "subq";
  1105. } else if ((Opcode == BIT_OP) && (Function == BIS_FUNC)) {
  1106. OpName = "bis";
  1107. } else {
  1108. break;
  1109. }
  1110. if (Instruction.OpReg.RbvType == RBV_REGISTER_FORMAT) {
  1111. sprintf(Operands, "%s, %s, %s", RaName, RbName, RcName);
  1112. } else {
  1113. sprintf(Operands, "%s, $%d, %s", RaName, Literal8, RcName);
  1114. }
  1115. sprintf(Comments, "%s = %Lx", RcName, IntegerRegister[Rc]);
  1116. break;
  1117. case FPOP_OP :
  1118. if (Instruction.FpOp.Function == CPYS_FUNC) {
  1119. OpName = "cpys";
  1120. sprintf(Operands, "f%d, f%d, f%d", Ra, Rb, Rc);
  1121. sprintf(Comments, "f%d = %Lx", Rc, FloatingRegister[Rc]);
  1122. }
  1123. break;
  1124. case STT_OP :
  1125. OpName = "stt";
  1126. sprintf(Operands, "f%d, $%d(%s)", Ra, Offset16, RbName);
  1127. sprintf(Comments, "f%d = %Lx", Ra, FloatingRegister[Ra]);
  1128. break;
  1129. }
  1130. if (OpName == NULL) {
  1131. OpName = "???";
  1132. sprintf(Operands, "...");
  1133. sprintf(Comments, "Unknown to virtual unwind.");
  1134. }
  1135. DbgPrint(" %08lx: %08lx %-5s %-16s // %s\n",
  1136. ControlPc, Instruction.Long, OpName, Operands, Comments);
  1137. }
  1138. return;
  1139. }
  1140. #define _RtlpFoundTrapFrame(NextPc) \
  1141. if (RtlDebugFlags & RTL_DBG_VIRTUAL_UNWIND) { \
  1142. DbgPrint(" *** Looks like a trap frame (fake prologue), Fir = %lx\n", \
  1143. NextPc); \
  1144. }
  1145. #define _RtlpVirtualUnwindExit(NextPc, ContextRecord, EstablisherFrame) \
  1146. if (RtlDebugFlags & RTL_DBG_VIRTUAL_UNWIND) { \
  1147. DbgPrint("RtlVirtualUnwind: EstablisherFrame Virtual = %08lx, Real = %08lx\n", \
  1148. (EstablisherFrame)->Virtual, (EstablisherFrame)->Real); \
  1149. DbgPrint("RtlVirtualUnwind: returning NextPc = %lx, sp = %lx\n\n", \
  1150. NextPc, ContextRecord->IntSp); \
  1151. }
  1152. #else
  1153. #define _RtlpDebugDisassemble(ControlPc, ContextRecord)
  1154. #define _RtlpFoundTrapFrame(NextPc)
  1155. #define _RtlpVirtualUnwindExit(NextPc, ContextRecord, EstablisherFrame)
  1156. #endif
  1157. ULONG
  1158. RtlVirtualUnwind (
  1159. IN ULONG ControlPc,
  1160. IN PRUNTIME_FUNCTION FunctionEntry,
  1161. IN OUT PCONTEXT ContextRecord,
  1162. OUT PBOOLEAN InFunction,
  1163. OUT PFRAME_POINTERS EstablisherFrame,
  1164. IN OUT PKNONVOLATILE_CONTEXT_POINTERS ContextPointers OPTIONAL
  1165. )
  1166. /*++
  1167. Routine Description:
  1168. This function virtually unwinds the specified function by executing its
  1169. prologue code backwards. Given the current context and the instructions
  1170. that preserve registers in the prologue, it is possible to recreate the
  1171. nonvolatile context at the point the function was called.
  1172. If the function is a leaf function, then the address where control left
  1173. the previous frame is obtained from the context record. If the function
  1174. is a nested function, but not an exception or interrupt frame, then the
  1175. prologue code is executed backwards and the address where control left
  1176. the previous frame is obtained from the updated context record.
  1177. Otherwise, an exception or interrupt entry to the system is being unwound
  1178. and a specially coded prologue restores the return address twice. Once
  1179. from the fault instruction address and once from the saved return address
  1180. register. The first restore is returned as the function value and the
  1181. second restore is placed in the updated context record.
  1182. During the unwind, the virtual and real frame pointers for the function
  1183. are calculated and returned in the given frame pointers structure.
  1184. If a context pointers record is specified, then the address where each
  1185. register is restored from is recorded in the appropriate element of the
  1186. context pointers record.
  1187. Arguments:
  1188. ControlPc - Supplies the address where control left the specified
  1189. function.
  1190. FunctionEntry - Supplies the address of the function table entry for the
  1191. specified function.
  1192. ContextRecord - Supplies the address of a context record.
  1193. InFunction - Supplies a pointer to a variable that receives whether the
  1194. control PC is within the current function.
  1195. EstablisherFrame - Supplies a pointer to a frame pointers structure
  1196. that will receive the values for the virtual frame pointer and the
  1197. real frame pointer. The value of the real frame pointer is reliable
  1198. only when InFunction is TRUE.
  1199. ContextPointers - Supplies an optional pointer to a context pointers
  1200. record.
  1201. Return Value:
  1202. The address where control left the previous frame is returned as the
  1203. function value.
  1204. Implementation Notes:
  1205. N.B. "where control left" is not the "return address" of the call in the
  1206. previous frame. For normal frames, NextPc points to the last instruction
  1207. that completed in the previous frame (the JSR/BSR). The difference between
  1208. NextPc and NextPc + 4 (return address) is important for correct behavior
  1209. in boundary cases of exception addresses and scope tables.
  1210. For exception and interrupt frames, NextPc is obtained from the trap frame
  1211. contination address (Fir). For faults and synchronous traps, NextPc is both
  1212. the last instruction to execute in the previous frame and the next
  1213. instruction to execute if the function were to return. For asynchronous
  1214. traps, NextPc is the continuation address. It is the responsibility of the
  1215. compiler to insert TRAPB instructions to insure asynchronous traps do not
  1216. occur outside the scope from the instruction(s) that caused them.
  1217. N.B. in this and other files where RtlVirtualUnwind is used, the variable
  1218. named NextPc is perhaps more accurately, LastPc - the last PC value in
  1219. the previous frame, or CallPc - the address of the call instruction, or
  1220. ControlPc - the address where control left the previous frame. Instead
  1221. think of NextPc as the next PC to use in another call to virtual unwind.
  1222. The Alpha version of virtual unwind is similar in design, but slightly
  1223. more complex than the Mips version. This is because Alpha compilers
  1224. are given more flexibility to optimize generated code and instruction
  1225. sequences, including within procedure prologues. In addition, because of
  1226. the current inability of the GEM compiler to materialize virtual frame
  1227. pointers, this function must manage both virtual and real frame pointers.
  1228. --*/
  1229. {
  1230. ULONG Address;
  1231. ULONG DecrementOffset;
  1232. ULONG DecrementRegister;
  1233. ALPHA_INSTRUCTION FollowingInstruction;
  1234. PULONGLONG FloatingRegister;
  1235. ULONG FrameSize;
  1236. ULONG Function;
  1237. ALPHA_INSTRUCTION Instruction;
  1238. PULONGLONG IntegerRegister;
  1239. ULONG Literal8;
  1240. ULONG NextPc;
  1241. LONG Offset16;
  1242. ULONG Opcode;
  1243. ULONG Ra;
  1244. ULONG Rb;
  1245. ULONG Rc;
  1246. BOOLEAN RestoredRa;
  1247. BOOLEAN RestoredSp;
  1248. #if DBG
  1249. if (RtlDebugFlags & RTL_DBG_VIRTUAL_UNWIND) {
  1250. DbgPrint("\nRtlVirtualUnwind(ControlPc = %lx, FunctionEntry = %lx,) sp = %lx\n",
  1251. ControlPc, FunctionEntry, ContextRecord->IntSp);
  1252. }
  1253. #endif
  1254. #if DBG
  1255. if ((FunctionEntry == NULL) || (ControlPc & 0x3) ||
  1256. (FunctionEntry->BeginAddress >= FunctionEntry->EndAddress) ||
  1257. #if 0
  1258. (ControlPc < FunctionEntry->BeginAddress) ||
  1259. (ControlPc >= FunctionEntry->EndAddress) ||
  1260. #endif
  1261. (FunctionEntry->PrologEndAddress < FunctionEntry->BeginAddress) ||
  1262. (FunctionEntry->PrologEndAddress >= FunctionEntry->EndAddress)) {
  1263. DbgPrint("\n****** Warning - invalid PC or function table entry (virtual unwind).\n");
  1264. return ControlPc;
  1265. }
  1266. #endif
  1267. //
  1268. // Set the base address of the integer and floating register arrays within
  1269. // the context record. Each set of 32 registers is known to be contiguous.
  1270. //
  1271. IntegerRegister = &ContextRecord->IntV0;
  1272. FloatingRegister = &ContextRecord->FltF0;
  1273. //
  1274. // Handle the epilogue case where the next instruction is a return.
  1275. //
  1276. // Exception handlers cannot be called if the ControlPc is within the
  1277. // epilogue because exception handlers expect to operate with a current
  1278. // stack frame. The value of SP is not current within the epilogue.
  1279. //
  1280. Instruction.Long = *((PULONG)ControlPc);
  1281. if (IS_RETURN_0001_INSTRUCTION(Instruction.Long)) {
  1282. Rb = Instruction.Jump.Rb;
  1283. NextPc = (ULONG)IntegerRegister[Rb] - 4;
  1284. //
  1285. // The instruction at the point where control left the specified
  1286. // function is a return, so any saved registers have already been
  1287. // restored, and the stack pointer has already been adjusted. The
  1288. // stack does not need to be unwound in this case and the saved
  1289. // return address register is returned as the function value.
  1290. //
  1291. // In fact, reverse execution of the prologue is not possible in
  1292. // this case: the stack pointer has already been incremented and
  1293. // so, for this frame, neither a valid stack pointer nor frame
  1294. // pointer exists from which to begin reverse execution of the
  1295. // prologue. In addition, the integrity of any data on the stack
  1296. // below the stack pointer is never guaranteed (due to interrupts
  1297. // and exceptions).
  1298. //
  1299. // The epilogue instruction sequence is:
  1300. //
  1301. // ==> ret zero, (Ra), 1 // return
  1302. // or
  1303. //
  1304. // mov ra, Rx // save return address
  1305. // ...
  1306. // ==> ret zero, (Rx), 1 // return
  1307. //
  1308. EstablisherFrame->Real = 0;
  1309. EstablisherFrame->Virtual = (ULONG)ContextRecord->IntSp;
  1310. *InFunction = FALSE;
  1311. _RtlpDebugDisassemble(ControlPc, ContextRecord);
  1312. _RtlpVirtualUnwindExit(NextPc, ContextRecord, EstablisherFrame);
  1313. return NextPc;
  1314. }
  1315. //
  1316. // Handle the epilogue case where the next two instructions are a stack
  1317. // frame deallocation and a return.
  1318. //
  1319. FollowingInstruction.Long = *((PULONG)(ControlPc + 4));
  1320. if (IS_RETURN_0001_INSTRUCTION(FollowingInstruction.Long)) {
  1321. Rb = FollowingInstruction.Jump.Rb;
  1322. NextPc = (ULONG)IntegerRegister[Rb] - 4;
  1323. //
  1324. // The second instruction following the point where control
  1325. // left the specified function is a return. If the instruction
  1326. // before the return is a stack increment instruction, then all
  1327. // saved registers have already been restored except for SP.
  1328. // The value of the stack pointer register cannot be recovered
  1329. // through reverse execution of the prologue because in order
  1330. // to begin reverse execution either the stack pointer or the
  1331. // frame pointer (if any) must still be valid.
  1332. //
  1333. // Instead, the effect that the stack increment instruction
  1334. // would have had on the context is manually applied to the
  1335. // current context. This is forward execution of the epilogue
  1336. // rather than reverse execution of the prologue.
  1337. //
  1338. // In an epilogue, as in a prologue, the stack pointer is always
  1339. // adjusted with a single instruction: either an immediate-value
  1340. // (lda) or a register-value (addq) add instruction.
  1341. //
  1342. Function = Instruction.OpReg.Function;
  1343. Offset16 = Instruction.Memory.MemDisp;
  1344. Opcode = Instruction.OpReg.Opcode;
  1345. Ra = Instruction.OpReg.Ra;
  1346. Rb = Instruction.OpReg.Rb;
  1347. Rc = Instruction.OpReg.Rc;
  1348. if ((Opcode == LDA_OP) && (Ra == SP_REG)) {
  1349. //
  1350. // Load Address instruction.
  1351. //
  1352. // Since the destination (Ra) register is SP, an immediate-
  1353. // value stack deallocation operation is being performed. The
  1354. // displacement value should be added to SP. The displacement
  1355. // value is assumed to be positive. The amount of stack
  1356. // deallocation possible using this instruction ranges from
  1357. // 16 to 32752 (32768 - 16) bytes. The base register (Rb) is
  1358. // usually SP, but may be another register.
  1359. //
  1360. // The epilogue instruction sequence is:
  1361. //
  1362. // ==> lda sp, +N(sp) // deallocate stack frame
  1363. // ret zero, (ra) // return
  1364. // or
  1365. //
  1366. // ==> lda sp, +N(Rx) // restore SP and deallocate frame
  1367. // ret zero, (ra) // return
  1368. //
  1369. ContextRecord->IntSp = Offset16 + IntegerRegister[Rb];
  1370. EstablisherFrame->Real = 0;
  1371. EstablisherFrame->Virtual = (ULONG)ContextRecord->IntSp;
  1372. *InFunction = FALSE;
  1373. _RtlpDebugDisassemble(ControlPc, ContextRecord);
  1374. _RtlpDebugDisassemble(ControlPc + 4, ContextRecord);
  1375. _RtlpVirtualUnwindExit(NextPc, ContextRecord, EstablisherFrame);
  1376. return NextPc;
  1377. } else if ((Opcode == ARITH_OP) && (Function == ADDQ_FUNC) &&
  1378. (Rc == SP_REG) &&
  1379. (Instruction.OpReg.RbvType == RBV_REGISTER_FORMAT)) {
  1380. //
  1381. // Add Quadword instruction.
  1382. //
  1383. // Since both source operands are registers, and the
  1384. // destination register is SP, a register-value stack
  1385. // deallocation is being performed. The value of the two
  1386. // source registers should be added and this is the new
  1387. // value of SP. One of the source registers is usually SP,
  1388. // but may be another register.
  1389. //
  1390. // The epilogue instruction sequence is:
  1391. //
  1392. // ldiq Rx, N // set [large] frame size
  1393. // ...
  1394. // ==> addq sp, Rx, sp // deallocate stack frame
  1395. // ret zero, (ra) // return
  1396. // or
  1397. //
  1398. // ==> addq Rx, Ry, sp // restore SP and deallocate frame
  1399. // ret zero, (ra) // return
  1400. //
  1401. ContextRecord->IntSp = IntegerRegister[Ra] + IntegerRegister[Rb];
  1402. EstablisherFrame->Real = 0;
  1403. EstablisherFrame->Virtual = (ULONG)ContextRecord->IntSp;
  1404. *InFunction = FALSE;
  1405. _RtlpDebugDisassemble(ControlPc, ContextRecord);
  1406. _RtlpDebugDisassemble(ControlPc + 4, ContextRecord);
  1407. _RtlpVirtualUnwindExit(NextPc, ContextRecord, EstablisherFrame);
  1408. return NextPc;
  1409. }
  1410. }
  1411. //
  1412. // By default set the frame pointers to the current value of SP.
  1413. //
  1414. // When a procedure is called, the value of SP before the stack
  1415. // allocation instruction is the virtual frame pointer. When reverse
  1416. // executing instructions in the prologue, the value of SP before the
  1417. // stack allocation instruction is encountered is the real frame
  1418. // pointer. This is the current value of SP unless the procedure uses
  1419. // a frame pointer (e.g., FP_REG).
  1420. //
  1421. EstablisherFrame->Real = (ULONG)ContextRecord->IntSp;
  1422. EstablisherFrame->Virtual = (ULONG)ContextRecord->IntSp;
  1423. //
  1424. // If the address where control left the specified function is beyond
  1425. // the end of the prologue, then the control PC is considered to be
  1426. // within the function and the control address is set to the end of
  1427. // the prologue. Otherwise, the control PC is not considered to be
  1428. // within the function (i.e., the prologue).
  1429. //
  1430. // N.B. PrologEndAddress is equal to BeginAddress for a leaf function.
  1431. //
  1432. // The low-order two bits of PrologEndAddress are reserved for the IEEE
  1433. // exception mode and so must be masked out.
  1434. //
  1435. if ((ControlPc < FunctionEntry->BeginAddress) ||
  1436. (ControlPc >= FunctionEntry->PrologEndAddress)) {
  1437. *InFunction = TRUE;
  1438. ControlPc = (FunctionEntry->PrologEndAddress & (~0x3));
  1439. } else {
  1440. *InFunction = FALSE;
  1441. }
  1442. //
  1443. // Scan backward through the prologue to reload callee saved registers
  1444. // that were stored or copied and to increment the stack pointer if it
  1445. // was decremented.
  1446. //
  1447. DecrementRegister = ZERO_REG;
  1448. NextPc = (ULONG)ContextRecord->IntRa - 4;
  1449. RestoredRa = FALSE;
  1450. RestoredSp = FALSE;
  1451. while (ControlPc > FunctionEntry->BeginAddress) {
  1452. //
  1453. // Get instruction value, decode fields, case on opcode value, and
  1454. // reverse register store and stack decrement operations.
  1455. // N.B. The location of Opcode, Ra, Rb, and Rc is the same across
  1456. // all opcode formats. The same is not true for Function.
  1457. //
  1458. ControlPc -= 4;
  1459. Instruction.Long = *((PULONG)ControlPc);
  1460. Function = Instruction.OpReg.Function;
  1461. Literal8 = Instruction.OpLit.Literal;
  1462. Offset16 = Instruction.Memory.MemDisp;
  1463. Opcode = Instruction.OpReg.Opcode;
  1464. Ra = Instruction.OpReg.Ra;
  1465. Rb = Instruction.OpReg.Rb;
  1466. Rc = Instruction.OpReg.Rc;
  1467. //
  1468. // Compare against each instruction type that will affect the context
  1469. // and that is allowed in a prologue. Any other instructions found
  1470. // in the prologue will be ignored since they are assumed to have no
  1471. // effect on the context.
  1472. //
  1473. switch (Opcode) {
  1474. case STQ_OP :
  1475. //
  1476. // Store Quad instruction.
  1477. //
  1478. // If the base register is SP, then reload the source register
  1479. // value from the value stored on the stack.
  1480. //
  1481. // The prologue instruction sequence is:
  1482. //
  1483. // ==> stq Rx, N(sp) // save integer register Rx
  1484. //
  1485. if ((Rb == SP_REG) && (Ra != ZERO_REG)) {
  1486. //
  1487. // Reload the register by retrieving the value previously
  1488. // stored on the stack.
  1489. //
  1490. Address = Offset16 + ContextRecord->IntSp;
  1491. IntegerRegister[Ra] = *((PULONGLONG)Address);
  1492. //
  1493. // If the destination register is RA and this is the first
  1494. // time that RA is being restored, then set the address of
  1495. // where control left the previous frame. Otherwise, if this
  1496. // is the second time RA is being restored, then the first
  1497. // one was an interrupt or exception address and the return
  1498. // PC should not have been biased by 4.
  1499. //
  1500. if (Ra == RA_REG) {
  1501. if (RestoredRa == FALSE) {
  1502. NextPc = (ULONG)ContextRecord->IntRa - 4;
  1503. RestoredRa = TRUE;
  1504. } else {
  1505. NextPc += 4;
  1506. _RtlpFoundTrapFrame(NextPc);
  1507. }
  1508. //
  1509. // Otherwise, if the destination register is SP and this is
  1510. // the first time that SP is being restored, then set the
  1511. // establisher frame pointers.
  1512. //
  1513. } else if ((Ra == SP_REG) && (RestoredSp == FALSE)) {
  1514. EstablisherFrame->Virtual = (ULONG)ContextRecord->IntSp;
  1515. EstablisherFrame->Real = (ULONG)ContextRecord->IntSp;
  1516. RestoredSp = TRUE;
  1517. }
  1518. //
  1519. // If a context pointer record is specified, then record
  1520. // the address where the destination register contents
  1521. // are stored.
  1522. //
  1523. if (ARGUMENT_PRESENT(ContextPointers)) {
  1524. ContextPointers->IntegerContext[Ra] = (PULONGLONG)Address;
  1525. }
  1526. _RtlpDebugDisassemble(ControlPc, ContextRecord);
  1527. }
  1528. break;
  1529. case LDAH_OP :
  1530. Offset16 <<= 16;
  1531. case LDA_OP :
  1532. //
  1533. // Load Address High, Load Address instruction.
  1534. //
  1535. // There are several cases where the lda and/or ldah instructions
  1536. // are used: one to decrement the stack pointer directly, and the
  1537. // others to load immediate values into another register and that
  1538. // register is then used to decrement the stack pointer.
  1539. //
  1540. // In the examples below, as a single instructions or as a pair,
  1541. // a lda may be substituted for a ldah and visa-versa.
  1542. //
  1543. if (Ra == SP_REG) {
  1544. if (Rb == SP_REG) {
  1545. //
  1546. // If both the destination (Ra) and base (Rb) registers
  1547. // are SP, then a standard stack allocation was performed
  1548. // and the negated displacement value is the stack frame
  1549. // size. The amount of stack allocation possible using
  1550. // the lda instruction ranges from 16 to 32768 bytes and
  1551. // the amount of stack allocation possible using the ldah
  1552. // instruction ranges from 65536 to 2GB in multiples of
  1553. // 65536 bytes. It is rare for the ldah instruction to be
  1554. // used in this manner.
  1555. //
  1556. // The prologue instruction sequence is:
  1557. //
  1558. // ==> lda sp, -N(sp) // allocate stack frame
  1559. //
  1560. FrameSize = -Offset16;
  1561. goto StackAllocation;
  1562. } else {
  1563. //
  1564. // The destination register is SP and the base register
  1565. // is not SP, so this instruction must be the second
  1566. // half of an instruction pair to allocate a large size
  1567. // (>32768 bytes) stack frame. Save the displacement value
  1568. // as the partial decrement value and postpone adjusting
  1569. // the value of SP until the first instruction of the pair
  1570. // is encountered.
  1571. //
  1572. // The prologue instruction sequence is:
  1573. //
  1574. // ldah Rx, -N(sp) // prepare new SP (upper)
  1575. // ==> lda sp, sN(Rx) // allocate stack frame
  1576. //
  1577. DecrementRegister = Rb;
  1578. DecrementOffset = Offset16;
  1579. _RtlpDebugDisassemble(ControlPc, ContextRecord);
  1580. }
  1581. } else if (Ra == DecrementRegister) {
  1582. if (Rb == DecrementRegister) {
  1583. //
  1584. // Both the destination and base registers are the
  1585. // decrement register, so this instruction exists as the
  1586. // second half of a two instruction pair to load a
  1587. // 31-bit immediate value into the decrement register.
  1588. // Save the displacement value as the partial decrement
  1589. // value.
  1590. //
  1591. // The prologue instruction sequence is:
  1592. //
  1593. // ldah Rx, +N(zero) // set frame size (upper)
  1594. // ==> lda Rx, sN(Rx) // set frame size (+lower)
  1595. // ...
  1596. // subq sp, Rx, sp // allocate stack frame
  1597. //
  1598. DecrementOffset += Offset16;
  1599. _RtlpDebugDisassemble(ControlPc, ContextRecord);
  1600. } else if (Rb == ZERO_REG) {
  1601. //
  1602. // The destination register is the decrement register and
  1603. // the base register is zero, so this instruction exists
  1604. // to load an immediate value into the decrement register.
  1605. // The stack frame size is the new displacement value added
  1606. // to the previous displacement value, if any.
  1607. //
  1608. // The prologue instruction sequence is:
  1609. //
  1610. // ==> lda Rx, +N(zero) // set frame size
  1611. // ...
  1612. // subq sp, Rx, sp // allocate stack frame
  1613. // or
  1614. //
  1615. // ==> ldah Rx, +N(zero) // set frame size (upper)
  1616. // lda Rx, sN(Rx) // set frame size (+lower)
  1617. // ...
  1618. // subq sp, Rx, sp // allocate stack frame
  1619. //
  1620. FrameSize = (Offset16 + DecrementOffset);
  1621. goto StackAllocation;
  1622. } else if (Rb == SP_REG) {
  1623. //
  1624. // The destination (Ra) register is SP and the base (Rb)
  1625. // register is the decrement register, so a two
  1626. // instruction, large size (>32768 bytes) stack frame
  1627. // allocation was performed. Add the new displacement
  1628. // value to the previous displacement value. The negated
  1629. // displacement value is the stack frame size.
  1630. //
  1631. // The prologue instruction sequence is:
  1632. //
  1633. // ==> ldah Rx, -N(sp) // prepare new SP (upper)
  1634. // lda sp, sN(Rx) // allocate stack frame
  1635. //
  1636. FrameSize = -(Offset16 + (LONG)DecrementOffset);
  1637. goto StackAllocation;
  1638. }
  1639. }
  1640. break;
  1641. case ARITH_OP :
  1642. if ((Function == ADDQ_FUNC) &&
  1643. (Instruction.OpReg.RbvType != RBV_REGISTER_FORMAT)) {
  1644. //
  1645. // Add Quadword (immediate) instruction.
  1646. //
  1647. // If the first source register is zero, and the second
  1648. // operand is a literal, and the destination register is
  1649. // the decrement register, then the instruction exists
  1650. // to load an unsigned immediate value less than 256 into
  1651. // the decrement register. The immediate value is the stack
  1652. // frame size.
  1653. //
  1654. // The prologue instruction sequence is:
  1655. //
  1656. // ==> addq zero, N, Rx // set frame size
  1657. // ...
  1658. // subq sp, Rx, sp // allocate stack frame
  1659. //
  1660. if ((Ra == ZERO_REG) && (Rc == DecrementRegister)) {
  1661. FrameSize = Literal8;
  1662. goto StackAllocation;
  1663. }
  1664. } else if ((Function == SUBQ_FUNC) &&
  1665. (Instruction.OpReg.RbvType == RBV_REGISTER_FORMAT)) {
  1666. //
  1667. // Subtract Quadword (register) instruction.
  1668. //
  1669. // If both source operands are registers and the first
  1670. // source (minuend) register and the destination
  1671. // (difference) register are both SP, then a register value
  1672. // stack allocation was performed and the second source
  1673. // (subtrahend) register value will be added to SP when its
  1674. // value is known. Until that time save the register number of
  1675. // this decrement register.
  1676. //
  1677. // The prologue instruction sequence is:
  1678. //
  1679. // ldiq Rx, N // set frame size
  1680. // ...
  1681. // ==> subq sp, Rx, sp // allocate stack frame
  1682. //
  1683. if ((Ra == SP_REG) && (Rc == SP_REG)) {
  1684. DecrementRegister = Rb;
  1685. DecrementOffset = 0;
  1686. _RtlpDebugDisassemble(ControlPc, ContextRecord);
  1687. }
  1688. }
  1689. break;
  1690. case BIT_OP :
  1691. //
  1692. // If the second operand is a register the bit set instruction
  1693. // may be a register move instruction, otherwise if the second
  1694. // operand is a literal, the bit set instruction may be a load
  1695. // immediate value instruction.
  1696. //
  1697. if ((Function == BIS_FUNC) && (Rc != ZERO_REG)) {
  1698. if (Instruction.OpReg.RbvType == RBV_REGISTER_FORMAT) {
  1699. //
  1700. // Bit Set (register move) instruction.
  1701. //
  1702. // If both source registers are the same register, or
  1703. // one of the source registers is zero, then this is a
  1704. // register move operation. Restore the value of the
  1705. // source register by copying the current destination
  1706. // register value back to the source register.
  1707. //
  1708. // The prologue instruction sequence is:
  1709. //
  1710. // ==> bis Rx, Rx, Ry // copy register Rx
  1711. // or
  1712. //
  1713. // ==> bis Rx, zero, Ry // copy register Rx
  1714. // or
  1715. //
  1716. // ==> bis zero, Rx, Ry // copy register Rx
  1717. //
  1718. if (Ra == ZERO_REG) {
  1719. //
  1720. // Map the third case above to the first case.
  1721. //
  1722. Ra = Rb;
  1723. } else if (Rb == ZERO_REG) {
  1724. //
  1725. // Map the second case above to the first case.
  1726. //
  1727. Rb = Ra;
  1728. }
  1729. if ((Ra == Rb) && (Ra != ZERO_REG)) {
  1730. IntegerRegister[Ra] = IntegerRegister[Rc];
  1731. //
  1732. // If the destination register is RA and this is the
  1733. // first time that RA is being restored, then set the
  1734. // address of where control left the previous frame.
  1735. // Otherwise, if this is the second time RA is being
  1736. // restored, then the first one was an interrupt or
  1737. // exception address and the return PC should not
  1738. // have been biased by 4.
  1739. //
  1740. if (Ra == RA_REG) {
  1741. if (RestoredRa == FALSE) {
  1742. NextPc = (ULONG)ContextRecord->IntRa - 4;
  1743. RestoredRa = TRUE;
  1744. } else {
  1745. NextPc += 4;
  1746. _RtlpFoundTrapFrame(NextPc);
  1747. }
  1748. //
  1749. // If the source register is SP and this is the first
  1750. // time SP is set, then this is a frame pointer set
  1751. // instruction. Reset the frame pointers to this new
  1752. // value of SP.
  1753. //
  1754. } else if ((Ra == SP_REG) && (RestoredSp == FALSE)) {
  1755. EstablisherFrame->Virtual = (ULONG)ContextRecord->IntSp;
  1756. EstablisherFrame->Real = (ULONG)ContextRecord->IntSp;
  1757. RestoredSp = TRUE;
  1758. }
  1759. _RtlpDebugDisassemble(ControlPc, ContextRecord);
  1760. }
  1761. } else {
  1762. //
  1763. // Bit Set (load immediate) instruction.
  1764. //
  1765. // If the first source register is zero, and the second
  1766. // operand is a literal, and the destination register is
  1767. // the decrement register, then this instruction exists
  1768. // to load an unsigned immediate value less than 256 into
  1769. // the decrement register. The decrement register value is
  1770. // the stack frame size.
  1771. //
  1772. // The prologue instruction sequence is:
  1773. //
  1774. // ==> bis zero, N, Rx // set frame size
  1775. // ...
  1776. // subq sp, Rx, sp // allocate stack frame
  1777. //
  1778. if ((Ra == ZERO_REG) && (Rc == DecrementRegister)) {
  1779. FrameSize = Literal8;
  1780. StackAllocation:
  1781. //
  1782. // Add the frame size to SP to reverse the stack frame
  1783. // allocation, leave the real frame pointer as is, set
  1784. // the virtual frame pointer with the updated SP value,
  1785. // and clear the decrement register.
  1786. //
  1787. ContextRecord->IntSp += FrameSize;
  1788. EstablisherFrame->Virtual = (ULONG)ContextRecord->IntSp;
  1789. DecrementRegister = ZERO_REG;
  1790. _RtlpDebugDisassemble(ControlPc, ContextRecord);
  1791. }
  1792. }
  1793. }
  1794. break;
  1795. case STT_OP :
  1796. //
  1797. // Store T-Floating (quadword integer) instruction.
  1798. //
  1799. // If the base register is SP, then reload the source register
  1800. // value from the value stored on the stack.
  1801. //
  1802. // The prologue instruction sequence is:
  1803. //
  1804. // ==> stt Fx, N(sp) // save floating register Fx
  1805. //
  1806. if ((Rb == SP_REG) && (Ra != FZERO_REG)) {
  1807. //
  1808. // Reload the register by retrieving the value previously
  1809. // stored on the stack.
  1810. //
  1811. Address = Offset16 + ContextRecord->IntSp;
  1812. FloatingRegister[Ra] = *((PULONGLONG)Address);
  1813. //
  1814. // If a context pointer record is specified, then record
  1815. // the address where the destination register contents are
  1816. // stored.
  1817. //
  1818. if (ARGUMENT_PRESENT(ContextPointers)) {
  1819. ContextPointers->FloatingContext[Ra] = (PULONGLONG)Address;
  1820. }
  1821. _RtlpDebugDisassemble(ControlPc, ContextRecord);
  1822. }
  1823. break;
  1824. case FPOP_OP :
  1825. //
  1826. // N.B. The floating operate function field is not the same as
  1827. // the integer operate nor the jump function fields.
  1828. //
  1829. if (Instruction.FpOp.Function == CPYS_FUNC) {
  1830. //
  1831. // Copy Sign (floating-point move) instruction.
  1832. //
  1833. // If both source registers are the same register, then this is
  1834. // a floating-point register move operation. Restore the value
  1835. // of the source register by copying the current destination
  1836. // register value to the source register.
  1837. //
  1838. // The prologue instruction sequence is:
  1839. //
  1840. // ==> cpys Fx, Fx, Fy // copy floating register Fx
  1841. //
  1842. if ((Ra == Rb) && (Ra != FZERO_REG)) {
  1843. FloatingRegister[Ra] = FloatingRegister[Rc];
  1844. _RtlpDebugDisassemble(ControlPc, ContextRecord);
  1845. }
  1846. }
  1847. default :
  1848. break;
  1849. }
  1850. }
  1851. _RtlpVirtualUnwindExit(NextPc, ContextRecord, EstablisherFrame);
  1852. return NextPc;
  1853. }
  1854. ULONG
  1855. RtlpVirtualUnwind (
  1856. IN ULONG ControlPc,
  1857. IN PRUNTIME_FUNCTION FunctionEntry,
  1858. IN PCONTEXT ContextRecord,
  1859. OUT PBOOLEAN InFunction,
  1860. OUT PULONG EstablisherFrame
  1861. )
  1862. /*++
  1863. Routine Description:
  1864. This function virtually unwinds the specfified function by executing its
  1865. prologue code backwards.
  1866. If the function is a leaf function, then the address where control left
  1867. the previous frame is obtained from the context record. If the function
  1868. is a nested function, but not an exception or interrupt frame, then the
  1869. prologue code is executed backwards and the address where control left
  1870. the previous frame is obtained from the updated context record.
  1871. Otherwise, an exception or interrupt entry to the system is being unwound
  1872. and a specially coded prologue restores the return address twice. Once
  1873. from the fault instruction address and once from the saved return address
  1874. register. The first restore is returned as the function value and the
  1875. second restore is place in the updated context record.
  1876. If a context pointers record is specified, then the address where each
  1877. nonvolatile registers is restored from is recorded in the appropriate
  1878. element of the context pointers record.
  1879. N.B. This function copies the specified context record and only computes
  1880. the establisher frame and whether control is actually in a function.
  1881. Arguments:
  1882. ControlPc - Supplies the address where control left the specified
  1883. function.
  1884. FunctionEntry - Supplies the address of the function table entry for the
  1885. specified function.
  1886. ContextRecord - Supplies the address of a context record.
  1887. InFunction - Supplies a pointer to a variable that receives whether the
  1888. control PC is within the current function.
  1889. EstablisherFrame - Supplies a pointer to a variable that receives the
  1890. the establisher frame pointer value.
  1891. ContextPointers - Supplies an optional pointer to a context pointers
  1892. record.
  1893. Return Value:
  1894. The address where control left the previous frame is returned as the
  1895. function value.
  1896. --*/
  1897. {
  1898. CONTEXT LocalContext;
  1899. //
  1900. // Copy the context record so updates will not be reflected in the
  1901. // original copy and then virtually unwind to the caller of the
  1902. // specified control point.
  1903. //
  1904. RtlMoveMemory((PVOID)&LocalContext, ContextRecord, sizeof(CONTEXT));
  1905. return RtlVirtualUnwind(ControlPc,
  1906. FunctionEntry,
  1907. &LocalContext,
  1908. InFunction,
  1909. (PFRAME_POINTERS)EstablisherFrame,
  1910. NULL);
  1911. }