Source code of Windows XP (NT5)
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.

574 lines
15 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. getcalr.c
  5. Abstract:
  6. This module implements the routine RtlGetCallerAddress. It will
  7. return the address of the caller, and the callers caller to the
  8. specified procedure.
  9. Author:
  10. William K. Cheung (wcheung) 17-Jan-1996
  11. based on version by Larry Osterman (larryo) 18-Mar-1991
  12. Revision History:
  13. 18-Feb-2001 (silviuc) : added RtlCaptureStackBackTrace.
  14. --*/
  15. #include "ntrtlp.h"
  16. //
  17. // Undefine get callers address since it is defined as a macro.
  18. //
  19. #undef RtlGetCallersAddress
  20. ULONG
  21. RtlpWalkFrameChainExceptionFilter (
  22. ULONG ExceptionCode,
  23. PVOID ExceptionRecord
  24. );
  25. PRUNTIME_FUNCTION
  26. RtlpLookupFunctionEntryForStackWalks (
  27. IN ULONGLONG ControlPc,
  28. OUT PULONGLONG ImageBase,
  29. OUT PULONGLONG TargetGp
  30. )
  31. /*++
  32. Routine Description:
  33. This function searches the currently active function tables for an
  34. entry that corresponds to the specified PC value.
  35. This code is identical to RtlLookupFunctionEntry, except that it does not
  36. check the dynamic function table list.
  37. Arguments:
  38. ControlPc - Supplies the virtual address of an instruction bundle
  39. within the specified function.
  40. ImageBase - Returns the base address of the module to which the
  41. function belongs.
  42. TargetGp - Returns the global pointer value of the module.
  43. Return Value:
  44. If there is no entry in the function table for the specified PC, then
  45. NULL is returned. Otherwise, the address of the function table entry
  46. that corresponds to the specified PC is returned.
  47. --*/
  48. {
  49. PRUNTIME_FUNCTION FunctionEntry;
  50. PRUNTIME_FUNCTION FunctionTable;
  51. ULONG SizeOfExceptionTable;
  52. ULONG Size;
  53. LONG High;
  54. LONG Middle;
  55. LONG Low;
  56. USHORT i;
  57. //
  58. // Search for the image that includes the specified swizzled PC value.
  59. //
  60. *ImageBase = (ULONG_PTR)RtlPcToFileHeader((PVOID)ControlPc,
  61. (PVOID *)ImageBase);
  62. //
  63. // If an image is found that includes the specified PC, then locate the
  64. // function table for the image.
  65. //
  66. if ((PVOID)*ImageBase != NULL) {
  67. *TargetGp = (ULONG_PTR)(RtlImageDirectoryEntryToData(
  68. (PVOID)*ImageBase,
  69. TRUE,
  70. IMAGE_DIRECTORY_ENTRY_GLOBALPTR,
  71. &Size
  72. ));
  73. FunctionTable = (PRUNTIME_FUNCTION)RtlImageDirectoryEntryToData(
  74. (PVOID)*ImageBase,
  75. TRUE,
  76. IMAGE_DIRECTORY_ENTRY_EXCEPTION,
  77. &SizeOfExceptionTable);
  78. //
  79. // If a function table is located, then search the table for a
  80. // function table entry for the specified PC.
  81. //
  82. if (FunctionTable != NULL) {
  83. //
  84. // Initialize search indices.
  85. //
  86. Low = 0;
  87. High = (SizeOfExceptionTable / sizeof(RUNTIME_FUNCTION)) - 1;
  88. ControlPc = ControlPc - *ImageBase;
  89. //
  90. // Perform binary search on the function table for a function table
  91. // entry that subsumes the specified PC.
  92. //
  93. while (High >= Low) {
  94. //
  95. // Compute next probe index and test entry. If the specified PC
  96. // is greater than of equal to the beginning address and less
  97. // than the ending address of the function table entry, then
  98. // return the address of the function table entry. Otherwise,
  99. // continue the search.
  100. //
  101. Middle = (Low + High) >> 1;
  102. FunctionEntry = &FunctionTable[Middle];
  103. if (ControlPc < FunctionEntry->BeginAddress) {
  104. High = Middle - 1;
  105. } else if (ControlPc >= FunctionEntry->EndAddress) {
  106. Low = Middle + 1;
  107. } else {
  108. return FunctionEntry;
  109. }
  110. }
  111. }
  112. }
  113. return NULL;
  114. }
  115. VOID
  116. RtlGetCallersAddress (
  117. OUT PVOID *CallersPc,
  118. OUT PVOID *CallersCallersPc
  119. )
  120. /*++
  121. Routine Description:
  122. This routine returns the address of the routine that called the routine
  123. that called this routine, and the routine that called the routine that
  124. called this routine. For example, if A called B called C which called
  125. this routine, the return addresses in A and B would be returned.
  126. Arguments:
  127. CallersPc - Supplies a pointer to a variable that receives the address
  128. of the caller of the caller of this routine (B).
  129. CallersCallersPc - Supplies a pointer to a variable that receives the
  130. address of the caller of the caller of the caller of this routine
  131. (A).
  132. Return Value:
  133. None.
  134. Note:
  135. If either of the calling stack frames exceeds the limits of the stack,
  136. they are set to NULL.
  137. --*/
  138. {
  139. #ifdef REALLY_GET_CALLERS_CALLER
  140. CONTEXT ContextRecord;
  141. FRAME_POINTERS EstablisherFrame;
  142. PRUNTIME_FUNCTION FunctionEntry;
  143. BOOLEAN InFunction;
  144. ULONG_PTR NextPc;
  145. ULONGLONG HighStackLimit, LowStackLimit;
  146. ULONGLONG HighBStoreLimit, LowBStoreLimit;
  147. ULONGLONG ImageBase;
  148. ULONGLONG TargetGp;
  149. //
  150. // Assume the function table entries for the various routines cannot be
  151. // found or there are not four procedure activation records on the stack.
  152. //
  153. *CallersPc = NULL;
  154. *CallersCallersPc = NULL;
  155. //
  156. // Capture the current context.
  157. //
  158. RtlCaptureContext(&ContextRecord);
  159. NextPc = (ULONG_PTR)ContextRecord.BrRp;
  160. //
  161. // Get the high and low limits of the current thread's stack.
  162. //
  163. Rtlp64GetStackLimits(&LowStackLimit, &HighStackLimit);
  164. Rtlp64GetBStoreLimits(&LowBStoreLimit, &HighBStoreLimit);
  165. //
  166. // Attempt to unwind to the caller of this routine (C).
  167. //
  168. FunctionEntry = RtlpLookupFunctionEntryForStackWalks(NextPc, &ImageBase, &TargetGp);
  169. if (FunctionEntry != NULL) {
  170. //
  171. // A function entry was found for this routine. Virtually unwind
  172. // to the caller of this routine (C).
  173. //
  174. NextPc = RtlVirtualUnwind(NextPc,
  175. FunctionEntry,
  176. &ContextRecord,
  177. &InFunction,
  178. &EstablisherFrame,
  179. NULL);
  180. //
  181. // Attempt to unwind to the caller of the caller of this routine (B).
  182. //
  183. FunctionEntry = RtlpLookupFunctionEntryForStackWalks(NextPc);
  184. if ((FunctionEntry != NULL) && (((ULONG_PTR)ContextRecord.IntSp < HighStackLimit) && ((ULONG_PTR)ContextRecord.RsBSP > LowBStoreLimit))) {
  185. //
  186. // A function table entry was found for the caller of the caller
  187. // of this routine (B). Virtually unwind to the caller of the
  188. // caller of this routine (B).
  189. //
  190. NextPc = RtlVirtualUnwind(NextPc,
  191. FunctionEntry,
  192. &ContextRecord,
  193. &InFunction,
  194. &EstablisherFrame,
  195. NULL);
  196. *CallersPc = (PVOID)NextPc;
  197. //
  198. // Attempt to unwind to the caller of the caller of the caller
  199. // of the caller of this routine (A).
  200. //
  201. FunctionEntry = RtlpLookupFunctionEntryForStackWalks(NextPc);
  202. if ((FunctionEntry != NULL) && (((ULONG_PTR)ContextRecord.IntSp < HighStackLimit) && ((ULONG_PTR)ContextRecord.RsBSP > LowBStoreLimit))) {
  203. //
  204. // A function table entry was found for the caller of the
  205. // caller of the caller of this routine (A). Virtually unwind
  206. // to the caller of the caller of the caller of this routine
  207. // (A).
  208. //
  209. NextPc = RtlVirtualUnwind(NextPc,
  210. FunctionEntry,
  211. &ContextRecord,
  212. &InFunction,
  213. &EstablisherFrame,
  214. NULL);
  215. *CallersCallersPc = (PVOID)NextPc;
  216. }
  217. }
  218. }
  219. #else
  220. *CallersPc = NULL;
  221. *CallersCallersPc = NULL;
  222. #endif // REALLY_GET_CALLERS_CALLER
  223. return;
  224. }
  225. ULONG
  226. RtlWalkFrameChain (
  227. OUT PVOID *Callers,
  228. IN ULONG Count,
  229. IN ULONG Flags
  230. )
  231. /*++
  232. Routine Description:
  233. RtlpWalkFrameChain64
  234. Description:
  235. This function tries to walk the call chain and fill out a vector of
  236. return addresses. The function works only on IA64. It is possible that
  237. the function cannot fill the requested number of callers.
  238. In this case the function will just return with
  239. a less then requested count.
  240. In kernel mode the function should not take
  241. any exceptions (page faults) because it can be called at all sorts of
  242. irql levels. It needs to be tested if this is the case.
  243. Return value:
  244. The number of identified return addresses on the stack. This can be less
  245. then the Count requested if the stack ends or we encounter an error while
  246. virtually unwinding the stack.
  247. --*/
  248. {
  249. CONTEXT ContextRecord;
  250. FRAME_POINTERS EstablisherFrame;
  251. PRUNTIME_FUNCTION FunctionEntry;
  252. BOOLEAN InFunction;
  253. ULONG_PTR NextPc, ControlPc;
  254. ULONGLONG HighStackLimit, LowStackLimit;
  255. ULONGLONG HighBStoreLimit, LowBStoreLimit;
  256. ULONGLONG ImageBase;
  257. ULONGLONG TargetGp;
  258. ULONG CallersFound;
  259. //
  260. // In kernel mode avoid running at irql levels where we cannot
  261. // take page faults. The walking code will touch various sections
  262. // from driver images and this will cause page faults.
  263. //
  264. #ifdef NTOS_KERNEL_RUNTIME
  265. if (KeGetCurrentIrql() > PASSIVE_LEVEL) {
  266. return 0;
  267. }
  268. #endif
  269. //
  270. // Assume the function table entries for the various routines cannot be
  271. // found or there are not enough procedure activation records on the stack.
  272. //
  273. CallersFound = 0;
  274. RtlZeroMemory (Callers, Count * sizeof(PVOID));
  275. //
  276. // Capture the current context.
  277. //
  278. RtlCaptureContext (&ContextRecord);
  279. NextPc = (ULONG_PTR)ContextRecord.BrRp;
  280. //
  281. // Get the high and low limits of the current thread's stack.
  282. //
  283. Rtlp64GetStackLimits (&LowStackLimit, &HighStackLimit);
  284. Rtlp64GetBStoreLimits (&LowBStoreLimit, &HighBStoreLimit);
  285. //
  286. // Loop to get requested number of callers.
  287. //
  288. try {
  289. while (CallersFound < Count) {
  290. #ifdef NTOS_KERNEL_RUNTIME
  291. //
  292. // We need to check the NextPc value that we have got from
  293. // CaptureContext() or VirtualUnwind(). It can happen that
  294. // we pick up a bogus value from a session driver but in the
  295. // current process no session space is mapped.
  296. //
  297. if ((MmIsSessionAddress ((PVOID)NextPc) == TRUE) &&
  298. (MmGetSessionId (PsGetCurrentProcess()) == 0)) {
  299. break;
  300. }
  301. #endif
  302. FunctionEntry = RtlpLookupFunctionEntryForStackWalks (NextPc,
  303. &ImageBase,
  304. &TargetGp);
  305. //
  306. // If we cannot find a function table entry or we are not
  307. // within stack limits or backing store limits anymore
  308. // we are done.
  309. //
  310. if (FunctionEntry == NULL) {
  311. break;
  312. }
  313. if ((ULONG_PTR)(ContextRecord.IntSp) >= HighStackLimit ||
  314. (ULONG_PTR)(ContextRecord.IntSp) <= LowStackLimit) {
  315. break;
  316. }
  317. if ((ULONG_PTR)(ContextRecord.RsBSP) <= LowBStoreLimit ||
  318. (ULONG_PTR)(ContextRecord.RsBSP) >= HighBStoreLimit) {
  319. break;
  320. }
  321. //
  322. // A function table entry was found.
  323. // Virtually unwind to the caller of this routine.
  324. //
  325. NextPc = RtlVirtualUnwind (ImageBase,
  326. NextPc,
  327. FunctionEntry,
  328. &ContextRecord,
  329. &InFunction,
  330. &EstablisherFrame,
  331. NULL);
  332. Callers[CallersFound] = (PVOID)NextPc;
  333. CallersFound += 1;
  334. }
  335. } except (RtlpWalkFrameChainExceptionFilter (_exception_code(), _exception_info())) {
  336. CallersFound = 0;
  337. }
  338. return CallersFound;
  339. }
  340. USHORT
  341. RtlCaptureStackBackTrace(
  342. IN ULONG FramesToSkip,
  343. IN ULONG FramesToCapture,
  344. OUT PVOID *BackTrace,
  345. OUT PULONG BackTraceHash
  346. )
  347. /*++
  348. Routine Description:
  349. This routine walks up the stack frames, capturing the return address from
  350. each frame requested.
  351. Arguments:
  352. FramesToSkip - frames detected but not included in the stack trace
  353. FramesToCapture - frames to be captured in the stack trace buffer.
  354. One of the frames will be for RtlCaptureStackBackTrace.
  355. BackTrace - stack trace buffer
  356. BackTraceHash - very simple hash value that can be used to organize
  357. hash tables. It is just an arithmetic sum of the pointers in the
  358. stack trace buffer. If NULL then no hash value is computed.
  359. Return Value:
  360. Number of return addresses returned in the stack trace buffer.
  361. --*/
  362. {
  363. PVOID Trace [2 * MAX_STACK_DEPTH];
  364. ULONG FramesFound;
  365. ULONG HashValue;
  366. ULONG Index;
  367. //
  368. // One more frame to skip for the "capture" function (RtlWalkFrameChain).
  369. //
  370. FramesToSkip += 1;
  371. //
  372. // Sanity checks.
  373. //
  374. if (FramesToCapture + FramesToSkip >= 2 * MAX_STACK_DEPTH) {
  375. return 0;
  376. }
  377. FramesFound = RtlWalkFrameChain (Trace,
  378. FramesToCapture + FramesToSkip,
  379. 0);
  380. if (FramesFound <= FramesToSkip) {
  381. return 0;
  382. }
  383. for (Index = 0, HashValue = 0; Index < FramesToCapture; Index += 1) {
  384. if (FramesToSkip + Index >= FramesFound) {
  385. break;
  386. }
  387. BackTrace[Index] = Trace[FramesToSkip + Index];
  388. HashValue += PtrToUlong(BackTrace[Index]);
  389. }
  390. if (BackTraceHash != NULL) {
  391. *BackTraceHash = HashValue;
  392. }
  393. return (USHORT)Index;
  394. }
  395. ULONG
  396. RtlpWalkFrameChainExceptionFilter (
  397. ULONG ExceptionCode,
  398. PVOID ExceptionRecord
  399. )
  400. /*++
  401. Routine Description:
  402. This routine is the exception filter used by RtlWalkFramechain function.
  403. Arguments:
  404. ExceptionCode - exception code
  405. ExceptionRecord - structure with pointers to .exr and .cxr
  406. Return Value:
  407. Always EXCEPTION_EXECUTE_HANDLER.
  408. --*/
  409. {
  410. #if DBG
  411. DbgPrint ("Unexpected exception (info %p) in RtlWalkFrameChain ...\n",
  412. ExceptionRecord);
  413. DbgBreakPoint ();
  414. #endif
  415. return EXCEPTION_EXECUTE_HANDLER;
  416. }