Leaked source code of windows server 2003
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.

582 lines
16 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. RtlWalkFrameChain
  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. // IA64 code does not support any flags.
  261. //
  262. if (Flags != 0) {
  263. return 0;
  264. }
  265. //
  266. // In kernel mode avoid running at irql levels where we cannot
  267. // take page faults. The walking code will access various sections
  268. // from driver and system images and this will cause page faults.
  269. //
  270. #ifdef NTOS_KERNEL_RUNTIME
  271. if (KeAreAllApcsDisabled () == TRUE) {
  272. return 0;
  273. }
  274. #endif
  275. //
  276. // Assume the function table entries for the various routines cannot be
  277. // found or there are not enough procedure activation records on the stack.
  278. //
  279. CallersFound = 0;
  280. RtlZeroMemory (Callers, Count * sizeof(PVOID));
  281. //
  282. // Capture the current context.
  283. //
  284. RtlCaptureContext (&ContextRecord);
  285. NextPc = (ULONG_PTR)ContextRecord.BrRp;
  286. //
  287. // Get the high and low limits of the current thread's stack.
  288. //
  289. Rtlp64GetStackLimits (&LowStackLimit, &HighStackLimit);
  290. Rtlp64GetBStoreLimits (&LowBStoreLimit, &HighBStoreLimit);
  291. //
  292. // Loop to get requested number of callers.
  293. //
  294. try {
  295. while (CallersFound < Count) {
  296. #ifdef NTOS_KERNEL_RUNTIME
  297. //
  298. // We need to check the NextPc value that we have got from
  299. // CaptureContext() or VirtualUnwind(). It can happen that
  300. // we pick up a bogus value from a session driver but in the
  301. // current process no session space is mapped.
  302. //
  303. if ((MmIsSessionAddress ((PVOID)NextPc) == TRUE) &&
  304. (MmGetSessionId (PsGetCurrentProcess()) == 0)) {
  305. break;
  306. }
  307. #endif
  308. FunctionEntry = RtlpLookupFunctionEntryForStackWalks (NextPc,
  309. &ImageBase,
  310. &TargetGp);
  311. //
  312. // If we cannot find a function table entry or we are not
  313. // within stack limits or backing store limits anymore
  314. // we are done.
  315. //
  316. if (FunctionEntry == NULL) {
  317. break;
  318. }
  319. if ((ULONG_PTR)(ContextRecord.IntSp) >= HighStackLimit ||
  320. (ULONG_PTR)(ContextRecord.IntSp) <= LowStackLimit) {
  321. break;
  322. }
  323. if ((ULONG_PTR)(ContextRecord.RsBSP) <= LowBStoreLimit ||
  324. (ULONG_PTR)(ContextRecord.RsBSP) >= HighBStoreLimit) {
  325. break;
  326. }
  327. //
  328. // A function table entry was found.
  329. // Virtually unwind to the caller of this routine.
  330. //
  331. NextPc = RtlVirtualUnwind (ImageBase,
  332. NextPc,
  333. FunctionEntry,
  334. &ContextRecord,
  335. &InFunction,
  336. &EstablisherFrame,
  337. NULL);
  338. Callers[CallersFound] = (PVOID)NextPc;
  339. CallersFound += 1;
  340. }
  341. } except (RtlpWalkFrameChainExceptionFilter (_exception_code(), _exception_info())) {
  342. CallersFound = 0;
  343. }
  344. return CallersFound;
  345. }
  346. USHORT
  347. RtlCaptureStackBackTrace(
  348. IN ULONG FramesToSkip,
  349. IN ULONG FramesToCapture,
  350. OUT PVOID *BackTrace,
  351. OUT PULONG BackTraceHash
  352. )
  353. /*++
  354. Routine Description:
  355. This routine walks up the stack frames, capturing the return address from
  356. each frame requested.
  357. Arguments:
  358. FramesToSkip - frames detected but not included in the stack trace
  359. FramesToCapture - frames to be captured in the stack trace buffer.
  360. One of the frames will be for RtlCaptureStackBackTrace.
  361. BackTrace - stack trace buffer
  362. BackTraceHash - very simple hash value that can be used to organize
  363. hash tables. It is just an arithmetic sum of the pointers in the
  364. stack trace buffer. If NULL then no hash value is computed.
  365. Return Value:
  366. Number of return addresses returned in the stack trace buffer.
  367. --*/
  368. {
  369. PVOID Trace [2 * MAX_STACK_DEPTH];
  370. ULONG FramesFound;
  371. ULONG HashValue;
  372. ULONG Index;
  373. //
  374. // One more frame to skip for the "capture" function (RtlWalkFrameChain).
  375. //
  376. FramesToSkip += 1;
  377. //
  378. // Sanity checks.
  379. //
  380. if (FramesToCapture + FramesToSkip >= 2 * MAX_STACK_DEPTH) {
  381. return 0;
  382. }
  383. FramesFound = RtlWalkFrameChain (Trace,
  384. FramesToCapture + FramesToSkip,
  385. 0);
  386. if (FramesFound <= FramesToSkip) {
  387. return 0;
  388. }
  389. for (Index = 0, HashValue = 0; Index < FramesToCapture; Index += 1) {
  390. if (FramesToSkip + Index >= FramesFound) {
  391. break;
  392. }
  393. BackTrace[Index] = Trace[FramesToSkip + Index];
  394. HashValue += PtrToUlong(BackTrace[Index]);
  395. }
  396. if (BackTraceHash != NULL) {
  397. *BackTraceHash = HashValue;
  398. }
  399. return (USHORT)Index;
  400. }
  401. ULONG
  402. RtlpWalkFrameChainExceptionFilter (
  403. ULONG ExceptionCode,
  404. PVOID ExceptionRecord
  405. )
  406. /*++
  407. Routine Description:
  408. This routine is the exception filter used by RtlWalkFramechain function.
  409. Arguments:
  410. ExceptionCode - exception code
  411. ExceptionRecord - structure with pointers to .exr and .cxr
  412. Return Value:
  413. Always EXCEPTION_EXECUTE_HANDLER.
  414. --*/
  415. {
  416. #if DBG
  417. DbgPrint ("Unexpected exception (info %p) in RtlWalkFrameChain ...\n",
  418. ExceptionRecord);
  419. DbgBreakPoint ();
  420. #endif
  421. return EXCEPTION_EXECUTE_HANDLER;
  422. }