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.

712 lines
22 KiB

  1. //----------------------------------------------------------------------------
  2. //
  3. // Function entry cache.
  4. //
  5. // Copyright (C) Microsoft Corporation, 2000.
  6. //
  7. //----------------------------------------------------------------------------
  8. #include <nt.h>
  9. #include <ntrtl.h>
  10. #include <nturtl.h>
  11. #include <ntimage.h>
  12. #define NOEXTAPI
  13. #include <wdbgexts.h>
  14. #include <ntdbg.h>
  15. #include "private.h"
  16. #include "symbols.h"
  17. #include "globals.h"
  18. #include "fecache.hpp"
  19. //----------------------------------------------------------------------------
  20. //
  21. // FunctionEntryCache.
  22. //
  23. //----------------------------------------------------------------------------
  24. FunctionEntryCache::FunctionEntryCache(ULONG ImageDataSize,
  25. ULONG CacheDataSize,
  26. ULONG Machine)
  27. {
  28. m_ImageDataSize = ImageDataSize;
  29. m_CacheDataSize = CacheDataSize;
  30. m_Machine = Machine;
  31. m_Entries = NULL;
  32. }
  33. FunctionEntryCache::~FunctionEntryCache(void)
  34. {
  35. if (m_Entries != NULL)
  36. {
  37. MemFree(m_Entries);
  38. }
  39. }
  40. BOOL
  41. FunctionEntryCache::Initialize(ULONG MaxEntries, ULONG ReplaceAt)
  42. {
  43. // Already initialized.
  44. if (m_Entries != NULL) {
  45. return TRUE;
  46. }
  47. m_Entries = (FeCacheEntry*)MemAlloc(sizeof(FeCacheEntry) * MaxEntries);
  48. if (m_Entries == NULL) {
  49. return FALSE;
  50. }
  51. m_MaxEntries = MaxEntries;
  52. m_ReplaceAt = ReplaceAt;
  53. m_Used = 0;
  54. m_Next = 0;
  55. return TRUE;
  56. }
  57. FeCacheEntry*
  58. FunctionEntryCache::Find(
  59. HANDLE Process,
  60. ULONG64 CodeOffset,
  61. PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemory,
  62. PGET_MODULE_BASE_ROUTINE64 GetModuleBase,
  63. PFUNCTION_TABLE_ACCESS_ROUTINE64 GetFunctionEntry
  64. )
  65. {
  66. FeCacheEntry* FunctionEntry;
  67. FE_DEBUG(("\nFunctionEntryCache::Find(ControlPc=%.8I64x, Machine=%X)\n",
  68. CodeOffset, m_Machine));
  69. // Look for a static or dynamic function entry.
  70. FunctionEntry = FindDirect( Process, CodeOffset, ReadMemory,
  71. GetModuleBase, GetFunctionEntry );
  72. if (FunctionEntry == NULL) {
  73. return NULL;
  74. }
  75. //
  76. // The capability exists for more than one function entry
  77. // to map to the same function. This permits a function to
  78. // have discontiguous code segments described by separate
  79. // function table entries. If the ending prologue address
  80. // is not within the limits of the begining and ending
  81. // address of the function table entry, then the prologue
  82. // ending address is the address of the primary function
  83. // table entry that accurately describes the ending prologue
  84. // address.
  85. //
  86. FunctionEntry = SearchForPrimaryEntry(FunctionEntry, Process, ReadMemory,
  87. GetModuleBase, GetFunctionEntry);
  88. #if DBG
  89. if (tlsvar(DebugFunctionEntries)) {
  90. if (FunctionEntry == NULL) {
  91. dbPrint("FunctionEntryCache::Find returning NULL\n");
  92. } else {
  93. if (FunctionEntry->Address) {
  94. dbPrint("FunctionEntryCache::Find returning "
  95. "FunctionEntry=%.8I64x %s\n",
  96. FunctionEntry->Address,
  97. FunctionEntry->Description);
  98. } else {
  99. dbPrint("FunctionEntryCache::Find returning "
  100. "FunctionEntry=%.8I64x %s\n",
  101. (ULONG64)(LONG64)(LONG_PTR)FunctionEntry,
  102. FunctionEntry->Description);
  103. }
  104. }
  105. }
  106. #endif
  107. return FunctionEntry;
  108. }
  109. FeCacheEntry*
  110. FunctionEntryCache::FindDirect(
  111. HANDLE Process,
  112. ULONG64 CodeOffset,
  113. PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemory,
  114. PGET_MODULE_BASE_ROUTINE64 GetModuleBase,
  115. PFUNCTION_TABLE_ACCESS_ROUTINE64 GetFunctionEntry
  116. )
  117. {
  118. FeCacheEntry* FunctionEntry;
  119. ULONG64 ModuleBase;
  120. //
  121. // Look for function entry in static function tables.
  122. //
  123. FunctionEntry = FindStatic( Process, CodeOffset, ReadMemory,
  124. GetModuleBase, GetFunctionEntry,
  125. &ModuleBase );
  126. FE_DEBUG((" FindDirect: ControlPc=0x%I64x functionEntry=0x%p\n"
  127. " FindStatic %s\n", CodeOffset, FunctionEntry,
  128. FunctionEntry != NULL ? "succeeded" : "FAILED"));
  129. if (FunctionEntry != NULL) {
  130. return FunctionEntry;
  131. }
  132. //
  133. // If not in static image range and no static function entry
  134. // found use FunctionEntryCallback routine (if present) for
  135. // dynamic function entry or some other source of pdata (e.g.
  136. // saved pdata information for ROM images).
  137. //
  138. PPROCESS_ENTRY ProcessEntry = FindProcessEntry( Process );
  139. if (ProcessEntry == NULL) {
  140. return NULL;
  141. }
  142. PVOID RawEntry;
  143. if (!ModuleBase) {
  144. if (!IsImageMachineType64(m_Machine) &&
  145. ProcessEntry->pFunctionEntryCallback32) {
  146. RawEntry = ProcessEntry->pFunctionEntryCallback32
  147. (Process, (ULONG)CodeOffset,
  148. (PVOID)ProcessEntry->FunctionEntryUserContext);
  149. } else if (ProcessEntry->pFunctionEntryCallback64) {
  150. RawEntry = ProcessEntry->pFunctionEntryCallback64
  151. (Process, CodeOffset, ProcessEntry->FunctionEntryUserContext);
  152. if (RawEntry != NULL) {
  153. FunctionEntry = FillTemporary(Process, RawEntry);
  154. FE_SET_DESC(FunctionEntry, "from FunctionEntryCallback64");
  155. }
  156. }
  157. if (FunctionEntry != NULL) {
  158. FE_DEBUG((" FindDirect: got dynamic entry\n"));
  159. } else if (GetFunctionEntry != NULL) {
  160. // VC 6 didn't supply a GetModuleBase callback so this code is
  161. // to make stack walking backward compatible.
  162. //
  163. // If we don't have a function by now, use the old-style function
  164. // entry callback and let VC give it to us. Note that MSDN
  165. // documentation indicates that this callback should return
  166. // a 3-field IMAGE_FUNCTION_ENTRY structure, but VC 6 actually
  167. // returns the 5-field IMAGE_RUNTIME_FUNCTION_ENTRY. Since
  168. // the purpose of this hack is to make VC 6 work just go with the
  169. // way VC 6 does it rather than what MSDN says.
  170. RawEntry = GetFunctionEntry(Process, CodeOffset);
  171. if (RawEntry != NULL) {
  172. FunctionEntry = FillTemporary(Process, RawEntry);
  173. FE_SET_DESC(FunctionEntry, "from GetFunctionEntry");
  174. FE_DEBUG((" FindDirect: got user entry\n"));
  175. }
  176. }
  177. } else {
  178. // Nothing has turned up a function entry but we do have a
  179. // module base address. One possibility is that this is the
  180. // kernel debugger and the pdata section is not paged in.
  181. // The last ditch attempt for a function entry will be an
  182. // internal dbghelp call to get the pdata entry from the
  183. // debug info. This is not great because the data in the debug
  184. // section is incomplete and potentially out of date, but in
  185. // most cases it works and makes it possible to get user-mode
  186. // stack traces in the kernel debugger.
  187. PIMGHLP_RVA_FUNCTION_DATA RvaEntry =
  188. GetFunctionEntryFromDebugInfo( ProcessEntry, CodeOffset );
  189. if (RvaEntry != NULL) {
  190. FeCacheData Data;
  191. TranslateRvaDataToRawData(RvaEntry, ModuleBase, &Data);
  192. FunctionEntry = FillTemporary(Process, &Data);
  193. FE_SET_DESC(FunctionEntry, "from GetFunctionEntryFromDebugInfo");
  194. FE_DEBUG((" FindDirect: got debug info entry\n"));
  195. }
  196. }
  197. return FunctionEntry;
  198. }
  199. FeCacheEntry*
  200. FunctionEntryCache::FindStatic(
  201. HANDLE Process,
  202. ULONG64 CodeOffset,
  203. PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemory,
  204. PGET_MODULE_BASE_ROUTINE64 GetModuleBase,
  205. PFUNCTION_TABLE_ACCESS_ROUTINE64 GetFunctionEntry,
  206. PULONG64 ModuleBase
  207. )
  208. {
  209. ULONG RelCodeOffset;
  210. *ModuleBase = GetModuleBase( Process, CodeOffset );
  211. if (CodeOffset - *ModuleBase > 0xffffffff) {
  212. return NULL;
  213. }
  214. RelCodeOffset = (ULONG)(CodeOffset - *ModuleBase);
  215. FE_DEBUG((" FindStatic: ControlPc=0x%I64x ImageBase=0x%I64x\n"
  216. " biasedControlPc=0x%lx\n",
  217. CodeOffset, *ModuleBase, RelCodeOffset));
  218. FeCacheEntry* FunctionEntry;
  219. ULONG Index;
  220. //
  221. // Check the array of recently fetched function entries
  222. //
  223. FunctionEntry = m_Entries;
  224. for (Index = 0; Index < m_Used; Index++) {
  225. if (FunctionEntry->Process == Process &&
  226. FunctionEntry->ModuleBase == *ModuleBase &&
  227. RelCodeOffset >= FunctionEntry->RelBegin &&
  228. RelCodeOffset < FunctionEntry->RelEnd) {
  229. FE_DEBUG((" FindStatic: cache hit - index=%ld\n", Index));
  230. return FunctionEntry;
  231. }
  232. FunctionEntry++;
  233. }
  234. //
  235. // If an image was found that included the specified code, then locate the
  236. // function table for the image.
  237. //
  238. if (*ModuleBase == 0) {
  239. return NULL;
  240. }
  241. ULONG64 FunctionTable;
  242. ULONG SizeOfFunctionTable;
  243. FunctionTable = FunctionTableBase( Process, ReadMemory, *ModuleBase,
  244. &SizeOfFunctionTable );
  245. if (FunctionTable == NULL) {
  246. return NULL;
  247. }
  248. FE_DEBUG((" FindStatic: functionTable=0x%I64x "
  249. "sizeOfFunctionTable=%ld count:%ld\n",
  250. FunctionTable, SizeOfFunctionTable,
  251. SizeOfFunctionTable / m_ImageDataSize));
  252. LONG High;
  253. LONG Low;
  254. LONG Middle;
  255. //
  256. // If a function table is located, then search the function table
  257. // for a function table entry for the specified code offset.
  258. //
  259. Low = 0;
  260. High = (SizeOfFunctionTable / m_ImageDataSize) - 1;
  261. //
  262. // Perform binary search on the function table for a function table
  263. // entry that subsumes the specified code offset.
  264. //
  265. while (High >= Low) {
  266. //
  267. // Compute next probe index and test entry. If the specified PC
  268. // is greater than of equal to the beginning address and less
  269. // than the ending address of the function table entry, then
  270. // return the address of the function table entry. Otherwise,
  271. // continue the search.
  272. //
  273. Middle = (Low + High) >> 1;
  274. ULONG64 NextFunctionTableEntry = FunctionTable +
  275. Middle * m_ImageDataSize;
  276. //
  277. // Fetch the function entry and bail if there is an error reading it
  278. //
  279. FunctionEntry = ReadImage( Process, NextFunctionTableEntry,
  280. ReadMemory, GetModuleBase );
  281. if (FunctionEntry == NULL) {
  282. FE_DEBUG((" FindStatic: ReadImage "
  283. "functionEntryAddress=0x%I64x FAILED\n",
  284. NextFunctionTableEntry));
  285. return NULL;
  286. }
  287. if (RelCodeOffset < FunctionEntry->RelBegin) {
  288. High = Middle - 1;
  289. } else if (RelCodeOffset >= FunctionEntry->RelEnd) {
  290. Low = Middle + 1;
  291. } else {
  292. return Promote( FunctionEntry );
  293. }
  294. }
  295. return NULL;
  296. }
  297. FeCacheEntry*
  298. FunctionEntryCache::ReadImage(
  299. HANDLE Process,
  300. ULONG64 Address,
  301. PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemory,
  302. PGET_MODULE_BASE_ROUTINE64 GetModuleBase
  303. )
  304. {
  305. FeCacheEntry* FunctionEntry;
  306. ULONG Index;
  307. // Check the array of recently fetched function entries.
  308. FunctionEntry = m_Entries;
  309. for (Index = 0; Index < m_Used; Index++) {
  310. if (FunctionEntry->Process == Process &&
  311. FunctionEntry->Address == Address ) {
  312. return FunctionEntry;
  313. }
  314. FunctionEntry++;
  315. }
  316. FeCacheData Data;
  317. DWORD Done;
  318. if (!ReadMemory(Process, Address, &Data, m_ImageDataSize, &Done) ||
  319. Done != m_ImageDataSize) {
  320. return NULL;
  321. }
  322. // If not in the cache, replace the entry that m_Next
  323. // points to. m_Next cycles through the last part of the
  324. // table and function entries we want to keep are promoted to the first
  325. // part of the table so they don't get overwritten by new ones being read
  326. // as part of the binary search through function entry tables.
  327. if (m_Used < m_MaxEntries) {
  328. m_Used++;
  329. m_Next = m_Used;
  330. } else {
  331. m_Next++;
  332. if (m_Next >= m_MaxEntries) {
  333. m_Next = m_ReplaceAt + 1;
  334. }
  335. }
  336. FunctionEntry = m_Entries + (m_Next - 1);
  337. FunctionEntry->Data = Data;
  338. FunctionEntry->Address = Address;
  339. FunctionEntry->Process = Process;
  340. FunctionEntry->ModuleBase = GetModuleBase(Process, Address);
  341. FE_SET_DESC(FunctionEntry, "from target process");
  342. // Translate after all other information is filled in so
  343. // the translation routine can use it.
  344. TranslateRawData(FunctionEntry);
  345. return FunctionEntry;
  346. }
  347. void
  348. FunctionEntryCache::InvalidateProcessOrModule(HANDLE Process, ULONG64 Base)
  349. {
  350. FeCacheEntry* FunctionEntry;
  351. ULONG Index;
  352. FunctionEntry = m_Entries;
  353. Index = 0;
  354. while (Index < m_Used) {
  355. if (FunctionEntry->Process == Process &&
  356. (Base == 0 || FunctionEntry->ModuleBase == Base)) {
  357. // Pull the last entry down into this slot
  358. // to keep things packed. There's no need
  359. // to update m_Next as this will open a
  360. // new slot for use and m_Next will be reset
  361. // when it is used.
  362. *FunctionEntry = m_Entries[--m_Used];
  363. } else {
  364. Index++;
  365. FunctionEntry++;
  366. }
  367. }
  368. }
  369. FeCacheEntry*
  370. FunctionEntryCache::Promote(FeCacheEntry* Entry)
  371. {
  372. ULONG Index;
  373. ULONG Move;
  374. Index = (ULONG)(Entry - m_Entries);
  375. // Make sure it's promoted out of the temporary area.
  376. if (Index >= m_ReplaceAt) {
  377. Move = Index - (m_ReplaceAt - 3);
  378. } else {
  379. Move = ( Index >= 3 ) ? 3 : 1;
  380. }
  381. if (Index > Move) {
  382. FeCacheEntry Temp = *Entry;
  383. *Entry = m_Entries[Index - Move];
  384. m_Entries[Index - Move] = Temp;
  385. Index -= Move;
  386. }
  387. return m_Entries + Index;
  388. }
  389. ULONG64
  390. FunctionEntryCache::FunctionTableBase(
  391. HANDLE Process,
  392. PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemory,
  393. ULONG64 Base,
  394. PULONG Size
  395. )
  396. {
  397. ULONG64 NtHeaders;
  398. ULONG64 ExceptionDirectoryEntryAddress;
  399. IMAGE_DATA_DIRECTORY ExceptionData;
  400. IMAGE_DOS_HEADER DosHeaderData;
  401. DWORD Done;
  402. // Read DOS header to calculate the address of the NT header.
  403. if (!ReadMemory( Process, Base, &DosHeaderData, sizeof(DosHeaderData),
  404. &Done ) ||
  405. Done != sizeof(DosHeaderData)) {
  406. return 0;
  407. }
  408. if (DosHeaderData.e_magic != IMAGE_DOS_SIGNATURE) {
  409. return 0;
  410. }
  411. NtHeaders = Base + DosHeaderData.e_lfanew;
  412. if (IsImageMachineType64(m_Machine)) {
  413. ExceptionDirectoryEntryAddress = NtHeaders +
  414. FIELD_OFFSET(IMAGE_NT_HEADERS64,OptionalHeader) +
  415. FIELD_OFFSET(IMAGE_OPTIONAL_HEADER64,DataDirectory) +
  416. IMAGE_DIRECTORY_ENTRY_EXCEPTION * sizeof(IMAGE_DATA_DIRECTORY);
  417. } else {
  418. ExceptionDirectoryEntryAddress = NtHeaders +
  419. FIELD_OFFSET(IMAGE_NT_HEADERS32,OptionalHeader) +
  420. FIELD_OFFSET(IMAGE_OPTIONAL_HEADER32,DataDirectory) +
  421. IMAGE_DIRECTORY_ENTRY_EXCEPTION * sizeof(IMAGE_DATA_DIRECTORY);
  422. }
  423. // Read NT header to get the image data directory.
  424. if (!ReadMemory( Process, ExceptionDirectoryEntryAddress, &ExceptionData,
  425. sizeof(IMAGE_DATA_DIRECTORY), &Done ) ||
  426. Done != sizeof(IMAGE_DATA_DIRECTORY)) {
  427. return 0;
  428. }
  429. *Size = ExceptionData.Size;
  430. return Base + ExceptionData.VirtualAddress;
  431. }
  432. FeCacheEntry*
  433. FunctionEntryCache::SearchForPrimaryEntry(
  434. FeCacheEntry* CacheEntry,
  435. HANDLE Process,
  436. PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemory,
  437. PGET_MODULE_BASE_ROUTINE64 GetModuleBase,
  438. PFUNCTION_TABLE_ACCESS_ROUTINE64 GetFunctionEntry
  439. )
  440. {
  441. // Assume all entries are primary.
  442. return CacheEntry;
  443. }
  444. //----------------------------------------------------------------------------
  445. //
  446. // Ia64FunctionEntryCache.
  447. //
  448. //----------------------------------------------------------------------------
  449. void
  450. Ia64FunctionEntryCache::TranslateRawData(FeCacheEntry* Entry)
  451. {
  452. Entry->RelBegin = Entry->Data.Ia64.BeginAddress & ~15;
  453. Entry->RelEnd = (Entry->Data.Ia64.EndAddress + 15) & ~15;
  454. }
  455. void
  456. Ia64FunctionEntryCache::TranslateRvaDataToRawData
  457. (PIMGHLP_RVA_FUNCTION_DATA RvaData, ULONG64 ModuleBase,
  458. FeCacheData* Data)
  459. {
  460. Data->Ia64.BeginAddress = RvaData->rvaBeginAddress;
  461. Data->Ia64.EndAddress = RvaData->rvaEndAddress;
  462. Data->Ia64.UnwindInfoAddress = RvaData->rvaPrologEndAddress;
  463. }
  464. #if DBG
  465. void
  466. ShowRuntimeFunctionIa64(
  467. FeCacheEntry* FunctionEntry,
  468. PSTR Label
  469. )
  470. {
  471. if (!tlsvar(DebugFunctionEntries)) {
  472. return;
  473. }
  474. if ( FunctionEntry ) {
  475. if (FunctionEntry->Address) {
  476. dbPrint(" 0x%I64x: %s\n", FunctionEntry->Address,
  477. Label ? Label : "" );
  478. }
  479. else {
  480. dbPrint(" %s\n", Label ? Label : "" );
  481. }
  482. dbPrint(" BeginAddress = 0x%x\n"
  483. " EndAddress = 0x%x\n"
  484. " UnwindInfoAddress = 0x%x\n",
  485. FunctionEntry->Data.Ia64.BeginAddress,
  486. FunctionEntry->Data.Ia64.EndAddress,
  487. FunctionEntry->Data.Ia64.UnwindInfoAddress );
  488. }
  489. else {
  490. dbPrint(" FunctionEntry NULL: %s\n", Label ? Label : "" );
  491. }
  492. }
  493. #endif // #if DBG
  494. //----------------------------------------------------------------------------
  495. //
  496. // Amd64FunctionEntryCache.
  497. //
  498. //----------------------------------------------------------------------------
  499. void
  500. Amd64FunctionEntryCache::TranslateRawData(FeCacheEntry* Entry)
  501. {
  502. Entry->RelBegin = Entry->Data.Amd64.BeginAddress;
  503. Entry->RelEnd = Entry->Data.Amd64.EndAddress;
  504. }
  505. void
  506. Amd64FunctionEntryCache::TranslateRvaDataToRawData
  507. (PIMGHLP_RVA_FUNCTION_DATA RvaData, ULONG64 ModuleBase,
  508. FeCacheData* Data)
  509. {
  510. Data->Amd64.BeginAddress = RvaData->rvaBeginAddress;
  511. Data->Amd64.EndAddress = RvaData->rvaEndAddress;
  512. Data->Amd64.UnwindInfoAddress = RvaData->rvaPrologEndAddress;
  513. }
  514. //----------------------------------------------------------------------------
  515. //
  516. // ArmFunctionEntryCache.
  517. //
  518. //----------------------------------------------------------------------------
  519. void
  520. ArmFunctionEntryCache::TranslateRawData(FeCacheEntry* Entry)
  521. {
  522. Entry->RelBegin = (ULONG)
  523. ((Entry->Data.Arm.BeginAddress & ~1) - Entry->ModuleBase);
  524. Entry->RelEnd = (ULONG)
  525. ((Entry->Data.Arm.EndAddress & ~1) - Entry->ModuleBase);
  526. }
  527. void
  528. ArmFunctionEntryCache::TranslateRvaDataToRawData
  529. (PIMGHLP_RVA_FUNCTION_DATA RvaData, ULONG64 ModuleBase,
  530. FeCacheData* Data)
  531. {
  532. Data->Arm.BeginAddress = (ULONG)(ModuleBase + RvaData->rvaBeginAddress);
  533. Data->Arm.EndAddress = (ULONG)(ModuleBase + RvaData->rvaEndAddress);
  534. Data->Arm.ExceptionHandler = 0;
  535. Data->Arm.HandlerData = 0;
  536. Data->Arm.PrologEndAddress =
  537. (ULONG)(ModuleBase + RvaData->rvaPrologEndAddress);
  538. }
  539. //----------------------------------------------------------------------------
  540. //
  541. // Functions.
  542. //
  543. //----------------------------------------------------------------------------
  544. FunctionEntryCache*
  545. GetFeCache(ULONG Machine, BOOL Create)
  546. {
  547. FunctionEntryCache* Cache;
  548. switch(Machine) {
  549. case IMAGE_FILE_MACHINE_AMD64:
  550. if (tlsvar(Amd64FunctionEntries) == NULL && Create) {
  551. tlsvar(Amd64FunctionEntries) = new Amd64FunctionEntryCache;
  552. if (tlsvar(Amd64FunctionEntries) == NULL) {
  553. return NULL;
  554. }
  555. }
  556. Cache = tlsvar(Amd64FunctionEntries);
  557. break;
  558. case IMAGE_FILE_MACHINE_IA64:
  559. if (tlsvar(Ia64FunctionEntries) == NULL && Create) {
  560. tlsvar(Ia64FunctionEntries) = new Ia64FunctionEntryCache;
  561. if (tlsvar(Ia64FunctionEntries) == NULL) {
  562. return NULL;
  563. }
  564. }
  565. Cache = tlsvar(Ia64FunctionEntries);
  566. break;
  567. case IMAGE_FILE_MACHINE_ARM:
  568. if (tlsvar(ArmFunctionEntries) == NULL && Create) {
  569. tlsvar(ArmFunctionEntries) = new ArmFunctionEntryCache;
  570. if (tlsvar(ArmFunctionEntries) == NULL) {
  571. return NULL;
  572. }
  573. }
  574. Cache = tlsvar(ArmFunctionEntries);
  575. break;
  576. default:
  577. return NULL;
  578. }
  579. if (Cache && !Cache->Initialize(60, 40)) {
  580. return NULL;
  581. }
  582. return Cache;
  583. }
  584. void
  585. ClearFeCaches(void)
  586. {
  587. if (tlsvar(Ia64FunctionEntries)) {
  588. delete (Ia64FunctionEntryCache*)tlsvar(Ia64FunctionEntries);
  589. tlsvar(Ia64FunctionEntries) = NULL;
  590. }
  591. if (tlsvar(Amd64FunctionEntries)) {
  592. delete (Amd64FunctionEntryCache*)tlsvar(Amd64FunctionEntries);
  593. tlsvar(Amd64FunctionEntries) = NULL;
  594. }
  595. if (tlsvar(ArmFunctionEntries)) {
  596. delete (ArmFunctionEntryCache*)tlsvar(ArmFunctionEntries);
  597. tlsvar(ArmFunctionEntries) = NULL;
  598. }
  599. }