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.

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