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.

710 lines
20 KiB

  1. /*++
  2. Copyright (c) 2001 Microsoft Corporation
  3. Module Name:
  4. lookup.c
  5. Abstract:
  6. This module implements function table lookup for platforms with table
  7. base exception handling.
  8. Author:
  9. David N. Cutler (davec) 30-May-2001
  10. Revision History:
  11. --*/
  12. #include "ntrtlp.h"
  13. //
  14. // Define external data.
  15. //
  16. #if defined(NTOS_KERNEL_RUNTIME)
  17. #if defined(_AMD64_) // ****** temp ******
  18. #pragma alloc_text(INIT, RtlInitializeHistoryTable)
  19. #endif // ****** temp ******
  20. #else
  21. #include "..\ntdll\ldrp.h"
  22. extern PVOID NtDllBase;
  23. #endif
  24. //
  25. // Define global unwind history table to hold the constant unwind entries
  26. // for exception dispatch followed by unwind.
  27. //
  28. UNWIND_HISTORY_TABLE RtlpUnwindHistoryTable = {
  29. 0, UNWIND_HISTORY_TABLE_NONE, - 1, 0};
  30. #if defined(_AMD64_) // ****** temp ******
  31. VOID
  32. RtlInitializeHistoryTable (
  33. VOID
  34. )
  35. /*++
  36. Routine Description:
  37. This function initializes the global unwind history table.
  38. Arguments:
  39. None.
  40. Return Value:
  41. None.
  42. --*/
  43. {
  44. ULONG64 BeginAddress;
  45. ULONG64 ControlPc;
  46. ULONG64 EndAddress;
  47. PVOID *FunctionAddressTable;
  48. PRUNTIME_FUNCTION FunctionEntry;
  49. ULONG64 ImageBase;
  50. ULONG Index;
  51. //
  52. // Lookup function entries from the function address table until a NULL
  53. // entry is encountered or the unwind history table is full.
  54. //
  55. FunctionAddressTable = &RtlpFunctionAddressTable[0];
  56. Index = 0;
  57. while ((Index < UNWIND_HISTORY_TABLE_SIZE) &&
  58. (*FunctionAddressTable != NULL)) {
  59. ControlPc = (ULONG64)*FunctionAddressTable++;
  60. FunctionEntry = RtlLookupFunctionEntry(ControlPc,
  61. &ImageBase,
  62. NULL);
  63. ASSERT(FunctionEntry != NULL);
  64. BeginAddress = FunctionEntry->BeginAddress + ImageBase;
  65. EndAddress = FunctionEntry->EndAddress + ImageBase;
  66. RtlpUnwindHistoryTable.Entry[Index].ImageBase = ImageBase;
  67. RtlpUnwindHistoryTable.Entry[Index].FunctionEntry = FunctionEntry;
  68. if (BeginAddress < RtlpUnwindHistoryTable.LowAddress) {
  69. RtlpUnwindHistoryTable.LowAddress = BeginAddress;
  70. }
  71. if (EndAddress > RtlpUnwindHistoryTable.HighAddress) {
  72. RtlpUnwindHistoryTable.HighAddress = EndAddress;
  73. }
  74. Index += 1;
  75. }
  76. RtlpUnwindHistoryTable.Count = Index;
  77. return;
  78. }
  79. #endif // ****** temp ******
  80. PRUNTIME_FUNCTION
  81. RtlpSearchInvertedFunctionTable (
  82. PINVERTED_FUNCTION_TABLE InvertedTable,
  83. PVOID ControlPc,
  84. OUT PVOID *ImageBase,
  85. #if defined(_IA64_)
  86. OUT PULONG64 Gp,
  87. #endif
  88. OUT PULONG SizeOfTable
  89. )
  90. /*++
  91. Routine Description:
  92. This function searches for a matching entry in an inverted function
  93. table using the specified control PC value.
  94. N.B. It is assumed that appropriate locks are held when this routine
  95. is called.
  96. Arguments:
  97. InvertedTable - Supplies a pointer to an inverted function table.
  98. ControlPc - Supplies a PC value to to use in searching the inverted
  99. function table.
  100. ImageBase - Supplies a pointer to a variable that receives the base
  101. address of the corresponding module.
  102. SizeOfTable - Supplies a pointer to a variable that recevies the size
  103. of the function table in bytes.
  104. Return Value:
  105. If a matching entry is located in the specified function table, then
  106. the function table address is returned as the function value. Otherwise,
  107. a value of NULL is returned.
  108. --*/
  109. {
  110. PVOID Bound;
  111. LONG High;
  112. ULONG Index;
  113. PINVERTED_FUNCTION_TABLE_ENTRY InvertedEntry;
  114. LONG Low;
  115. LONG Middle;
  116. //
  117. // If there are any entries in the specified inverted function table,
  118. // then search the table for a matching entry.
  119. //
  120. if (InvertedTable->CurrentSize != 0) {
  121. Low = 0;
  122. High = InvertedTable->CurrentSize - 1;
  123. while (High >= Low) {
  124. //
  125. // Compute next probe index and test entry. If the specified
  126. // control PC is greater than of equal to the beginning address
  127. // and less than the ending address of the inverted function
  128. // table entry, then return the address of the function table.
  129. // Otherwise, continue the search.
  130. //
  131. Middle = (Low + High) >> 1;
  132. InvertedEntry = &InvertedTable->TableEntry[Middle];
  133. Bound = (PVOID)((ULONG_PTR)InvertedEntry->ImageBase + InvertedEntry->SizeOfImage);
  134. if (ControlPc < InvertedEntry->ImageBase) {
  135. High = Middle - 1;
  136. } else if (ControlPc >= Bound) {
  137. Low = Middle + 1;
  138. } else {
  139. *ImageBase = InvertedEntry->ImageBase;
  140. #if defined(_IA64_)
  141. *Gp = InvertedEntry->Gp;
  142. #endif
  143. *SizeOfTable = InvertedEntry->SizeOfTable;
  144. return InvertedEntry->FunctionTable;
  145. }
  146. }
  147. }
  148. return NULL;
  149. }
  150. PRUNTIME_FUNCTION
  151. RtlpLookupFunctionTable (
  152. IN PVOID ControlPc,
  153. OUT PVOID *ImageBase,
  154. #if defined(_IA64_)
  155. OUT PULONG64 Gp,
  156. #endif
  157. OUT PULONG SizeOfTable
  158. )
  159. /*++
  160. Routine Description:
  161. This function looks up the control PC in the loaded module list, and
  162. returns the image base, the size of the function table, and the address
  163. of the function table.
  164. Arguments:
  165. ControlPc - Supplies an address in the module to be looked up.
  166. ImageBase - Supplies a pointer to a variable that receives the base
  167. address of the corresponding module.
  168. SizeOfTable - Supplies a pointer to a variable that recevies the size
  169. of the function table in bytes.
  170. Return Value:
  171. If a module is found that contains the specified control PC value and
  172. that module contains a function table, then the address of the function
  173. table is returned as the function value. Otherwise, NULL is returned.
  174. --*/
  175. {
  176. PVOID Base;
  177. ULONG_PTR Bound;
  178. PLDR_DATA_TABLE_ENTRY Entry;
  179. PRUNTIME_FUNCTION FunctionTable;
  180. PLIST_ENTRY Next;
  181. #if defined(NTOS_KERNEL_RUNTIME)
  182. KIRQL OldIrql;
  183. //
  184. // Acquire the loaded module list spinlock and scan the list for the
  185. // specified PC value if the list has been initialized.
  186. //
  187. OldIrql = KeGetCurrentIrql();
  188. if (OldIrql < DISPATCH_LEVEL) {
  189. KeRaiseIrqlToDpcLevel();
  190. }
  191. ExAcquireSpinLockAtDpcLevel(&PsLoadedModuleSpinLock);
  192. FunctionTable = RtlpSearchInvertedFunctionTable(&PsInvertedFunctionTable,
  193. ControlPc,
  194. &Base,
  195. #if defined(_IA64_)
  196. Gp,
  197. #endif
  198. SizeOfTable);
  199. if ((FunctionTable == NULL) &&
  200. (PsInvertedFunctionTable.Overflow != FALSE)) {
  201. Next = PsLoadedModuleList.Flink;
  202. if (Next != NULL) {
  203. while (Next != &PsLoadedModuleList) {
  204. Entry = CONTAINING_RECORD(Next,
  205. LDR_DATA_TABLE_ENTRY,
  206. InLoadOrderLinks);
  207. Next = Next->Flink;
  208. Base = Entry->DllBase;
  209. Bound = (ULONG_PTR)Base + Entry->SizeOfImage;
  210. if (((ULONG_PTR)ControlPc >= (ULONG_PTR)Base) &&
  211. ((ULONG_PTR)ControlPc < Bound)) {
  212. //
  213. // Lookup function table address and size.
  214. //
  215. #if defined(_IA64_)
  216. *Gp = (ULONG64)(RtlImageDirectoryEntryToData(Base,
  217. TRUE,
  218. IMAGE_DIRECTORY_ENTRY_GLOBALPTR,
  219. SizeOfTable));
  220. #endif
  221. FunctionTable = RtlImageDirectoryEntryToData(Base,
  222. TRUE,
  223. IMAGE_DIRECTORY_ENTRY_EXCEPTION,
  224. SizeOfTable);
  225. break;
  226. }
  227. }
  228. }
  229. }
  230. //
  231. // Release the loaded module list spin lock.
  232. //
  233. ExReleaseSpinLock(&PsLoadedModuleSpinLock, OldIrql);
  234. #else
  235. BOOLEAN InLdrInit;
  236. MEMORY_BASIC_INFORMATION MemoryInformation;
  237. PLIST_ENTRY ModuleListHead;
  238. PIMAGE_NT_HEADERS NtHeaders;
  239. PPEB Peb;
  240. PTEB Teb;
  241. NTSTATUS Status;
  242. //
  243. // Acquire the Loader lock for the current process and scan the loaded
  244. // module list for the specified PC value if all the data structures
  245. // have been initialized.
  246. //
  247. FunctionTable = NULL;
  248. InLdrInit = LdrpInLdrInit;
  249. if ((InLdrInit == FALSE) &&
  250. (RtlTryEnterCriticalSection(&LdrpLoaderLock) == FALSE)) {
  251. //
  252. // The loader lock could not be acquired. Call the system to find the
  253. // image that contains the control PC.
  254. //
  255. Status = NtQueryVirtualMemory(NtCurrentProcess(),
  256. ControlPc,
  257. MemoryBasicInformation,
  258. &MemoryInformation,
  259. sizeof(MEMORY_BASIC_INFORMATION),
  260. NULL);
  261. if (NT_SUCCESS(Status) &&
  262. (MemoryInformation.Type == MEM_IMAGE)) {
  263. //
  264. // Lookup function table address and size.
  265. //
  266. Base = MemoryInformation.AllocationBase;
  267. #if defined(_IA64_)
  268. *Gp = (ULONG64)(RtlImageDirectoryEntryToData(Base,
  269. TRUE,
  270. IMAGE_DIRECTORY_ENTRY_GLOBALPTR,
  271. SizeOfTable));
  272. #endif
  273. FunctionTable = RtlImageDirectoryEntryToData(Base,
  274. TRUE,
  275. IMAGE_DIRECTORY_ENTRY_EXCEPTION,
  276. SizeOfTable);
  277. }
  278. } else {
  279. //
  280. // The loader lock was acquired or the loader is being initialized.
  281. // Search the loaded module list if it is currently defined. Otherwise,
  282. // set the values for ntdll.
  283. //
  284. Teb = NtCurrentTeb();
  285. if (Teb != NULL) {
  286. Peb = Teb->ProcessEnvironmentBlock;
  287. if (Peb->Ldr != NULL) {
  288. FunctionTable = RtlpSearchInvertedFunctionTable(&LdrpInvertedFunctionTable,
  289. ControlPc,
  290. &Base,
  291. #if defined(_IA64_)
  292. Gp,
  293. #endif
  294. SizeOfTable);
  295. if ((FunctionTable == NULL) &&
  296. ((InLdrInit != FALSE) ||
  297. (LdrpInvertedFunctionTable.Overflow != FALSE))) {
  298. ModuleListHead = &Peb->Ldr->InLoadOrderModuleList;
  299. Next = ModuleListHead->Flink;
  300. if (Next != NULL) {
  301. while (Next != ModuleListHead) {
  302. Entry = CONTAINING_RECORD(Next,
  303. LDR_DATA_TABLE_ENTRY,
  304. InLoadOrderLinks);
  305. Next = Next->Flink;
  306. Base = Entry->DllBase;
  307. Bound = (ULONG_PTR)Base + Entry->SizeOfImage;
  308. if (((ULONG_PTR)ControlPc >= (ULONG_PTR)Base) &&
  309. ((ULONG_PTR)ControlPc < Bound)) {
  310. #if defined(_IA64_)
  311. *Gp = (ULONG64)(RtlImageDirectoryEntryToData(Base,
  312. TRUE,
  313. IMAGE_DIRECTORY_ENTRY_GLOBALPTR,
  314. SizeOfTable));
  315. #endif
  316. FunctionTable = RtlImageDirectoryEntryToData(Base,
  317. TRUE,
  318. IMAGE_DIRECTORY_ENTRY_EXCEPTION,
  319. SizeOfTable);
  320. break;
  321. }
  322. }
  323. }
  324. }
  325. } else {
  326. //
  327. // The loaded module list has not been initialized. Therefore,
  328. // the current executing code must be in ntdll. If ntddl base
  329. // is not NULL and the control PC is within the ntdll range,
  330. // then return the information for ntdll.
  331. //
  332. if (NtDllBase != NULL) {
  333. Base = NtDllBase;
  334. NtHeaders = RtlImageNtHeader(Base);
  335. if (NtHeaders != NULL) {
  336. Bound = (ULONG_PTR)Base + NtHeaders->OptionalHeader.SizeOfImage;
  337. if (((ULONG_PTR)ControlPc >= (ULONG_PTR)Base) &&
  338. ((ULONG_PTR)ControlPc < Bound)) {
  339. #if defined(_IA64_)
  340. *Gp = (ULONG64)(RtlImageDirectoryEntryToData(Base,
  341. TRUE,
  342. IMAGE_DIRECTORY_ENTRY_GLOBALPTR,
  343. SizeOfTable));
  344. #endif
  345. FunctionTable = RtlImageDirectoryEntryToData(Base,
  346. TRUE,
  347. IMAGE_DIRECTORY_ENTRY_EXCEPTION,
  348. SizeOfTable);
  349. }
  350. }
  351. }
  352. }
  353. }
  354. //
  355. // Release the loader lock if it was acquired.
  356. //
  357. if (InLdrInit == FALSE) {
  358. RtlLeaveCriticalSection(&LdrpLoaderLock);
  359. }
  360. }
  361. #endif
  362. //
  363. // Set the image base address and return the function table address.
  364. //
  365. *ImageBase = Base;
  366. return FunctionTable;
  367. }
  368. VOID
  369. RtlInsertInvertedFunctionTable (
  370. PINVERTED_FUNCTION_TABLE InvertedTable,
  371. PVOID ImageBase,
  372. ULONG SizeOfImage
  373. )
  374. /*++
  375. Routine Description:
  376. This function inserts an entry in an inverted function table if there
  377. is room in the table. Otherwise, no operation is performed.
  378. N.B. It is assumed that appropriate locks are held when this routine
  379. is called.
  380. N.B. If the inverted function table overflows, then it is treated as
  381. a cache. This is unlikely to happen, however.
  382. Arguments:
  383. InvertedTable - Supplies a pointer to the inverted function table in
  384. which the specified entry is to be inserted.
  385. ImageBase - Supplies the base address of the containing image.
  386. SizeOfImage - Supplies the size of the image.
  387. Return Value:
  388. None.
  389. --*/
  390. {
  391. ULONG CurrentSize;
  392. PRUNTIME_FUNCTION FunctionTable;
  393. #if defined(_IA64_)
  394. ULONG64 Gp;
  395. #endif
  396. ULONG Index;
  397. ULONG SizeOfTable;
  398. //
  399. // If the inverted table is not full, then insert the entry in the
  400. // specified inverted table.
  401. //
  402. CurrentSize = InvertedTable->CurrentSize;
  403. if (CurrentSize != InvertedTable->MaximumSize) {
  404. //
  405. // If the inverted table has no entries, then insert the new entry as
  406. // the first entry. Otherwise, search the inverted table for the proper
  407. // insert position, shuffle the table, and insert the new entry.
  408. //
  409. Index = 0;
  410. if (CurrentSize != 0) {
  411. for (Index = 0; Index < CurrentSize; Index += 1) {
  412. if (ImageBase < InvertedTable->TableEntry[Index].ImageBase) {
  413. break;
  414. }
  415. }
  416. //
  417. // If the new entry does not go at the end of the specified table,
  418. // then shuffle the table down to make room for the new entry.
  419. //
  420. if (Index != CurrentSize) {
  421. RtlMoveMemory(&InvertedTable->TableEntry[Index + 1],
  422. &InvertedTable->TableEntry[Index],
  423. (CurrentSize - Index) * sizeof(INVERTED_FUNCTION_TABLE_ENTRY));
  424. }
  425. }
  426. //
  427. // Insert the specified entry in the specified inverted function table.
  428. //
  429. FunctionTable = RtlImageDirectoryEntryToData (ImageBase,
  430. TRUE,
  431. IMAGE_DIRECTORY_ENTRY_EXCEPTION,
  432. &SizeOfTable);
  433. InvertedTable->TableEntry[Index].FunctionTable = FunctionTable;
  434. InvertedTable->TableEntry[Index].ImageBase = ImageBase;
  435. InvertedTable->TableEntry[Index].SizeOfImage = SizeOfImage;
  436. InvertedTable->TableEntry[Index].SizeOfTable = SizeOfTable;
  437. #if defined(_IA64_)
  438. Gp = (ULONG64)RtlImageDirectoryEntryToData (ImageBase,
  439. TRUE,
  440. IMAGE_DIRECTORY_ENTRY_GLOBALPTR,
  441. &SizeOfTable);
  442. InvertedTable->TableEntry[Index].Gp = Gp;
  443. #endif
  444. InvertedTable->CurrentSize += 1;
  445. } else {
  446. InvertedTable->Overflow = TRUE;
  447. }
  448. return;
  449. }
  450. VOID
  451. RtlRemoveInvertedFunctionTable (
  452. PINVERTED_FUNCTION_TABLE InvertedTable,
  453. PVOID ImageBase
  454. )
  455. /*++
  456. Routine Description:
  457. This routine removes an entry from an inverted function table.
  458. N.B. It is assumed that appropriate locks are held when this routine
  459. is called.
  460. Arguments:
  461. InvertedTable - Supplies a pointer to the inverted function table from
  462. which the specified entry is to be removed.
  463. ImageBase - Supplies the base address of the containing image.
  464. Return Value:
  465. None.
  466. --*/
  467. {
  468. ULONG CurrentSize;
  469. ULONG Index;
  470. //
  471. // Search for an entry in the specified inverted table that matches the
  472. // image base.
  473. //
  474. // N.B. It is possible a matching entry is not in the inverted table
  475. // the table was full when an attempt was made to insert the
  476. // corresponding entry.
  477. //
  478. CurrentSize = InvertedTable->CurrentSize;
  479. for (Index = 0; Index < CurrentSize; Index += 1) {
  480. if (ImageBase == InvertedTable->TableEntry[Index].ImageBase) {
  481. break;
  482. }
  483. }
  484. //
  485. // If the entry was found in the inverted table, then remove the entry
  486. // and reduce the size of the table.
  487. //
  488. if (Index != CurrentSize) {
  489. //
  490. // If the size of the table is not one, then shuffle the table and
  491. // remove the specified entry.
  492. //
  493. if (CurrentSize != 1) {
  494. RtlCopyMemory(&InvertedTable->TableEntry[Index],
  495. &InvertedTable->TableEntry[Index + 1],
  496. (CurrentSize - Index - 1) * sizeof(INVERTED_FUNCTION_TABLE_ENTRY));
  497. }
  498. //
  499. // Reduce the size of the inverted table.
  500. //
  501. InvertedTable->CurrentSize -= 1;
  502. }
  503. return;
  504. }