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.

8515 lines
257 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. sysload.c
  5. Abstract:
  6. This module contains the code to load DLLs into the system portion of
  7. the address space and calls the DLL at its initialization entry point.
  8. Author:
  9. Lou Perazzoli 21-May-1991
  10. Landy Wang 02-June-1997
  11. Revision History:
  12. --*/
  13. #include "mi.h"
  14. #include "hotpatch.h"
  15. KMUTANT MmSystemLoadLock;
  16. LONG MmTotalSystemDriverPages;
  17. ULONG MmDriverCommit;
  18. LONG MiFirstDriverLoadEver = 0;
  19. //
  20. // This key is set to TRUE to make more memory below 16mb available for drivers.
  21. // It can be cleared via the registry.
  22. //
  23. LOGICAL MmMakeLowMemory = TRUE;
  24. //
  25. // Enabled via the registry to identify drivers which unload without releasing
  26. // resources or still have active timers, etc.
  27. //
  28. PUNLOADED_DRIVERS MmUnloadedDrivers;
  29. ULONG MmLastUnloadedDriver;
  30. ULONG MiTotalUnloads;
  31. ULONG MiUnloadsSkipped;
  32. //
  33. // This can be set by the registry.
  34. //
  35. ULONG MmEnforceWriteProtection = 1;
  36. //
  37. // Referenced by ke\bugcheck.c.
  38. //
  39. PVOID ExPoolCodeStart;
  40. PVOID ExPoolCodeEnd;
  41. PVOID MmPoolCodeStart;
  42. PVOID MmPoolCodeEnd;
  43. PVOID MmPteCodeStart;
  44. PVOID MmPteCodeEnd;
  45. extern LONG MiSessionLeaderExists;
  46. PVOID
  47. MiCacheImageSymbols (
  48. IN PVOID ImageBase
  49. );
  50. NTSTATUS
  51. MiResolveImageReferences (
  52. PVOID ImageBase,
  53. IN PUNICODE_STRING ImageFileDirectory,
  54. IN PUNICODE_STRING NamePrefix OPTIONAL,
  55. OUT PCHAR *MissingProcedureName,
  56. OUT PWSTR *MissingDriverName,
  57. OUT PLOAD_IMPORTS *LoadedImports
  58. );
  59. NTSTATUS
  60. MiSnapThunk (
  61. IN PVOID DllBase,
  62. IN PVOID ImageBase,
  63. IN PIMAGE_THUNK_DATA NameThunk,
  64. OUT PIMAGE_THUNK_DATA AddrThunk,
  65. IN PIMAGE_EXPORT_DIRECTORY ExportDirectory,
  66. IN ULONG ExportSize,
  67. IN LOGICAL SnapForwarder,
  68. OUT PCHAR *MissingProcedureName
  69. );
  70. NTSTATUS
  71. MiLoadImageSection (
  72. IN OUT PSECTION *InputSectionPointer,
  73. OUT PVOID *ImageBase,
  74. IN PUNICODE_STRING ImageFileName,
  75. IN ULONG LoadInSessionSpace,
  76. IN PKLDR_DATA_TABLE_ENTRY FoundDataTableEntry
  77. );
  78. VOID
  79. MiEnablePagingOfDriver (
  80. IN PVOID ImageHandle
  81. );
  82. VOID
  83. MiSetPagingOfDriver (
  84. IN PMMPTE PointerPte,
  85. IN PMMPTE LastPte
  86. );
  87. PVOID
  88. MiLookupImageSectionByName (
  89. IN PVOID Base,
  90. IN LOGICAL MappedAsImage,
  91. IN PCHAR SectionName,
  92. OUT PULONG SectionSize
  93. );
  94. VOID
  95. MiClearImports (
  96. IN PKLDR_DATA_TABLE_ENTRY DataTableEntry
  97. );
  98. NTSTATUS
  99. MiBuildImportsForBootDrivers (
  100. VOID
  101. );
  102. NTSTATUS
  103. MmCheckSystemImage (
  104. IN HANDLE ImageFileHandle,
  105. IN LOGICAL PurgeSection
  106. );
  107. LONG
  108. MiMapCacheExceptionFilter (
  109. OUT PNTSTATUS Status,
  110. IN PEXCEPTION_POINTERS ExceptionPointer
  111. );
  112. ULONG
  113. MiSetProtectionOnTransitionPte (
  114. IN PMMPTE PointerPte,
  115. IN ULONG ProtectionMask
  116. );
  117. NTSTATUS
  118. MiDereferenceImports (
  119. IN PLOAD_IMPORTS ImportList
  120. );
  121. LOGICAL
  122. MiCallDllUnloadAndUnloadDll (
  123. IN PKLDR_DATA_TABLE_ENTRY DataTableEntry
  124. );
  125. PVOID
  126. MiLocateExportName (
  127. IN PVOID DllBase,
  128. IN PCHAR FunctionName
  129. );
  130. VOID
  131. MiRememberUnloadedDriver (
  132. IN PUNICODE_STRING DriverName,
  133. IN PVOID Address,
  134. IN ULONG Length
  135. );
  136. VOID
  137. MiWriteProtectSystemImage (
  138. IN PVOID DllBase
  139. );
  140. VOID
  141. MiLocateKernelSections (
  142. IN PKLDR_DATA_TABLE_ENTRY DataTableEntry
  143. );
  144. VOID
  145. MiCaptureImageExceptionValues (
  146. IN PKLDR_DATA_TABLE_ENTRY DataTableEntry
  147. );
  148. VOID
  149. MiUpdateThunks (
  150. IN PLOADER_PARAMETER_BLOCK LoaderBlock,
  151. IN PVOID OldAddress,
  152. IN PVOID NewAddress,
  153. IN ULONG NumberOfBytes
  154. );
  155. PVOID
  156. MiFindExportedRoutineByName (
  157. IN PVOID DllBase,
  158. IN PANSI_STRING AnsiImageRoutineName
  159. );
  160. LOGICAL
  161. MiChargeResidentAvailable (
  162. IN PFN_NUMBER NumberOfPages,
  163. IN ULONG Id
  164. );
  165. LOGICAL
  166. MiUseLargeDriverPage (
  167. IN ULONG NumberOfPtes,
  168. IN OUT PVOID *ImageBaseAddress,
  169. IN PUNICODE_STRING PrefixedImageName,
  170. IN ULONG Pass
  171. );
  172. VOID
  173. MiRundownHotpatchList (
  174. PRTL_PATCH_HEADER PatchHead
  175. );
  176. VOID
  177. MiSessionProcessGlobalSubsections (
  178. IN PKLDR_DATA_TABLE_ENTRY DataTableEntry
  179. );
  180. #ifdef ALLOC_PRAGMA
  181. #pragma alloc_text(PAGE,MmCheckSystemImage)
  182. #pragma alloc_text(PAGE,MmLoadSystemImage)
  183. #pragma alloc_text(PAGE,MiResolveImageReferences)
  184. #pragma alloc_text(PAGE,MiSnapThunk)
  185. #pragma alloc_text(PAGE,MiEnablePagingOfDriver)
  186. #pragma alloc_text(PAGE,MmPageEntireDriver)
  187. #pragma alloc_text(PAGE,MiDereferenceImports)
  188. #pragma alloc_text(PAGE,MiCallDllUnloadAndUnloadDll)
  189. #pragma alloc_text(PAGE,MiLocateExportName)
  190. #pragma alloc_text(PAGE,MiClearImports)
  191. #pragma alloc_text(PAGE,MmGetSystemRoutineAddress)
  192. #pragma alloc_text(PAGE,MiFindExportedRoutineByName)
  193. #pragma alloc_text(PAGE,MmCallDllInitialize)
  194. #pragma alloc_text(PAGE,MmResetDriverPaging)
  195. #pragma alloc_text(PAGE,MmUnloadSystemImage)
  196. #pragma alloc_text(PAGE,MiLoadImageSection)
  197. #pragma alloc_text(PAGE,MiRememberUnloadedDriver)
  198. #pragma alloc_text(PAGE,MiUseLargeDriverPage)
  199. #pragma alloc_text(PAGE,MiMakeEntireImageCopyOnWrite)
  200. #pragma alloc_text(PAGE,MiWriteProtectSystemImage)
  201. #pragma alloc_text(PAGE,MiSessionProcessGlobalSubsections)
  202. #pragma alloc_text(PAGE,MiCaptureImageExceptionValues)
  203. #pragma alloc_text(INIT,MiBuildImportsForBootDrivers)
  204. #pragma alloc_text(INIT,MiReloadBootLoadedDrivers)
  205. #pragma alloc_text(INIT,MiUpdateThunks)
  206. #pragma alloc_text(INIT,MiInitializeLoadedModuleList)
  207. #pragma alloc_text(INIT,MiLocateKernelSections)
  208. #if !defined(NT_UP)
  209. #pragma alloc_text(PAGE,MmVerifyImageIsOkForMpUse)
  210. #endif
  211. #endif
  212. CHAR MiPteStr[] = "\0";
  213. VOID
  214. MiProcessLoaderEntry (
  215. IN PKLDR_DATA_TABLE_ENTRY DataTableEntry,
  216. IN LOGICAL Insert
  217. )
  218. /*++
  219. Routine Description:
  220. This function is a nonpaged wrapper which acquires the PsLoadedModuleList
  221. lock to insert a new entry.
  222. Arguments:
  223. DataTableEntry - Supplies the loaded module list entry to insert/remove.
  224. Insert - Supplies TRUE if the entry should be inserted, FALSE if the entry
  225. should be removed.
  226. Return Value:
  227. None.
  228. Environment:
  229. Kernel mode. Normal APCs disabled (critical region held).
  230. --*/
  231. {
  232. KIRQL OldIrql;
  233. ExAcquireResourceExclusiveLite (&PsLoadedModuleResource, TRUE);
  234. ExAcquireSpinLock (&PsLoadedModuleSpinLock, &OldIrql);
  235. if (Insert == TRUE) {
  236. InsertTailList (&PsLoadedModuleList, &DataTableEntry->InLoadOrderLinks);
  237. #if defined (_WIN64)
  238. RtlInsertInvertedFunctionTable (&PsInvertedFunctionTable,
  239. DataTableEntry->DllBase,
  240. DataTableEntry->SizeOfImage);
  241. #endif
  242. }
  243. else {
  244. #if defined (_WIN64)
  245. RtlRemoveInvertedFunctionTable (&PsInvertedFunctionTable,
  246. DataTableEntry->DllBase);
  247. #endif
  248. RemoveEntryList (&DataTableEntry->InLoadOrderLinks);
  249. }
  250. ExReleaseSpinLock (&PsLoadedModuleSpinLock, OldIrql);
  251. ExReleaseResourceLite (&PsLoadedModuleResource);
  252. }
  253. typedef struct _MI_LARGE_PAGE_DRIVER_ENTRY {
  254. LIST_ENTRY Links;
  255. UNICODE_STRING BaseName;
  256. } MI_LARGE_PAGE_DRIVER_ENTRY, *PMI_LARGE_PAGE_DRIVER_ENTRY;
  257. LIST_ENTRY MiLargePageDriverList;
  258. ULONG MiLargePageAllDrivers;
  259. VOID
  260. MiInitializeDriverLargePageList (
  261. VOID
  262. )
  263. /*++
  264. Routine Description:
  265. Parse the registry settings and set up the list of driver names that we'll
  266. try to load in large pages.
  267. Arguments:
  268. None.
  269. Return Value:
  270. None.
  271. Environment:
  272. Kernel mode, Phase 0 Initialization.
  273. Nonpaged pool exists but not paged pool.
  274. The PsLoadedModuleList has not been set up yet AND the boot drivers
  275. have NOT been relocated to their final resting places.
  276. --*/
  277. {
  278. PWCHAR Start;
  279. PWCHAR End;
  280. PWCHAR Walk;
  281. ULONG NameLength;
  282. PMI_LARGE_PAGE_DRIVER_ENTRY Entry;
  283. InitializeListHead (&MiLargePageDriverList);
  284. if (MmLargePageDriverBufferLength == (ULONG)-1) {
  285. return;
  286. }
  287. Start = MmLargePageDriverBuffer;
  288. End = MmLargePageDriverBuffer + (MmLargePageDriverBufferLength - sizeof(WCHAR)) / sizeof(WCHAR);
  289. while (Start < End) {
  290. if (UNICODE_WHITESPACE(*Start)) {
  291. Start += 1;
  292. continue;
  293. }
  294. if (*Start == (WCHAR)'*') {
  295. MiLargePageAllDrivers = 1;
  296. break;
  297. }
  298. for (Walk = Start; Walk < End; Walk += 1) {
  299. if (UNICODE_WHITESPACE(*Walk)) {
  300. break;
  301. }
  302. }
  303. //
  304. // Got a string - add it to our list.
  305. //
  306. NameLength = (ULONG)(Walk - Start) * sizeof (WCHAR);
  307. Entry = ExAllocatePoolWithTag (NonPagedPool,
  308. sizeof (MI_LARGE_PAGE_DRIVER_ENTRY),
  309. 'pLmM');
  310. if (Entry == NULL) {
  311. break;
  312. }
  313. Entry->BaseName.Buffer = Start;
  314. Entry->BaseName.Length = (USHORT) NameLength;
  315. Entry->BaseName.MaximumLength = (USHORT) NameLength;
  316. InsertTailList (&MiLargePageDriverList, &Entry->Links);
  317. Start = Walk + 1;
  318. }
  319. return;
  320. }
  321. LOGICAL
  322. MiUseLargeDriverPage (
  323. IN ULONG NumberOfPtes,
  324. IN OUT PVOID *ImageBaseAddress,
  325. IN PUNICODE_STRING BaseImageName,
  326. IN ULONG Pass
  327. )
  328. /*++
  329. Routine Description:
  330. This routine checks whether the specified image should be loaded into
  331. a large page address space, and if so, tries to load it.
  332. Arguments:
  333. NumberOfPtes - Supplies the number of PTEs to map for the image.
  334. ImageBaseAddress - Supplies the current address the image header is at,
  335. and returns the (new) address for the image header.
  336. BaseImageName - Supplies the base path name of the image to load.
  337. Pass - Supplies 0 when called from Phase for the boot drivers, 1 otherwise.
  338. Return Value:
  339. TRUE if large pages were used, FALSE if not.
  340. --*/
  341. {
  342. PFN_NUMBER PagesRequired;
  343. PFN_NUMBER ResidentPages;
  344. PLIST_ENTRY NextEntry;
  345. PVOID SmallVa;
  346. PVOID LargeVa;
  347. PVOID LargeBaseVa;
  348. LOGICAL UseLargePages;
  349. PFN_NUMBER PageFrameIndex;
  350. PFN_NUMBER NumberOfPages;
  351. MMPTE PteContents;
  352. PMMPTE SmallPte;
  353. PMMPTE LastSmallPte;
  354. PMI_LARGE_PAGE_DRIVER_ENTRY LargePageDriverEntry;
  355. #ifdef _X86_
  356. ULONG ProcessorFeatures;
  357. #endif
  358. ASSERT (KeGetCurrentIrql () <= APC_LEVEL);
  359. ASSERT (*ImageBaseAddress >= MmSystemRangeStart);
  360. #ifdef _X86_
  361. if ((KeFeatureBits & KF_LARGE_PAGE) == 0) {
  362. return FALSE;
  363. }
  364. //
  365. // Capture cr4 to see if large page support has been enabled in the chip
  366. // yet (late in Phase 1). Large page PDEs cannot be used until then.
  367. //
  368. // mov eax, cr4
  369. //
  370. _asm {
  371. _emit 00fh
  372. _emit 020h
  373. _emit 0e0h
  374. mov ProcessorFeatures, eax
  375. }
  376. if ((ProcessorFeatures & CR4_PSE) == 0) {
  377. return FALSE;
  378. }
  379. #endif
  380. //
  381. // Check the number of free system PTEs left to prevent a runaway registry
  382. // key from exhausting all the system PTEs.
  383. //
  384. if (MmTotalFreeSystemPtes[SystemPteSpace] < 16 * (MM_MINIMUM_VA_FOR_LARGE_PAGE >> PAGE_SHIFT)) {
  385. return FALSE;
  386. }
  387. if (MiLargePageAllDrivers == 0) {
  388. UseLargePages = FALSE;
  389. //
  390. // Check to see if this name exists in the large page image list.
  391. //
  392. NextEntry = MiLargePageDriverList.Flink;
  393. while (NextEntry != &MiLargePageDriverList) {
  394. LargePageDriverEntry = CONTAINING_RECORD (NextEntry,
  395. MI_LARGE_PAGE_DRIVER_ENTRY,
  396. Links);
  397. if (RtlEqualUnicodeString (BaseImageName,
  398. &LargePageDriverEntry->BaseName,
  399. TRUE)) {
  400. UseLargePages = TRUE;
  401. break;
  402. }
  403. NextEntry = NextEntry->Flink;
  404. }
  405. if (UseLargePages == FALSE) {
  406. return FALSE;
  407. }
  408. }
  409. //
  410. // First try to get physically contiguous memory for this driver.
  411. // Note we must allocate the entire large page here even though we will
  412. // almost always only use a portion of it. This is to ensure no other
  413. // frames in it can be mapped with a different cache attribute. After
  414. // updating the cache attribute lists, we'll immediately free the excess.
  415. // Note that the driver's INIT section will be subsequently freed
  416. // and clearly the cache attribute lists must be correct to support that
  417. // as well.
  418. //
  419. // Don't take memory below 16MB as we want to leave that available for
  420. // ISA drivers supporting older hardware that may require it.
  421. //
  422. NumberOfPages = (PFN_NUMBER) MI_ROUND_TO_SIZE (
  423. NumberOfPtes,
  424. MM_MINIMUM_VA_FOR_LARGE_PAGE >> PAGE_SHIFT);
  425. PageFrameIndex = MiFindContiguousPages ((16 * 1024 * 1024) >> PAGE_SHIFT,
  426. MmHighestPossiblePhysicalPage,
  427. MM_MINIMUM_VA_FOR_LARGE_PAGE >> PAGE_SHIFT,
  428. NumberOfPages,
  429. MmCached);
  430. //
  431. // If a contiguous range is not available then large pages cannot be used
  432. // for this driver at this time.
  433. //
  434. if (PageFrameIndex == 0) {
  435. return FALSE;
  436. }
  437. //
  438. // Add the contiguous range to the must-be-cached list so that the excess
  439. // memory (and INIT section) can be safely freed to the page lists.
  440. //
  441. if (MiAddCachedRange (PageFrameIndex, PageFrameIndex + NumberOfPages - 1) == FALSE) {
  442. MiFreeContiguousPages (PageFrameIndex, NumberOfPages);
  443. return FALSE;
  444. }
  445. //
  446. // Try to get large virtual address space for this driver.
  447. //
  448. LargeVa = MiMapWithLargePages (PageFrameIndex,
  449. NumberOfPages,
  450. MM_EXECUTE_READWRITE,
  451. MmCached);
  452. if (LargeVa == NULL) {
  453. MiRemoveCachedRange (PageFrameIndex, PageFrameIndex + NumberOfPages - 1);
  454. MiFreeContiguousPages (PageFrameIndex, NumberOfPages);
  455. return FALSE;
  456. }
  457. LargeBaseVa = LargeVa;
  458. //
  459. // Copy the driver a page at a time as in rare cases, it may have holes.
  460. //
  461. SmallPte = MiGetPteAddress (*ImageBaseAddress);
  462. LastSmallPte = SmallPte + NumberOfPtes;
  463. SmallVa = MiGetVirtualAddressMappedByPte (SmallPte);
  464. while (SmallPte < LastSmallPte) {
  465. PteContents = *SmallPte;
  466. if (PteContents.u.Hard.Valid == 1) {
  467. RtlCopyMemory (LargeVa, SmallVa, PAGE_SIZE);
  468. }
  469. else {
  470. //
  471. // Retain this page in the large page mapping to simplify unload -
  472. // ie: it can always free a single contiguous range.
  473. //
  474. }
  475. SmallPte += 1;
  476. LargeVa = (PVOID) ((PCHAR)LargeVa + PAGE_SIZE);
  477. SmallVa = (PVOID) ((PCHAR)SmallVa + PAGE_SIZE);
  478. }
  479. //
  480. // Inform our caller of the new (large page) address so the loader data
  481. // table entry gets created with it & fixups done accordingly, etc.
  482. //
  483. *ImageBaseAddress = LargeBaseVa;
  484. if (Pass != 0) {
  485. //
  486. // The system is fully booted so get rid of the original mapping now.
  487. // Otherwise, we're in Phase 0, so the caller gets rid of the original
  488. // mapping.
  489. //
  490. SmallPte -= NumberOfPtes;
  491. PagesRequired = MiDeleteSystemPagableVm (SmallPte,
  492. NumberOfPtes,
  493. ZeroKernelPte,
  494. FALSE,
  495. &ResidentPages);
  496. //
  497. // Non boot-loaded drivers have system PTEs and commit charged.
  498. //
  499. MiReleaseSystemPtes (SmallPte, (ULONG)NumberOfPtes, SystemPteSpace);
  500. InterlockedExchangeAdd (&MmTotalSystemDriverPages,
  501. 0 - (ULONG)(PagesRequired - ResidentPages));
  502. MI_INCREMENT_RESIDENT_AVAILABLE (ResidentPages,
  503. MM_RESAVAIL_FREE_UNLOAD_SYSTEM_IMAGE1);
  504. MiReturnCommitment (PagesRequired);
  505. MM_TRACK_COMMIT (MM_DBG_COMMIT_RETURN_DRIVER_UNLOAD1, PagesRequired);
  506. }
  507. //
  508. // Free the unused trailing portion (and their resident available charge)
  509. // of the large page mapping.
  510. //
  511. MiFreeContiguousPages (PageFrameIndex + NumberOfPtes,
  512. NumberOfPages - NumberOfPtes);
  513. return TRUE;
  514. }
  515. VOID
  516. MiCaptureImageExceptionValues (
  517. IN PKLDR_DATA_TABLE_ENTRY DataTableEntry
  518. )
  519. /*++
  520. Routine Description:
  521. This function stores the exception table information from the image in the
  522. loader data table entry.
  523. Arguments:
  524. DataTableEntry - Supplies the kernel's data table entry.
  525. Return Value:
  526. None.
  527. Environment:
  528. Kernel mode, APC_LEVEL or below, arbitrary process context.
  529. --*/
  530. {
  531. PVOID CurrentBase;
  532. PIMAGE_NT_HEADERS NtHeader;
  533. CurrentBase = (PVOID) DataTableEntry->DllBase;
  534. NtHeader = RtlImageNtHeader (CurrentBase);
  535. #if defined(_X86_)
  536. if (NtHeader->OptionalHeader.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_NO_SEH) {
  537. DataTableEntry->ExceptionTable = (PCHAR)LongToPtr(-1);
  538. DataTableEntry->ExceptionTableSize = (ULONG)-1;
  539. } else {
  540. PIMAGE_LOAD_CONFIG_DIRECTORY32 LoadConfig;
  541. ULONG LoadConfigSize;
  542. if (IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG < NtHeader->OptionalHeader.NumberOfRvaAndSizes) {
  543. LoadConfig = (PIMAGE_LOAD_CONFIG_DIRECTORY32)((PCHAR)CurrentBase +
  544. NtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG].VirtualAddress);
  545. LoadConfigSize = NtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG].Size;
  546. if (LoadConfig &&
  547. LoadConfigSize &&
  548. LoadConfig->Size >= RTL_SIZEOF_THROUGH_FIELD(IMAGE_LOAD_CONFIG_DIRECTORY32, SEHandlerCount) &&
  549. LoadConfig->SEHandlerTable &&
  550. LoadConfig->SEHandlerCount
  551. )
  552. {
  553. DataTableEntry->ExceptionTable = (PVOID)LoadConfig->SEHandlerTable;
  554. DataTableEntry->ExceptionTableSize = LoadConfig->SEHandlerCount;
  555. } else {
  556. DataTableEntry->ExceptionTable = 0;
  557. DataTableEntry->ExceptionTableSize = 0;
  558. }
  559. }
  560. }
  561. #else
  562. #if defined(_IA64_)
  563. if (IMAGE_DIRECTORY_ENTRY_GLOBALPTR < NtHeader->OptionalHeader.NumberOfRvaAndSizes) {
  564. DataTableEntry->GpValue = (PCHAR)CurrentBase +
  565. NtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_GLOBALPTR].VirtualAddress;
  566. }
  567. #endif
  568. if (IMAGE_DIRECTORY_ENTRY_EXCEPTION < NtHeader->OptionalHeader.NumberOfRvaAndSizes) {
  569. DataTableEntry->ExceptionTable = (PCHAR)CurrentBase +
  570. NtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].VirtualAddress;
  571. DataTableEntry->ExceptionTableSize =
  572. NtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].Size;
  573. }
  574. #endif
  575. }
  576. NTSTATUS
  577. MmLoadSystemImage (
  578. IN PUNICODE_STRING ImageFileName,
  579. IN PUNICODE_STRING NamePrefix OPTIONAL,
  580. IN PUNICODE_STRING LoadedBaseName OPTIONAL,
  581. IN ULONG LoadFlags,
  582. OUT PVOID *ImageHandle,
  583. OUT PVOID *ImageBaseAddress
  584. )
  585. /*++
  586. Routine Description:
  587. This routine reads the image pages from the specified section into
  588. the system and returns the address of the DLL's header.
  589. At successful completion, the Section is referenced so it remains
  590. until the system image is unloaded.
  591. Arguments:
  592. ImageFileName - Supplies the full path name (including the image name)
  593. of the image to load.
  594. NamePrefix - If present, supplies the prefix to use with the image name on
  595. load operations. This is used to load the same image multiple
  596. times, by using different prefixes.
  597. LoadedBaseName - If present, supplies the base name to use on the
  598. loaded image instead of the base name found on the
  599. image name.
  600. LoadFlags - Supplies a combination of bit flags as follows:
  601. MM_LOAD_IMAGE_IN_SESSION :
  602. - Supplies whether to load this image in session space.
  603. Each session gets a different copy of this driver with
  604. pages shared as much as possible via copy on write.
  605. MM_LOAD_IMAGE_AND_LOCKDOWN :
  606. - Supplies TRUE if the image pages should be made
  607. nonpagable.
  608. ImageHandle - Returns an opaque pointer to the referenced section object
  609. of the image that was loaded.
  610. ImageBaseAddress - Returns the image base within the system.
  611. Return Value:
  612. Status of the load operation.
  613. Environment:
  614. Kernel mode, APC_LEVEL or below, arbitrary process context.
  615. --*/
  616. {
  617. LONG OldValue;
  618. ULONG i;
  619. ULONG DebugInfoSize;
  620. PIMAGE_DATA_DIRECTORY DataDirectory;
  621. PIMAGE_DEBUG_DIRECTORY DebugDir;
  622. PNON_PAGED_DEBUG_INFO ssHeader;
  623. PMMPTE PointerPte;
  624. PSUBSECTION Subsection;
  625. PCONTROL_AREA ControlArea;
  626. SIZE_T DataTableEntrySize;
  627. PWSTR BaseDllNameBuffer;
  628. PKLDR_DATA_TABLE_ENTRY DataTableEntry;
  629. KLDR_DATA_TABLE_ENTRY TempDataTableEntry;
  630. PKLDR_DATA_TABLE_ENTRY FoundDataTableEntry;
  631. NTSTATUS Status;
  632. PSECTION SectionPointer;
  633. PIMAGE_NT_HEADERS NtHeaders;
  634. UNICODE_STRING PrefixedImageName;
  635. UNICODE_STRING BaseName;
  636. UNICODE_STRING BaseDirectory;
  637. OBJECT_ATTRIBUTES ObjectAttributes;
  638. HANDLE FileHandle;
  639. HANDLE SectionHandle;
  640. IO_STATUS_BLOCK IoStatus;
  641. PCHAR NameBuffer;
  642. PLIST_ENTRY NextEntry;
  643. ULONG NumberOfPtes;
  644. PCHAR MissingProcedureName;
  645. PWSTR MissingDriverName;
  646. PWSTR PrintableMissingDriverName;
  647. PLOAD_IMPORTS LoadedImports;
  648. LOGICAL AlreadyOpen;
  649. LOGICAL IssueUnloadOnFailure;
  650. LOGICAL LoadLockOwned;
  651. ULONG SectionAccess;
  652. PKTHREAD CurrentThread;
  653. PAGED_CODE();
  654. if (LoadFlags & MM_LOAD_IMAGE_IN_SESSION) {
  655. ASSERT (NamePrefix == NULL);
  656. ASSERT (LoadedBaseName == NULL);
  657. if ((PsGetCurrentProcess()->Flags & PS_PROCESS_FLAGS_IN_SESSION) == 0) {
  658. return STATUS_NO_MEMORY;
  659. }
  660. }
  661. LoadLockOwned = FALSE;
  662. LoadedImports = (PLOAD_IMPORTS) NO_IMPORTS_USED;
  663. SectionPointer = NULL;
  664. FileHandle = (HANDLE)0;
  665. MissingProcedureName = NULL;
  666. MissingDriverName = NULL;
  667. IssueUnloadOnFailure = FALSE;
  668. FoundDataTableEntry = NULL;
  669. NameBuffer = ExAllocatePoolWithTag (NonPagedPool,
  670. MAXIMUM_FILENAME_LENGTH,
  671. 'nLmM');
  672. if (NameBuffer == NULL) {
  673. return STATUS_INSUFFICIENT_RESOURCES;
  674. }
  675. //
  676. // Initializing these is not needed for correctness, but
  677. // without it the compiler cannot compile this code W4 to check
  678. // for use of uninitialized variables.
  679. //
  680. SATISFY_OVERZEALOUS_COMPILER (NumberOfPtes = (ULONG)-1);
  681. DataTableEntry = NULL;
  682. //
  683. // Get name roots.
  684. //
  685. if (ImageFileName->Buffer[0] == OBJ_NAME_PATH_SEPARATOR) {
  686. PWCHAR p;
  687. ULONG l;
  688. p = &ImageFileName->Buffer[ImageFileName->Length>>1];
  689. while (*(p-1) != OBJ_NAME_PATH_SEPARATOR) {
  690. p--;
  691. }
  692. l = (ULONG)(&ImageFileName->Buffer[ImageFileName->Length>>1] - p);
  693. l *= sizeof(WCHAR);
  694. BaseName.Length = (USHORT)l;
  695. BaseName.Buffer = p;
  696. }
  697. else {
  698. BaseName.Length = ImageFileName->Length;
  699. BaseName.Buffer = ImageFileName->Buffer;
  700. }
  701. BaseName.MaximumLength = BaseName.Length;
  702. BaseDirectory = *ImageFileName;
  703. BaseDirectory.Length = (USHORT)(BaseDirectory.Length - BaseName.Length);
  704. BaseDirectory.MaximumLength = BaseDirectory.Length;
  705. PrefixedImageName = *ImageFileName;
  706. //
  707. // If there's a name prefix, add it to the PrefixedImageName.
  708. //
  709. if (NamePrefix) {
  710. PrefixedImageName.MaximumLength = (USHORT)(BaseDirectory.Length + NamePrefix->Length + BaseName.Length);
  711. PrefixedImageName.Buffer = ExAllocatePoolWithTag (
  712. NonPagedPool,
  713. PrefixedImageName.MaximumLength,
  714. 'dLmM');
  715. if (!PrefixedImageName.Buffer) {
  716. ExFreePool (NameBuffer);
  717. return STATUS_INSUFFICIENT_RESOURCES;
  718. }
  719. PrefixedImageName.Length = 0;
  720. RtlAppendUnicodeStringToString(&PrefixedImageName, &BaseDirectory);
  721. RtlAppendUnicodeStringToString(&PrefixedImageName, NamePrefix);
  722. RtlAppendUnicodeStringToString(&PrefixedImageName, &BaseName);
  723. //
  724. // Alter the basename to match.
  725. //
  726. BaseName.Buffer = PrefixedImageName.Buffer + BaseDirectory.Length / sizeof(WCHAR);
  727. BaseName.Length = (USHORT)(BaseName.Length + NamePrefix->Length);
  728. BaseName.MaximumLength = (USHORT)(BaseName.MaximumLength + NamePrefix->Length);
  729. }
  730. //
  731. // If there's a loaded base name, use it instead of the base name.
  732. //
  733. if (LoadedBaseName) {
  734. BaseName = *LoadedBaseName;
  735. }
  736. #if DBG
  737. if (NtGlobalFlag & FLG_SHOW_LDR_SNAPS) {
  738. DbgPrint ("MM:SYSLDR Loading %wZ (%wZ) %s\n",
  739. &PrefixedImageName,
  740. &BaseName,
  741. (LoadFlags & MM_LOAD_IMAGE_IN_SESSION) ? "in session space" : " ");
  742. }
  743. #endif
  744. AlreadyOpen = FALSE;
  745. ReCheckLoaderList:
  746. //
  747. // Arbitrary process context so prevent suspend APCs now.
  748. //
  749. ASSERT (LoadLockOwned == FALSE);
  750. LoadLockOwned = TRUE;
  751. CurrentThread = KeGetCurrentThread ();
  752. KeEnterCriticalRegionThread (CurrentThread);
  753. KeWaitForSingleObject (&MmSystemLoadLock,
  754. WrVirtualMemory,
  755. KernelMode,
  756. FALSE,
  757. (PLARGE_INTEGER)NULL);
  758. //
  759. // Check to see if this name already exists in the loader database.
  760. //
  761. NextEntry = PsLoadedModuleList.Flink;
  762. while (NextEntry != &PsLoadedModuleList) {
  763. DataTableEntry = CONTAINING_RECORD (NextEntry,
  764. KLDR_DATA_TABLE_ENTRY,
  765. InLoadOrderLinks);
  766. if (RtlEqualUnicodeString (&PrefixedImageName,
  767. &DataTableEntry->FullDllName,
  768. TRUE)) {
  769. break;
  770. }
  771. NextEntry = NextEntry->Flink;
  772. }
  773. if (NextEntry != &PsLoadedModuleList) {
  774. //
  775. // Found a match in the loaded module list. See if it's acceptable.
  776. //
  777. // If this thread already loaded the image below and upon rechecking
  778. // finds some other thread did so also, then get rid of our object
  779. // now and use the other thread's inserted entry instead.
  780. //
  781. if (SectionPointer != NULL) {
  782. ObDereferenceObject (SectionPointer);
  783. SectionPointer = NULL;
  784. }
  785. if ((LoadFlags & MM_LOAD_IMAGE_IN_SESSION) == 0) {
  786. if (MI_IS_SESSION_ADDRESS (DataTableEntry->DllBase) == TRUE) {
  787. //
  788. // The caller is trying to load a driver in systemwide space
  789. // that has already been loaded in session space. This is
  790. // not allowed.
  791. //
  792. Status = STATUS_CONFLICTING_ADDRESSES;
  793. }
  794. else {
  795. *ImageHandle = DataTableEntry;
  796. *ImageBaseAddress = DataTableEntry->DllBase;
  797. Status = STATUS_IMAGE_ALREADY_LOADED;
  798. }
  799. goto return2;
  800. }
  801. if (MI_IS_SESSION_ADDRESS (DataTableEntry->DllBase) == FALSE) {
  802. //
  803. // The caller is trying to load a driver in session space
  804. // that has already been loaded in system space. This is
  805. // not allowed.
  806. //
  807. Status = STATUS_CONFLICTING_ADDRESSES;
  808. goto return2;
  809. }
  810. AlreadyOpen = TRUE;
  811. //
  812. // This image has already been loaded systemwide. If it's
  813. // already been loaded in this session space as well, just
  814. // bump the reference count using the already allocated
  815. // address. Otherwise, insert it into this session space.
  816. //
  817. Status = MiSessionInsertImage (DataTableEntry->DllBase);
  818. if (!NT_SUCCESS (Status)) {
  819. if (Status == STATUS_ALREADY_COMMITTED) {
  820. //
  821. // This driver's already been loaded in this session.
  822. //
  823. ASSERT (DataTableEntry->LoadCount >= 1);
  824. *ImageHandle = DataTableEntry;
  825. *ImageBaseAddress = DataTableEntry->DllBase;
  826. Status = STATUS_SUCCESS;
  827. }
  828. //
  829. // The LoadCount should generally not be 0 here, but it is
  830. // possible in the case where an attempt has been made to
  831. // unload a DLL on last dereference, but the DLL refused to
  832. // unload.
  833. //
  834. goto return2;
  835. }
  836. //
  837. // This driver is already loaded in the system, but not in
  838. // this particular session - share it now.
  839. //
  840. FoundDataTableEntry = DataTableEntry;
  841. DataTableEntry->LoadCount += 1;
  842. ASSERT (DataTableEntry->SectionPointer != NULL);
  843. SectionPointer = DataTableEntry->SectionPointer;
  844. }
  845. else if (SectionPointer == NULL) {
  846. //
  847. // This image is not already loaded.
  848. //
  849. // A NULL SectionPointer indicates this thread didn't already load
  850. // this image below either, so go and get it.
  851. //
  852. // Release the load lock first as getting the image is not cheap.
  853. //
  854. KeReleaseMutant (&MmSystemLoadLock, 1, FALSE, FALSE);
  855. KeLeaveCriticalRegionThread (CurrentThread);
  856. LoadLockOwned = FALSE;
  857. InterlockedOr (&MiFirstDriverLoadEver, 0x1);
  858. //
  859. // Check and see if a user wants to replace this binary
  860. // via a transfer through the kernel debugger. If this
  861. // fails just continue on with the existing file.
  862. //
  863. if ((KdDebuggerEnabled) && (KdDebuggerNotPresent == FALSE)) {
  864. Status = KdPullRemoteFile (ImageFileName,
  865. FILE_ATTRIBUTE_NORMAL,
  866. FILE_OVERWRITE_IF,
  867. FILE_SYNCHRONOUS_IO_NONALERT);
  868. if (NT_SUCCESS (Status)) {
  869. DbgPrint ("MmLoadSystemImage: Pulled %wZ from kd\n",
  870. ImageFileName);
  871. }
  872. }
  873. DataTableEntry = NULL;
  874. //
  875. // Attempt to open the driver image itself. If this fails, then the
  876. // driver image cannot be located, so nothing else matters.
  877. //
  878. InitializeObjectAttributes (&ObjectAttributes,
  879. ImageFileName,
  880. (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE),
  881. NULL,
  882. NULL);
  883. Status = ZwOpenFile (&FileHandle,
  884. FILE_EXECUTE,
  885. &ObjectAttributes,
  886. &IoStatus,
  887. FILE_SHARE_READ | FILE_SHARE_DELETE,
  888. 0);
  889. if (!NT_SUCCESS (Status)) {
  890. #if DBG
  891. if (NtGlobalFlag & FLG_SHOW_LDR_SNAPS) {
  892. DbgPrint ("MmLoadSystemImage: cannot open %wZ\n",
  893. ImageFileName);
  894. }
  895. #endif
  896. //
  897. // File not found.
  898. //
  899. goto return2;
  900. }
  901. Status = MmCheckSystemImage (FileHandle, FALSE);
  902. if ((Status == STATUS_IMAGE_CHECKSUM_MISMATCH) ||
  903. (Status == STATUS_IMAGE_MP_UP_MISMATCH) ||
  904. (Status == STATUS_INVALID_IMAGE_PROTECT)) {
  905. goto return1;
  906. }
  907. //
  908. // Now attempt to create an image section for the file. If this fails,
  909. // then the driver file is not an image. Session space drivers are
  910. // shared text with copy on write data, so don't allow writes here.
  911. //
  912. if (LoadFlags & MM_LOAD_IMAGE_IN_SESSION) {
  913. SectionAccess = SECTION_MAP_READ | SECTION_MAP_EXECUTE;
  914. }
  915. else {
  916. SectionAccess = SECTION_ALL_ACCESS;
  917. }
  918. InitializeObjectAttributes (&ObjectAttributes,
  919. NULL,
  920. (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE),
  921. NULL,
  922. NULL);
  923. Status = ZwCreateSection (&SectionHandle,
  924. SectionAccess,
  925. &ObjectAttributes,
  926. (PLARGE_INTEGER) NULL,
  927. PAGE_EXECUTE,
  928. SEC_IMAGE,
  929. FileHandle);
  930. if (!NT_SUCCESS(Status)) {
  931. goto return1;
  932. }
  933. //
  934. // Now reference the section handle. If this fails something is
  935. // very wrong because it is a kernel handle.
  936. //
  937. // N.B. ObRef sets SectionPointer to NULL on failure.
  938. //
  939. Status = ObReferenceObjectByHandle (SectionHandle,
  940. SECTION_MAP_EXECUTE,
  941. MmSectionObjectType,
  942. KernelMode,
  943. (PVOID *) &SectionPointer,
  944. (POBJECT_HANDLE_INFORMATION) NULL);
  945. ZwClose (SectionHandle);
  946. if (!NT_SUCCESS (Status)) {
  947. goto return1;
  948. }
  949. ControlArea = SectionPointer->Segment->ControlArea;
  950. if ((ControlArea->u.Flags.GlobalOnlyPerSession == 0) &&
  951. (ControlArea->u.Flags.Rom == 0)) {
  952. Subsection = (PSUBSECTION)(ControlArea + 1);
  953. }
  954. else {
  955. Subsection = (PSUBSECTION)((PLARGE_CONTROL_AREA)ControlArea + 1);
  956. }
  957. if ((Subsection->NextSubsection == NULL) &&
  958. ((LoadFlags & MM_LOAD_IMAGE_IN_SESSION) == 0)) {
  959. PSECTION SectionPointer2;
  960. //
  961. // The driver was linked with subsection alignment such that
  962. // it is mapped with one subsection. Since the CreateSection
  963. // above guarantees that the driver image is indeed a
  964. // satisfactory executable, map it directly now to reuse the
  965. // cache from the MmCheckSystemImage call above.
  966. //
  967. Status = ZwCreateSection (&SectionHandle,
  968. SectionAccess,
  969. &ObjectAttributes,
  970. (PLARGE_INTEGER) NULL,
  971. PAGE_EXECUTE,
  972. SEC_COMMIT,
  973. FileHandle);
  974. if (NT_SUCCESS(Status)) {
  975. Status = ObReferenceObjectByHandle (
  976. SectionHandle,
  977. SECTION_MAP_EXECUTE,
  978. MmSectionObjectType,
  979. KernelMode,
  980. (PVOID *) &SectionPointer2,
  981. (POBJECT_HANDLE_INFORMATION) NULL);
  982. ZwClose (SectionHandle);
  983. if (NT_SUCCESS (Status)) {
  984. //
  985. // The number of PTEs won't match if the image is
  986. // stripped and the debug directory crosses the last
  987. // sector boundary of the file. We could still use the
  988. // new section, but these cases are under 2% of all the
  989. // drivers loaded so don't bother.
  990. //
  991. if (SectionPointer->Segment->TotalNumberOfPtes == SectionPointer2->Segment->TotalNumberOfPtes) {
  992. ObDereferenceObject (SectionPointer);
  993. SectionPointer = SectionPointer2;
  994. }
  995. else {
  996. ObDereferenceObject (SectionPointer2);
  997. }
  998. }
  999. }
  1000. }
  1001. if ((LoadFlags & MM_LOAD_IMAGE_IN_SESSION) &&
  1002. (SectionPointer->Segment->ControlArea->u.Flags.FloppyMedia == 0)) {
  1003. //
  1004. // Check with all of the drivers along the path to win32k.sys to
  1005. // ensure that they are willing to follow the rules required
  1006. // of them and to give them a chance to lock down code and data
  1007. // that needs to be locked. If any of the drivers along the path
  1008. // refuses to participate, fail the win32k.sys load.
  1009. //
  1010. // It is assumed that all session drivers live on the same physical
  1011. // drive, so when the very first session driver is loaded, this
  1012. // check can be made.
  1013. //
  1014. // Note that this is important because these drivers are always
  1015. // paged directly in/out from the filesystem so the drive
  1016. // containing the filesystem better not get removed !
  1017. //
  1018. //
  1019. // This is skipped for the WinPE removable media boot case because
  1020. // the user may be running WinPE in RAM and want to swap out the
  1021. // boot media. In this case, the control area is marked as
  1022. // FloppyMedia (even if it was CD-based) as all the pages have
  1023. // already been converted to pagefile-backed.
  1024. //
  1025. do {
  1026. OldValue = MiFirstDriverLoadEver;
  1027. if (OldValue & 0x2) {
  1028. break;
  1029. }
  1030. if (InterlockedCompareExchange (&MiFirstDriverLoadEver, OldValue | 0x2, OldValue) == OldValue) {
  1031. Status = PpPagePathAssign (SectionPointer->Segment->ControlArea->FilePointer);
  1032. if (!NT_SUCCESS (Status)) {
  1033. KdPrint (("PpPagePathAssign FAILED for %wZ: %x\n",
  1034. ImageFileName, Status));
  1035. //
  1036. // Failing the insertion of win32k.sys' device
  1037. // in the pagefile path is commented out until
  1038. // the storage drivers have been modified to
  1039. // correctly handle this request. If this is
  1040. // added later, add code here to release relevant
  1041. // resources for this error path.
  1042. //
  1043. }
  1044. break;
  1045. }
  1046. } while (TRUE);
  1047. }
  1048. //
  1049. // Anything may have changed while the load lock was released.
  1050. // So before using the section we just created, go back and see
  1051. // if any other threads have slipped through and did it already.
  1052. //
  1053. goto ReCheckLoaderList;
  1054. }
  1055. else {
  1056. DataTableEntry = NULL;
  1057. }
  1058. //
  1059. // Load the driver from the filesystem and pick a virtual address for it.
  1060. // All session images are paged directly to and from the filesystem so these
  1061. // images remain busy.
  1062. //
  1063. Status = MiLoadImageSection (&SectionPointer,
  1064. ImageBaseAddress,
  1065. ImageFileName,
  1066. LoadFlags & MM_LOAD_IMAGE_IN_SESSION,
  1067. FoundDataTableEntry);
  1068. ASSERT (Status != STATUS_ALREADY_COMMITTED);
  1069. NumberOfPtes = SectionPointer->Segment->TotalNumberOfPtes;
  1070. //
  1071. // Normal drivers are dereferenced here and their images can then be
  1072. // overwritten. This is ok because we've already read the whole thing
  1073. // into memory and from here until reboot (or unload), we back them
  1074. // with the pagefile.
  1075. //
  1076. // Session space drivers are the exception - these images
  1077. // are inpaged from the filesystem and we need to keep our reference to
  1078. // the file so that it doesn't get overwritten.
  1079. //
  1080. if ((LoadFlags & MM_LOAD_IMAGE_IN_SESSION) == 0) {
  1081. if (NT_SUCCESS (Status)) {
  1082. //
  1083. // Move the driver into a large page if requested via the registry.
  1084. //
  1085. MiUseLargeDriverPage (SectionPointer->Segment->TotalNumberOfPtes,
  1086. ImageBaseAddress,
  1087. &BaseName,
  1088. 1);
  1089. }
  1090. ObDereferenceObject (SectionPointer);
  1091. SectionPointer = NULL;
  1092. }
  1093. //
  1094. // The module LoadCount will be 1 here if the module was just loaded.
  1095. // The LoadCount will be >1 if it was attached to by a session (as opposed
  1096. // to just loaded).
  1097. //
  1098. if (!NT_SUCCESS (Status)) {
  1099. if (AlreadyOpen == TRUE) {
  1100. //
  1101. // We're failing and we were just attaching to an already loaded
  1102. // driver. We don't want to go through the forced unload path
  1103. // because we've already deleted the address space so
  1104. // decrement our reference and clear the DataTableEntry.
  1105. //
  1106. ASSERT (DataTableEntry != NULL);
  1107. DataTableEntry->LoadCount -= 1;
  1108. DataTableEntry = NULL;
  1109. }
  1110. goto return1;
  1111. }
  1112. //
  1113. // Error recovery from this point out for sessions works as follows:
  1114. //
  1115. // For sessions, we may or may not have a DataTableEntry at this point.
  1116. // If we do, it's because we're attaching to a driver that has already
  1117. // been loaded - and the DataTableEntry->LoadCount has been bumped - so
  1118. // the error recovery from here on out is to just call
  1119. // MmUnloadSystemImage with the DataTableEntry.
  1120. //
  1121. // If this is the first load of a given driver into a session space, we
  1122. // have no DataTableEntry at this point. The view has already been mapped
  1123. // and committed and the group/session addresses reserved for this DLL.
  1124. // The error recovery path handles all this because
  1125. // MmUnloadSystemImage zeroes the relevant fields in the
  1126. // LDR_DATA_TABLE_ENTRY so that MmUnloadSystemImage works properly.
  1127. //
  1128. IssueUnloadOnFailure = TRUE;
  1129. if (AlreadyOpen == FALSE) {
  1130. if (((LoadFlags & MM_LOAD_IMAGE_IN_SESSION) == 0) ||
  1131. (*ImageBaseAddress != SectionPointer->Segment->BasedAddress)) {
  1132. //
  1133. // Apply the fixups to the section. Note session images need only
  1134. // be fixed up once per insertion in the loaded module list.
  1135. //
  1136. try {
  1137. Status = LdrRelocateImage (*ImageBaseAddress,
  1138. "SYSLDR",
  1139. STATUS_SUCCESS,
  1140. STATUS_CONFLICTING_ADDRESSES,
  1141. STATUS_INVALID_IMAGE_FORMAT);
  1142. } except (EXCEPTION_EXECUTE_HANDLER) {
  1143. Status = GetExceptionCode ();
  1144. KdPrint(("MM:sysload - LdrRelocateImage failed status %lx\n",
  1145. Status));
  1146. }
  1147. if (!NT_SUCCESS(Status)) {
  1148. //
  1149. // Unload the system image and dereference the section.
  1150. //
  1151. goto return1;
  1152. }
  1153. }
  1154. DebugInfoSize = 0;
  1155. DataDirectory = NULL;
  1156. DebugDir = NULL;
  1157. NtHeaders = RtlImageNtHeader (*ImageBaseAddress);
  1158. //
  1159. // Create a loader table entry for this driver before resolving the
  1160. // references so that any circular references can resolve properly.
  1161. //
  1162. if (LoadFlags & MM_LOAD_IMAGE_IN_SESSION) {
  1163. DebugInfoSize = sizeof (NON_PAGED_DEBUG_INFO);
  1164. if (IMAGE_DIRECTORY_ENTRY_DEBUG <
  1165. NtHeaders->OptionalHeader.NumberOfRvaAndSizes) {
  1166. DataDirectory = &NtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG];
  1167. if (DataDirectory->VirtualAddress &&
  1168. DataDirectory->Size &&
  1169. (DataDirectory->VirtualAddress + DataDirectory->Size) <
  1170. NtHeaders->OptionalHeader.SizeOfImage) {
  1171. DebugDir = (PIMAGE_DEBUG_DIRECTORY)
  1172. ((PUCHAR)(*ImageBaseAddress) +
  1173. DataDirectory->VirtualAddress);
  1174. DebugInfoSize += DataDirectory->Size;
  1175. for (i = 0;
  1176. i < DataDirectory->Size/sizeof(IMAGE_DEBUG_DIRECTORY);
  1177. i += 1) {
  1178. if ((DebugDir+i)->PointerToRawData &&
  1179. (DebugDir+i)->PointerToRawData <
  1180. NtHeaders->OptionalHeader.SizeOfImage &&
  1181. ((DebugDir+i)->PointerToRawData +
  1182. (DebugDir+i)->SizeOfData) <
  1183. NtHeaders->OptionalHeader.SizeOfImage) {
  1184. DebugInfoSize += (DebugDir+i)->SizeOfData;
  1185. }
  1186. }
  1187. }
  1188. DebugInfoSize = MI_ROUND_TO_SIZE(DebugInfoSize, sizeof(ULONG));
  1189. }
  1190. }
  1191. DataTableEntrySize = sizeof (KLDR_DATA_TABLE_ENTRY) +
  1192. DebugInfoSize +
  1193. BaseName.Length + sizeof(UNICODE_NULL);
  1194. DataTableEntry = ExAllocatePoolWithTag (NonPagedPool,
  1195. DataTableEntrySize,
  1196. 'dLmM');
  1197. if (DataTableEntry == NULL) {
  1198. Status = STATUS_INSUFFICIENT_RESOURCES;
  1199. goto return1;
  1200. }
  1201. //
  1202. // Initialize the flags and load count.
  1203. //
  1204. DataTableEntry->Flags = LDRP_LOAD_IN_PROGRESS;
  1205. DataTableEntry->LoadCount = 1;
  1206. DataTableEntry->LoadedImports = (PVOID)LoadedImports;
  1207. DataTableEntry->PatchInformation = NULL;
  1208. if ((NtHeaders->OptionalHeader.MajorOperatingSystemVersion >= 5) &&
  1209. (NtHeaders->OptionalHeader.MajorImageVersion >= 5)) {
  1210. DataTableEntry->Flags |= LDRP_ENTRY_NATIVE;
  1211. }
  1212. ssHeader = (PNON_PAGED_DEBUG_INFO) ((ULONG_PTR)DataTableEntry +
  1213. sizeof (KLDR_DATA_TABLE_ENTRY));
  1214. BaseDllNameBuffer = (PWSTR) ((ULONG_PTR)ssHeader + DebugInfoSize);
  1215. //
  1216. // If loading a session space image, store away some debug data.
  1217. //
  1218. DataTableEntry->NonPagedDebugInfo = NULL;
  1219. if (LoadFlags & MM_LOAD_IMAGE_IN_SESSION) {
  1220. DataTableEntry->NonPagedDebugInfo = ssHeader;
  1221. DataTableEntry->Flags |= LDRP_NON_PAGED_DEBUG_INFO;
  1222. ssHeader->Signature = NON_PAGED_DEBUG_SIGNATURE;
  1223. ssHeader->Flags = 1;
  1224. ssHeader->Size = DebugInfoSize;
  1225. ssHeader->Machine = NtHeaders->FileHeader.Machine;
  1226. ssHeader->Characteristics = NtHeaders->FileHeader.Characteristics;
  1227. ssHeader->TimeDateStamp = NtHeaders->FileHeader.TimeDateStamp;
  1228. ssHeader->CheckSum = NtHeaders->OptionalHeader.CheckSum;
  1229. ssHeader->SizeOfImage = NtHeaders->OptionalHeader.SizeOfImage;
  1230. ssHeader->ImageBase = (ULONG_PTR) *ImageBaseAddress;
  1231. if (DebugDir) {
  1232. RtlCopyMemory (ssHeader + 1,
  1233. DebugDir,
  1234. DataDirectory->Size);
  1235. DebugInfoSize = DataDirectory->Size;
  1236. for (i = 0;
  1237. i < DataDirectory->Size/sizeof(IMAGE_DEBUG_DIRECTORY);
  1238. i += 1) {
  1239. if ((DebugDir + i)->PointerToRawData &&
  1240. (DebugDir+i)->PointerToRawData <
  1241. NtHeaders->OptionalHeader.SizeOfImage &&
  1242. ((DebugDir+i)->PointerToRawData +
  1243. (DebugDir+i)->SizeOfData) <
  1244. NtHeaders->OptionalHeader.SizeOfImage) {
  1245. RtlCopyMemory ((PUCHAR)(ssHeader + 1) +
  1246. DebugInfoSize,
  1247. (PUCHAR)(*ImageBaseAddress) +
  1248. (DebugDir + i)->PointerToRawData,
  1249. (DebugDir + i)->SizeOfData);
  1250. //
  1251. // Reset the offset in the debug directory to point to
  1252. //
  1253. (((PIMAGE_DEBUG_DIRECTORY)(ssHeader + 1)) + i)->
  1254. PointerToRawData = DebugInfoSize;
  1255. DebugInfoSize += (DebugDir+i)->SizeOfData;
  1256. }
  1257. else {
  1258. (((PIMAGE_DEBUG_DIRECTORY)(ssHeader + 1)) + i)->
  1259. PointerToRawData = 0;
  1260. }
  1261. }
  1262. }
  1263. }
  1264. //
  1265. // Initialize the address of the DLL image file header and the entry
  1266. // point address.
  1267. //
  1268. DataTableEntry->DllBase = *ImageBaseAddress;
  1269. DataTableEntry->EntryPoint =
  1270. ((PCHAR)*ImageBaseAddress + NtHeaders->OptionalHeader.AddressOfEntryPoint);
  1271. DataTableEntry->SizeOfImage = NumberOfPtes << PAGE_SHIFT;
  1272. DataTableEntry->CheckSum = NtHeaders->OptionalHeader.CheckSum;
  1273. DataTableEntry->SectionPointer = (PVOID) SectionPointer;
  1274. //
  1275. // Store the DLL name.
  1276. //
  1277. DataTableEntry->BaseDllName.Buffer = BaseDllNameBuffer;
  1278. DataTableEntry->BaseDllName.Length = BaseName.Length;
  1279. DataTableEntry->BaseDllName.MaximumLength = BaseName.Length;
  1280. RtlCopyMemory (DataTableEntry->BaseDllName.Buffer,
  1281. BaseName.Buffer,
  1282. BaseName.Length);
  1283. DataTableEntry->BaseDllName.Buffer[BaseName.Length/sizeof(WCHAR)] = UNICODE_NULL;
  1284. DataTableEntry->FullDllName.Buffer = ExAllocatePoolWithTag (PagedPool | POOL_COLD_ALLOCATION,
  1285. PrefixedImageName.Length + sizeof(UNICODE_NULL),
  1286. 'TDmM');
  1287. if (DataTableEntry->FullDllName.Buffer == NULL) {
  1288. //
  1289. // Pool could not be allocated, just set the length to 0.
  1290. //
  1291. DataTableEntry->FullDllName.Length = 0;
  1292. DataTableEntry->FullDllName.MaximumLength = 0;
  1293. }
  1294. else {
  1295. DataTableEntry->FullDllName.Length = PrefixedImageName.Length;
  1296. DataTableEntry->FullDllName.MaximumLength = PrefixedImageName.Length;
  1297. RtlCopyMemory (DataTableEntry->FullDllName.Buffer,
  1298. PrefixedImageName.Buffer,
  1299. PrefixedImageName.Length);
  1300. DataTableEntry->FullDllName.Buffer[PrefixedImageName.Length/sizeof(WCHAR)] = UNICODE_NULL;
  1301. }
  1302. //
  1303. // Capture the exception table data info
  1304. //
  1305. MiCaptureImageExceptionValues (DataTableEntry);
  1306. //
  1307. // Acquire the loaded module list resource and insert this entry
  1308. // into the list.
  1309. //
  1310. MiProcessLoaderEntry (DataTableEntry, TRUE);
  1311. MissingProcedureName = NameBuffer;
  1312. try {
  1313. //
  1314. // Resolving the image references results in other DLLs being
  1315. // loaded if they are referenced by the module that was just loaded.
  1316. // An example is when an OEM printer or FAX driver links with
  1317. // other general libraries. This is not a problem for session space
  1318. // because the general libraries do not have the global data issues
  1319. // that win32k.sys and the video drivers do. So we just call the
  1320. // standard kernel reference resolver and any referenced libraries
  1321. // get loaded into system global space. Code in the routine
  1322. // restricts which libraries can be referenced by a driver.
  1323. //
  1324. Status = MiResolveImageReferences (*ImageBaseAddress,
  1325. &BaseDirectory,
  1326. NamePrefix,
  1327. &MissingProcedureName,
  1328. &MissingDriverName,
  1329. &LoadedImports);
  1330. } except (EXCEPTION_EXECUTE_HANDLER) {
  1331. Status = GetExceptionCode ();
  1332. KdPrint(("MM:sysload - ResolveImageReferences failed status %x\n",
  1333. Status));
  1334. }
  1335. if (!NT_SUCCESS (Status)) {
  1336. #if DBG
  1337. if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
  1338. ASSERT (MissingProcedureName == NULL);
  1339. }
  1340. if ((Status == STATUS_DRIVER_ORDINAL_NOT_FOUND) ||
  1341. (Status == STATUS_OBJECT_NAME_NOT_FOUND) ||
  1342. (Status == STATUS_DRIVER_ENTRYPOINT_NOT_FOUND)) {
  1343. if ((ULONG_PTR)MissingProcedureName & ~((ULONG_PTR) (X64K-1))) {
  1344. //
  1345. // If not an ordinal, print string.
  1346. //
  1347. DbgPrint ("MissingProcedureName %s\n", MissingProcedureName);
  1348. }
  1349. else {
  1350. DbgPrint ("MissingProcedureName 0x%p\n", MissingProcedureName);
  1351. }
  1352. }
  1353. if (MissingDriverName != NULL) {
  1354. PrintableMissingDriverName = (PWSTR)((ULONG_PTR)MissingDriverName & ~0x1);
  1355. DbgPrint ("MissingDriverName %ws\n", PrintableMissingDriverName);
  1356. }
  1357. #endif
  1358. MiProcessLoaderEntry (DataTableEntry, FALSE);
  1359. if (DataTableEntry->FullDllName.Buffer != NULL) {
  1360. ExFreePool (DataTableEntry->FullDllName.Buffer);
  1361. }
  1362. ExFreePool (DataTableEntry);
  1363. DataTableEntry = NULL;
  1364. goto return1;
  1365. }
  1366. PERFINFO_IMAGE_LOAD (DataTableEntry);
  1367. //
  1368. // Reinitialize the flags and update the loaded imports.
  1369. //
  1370. DataTableEntry->Flags |= (LDRP_SYSTEM_MAPPED | LDRP_ENTRY_PROCESSED | LDRP_MM_LOADED);
  1371. DataTableEntry->Flags &= ~LDRP_LOAD_IN_PROGRESS;
  1372. DataTableEntry->LoadedImports = LoadedImports;
  1373. MiApplyDriverVerifier (DataTableEntry, NULL);
  1374. if (LoadFlags & MM_LOAD_IMAGE_IN_SESSION) {
  1375. //
  1376. // The session image was mapped entirely read-write on initial
  1377. // creation. Now that the relocations (if any), image
  1378. // resolution and import table updates are complete, correct
  1379. // permissions can be applied.
  1380. //
  1381. // Make the entire image copy on write. The subsequent call
  1382. // to MiWriteProtectSystemImage will make various portions
  1383. // readonly. Then apply flip various subsections into global
  1384. // shared mode if their attributes specify it.
  1385. //
  1386. PointerPte = MiGetPteAddress (DataTableEntry->DllBase);
  1387. MiSetSystemCodeProtection (PointerPte,
  1388. PointerPte + NumberOfPtes - 1,
  1389. MM_EXECUTE_WRITECOPY);
  1390. }
  1391. MiWriteProtectSystemImage (DataTableEntry->DllBase);
  1392. if (LoadFlags & MM_LOAD_IMAGE_IN_SESSION) {
  1393. MiSessionProcessGlobalSubsections (DataTableEntry);
  1394. }
  1395. if (PsImageNotifyEnabled) {
  1396. IMAGE_INFO ImageInfo;
  1397. ImageInfo.Properties = 0;
  1398. ImageInfo.ImageAddressingMode = IMAGE_ADDRESSING_MODE_32BIT;
  1399. ImageInfo.SystemModeImage = TRUE;
  1400. ImageInfo.ImageSize = DataTableEntry->SizeOfImage;
  1401. ImageInfo.ImageBase = *ImageBaseAddress;
  1402. ImageInfo.ImageSelector = 0;
  1403. ImageInfo.ImageSectionNumber = 0;
  1404. PsCallImageNotifyRoutines(ImageFileName, (HANDLE)NULL, &ImageInfo);
  1405. }
  1406. if (MiCacheImageSymbols (*ImageBaseAddress)) {
  1407. //
  1408. // TEMP TEMP TEMP rip out when debugger converted
  1409. //
  1410. ANSI_STRING AnsiName;
  1411. UNICODE_STRING UnicodeName;
  1412. //
  1413. // \SystemRoot is 11 characters in length
  1414. //
  1415. if (PrefixedImageName.Length > (11 * sizeof (WCHAR )) &&
  1416. !_wcsnicmp (PrefixedImageName.Buffer, (const PUSHORT)L"\\SystemRoot", 11)) {
  1417. UnicodeName = PrefixedImageName;
  1418. UnicodeName.Buffer += 11;
  1419. UnicodeName.Length -= (11 * sizeof (WCHAR));
  1420. sprintf (NameBuffer, "%ws%wZ", &SharedUserData->NtSystemRoot[2], &UnicodeName);
  1421. }
  1422. else {
  1423. sprintf (NameBuffer, "%wZ", &BaseName);
  1424. }
  1425. RtlInitString (&AnsiName, NameBuffer);
  1426. DbgLoadImageSymbols (&AnsiName,
  1427. *ImageBaseAddress,
  1428. (ULONG_PTR) -1);
  1429. DataTableEntry->Flags |= LDRP_DEBUG_SYMBOLS_LOADED;
  1430. }
  1431. }
  1432. //
  1433. // Flush the instruction cache on all systems in the configuration.
  1434. //
  1435. KeSweepIcache (TRUE);
  1436. *ImageHandle = DataTableEntry;
  1437. Status = STATUS_SUCCESS;
  1438. //
  1439. // Session images are always paged by default.
  1440. // Non-session images get paged now.
  1441. //
  1442. if (LoadFlags & MM_LOAD_IMAGE_IN_SESSION) {
  1443. MI_LOG_SESSION_DATA_START (DataTableEntry);
  1444. }
  1445. else if ((LoadFlags & MM_LOAD_IMAGE_AND_LOCKDOWN) == 0) {
  1446. ASSERT (SectionPointer == NULL);
  1447. MiEnablePagingOfDriver (DataTableEntry);
  1448. }
  1449. return1:
  1450. if (!NT_SUCCESS(Status)) {
  1451. if (IssueUnloadOnFailure == TRUE) {
  1452. if (DataTableEntry == NULL) {
  1453. RtlZeroMemory (&TempDataTableEntry, sizeof (KLDR_DATA_TABLE_ENTRY));
  1454. DataTableEntry = &TempDataTableEntry;
  1455. DataTableEntry->DllBase = *ImageBaseAddress;
  1456. DataTableEntry->SizeOfImage = NumberOfPtes << PAGE_SHIFT;
  1457. DataTableEntry->LoadCount = 1;
  1458. DataTableEntry->LoadedImports = LoadedImports;
  1459. if ((AlreadyOpen == FALSE) && (SectionPointer != NULL)) {
  1460. DataTableEntry->SectionPointer = (PVOID) SectionPointer;
  1461. }
  1462. }
  1463. #if DBG
  1464. else {
  1465. //
  1466. // If DataTableEntry is NULL, then we are unloading before one
  1467. // got created. Once a LDR_DATA_TABLE_ENTRY is created, the
  1468. // load cannot fail, so if it exists here, at least one other
  1469. // session contains this image as well.
  1470. //
  1471. ASSERT (DataTableEntry->LoadCount > 1);
  1472. }
  1473. #endif
  1474. MmUnloadSystemImage ((PVOID)DataTableEntry);
  1475. }
  1476. if ((AlreadyOpen == FALSE) && (SectionPointer != NULL)) {
  1477. //
  1478. // This is needed for failed win32k.sys loads or any session's
  1479. // load of the first instance of a driver.
  1480. //
  1481. ObDereferenceObject (SectionPointer);
  1482. }
  1483. }
  1484. if (LoadLockOwned == TRUE) {
  1485. KeReleaseMutant (&MmSystemLoadLock, 1, FALSE, FALSE);
  1486. KeLeaveCriticalRegionThread (CurrentThread);
  1487. LoadLockOwned = FALSE;
  1488. }
  1489. if (FileHandle) {
  1490. ZwClose (FileHandle);
  1491. }
  1492. if (!NT_SUCCESS(Status)) {
  1493. UNICODE_STRING ErrorStrings[4];
  1494. ULONG UniqueErrorValue;
  1495. ULONG StringSize;
  1496. ULONG StringCount;
  1497. ANSI_STRING AnsiString;
  1498. UNICODE_STRING ProcedureName = {0};
  1499. UNICODE_STRING DriverName;
  1500. ULONG i;
  1501. PWCHAR temp;
  1502. PWCHAR ptr;
  1503. ULONG PacketSize;
  1504. SIZE_T length;
  1505. PIO_ERROR_LOG_PACKET ErrLog;
  1506. //
  1507. // The driver could not be loaded - log an event with the details.
  1508. //
  1509. StringSize = 0;
  1510. *(&ErrorStrings[0]) = *ImageFileName;
  1511. StringSize += (ImageFileName->Length + sizeof(UNICODE_NULL));
  1512. StringCount = 1;
  1513. UniqueErrorValue = 0;
  1514. PrintableMissingDriverName = (PWSTR)((ULONG_PTR)MissingDriverName & ~0x1);
  1515. if ((Status == STATUS_DRIVER_ORDINAL_NOT_FOUND) ||
  1516. (Status == STATUS_DRIVER_ENTRYPOINT_NOT_FOUND) ||
  1517. (Status == STATUS_OBJECT_NAME_NOT_FOUND) ||
  1518. (Status == STATUS_PROCEDURE_NOT_FOUND)) {
  1519. ErrorStrings[1].Buffer = L"cannot find";
  1520. length = wcslen(ErrorStrings[1].Buffer) * sizeof(WCHAR);
  1521. ErrorStrings[1].Length = (USHORT) length;
  1522. StringSize += (ULONG)(length + sizeof (UNICODE_NULL));
  1523. StringCount += 1;
  1524. RtlInitUnicodeString (&DriverName, PrintableMissingDriverName);
  1525. StringSize += (DriverName.Length + sizeof(UNICODE_NULL));
  1526. StringCount += 1;
  1527. *(&ErrorStrings[2]) = *(&DriverName);
  1528. if ((ULONG_PTR)MissingProcedureName & ~((ULONG_PTR) (X64K-1))) {
  1529. //
  1530. // If not an ordinal, pass as a Unicode string
  1531. //
  1532. RtlInitAnsiString (&AnsiString, MissingProcedureName);
  1533. if (NT_SUCCESS (RtlAnsiStringToUnicodeString (&ProcedureName, &AnsiString, TRUE))) {
  1534. StringSize += (ProcedureName.Length + sizeof(UNICODE_NULL));
  1535. StringCount += 1;
  1536. *(&ErrorStrings[3]) = *(&ProcedureName);
  1537. }
  1538. else {
  1539. goto GenericError;
  1540. }
  1541. }
  1542. else {
  1543. //
  1544. // Just pass ordinal values as is in the UniqueErrorValue.
  1545. //
  1546. UniqueErrorValue = PtrToUlong (MissingProcedureName);
  1547. }
  1548. }
  1549. else {
  1550. GenericError:
  1551. UniqueErrorValue = (ULONG) Status;
  1552. if (MmIsRetryIoStatus(Status)) {
  1553. //
  1554. // Coalesce the various low memory values into just one.
  1555. //
  1556. Status = STATUS_INSUFFICIENT_RESOURCES;
  1557. }
  1558. else {
  1559. //
  1560. // Ideally, the real failing status should be returned. However,
  1561. // we need to do a full release worth of testing (ie Longhorn)
  1562. // before making that change.
  1563. //
  1564. Status = STATUS_DRIVER_UNABLE_TO_LOAD;
  1565. }
  1566. ErrorStrings[1].Buffer = L"failed to load";
  1567. length = wcslen(ErrorStrings[1].Buffer) * sizeof(WCHAR);
  1568. ErrorStrings[1].Length = (USHORT) length;
  1569. StringSize += (ULONG)(length + sizeof (UNICODE_NULL));
  1570. StringCount += 1;
  1571. }
  1572. PacketSize = sizeof (IO_ERROR_LOG_PACKET) + StringSize;
  1573. //
  1574. // Enforce I/O manager interface (ie: UCHAR) size restrictions.
  1575. //
  1576. if (PacketSize < MAXUCHAR) {
  1577. ErrLog = IoAllocateGenericErrorLogEntry ((UCHAR)PacketSize);
  1578. if (ErrLog != NULL) {
  1579. //
  1580. // Fill it in and write it out as a single string.
  1581. //
  1582. ErrLog->ErrorCode = STATUS_LOG_HARD_ERROR;
  1583. ErrLog->FinalStatus = Status;
  1584. ErrLog->UniqueErrorValue = UniqueErrorValue;
  1585. ErrLog->StringOffset = (USHORT) sizeof (IO_ERROR_LOG_PACKET);
  1586. temp = (PWCHAR) ((PUCHAR) ErrLog + ErrLog->StringOffset);
  1587. for (i = 0; i < StringCount; i += 1) {
  1588. ptr = ErrorStrings[i].Buffer;
  1589. RtlCopyMemory (temp, ptr, ErrorStrings[i].Length);
  1590. temp += (ErrorStrings[i].Length / sizeof (WCHAR));
  1591. *temp = L' ';
  1592. temp += 1;
  1593. }
  1594. *(temp - 1) = UNICODE_NULL;
  1595. ErrLog->NumberOfStrings = 1;
  1596. IoWriteErrorLogEntry (ErrLog);
  1597. }
  1598. }
  1599. //
  1600. // The only way this pointer has the low bit set is if we are expected
  1601. // to free the pool containing the name. Typically the name points at
  1602. // a loaded module list entry and so no one has to free it and in this
  1603. // case the low bit will NOT be set. If the module could not be found
  1604. // and was therefore not loaded, then we left a piece of pool around
  1605. // containing the name since there is no loaded module entry already -
  1606. // this must be released now.
  1607. //
  1608. if ((ULONG_PTR)MissingDriverName & 0x1) {
  1609. ExFreePool (PrintableMissingDriverName);
  1610. }
  1611. if (ProcedureName.Buffer != NULL) {
  1612. RtlFreeUnicodeString (&ProcedureName);
  1613. }
  1614. ExFreePool (NameBuffer);
  1615. return Status;
  1616. }
  1617. return2:
  1618. if (LoadLockOwned == TRUE) {
  1619. KeReleaseMutant (&MmSystemLoadLock, 1, FALSE, FALSE);
  1620. KeLeaveCriticalRegionThread (CurrentThread);
  1621. LoadLockOwned = FALSE;
  1622. }
  1623. if (NamePrefix) {
  1624. ExFreePool (PrefixedImageName.Buffer);
  1625. }
  1626. ExFreePool (NameBuffer);
  1627. return Status;
  1628. }
  1629. VOID
  1630. MiReturnFailedSessionPages (
  1631. IN PMMPTE PointerPte,
  1632. IN PMMPTE LastPte
  1633. )
  1634. /*++
  1635. Routine Description:
  1636. This routine is a nonpaged wrapper which undoes session image loads
  1637. that failed midway through reading in the pages.
  1638. Arguments:
  1639. PointerPte - Supplies the starting PTE for the range to unload.
  1640. LastPte - Supplies the ending PTE for the range to unload.
  1641. Return Value:
  1642. None.
  1643. --*/
  1644. {
  1645. KIRQL OldIrql;
  1646. PMMPFN Pfn1;
  1647. PMMPFN Pfn2;
  1648. PFN_NUMBER PageFrameIndex;
  1649. LOCK_PFN (OldIrql);
  1650. while (PointerPte <= LastPte) {
  1651. if (PointerPte->u.Hard.Valid == 1) {
  1652. //
  1653. // Delete the page.
  1654. //
  1655. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte);
  1656. //
  1657. // Set the pointer to PTE as empty so the page
  1658. // is deleted when the reference count goes to zero.
  1659. //
  1660. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  1661. Pfn2 = MI_PFN_ELEMENT (Pfn1->u4.PteFrame);
  1662. MiDecrementShareCount (Pfn2, Pfn1->u4.PteFrame);
  1663. MI_SET_PFN_DELETED (Pfn1);
  1664. MiDecrementShareCount (Pfn1, PageFrameIndex);
  1665. MI_WRITE_INVALID_PTE (PointerPte, ZeroPte);
  1666. }
  1667. PointerPte += 1;
  1668. }
  1669. UNLOCK_PFN (OldIrql);
  1670. }
  1671. NTSTATUS
  1672. MiLoadImageSection (
  1673. IN OUT PSECTION *InputSectionPointer,
  1674. OUT PVOID *ImageBaseAddress,
  1675. IN PUNICODE_STRING ImageFileName,
  1676. IN ULONG LoadInSessionSpace,
  1677. IN PKLDR_DATA_TABLE_ENTRY FoundDataTableEntry
  1678. )
  1679. /*++
  1680. Routine Description:
  1681. This routine loads the specified image into the kernel part of the
  1682. address space.
  1683. Arguments:
  1684. InputSectionPointer - Supplies the section object for the image. This may
  1685. be replaced by a pagefile-backed section (for
  1686. protection purposes) for session images if it is
  1687. determined that the image section is concurrently
  1688. being accessed by a user app.
  1689. ImageBaseAddress - Returns the address that the image header is at.
  1690. ImageFileName - Supplies the full path name (including the image name)
  1691. of the image to load.
  1692. LoadInSessionSpace - Supplies nonzero to load this image in session space.
  1693. Each session gets a different copy of this driver with
  1694. pages shared as much as possible via copy on write.
  1695. Supplies zero if this image should be loaded in global
  1696. space.
  1697. FoundDataTableEntry - Supplies the loader data table entry if the image
  1698. has already been loaded. This can only happen for
  1699. session space. It means this driver has already
  1700. been loaded into a different session, so this session
  1701. still needs to map it.
  1702. Return Value:
  1703. Status of the operation.
  1704. --*/
  1705. {
  1706. KAPC_STATE ApcState;
  1707. PFN_NUMBER PagesRequired;
  1708. PFN_NUMBER ActualPagesUsed;
  1709. PSECTION SectionPointer;
  1710. PSECTION NewSectionPointer;
  1711. PVOID OpaqueSession;
  1712. PMMPTE ProtoPte;
  1713. PMMPTE FirstPte;
  1714. PMMPTE LastPte;
  1715. PMMPTE PointerPte;
  1716. PEPROCESS Process;
  1717. PEPROCESS TargetProcess;
  1718. ULONG NumberOfPtes;
  1719. MMPTE PteContents;
  1720. MMPTE TempPte;
  1721. PFN_NUMBER PageFrameIndex;
  1722. PVOID UserVa;
  1723. PVOID SystemVa;
  1724. NTSTATUS Status;
  1725. NTSTATUS ExceptionStatus;
  1726. PVOID Base;
  1727. ULONG_PTR ViewSize;
  1728. LARGE_INTEGER SectionOffset;
  1729. LOGICAL LoadSymbols;
  1730. PVOID BaseAddress;
  1731. PCONTROL_AREA ControlArea;
  1732. PSUBSECTION Subsection;
  1733. PAGED_CODE();
  1734. #if !DBG
  1735. UNREFERENCED_PARAMETER (ImageFileName);
  1736. #endif
  1737. SectionPointer = *InputSectionPointer;
  1738. NumberOfPtes = SectionPointer->Segment->TotalNumberOfPtes;
  1739. if (LoadInSessionSpace != 0) {
  1740. //
  1741. // Allocate a unique systemwide session space virtual address for
  1742. // the driver.
  1743. //
  1744. if (FoundDataTableEntry == NULL) {
  1745. Status = MiSessionWideReserveImageAddress (SectionPointer,
  1746. &BaseAddress,
  1747. &NewSectionPointer);
  1748. if (!NT_SUCCESS(Status)) {
  1749. return Status;
  1750. }
  1751. if (NewSectionPointer != NULL) {
  1752. SectionPointer = NewSectionPointer;
  1753. *InputSectionPointer = NewSectionPointer;
  1754. }
  1755. }
  1756. else {
  1757. BaseAddress = FoundDataTableEntry->DllBase;
  1758. }
  1759. #if DBG
  1760. if (NtGlobalFlag & FLG_SHOW_LDR_SNAPS) {
  1761. DbgPrint ("MM: MiLoadImageSection: Image %wZ, BasedAddress 0x%p, Allocated Session BaseAddress 0x%p\n",
  1762. ImageFileName,
  1763. SectionPointer->Segment->BasedAddress,
  1764. BaseAddress);
  1765. }
  1766. #endif
  1767. //
  1768. // Session images are mapped backed directly by the file image.
  1769. // All pristine pages of the image will be shared across all
  1770. // sessions, with each page treated as copy-on-write on first write.
  1771. //
  1772. // NOTE: This makes the file image "busy", a different behavior
  1773. // as normal kernel drivers are backed by the paging file only.
  1774. //
  1775. Status = MiShareSessionImage (BaseAddress, SectionPointer);
  1776. if (!NT_SUCCESS (Status)) {
  1777. MiRemoveImageSessionWide (FoundDataTableEntry,
  1778. BaseAddress,
  1779. NumberOfPtes << PAGE_SHIFT);
  1780. return Status;
  1781. }
  1782. *ImageBaseAddress = BaseAddress;
  1783. return Status;
  1784. }
  1785. ASSERT (FoundDataTableEntry == NULL);
  1786. //
  1787. // Calculate the number of pages required to load this image.
  1788. //
  1789. // Start out by charging for everything and subtract out any gap
  1790. // pages after the image loads successfully.
  1791. //
  1792. PagesRequired = NumberOfPtes;
  1793. ActualPagesUsed = 0;
  1794. //
  1795. // See if ample pages exist to load this image.
  1796. //
  1797. if (MiChargeResidentAvailable (PagesRequired, MM_RESAVAIL_ALLOCATE_LOAD_SYSTEM_IMAGE) == FALSE) {
  1798. return STATUS_INSUFFICIENT_RESOURCES;
  1799. }
  1800. //
  1801. // Reserve the necessary system address space.
  1802. //
  1803. FirstPte = MiReserveSystemPtes (NumberOfPtes, SystemPteSpace);
  1804. if (FirstPte == NULL) {
  1805. MI_INCREMENT_RESIDENT_AVAILABLE (PagesRequired,
  1806. MM_RESAVAIL_FREE_LOAD_SYSTEM_IMAGE1);
  1807. return STATUS_INSUFFICIENT_RESOURCES;
  1808. }
  1809. PointerPte = FirstPte;
  1810. SystemVa = MiGetVirtualAddressMappedByPte (PointerPte);
  1811. if (MiChargeCommitment (PagesRequired, NULL) == FALSE) {
  1812. MI_INCREMENT_RESIDENT_AVAILABLE (PagesRequired,
  1813. MM_RESAVAIL_FREE_LOAD_SYSTEM_IMAGE1);
  1814. MiReleaseSystemPtes (FirstPte, NumberOfPtes, SystemPteSpace);
  1815. return STATUS_INSUFFICIENT_RESOURCES;
  1816. }
  1817. MM_TRACK_COMMIT (MM_DBG_COMMIT_DRIVER_PAGES, PagesRequired);
  1818. InterlockedExchangeAdd ((PLONG)&MmDriverCommit, (LONG) PagesRequired);
  1819. //
  1820. // Map a view into the user portion of the address space.
  1821. //
  1822. Process = PsGetCurrentProcess ();
  1823. //
  1824. // Since callees are not always in the context of the system process,
  1825. // attach here when necessary to guarantee the driver load occurs in a
  1826. // known safe address space to prevent security holes.
  1827. //
  1828. OpaqueSession = NULL;
  1829. KeStackAttachProcess (&PsInitialSystemProcess->Pcb, &ApcState);
  1830. ZERO_LARGE (SectionOffset);
  1831. Base = NULL;
  1832. ViewSize = 0;
  1833. if (NtGlobalFlag & FLG_ENABLE_KDEBUG_SYMBOL_LOAD) {
  1834. LoadSymbols = TRUE;
  1835. NtGlobalFlag &= ~FLG_ENABLE_KDEBUG_SYMBOL_LOAD;
  1836. }
  1837. else {
  1838. LoadSymbols = FALSE;
  1839. }
  1840. TargetProcess = PsGetCurrentProcess ();
  1841. Status = MmMapViewOfSection (SectionPointer,
  1842. TargetProcess,
  1843. &Base,
  1844. 0,
  1845. 0,
  1846. &SectionOffset,
  1847. &ViewSize,
  1848. ViewUnmap,
  1849. 0,
  1850. PAGE_EXECUTE);
  1851. if (LoadSymbols) {
  1852. NtGlobalFlag |= FLG_ENABLE_KDEBUG_SYMBOL_LOAD;
  1853. }
  1854. if (Status == STATUS_IMAGE_MACHINE_TYPE_MISMATCH) {
  1855. Status = STATUS_INVALID_IMAGE_FORMAT;
  1856. }
  1857. if (!NT_SUCCESS(Status)) {
  1858. KeUnstackDetachProcess (&ApcState);
  1859. MI_INCREMENT_RESIDENT_AVAILABLE (PagesRequired,
  1860. MM_RESAVAIL_FREE_LOAD_SYSTEM_IMAGE2);
  1861. MiReleaseSystemPtes (FirstPte, NumberOfPtes, SystemPteSpace);
  1862. MiReturnCommitment (PagesRequired);
  1863. return Status;
  1864. }
  1865. //
  1866. // Allocate a physical page(s) and copy the image data.
  1867. // Note for session drivers, the physical pages have already
  1868. // been allocated and just data copying is done here.
  1869. //
  1870. ControlArea = SectionPointer->Segment->ControlArea;
  1871. if ((ControlArea->u.Flags.GlobalOnlyPerSession == 0) &&
  1872. (ControlArea->u.Flags.Rom == 0)) {
  1873. Subsection = (PSUBSECTION)(ControlArea + 1);
  1874. }
  1875. else {
  1876. Subsection = (PSUBSECTION)((PLARGE_CONTROL_AREA)ControlArea + 1);
  1877. }
  1878. ASSERT (Subsection->SubsectionBase != NULL);
  1879. ProtoPte = Subsection->SubsectionBase;
  1880. *ImageBaseAddress = SystemVa;
  1881. UserVa = Base;
  1882. TempPte = ValidKernelPte;
  1883. TempPte.u.Long |= MM_PTE_EXECUTE;
  1884. LastPte = ProtoPte + NumberOfPtes;
  1885. ExceptionStatus = STATUS_SUCCESS;
  1886. while (ProtoPte < LastPte) {
  1887. PteContents = *ProtoPte;
  1888. if ((PteContents.u.Hard.Valid == 1) ||
  1889. (PteContents.u.Soft.Protection != MM_NOACCESS)) {
  1890. ActualPagesUsed += 1;
  1891. PageFrameIndex = MiAllocatePfn (PointerPte, MM_EXECUTE);
  1892. TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
  1893. MI_WRITE_VALID_PTE (PointerPte, TempPte);
  1894. ASSERT (MI_PFN_ELEMENT (PageFrameIndex)->u1.WsIndex == 0);
  1895. try {
  1896. RtlCopyMemory (SystemVa, UserVa, PAGE_SIZE);
  1897. } except (MiMapCacheExceptionFilter (&ExceptionStatus,
  1898. GetExceptionInformation())) {
  1899. //
  1900. // An exception occurred, unmap the view and
  1901. // return the error to the caller.
  1902. //
  1903. #if DBG
  1904. DbgPrint("MiLoadImageSection: Exception 0x%x copying driver SystemVa 0x%p, UserVa 0x%p\n",ExceptionStatus,SystemVa,UserVa);
  1905. #endif
  1906. MiReturnFailedSessionPages (FirstPte, PointerPte);
  1907. MI_INCREMENT_RESIDENT_AVAILABLE (PagesRequired,
  1908. MM_RESAVAIL_FREE_LOAD_SYSTEM_IMAGE3);
  1909. MiReleaseSystemPtes (FirstPte, NumberOfPtes, SystemPteSpace);
  1910. MiReturnCommitment (PagesRequired);
  1911. Status = MiUnmapViewOfSection (TargetProcess, Base, FALSE);
  1912. ASSERT (NT_SUCCESS (Status));
  1913. //
  1914. // Purge the section as we want these pages on the freelist
  1915. // instead of at the tail of standby, as we're completely
  1916. // done with the section. This is because other valuable
  1917. // standby pages end up getting reused (especially during
  1918. // bootup) when the section pages are the ones that really
  1919. // will never be referenced again.
  1920. //
  1921. // Note this isn't done for session images as they're
  1922. // inpaged directly from the filesystem via the section.
  1923. //
  1924. MmPurgeSection (ControlArea->FilePointer->SectionObjectPointer,
  1925. NULL,
  1926. 0,
  1927. FALSE);
  1928. KeUnstackDetachProcess (&ApcState);
  1929. return ExceptionStatus;
  1930. }
  1931. }
  1932. else {
  1933. //
  1934. // The PTE is no access.
  1935. //
  1936. MI_WRITE_INVALID_PTE (PointerPte, ZeroKernelPte);
  1937. }
  1938. ProtoPte += 1;
  1939. PointerPte += 1;
  1940. SystemVa = ((PCHAR)SystemVa + PAGE_SIZE);
  1941. UserVa = ((PCHAR)UserVa + PAGE_SIZE);
  1942. }
  1943. Status = MiUnmapViewOfSection (TargetProcess, Base, FALSE);
  1944. ASSERT (NT_SUCCESS (Status));
  1945. //
  1946. // Purge the section as we want these pages on the freelist instead of
  1947. // at the tail of standby, as we're completely done with the section.
  1948. // This is because other valuable standby pages end up getting reused
  1949. // (especially during bootup) when the section pages are the ones that
  1950. // really will never be referenced again.
  1951. //
  1952. MmPurgeSection (ControlArea->FilePointer->SectionObjectPointer,
  1953. NULL,
  1954. 0,
  1955. FALSE);
  1956. KeUnstackDetachProcess (&ApcState);
  1957. //
  1958. // Return any excess resident available and commit.
  1959. //
  1960. if (PagesRequired != ActualPagesUsed) {
  1961. ASSERT (PagesRequired > ActualPagesUsed);
  1962. PagesRequired -= ActualPagesUsed;
  1963. MI_INCREMENT_RESIDENT_AVAILABLE (PagesRequired,
  1964. MM_RESAVAIL_FREE_LOAD_SYSTEM_IMAGE_EXCESS);
  1965. MiReturnCommitment (PagesRequired);
  1966. }
  1967. return Status;
  1968. }
  1969. VOID
  1970. MmFreeDriverInitialization (
  1971. IN PVOID ImageHandle
  1972. )
  1973. /*++
  1974. Routine Description:
  1975. This routine removes the pages that relocate and debug information from
  1976. the address space of the driver.
  1977. NOTE: This routine looks at the last sections defined in the image
  1978. header and if that section is marked as DISCARDABLE in the
  1979. characteristics, it is removed from the image. This means
  1980. that all discardable sections at the end of the driver are
  1981. deleted.
  1982. Arguments:
  1983. SectionObject - Supplies the section object for the image.
  1984. Return Value:
  1985. None.
  1986. --*/
  1987. {
  1988. PKLDR_DATA_TABLE_ENTRY DataTableEntry;
  1989. PMMPTE PointerPte;
  1990. PMMPTE LastPte;
  1991. PFN_NUMBER NumberOfPtes;
  1992. PVOID Base;
  1993. PVOID StartVa;
  1994. ULONG i;
  1995. PIMAGE_NT_HEADERS NtHeaders;
  1996. PIMAGE_SECTION_HEADER NtSection;
  1997. PIMAGE_SECTION_HEADER FoundSection;
  1998. PFN_NUMBER PagesDeleted;
  1999. #if 0
  2000. PFN_NUMBER PageFrameIndex;
  2001. PMMPFN Pfn1;
  2002. KIRQL OldIrql;
  2003. #endif
  2004. DataTableEntry = (PKLDR_DATA_TABLE_ENTRY)ImageHandle;
  2005. Base = DataTableEntry->DllBase;
  2006. ASSERT (MI_IS_SESSION_ADDRESS (Base) == FALSE);
  2007. NumberOfPtes = DataTableEntry->SizeOfImage >> PAGE_SHIFT;
  2008. LastPte = MiGetPteAddress (Base) + NumberOfPtes;
  2009. NtHeaders = (PIMAGE_NT_HEADERS) RtlImageNtHeader (Base);
  2010. if (NtHeaders == NULL) {
  2011. return;
  2012. }
  2013. NtSection = (PIMAGE_SECTION_HEADER)((PCHAR)NtHeaders +
  2014. sizeof(ULONG) +
  2015. sizeof(IMAGE_FILE_HEADER) +
  2016. NtHeaders->FileHeader.SizeOfOptionalHeader
  2017. );
  2018. NtSection += NtHeaders->FileHeader.NumberOfSections;
  2019. FoundSection = NULL;
  2020. for (i = 0; i < NtHeaders->FileHeader.NumberOfSections; i += 1) {
  2021. NtSection -= 1;
  2022. if ((NtSection->Characteristics & IMAGE_SCN_MEM_DISCARDABLE) != 0) {
  2023. FoundSection = NtSection;
  2024. }
  2025. else {
  2026. //
  2027. // There was a non discardable section between this
  2028. // section and the last non discardable section, don't
  2029. // discard this section and don't look any more.
  2030. //
  2031. break;
  2032. }
  2033. }
  2034. if (FoundSection != NULL) {
  2035. StartVa = (PVOID) (ROUND_TO_PAGES (
  2036. (PCHAR)Base + FoundSection->VirtualAddress));
  2037. PointerPte = MiGetPteAddress (StartVa);
  2038. NumberOfPtes = (PFN_NUMBER)(LastPte - PointerPte);
  2039. if (NumberOfPtes != 0) {
  2040. if (MI_IS_PHYSICAL_ADDRESS (StartVa)) {
  2041. //
  2042. // Don't free the INIT code for a driver mapped by large pages
  2043. // because if it unloads later, we'd have to deal with
  2044. // discontiguous ranges of pages to free.
  2045. //
  2046. return;
  2047. #if 0
  2048. PagesDeleted = NumberOfPtes;
  2049. LOCK_PFN (OldIrql);
  2050. while (NumberOfPtes != 0) {
  2051. //
  2052. // On certain architectures, virtual addresses
  2053. // may be physical and hence have no corresponding PTE.
  2054. //
  2055. PageFrameIndex = MI_CONVERT_PHYSICAL_TO_PFN (StartVa);
  2056. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  2057. Pfn1->u2.ShareCount = 0;
  2058. Pfn1->u3.e2.ReferenceCount = 0;
  2059. MI_SET_PFN_DELETED (Pfn1);
  2060. MiInsertPageInFreeList (PageFrameIndex);
  2061. StartVa = (PVOID)((PUCHAR)StartVa + PAGE_SIZE);
  2062. NumberOfPtes -= 1;
  2063. }
  2064. UNLOCK_PFN (OldIrql);
  2065. #endif
  2066. }
  2067. else {
  2068. PagesDeleted = MiDeleteSystemPagableVm (PointerPte,
  2069. NumberOfPtes,
  2070. ZeroKernelPte,
  2071. FALSE,
  2072. NULL);
  2073. }
  2074. MI_INCREMENT_RESIDENT_AVAILABLE (PagesDeleted,
  2075. MM_RESAVAIL_FREE_DRIVER_INITIALIZATION);
  2076. MiReturnCommitment (PagesDeleted);
  2077. MM_TRACK_COMMIT (MM_DBG_COMMIT_RETURN_DRIVER_INIT_CODE, PagesDeleted);
  2078. InterlockedExchangeAdd ((PLONG)&MmDriverCommit,
  2079. (LONG) (0 - PagesDeleted));
  2080. }
  2081. }
  2082. return;
  2083. }
  2084. LOGICAL
  2085. MiChargeResidentAvailable (
  2086. IN PFN_NUMBER NumberOfPages,
  2087. IN ULONG Id
  2088. )
  2089. /*++
  2090. Routine Description:
  2091. This routine is a nonpaged wrapper to charge resident available pages.
  2092. Arguments:
  2093. NumberOfPages - Supplies the number of pages to charge.
  2094. Id - Supplies a tracking ID for debugging purposes.
  2095. Return Value:
  2096. TRUE if the pages were charged, FALSE if not.
  2097. --*/
  2098. {
  2099. KIRQL OldIrql;
  2100. LOCK_PFN (OldIrql);
  2101. if (MI_NONPAGABLE_MEMORY_AVAILABLE() <= (SPFN_NUMBER)NumberOfPages) {
  2102. UNLOCK_PFN (OldIrql);
  2103. return FALSE;
  2104. }
  2105. MI_DECREMENT_RESIDENT_AVAILABLE (NumberOfPages, Id);
  2106. UNLOCK_PFN (OldIrql);
  2107. return TRUE;
  2108. }
  2109. VOID
  2110. MiFlushPteListFreePfns (
  2111. IN PMMPTE_FLUSH_LIST PteFlushList
  2112. )
  2113. /*++
  2114. Routine Description:
  2115. This routine flushes all the PTEs in the PTE flush list.
  2116. If the list has overflowed, the entire TB is flushed.
  2117. This routine also decrements the sharecounts on the relevant PFNs.
  2118. Arguments:
  2119. PteFlushList - Supplies a pointer to the list to be flushed.
  2120. Return Value:
  2121. None.
  2122. Environment:
  2123. Kernel mode, working set mutex held, APC_LEVEL.
  2124. --*/
  2125. {
  2126. ULONG i;
  2127. PFN_NUMBER PageFrameIndex;
  2128. PMMPFN Pfn;
  2129. PMMPTE PointerPte;
  2130. MMPTE TempPte;
  2131. MMPTE PreviousPte;
  2132. KIRQL OldIrql;
  2133. ASSERT (KeAreAllApcsDisabled () == TRUE);
  2134. ASSERT (PteFlushList->Count != 0);
  2135. //
  2136. // Put the PTEs in transition and decrement the number of
  2137. // valid PTEs within the containing page table page. Note
  2138. // that for a private page, the page table page is still
  2139. // needed because the page is in transition.
  2140. //
  2141. LOCK_PFN (OldIrql);
  2142. for (i = 0; i < PteFlushList->Count; i += 1) {
  2143. PointerPte = MiGetPteAddress (PteFlushList->FlushVa[i]);
  2144. //
  2145. // If session space were allowed, we'd have to call
  2146. // MI_FLUSH_ENTIRE_SESSION_TB (TRUE, TRUE) below because
  2147. // Session space has no ASN - flush the entire TB.
  2148. //
  2149. ASSERT (MI_IS_SESSION_IMAGE_ADDRESS (MiGetVirtualAddressMappedByPte (PointerPte)) == FALSE);
  2150. TempPte = *PointerPte;
  2151. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (&TempPte);
  2152. Pfn = MI_PFN_ELEMENT (PageFrameIndex);
  2153. MI_MAKE_VALID_PTE_TRANSITION (TempPte,
  2154. Pfn->OriginalPte.u.Soft.Protection);
  2155. PreviousPte = *PointerPte;
  2156. MI_WRITE_INVALID_PTE (PointerPte, TempPte);
  2157. MI_CAPTURE_DIRTY_BIT_TO_PFN (&PreviousPte, Pfn);
  2158. MiDecrementShareCount (Pfn, PageFrameIndex);
  2159. }
  2160. //
  2161. // Flush the relevant entries from the translation buffer.
  2162. //
  2163. MiFlushPteList (PteFlushList, TRUE);
  2164. UNLOCK_PFN (OldIrql);
  2165. PteFlushList->Count = 0;
  2166. return;
  2167. }
  2168. VOID
  2169. MiEnablePagingOfDriver (
  2170. IN PVOID ImageHandle
  2171. )
  2172. {
  2173. ULONG Span;
  2174. PKLDR_DATA_TABLE_ENTRY DataTableEntry;
  2175. PMMPTE LastPte;
  2176. PMMPTE PointerPte;
  2177. PVOID Base;
  2178. ULONG i;
  2179. PIMAGE_NT_HEADERS NtHeaders;
  2180. PIMAGE_SECTION_HEADER FoundSection;
  2181. PIMAGE_OPTIONAL_HEADER OptionalHeader;
  2182. //
  2183. // Don't page kernel mode code if customer does not want it paged.
  2184. //
  2185. if (MmDisablePagingExecutive & MM_SYSTEM_CODE_LOCKED_DOWN) {
  2186. return;
  2187. }
  2188. //
  2189. // If the driver has pagable code, make it paged.
  2190. //
  2191. DataTableEntry = (PKLDR_DATA_TABLE_ENTRY) ImageHandle;
  2192. Base = DataTableEntry->DllBase;
  2193. NtHeaders = (PIMAGE_NT_HEADERS) RtlImageNtHeader (Base);
  2194. if (NtHeaders == NULL) {
  2195. return;
  2196. }
  2197. OptionalHeader = (PIMAGE_OPTIONAL_HEADER)((PCHAR)NtHeaders +
  2198. #if defined (_WIN64)
  2199. FIELD_OFFSET (IMAGE_NT_HEADERS64, OptionalHeader)
  2200. #else
  2201. FIELD_OFFSET (IMAGE_NT_HEADERS32, OptionalHeader)
  2202. #endif
  2203. );
  2204. FoundSection = IMAGE_FIRST_SECTION (NtHeaders);
  2205. i = NtHeaders->FileHeader.NumberOfSections;
  2206. PointerPte = NULL;
  2207. //
  2208. // Initializing LastPte is not needed for correctness, but without it
  2209. // the compiler cannot compile this code W4 to check for use of
  2210. // uninitialized variables.
  2211. //
  2212. LastPte = NULL;
  2213. while (i > 0) {
  2214. #if DBG
  2215. if ((*(PULONG)FoundSection->Name == 'tini') ||
  2216. (*(PULONG)FoundSection->Name == 'egap')) {
  2217. DbgPrint("driver %wZ has lower case sections (init or pagexxx)\n",
  2218. &DataTableEntry->FullDllName);
  2219. }
  2220. #endif //DBG
  2221. //
  2222. // Mark as pagable any section which starts with the
  2223. // first 4 characters PAGE or .eda (for the .edata section).
  2224. //
  2225. if ((*(PULONG)FoundSection->Name == 'EGAP') ||
  2226. (*(PULONG)FoundSection->Name == 'ade.')) {
  2227. //
  2228. // This section is pagable, save away the start and end.
  2229. //
  2230. if (PointerPte == NULL) {
  2231. //
  2232. // Previous section was NOT pagable, get the start address.
  2233. //
  2234. PointerPte = MiGetPteAddress ((PVOID)(ROUND_TO_PAGES (
  2235. (PCHAR)Base + FoundSection->VirtualAddress)));
  2236. }
  2237. //
  2238. // Generally, SizeOfRawData is larger than VirtualSize for each
  2239. // section because it includes the padding to get to the subsection
  2240. // alignment boundary. However, if the image is linked with
  2241. // subsection alignment == native page alignment, the linker will
  2242. // have VirtualSize be much larger than SizeOfRawData because it
  2243. // will account for all the bss.
  2244. //
  2245. Span = FoundSection->SizeOfRawData;
  2246. if (Span < FoundSection->Misc.VirtualSize) {
  2247. Span = FoundSection->Misc.VirtualSize;
  2248. }
  2249. LastPte = MiGetPteAddress ((PCHAR)Base +
  2250. FoundSection->VirtualAddress +
  2251. (OptionalHeader->SectionAlignment - 1) +
  2252. Span - PAGE_SIZE);
  2253. }
  2254. else {
  2255. //
  2256. // This section is not pagable, if the previous section was
  2257. // pagable, enable it.
  2258. //
  2259. if (PointerPte != NULL) {
  2260. MiSetPagingOfDriver (PointerPte, LastPte);
  2261. PointerPte = NULL;
  2262. }
  2263. }
  2264. i -= 1;
  2265. FoundSection += 1;
  2266. }
  2267. if (PointerPte != NULL) {
  2268. MiSetPagingOfDriver (PointerPte, LastPte);
  2269. }
  2270. }
  2271. VOID
  2272. MiSetPagingOfDriver (
  2273. IN PMMPTE PointerPte,
  2274. IN PMMPTE LastPte
  2275. )
  2276. /*++
  2277. Routine Description:
  2278. This routine marks the specified range of PTEs as pagable.
  2279. Arguments:
  2280. PointerPte - Supplies the starting PTE.
  2281. LastPte - Supplies the ending PTE.
  2282. Return Value:
  2283. None.
  2284. Environment:
  2285. Kernel Mode, IRQL of APC_LEVEL or below.
  2286. This routine could be made PAGELK but it is a high frequency routine
  2287. so it is actually better to keep it nonpaged to avoid bringing in the
  2288. entire PAGELK section.
  2289. --*/
  2290. {
  2291. PVOID Base;
  2292. PFN_NUMBER PageCount;
  2293. PFN_NUMBER PageFrameIndex;
  2294. PMMPFN Pfn;
  2295. MMPTE_FLUSH_LIST PteFlushList;
  2296. PAGED_CODE ();
  2297. Base = MiGetVirtualAddressMappedByPte (PointerPte);
  2298. if (MI_IS_PHYSICAL_ADDRESS (Base)) {
  2299. //
  2300. // No need to lock physical addresses.
  2301. //
  2302. return;
  2303. }
  2304. ASSERT (MI_IS_SESSION_IMAGE_ADDRESS (Base) == FALSE);
  2305. PageCount = 0;
  2306. PteFlushList.Count = 0;
  2307. LOCK_WORKING_SET (&MmSystemCacheWs);
  2308. while (PointerPte <= LastPte) {
  2309. //
  2310. // Check to make sure this PTE has not already been
  2311. // made pagable (or deleted). It is pagable if it
  2312. // is not valid, or if the PFN database wsindex element
  2313. // is non zero.
  2314. //
  2315. if (PointerPte->u.Hard.Valid == 1) {
  2316. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte);
  2317. Pfn = MI_PFN_ELEMENT (PageFrameIndex);
  2318. ASSERT (Pfn->u2.ShareCount == 1);
  2319. //
  2320. // If the wsindex is nonzero, then this page is already pagable
  2321. // and has a WSLE entry. Ignore it here and let the trimmer
  2322. // take it if memory comes under pressure.
  2323. //
  2324. if (Pfn->u1.WsIndex == 0) {
  2325. //
  2326. // Original PTE may need to be set for drivers loaded
  2327. // via ntldr.
  2328. //
  2329. if (Pfn->OriginalPte.u.Long == 0) {
  2330. Pfn->OriginalPte.u.Long = MM_KERNEL_DEMAND_ZERO_PTE;
  2331. Pfn->OriginalPte.u.Soft.Protection |= MM_EXECUTE;
  2332. }
  2333. PteFlushList.FlushVa[PteFlushList.Count] = Base;
  2334. PteFlushList.Count += 1;
  2335. if (PteFlushList.Count == MM_MAXIMUM_FLUSH_COUNT) {
  2336. MiFlushPteListFreePfns (&PteFlushList);
  2337. }
  2338. PageCount += 1;
  2339. }
  2340. }
  2341. Base = (PVOID)((PCHAR)Base + PAGE_SIZE);
  2342. PointerPte += 1;
  2343. }
  2344. if (PteFlushList.Count != 0) {
  2345. MiFlushPteListFreePfns (&PteFlushList);
  2346. }
  2347. UNLOCK_WORKING_SET (&MmSystemCacheWs);
  2348. if (PageCount != 0) {
  2349. InterlockedExchangeAdd (&MmTotalSystemDriverPages, (LONG) PageCount);
  2350. MI_INCREMENT_RESIDENT_AVAILABLE (PageCount,
  2351. MM_RESAVAIL_FREE_SET_DRIVER_PAGING);
  2352. }
  2353. }
  2354. PVOID
  2355. MmPageEntireDriver (
  2356. IN PVOID AddressWithinSection
  2357. )
  2358. /*++
  2359. Routine Description:
  2360. This routine allows a driver to page out all of its code and
  2361. data regardless of the attributes of the various image sections.
  2362. Note, this routine can be called multiple times with no
  2363. intervening calls to MmResetDriverPaging.
  2364. Arguments:
  2365. AddressWithinSection - Supplies an address within the driver, e.g.
  2366. DriverEntry.
  2367. Return Value:
  2368. Base address of driver.
  2369. Environment:
  2370. Kernel mode, APC_LEVEL or below.
  2371. --*/
  2372. {
  2373. PKLDR_DATA_TABLE_ENTRY DataTableEntry;
  2374. PMMPTE FirstPte;
  2375. PMMPTE LastPte;
  2376. PVOID BaseAddress;
  2377. PAGED_CODE();
  2378. DataTableEntry = MiLookupDataTableEntry (AddressWithinSection, FALSE);
  2379. if (DataTableEntry == NULL) {
  2380. return NULL;
  2381. }
  2382. //
  2383. // Don't page kernel mode code if disabled via registry.
  2384. //
  2385. if (MmDisablePagingExecutive & MM_SYSTEM_CODE_LOCKED_DOWN) {
  2386. return DataTableEntry->DllBase;
  2387. }
  2388. if (DataTableEntry->SectionPointer != NULL) {
  2389. //
  2390. // Driver is mapped as an image (ie: session space), this is always
  2391. // pagable.
  2392. //
  2393. ASSERT (MI_IS_SESSION_IMAGE_ADDRESS (AddressWithinSection) == TRUE);
  2394. return DataTableEntry->DllBase;
  2395. }
  2396. //
  2397. // Force any active DPCs to clear the system before we page the driver.
  2398. //
  2399. KeFlushQueuedDpcs ();
  2400. BaseAddress = DataTableEntry->DllBase;
  2401. FirstPte = MiGetPteAddress (BaseAddress);
  2402. LastPte = (FirstPte - 1) + (DataTableEntry->SizeOfImage >> PAGE_SHIFT);
  2403. ASSERT (MI_IS_SESSION_IMAGE_ADDRESS (AddressWithinSection) == FALSE);
  2404. MiSetPagingOfDriver (FirstPte, LastPte);
  2405. return BaseAddress;
  2406. }
  2407. VOID
  2408. MmResetDriverPaging (
  2409. IN PVOID AddressWithinSection
  2410. )
  2411. /*++
  2412. Routine Description:
  2413. This routines resets the driver paging to what the image specified.
  2414. Hence image sections such as the IAT, .text, .data will be locked
  2415. down in memory.
  2416. Note, there is no requirement that MmPageEntireDriver was called.
  2417. Arguments:
  2418. AddressWithinSection - Supplies an address within the driver, e.g.
  2419. DriverEntry.
  2420. Return Value:
  2421. None.
  2422. Environment:
  2423. Kernel mode, APC_LEVEL or below.
  2424. --*/
  2425. {
  2426. ULONG Span;
  2427. PKLDR_DATA_TABLE_ENTRY DataTableEntry;
  2428. PMMPTE LastPte;
  2429. PMMPTE PointerPte;
  2430. PVOID Base;
  2431. ULONG i;
  2432. PIMAGE_NT_HEADERS NtHeaders;
  2433. PIMAGE_SECTION_HEADER FoundSection;
  2434. PAGED_CODE();
  2435. //
  2436. // Don't page kernel mode code if disabled via registry.
  2437. //
  2438. if (MmDisablePagingExecutive & MM_SYSTEM_CODE_LOCKED_DOWN) {
  2439. return;
  2440. }
  2441. if (MI_IS_PHYSICAL_ADDRESS (AddressWithinSection)) {
  2442. return;
  2443. }
  2444. //
  2445. // If the driver has pagable code, make it paged.
  2446. //
  2447. DataTableEntry = MiLookupDataTableEntry (AddressWithinSection, FALSE);
  2448. if (DataTableEntry->SectionPointer != NULL) {
  2449. //
  2450. // Driver is mapped by image hence already paged.
  2451. //
  2452. ASSERT (MI_IS_SESSION_IMAGE_ADDRESS (AddressWithinSection) == TRUE);
  2453. return;
  2454. }
  2455. Base = DataTableEntry->DllBase;
  2456. NtHeaders = (PIMAGE_NT_HEADERS) RtlImageNtHeader (Base);
  2457. if (NtHeaders == NULL) {
  2458. return;
  2459. }
  2460. FoundSection = (PIMAGE_SECTION_HEADER)((PCHAR)NtHeaders +
  2461. sizeof(ULONG) +
  2462. sizeof(IMAGE_FILE_HEADER) +
  2463. NtHeaders->FileHeader.SizeOfOptionalHeader
  2464. );
  2465. i = NtHeaders->FileHeader.NumberOfSections;
  2466. PointerPte = NULL;
  2467. while (i > 0) {
  2468. #if DBG
  2469. if ((*(PULONG)FoundSection->Name == 'tini') ||
  2470. (*(PULONG)FoundSection->Name == 'egap')) {
  2471. DbgPrint("driver %wZ has lower case sections (init or pagexxx)\n",
  2472. &DataTableEntry->FullDllName);
  2473. }
  2474. #endif
  2475. //
  2476. // Don't lock down code for sections marked as discardable or
  2477. // sections marked with the first 4 characters PAGE or .eda
  2478. // (for the .edata section) or INIT.
  2479. //
  2480. if (((FoundSection->Characteristics & IMAGE_SCN_MEM_DISCARDABLE) != 0) ||
  2481. (*(PULONG)FoundSection->Name == 'EGAP') ||
  2482. (*(PULONG)FoundSection->Name == 'ade.') ||
  2483. (*(PULONG)FoundSection->Name == 'TINI')) {
  2484. NOTHING;
  2485. }
  2486. else {
  2487. //
  2488. // This section is nonpagable.
  2489. //
  2490. PointerPte = MiGetPteAddress (
  2491. (PCHAR)Base + FoundSection->VirtualAddress);
  2492. //
  2493. // Generally, SizeOfRawData is larger than VirtualSize for each
  2494. // section because it includes the padding to get to the subsection
  2495. // alignment boundary. However, if the image is linked with
  2496. // subsection alignment == native page alignment, the linker will
  2497. // have VirtualSize be much larger than SizeOfRawData because it
  2498. // will account for all the bss.
  2499. //
  2500. Span = FoundSection->SizeOfRawData;
  2501. if (Span < FoundSection->Misc.VirtualSize) {
  2502. Span = FoundSection->Misc.VirtualSize;
  2503. }
  2504. LastPte = MiGetPteAddress ((PCHAR)Base +
  2505. FoundSection->VirtualAddress +
  2506. (Span - 1));
  2507. ASSERT (PointerPte <= LastPte);
  2508. MiLockCode (PointerPte, LastPte, MM_LOCK_BY_NONPAGE);
  2509. }
  2510. i -= 1;
  2511. FoundSection += 1;
  2512. }
  2513. return;
  2514. }
  2515. VOID
  2516. MiClearImports (
  2517. IN PKLDR_DATA_TABLE_ENTRY DataTableEntry
  2518. )
  2519. /*++
  2520. Routine Description:
  2521. Free up the import list and clear the pointer. This stops the
  2522. recursion performed in MiDereferenceImports().
  2523. Arguments:
  2524. DataTableEntry - provided for the driver.
  2525. Return Value:
  2526. Status of the import list construction operation.
  2527. --*/
  2528. {
  2529. PAGED_CODE();
  2530. if (DataTableEntry->LoadedImports == (PVOID)LOADED_AT_BOOT) {
  2531. return;
  2532. }
  2533. if (DataTableEntry->LoadedImports == (PVOID)NO_IMPORTS_USED) {
  2534. NOTHING;
  2535. }
  2536. else if (SINGLE_ENTRY(DataTableEntry->LoadedImports)) {
  2537. NOTHING;
  2538. }
  2539. else {
  2540. //
  2541. // free the memory
  2542. //
  2543. ExFreePool ((PVOID)DataTableEntry->LoadedImports);
  2544. }
  2545. //
  2546. // stop the recursion
  2547. //
  2548. DataTableEntry->LoadedImports = (PVOID)LOADED_AT_BOOT;
  2549. }
  2550. VOID
  2551. MiRememberUnloadedDriver (
  2552. IN PUNICODE_STRING DriverName,
  2553. IN PVOID Address,
  2554. IN ULONG Length
  2555. )
  2556. /*++
  2557. Routine Description:
  2558. This routine saves information about unloaded drivers so that ones that
  2559. forget to delete lookaside lists or queues can be caught.
  2560. Arguments:
  2561. DriverName - Supplies a Unicode string containing the driver's name.
  2562. Address - Supplies the address the driver was loaded at.
  2563. Length - Supplies the number of bytes the driver load spanned.
  2564. Return Value:
  2565. None.
  2566. --*/
  2567. {
  2568. PUNLOADED_DRIVERS Entry;
  2569. ULONG NumberOfBytes;
  2570. if (DriverName->Length == 0) {
  2571. //
  2572. // This is an aborted load and the driver name hasn't been filled
  2573. // in yet. No need to save it.
  2574. //
  2575. return;
  2576. }
  2577. //
  2578. // Serialization is provided by the caller, so just update the list now.
  2579. // Note the allocations are nonpaged so they can be searched at bugcheck
  2580. // time.
  2581. //
  2582. if (MmUnloadedDrivers == NULL) {
  2583. NumberOfBytes = MI_UNLOADED_DRIVERS * sizeof (UNLOADED_DRIVERS);
  2584. MmUnloadedDrivers = (PUNLOADED_DRIVERS)ExAllocatePoolWithTag (NonPagedPool,
  2585. NumberOfBytes,
  2586. 'TDmM');
  2587. if (MmUnloadedDrivers == NULL) {
  2588. return;
  2589. }
  2590. RtlZeroMemory (MmUnloadedDrivers, NumberOfBytes);
  2591. MmLastUnloadedDriver = 0;
  2592. }
  2593. else if (MmLastUnloadedDriver >= MI_UNLOADED_DRIVERS) {
  2594. MmLastUnloadedDriver = 0;
  2595. }
  2596. Entry = &MmUnloadedDrivers[MmLastUnloadedDriver];
  2597. //
  2598. // Free the old entry as we recycle into the new.
  2599. //
  2600. RtlFreeUnicodeString (&Entry->Name);
  2601. Entry->Name.Buffer = ExAllocatePoolWithTag (NonPagedPool,
  2602. DriverName->Length,
  2603. 'TDmM');
  2604. if (Entry->Name.Buffer == NULL) {
  2605. Entry->Name.MaximumLength = 0;
  2606. Entry->Name.Length = 0;
  2607. MiUnloadsSkipped += 1;
  2608. return;
  2609. }
  2610. RtlCopyMemory(Entry->Name.Buffer, DriverName->Buffer, DriverName->Length);
  2611. Entry->Name.Length = DriverName->Length;
  2612. Entry->Name.MaximumLength = DriverName->MaximumLength;
  2613. Entry->StartAddress = Address;
  2614. Entry->EndAddress = (PVOID)((PCHAR)Address + Length);
  2615. KeQuerySystemTime (&Entry->CurrentTime);
  2616. MiTotalUnloads += 1;
  2617. MmLastUnloadedDriver += 1;
  2618. }
  2619. PUNICODE_STRING
  2620. MmLocateUnloadedDriver (
  2621. IN PVOID VirtualAddress
  2622. )
  2623. /*++
  2624. Routine Description:
  2625. This routine attempts to find the specified virtual address in the
  2626. unloaded driver list.
  2627. Arguments:
  2628. VirtualAddress - Supplies a virtual address that might be within a driver
  2629. that has already unloaded.
  2630. Return Value:
  2631. A pointer to a Unicode string containing the unloaded driver's name.
  2632. Environment:
  2633. Kernel mode, bugcheck time.
  2634. --*/
  2635. {
  2636. PUNLOADED_DRIVERS Entry;
  2637. ULONG i;
  2638. ULONG Index;
  2639. //
  2640. // No serialization is needed because we've crashed.
  2641. //
  2642. if (MmUnloadedDrivers == NULL) {
  2643. return NULL;
  2644. }
  2645. Index = MmLastUnloadedDriver - 1;
  2646. for (i = 0; i < MI_UNLOADED_DRIVERS; i += 1) {
  2647. if (Index >= MI_UNLOADED_DRIVERS) {
  2648. Index = MI_UNLOADED_DRIVERS - 1;
  2649. }
  2650. Entry = &MmUnloadedDrivers[Index];
  2651. if (Entry->Name.Buffer != NULL) {
  2652. if ((VirtualAddress >= Entry->StartAddress) &&
  2653. (VirtualAddress < Entry->EndAddress)) {
  2654. return &Entry->Name;
  2655. }
  2656. }
  2657. Index -= 1;
  2658. }
  2659. return NULL;
  2660. }
  2661. NTSTATUS
  2662. MmUnloadSystemImage (
  2663. IN PVOID ImageHandle
  2664. )
  2665. /*++
  2666. Routine Description:
  2667. This routine unloads a previously loaded system image and returns
  2668. the allocated resources.
  2669. Arguments:
  2670. ImageHandle - Supplies a pointer to the section object of the
  2671. image to unload.
  2672. Return Value:
  2673. Various NTSTATUS codes.
  2674. Environment:
  2675. Kernel mode, APC_LEVEL or below, arbitrary process context.
  2676. --*/
  2677. {
  2678. PFN_NUMBER PageFrameIndex;
  2679. PKLDR_DATA_TABLE_ENTRY DataTableEntry;
  2680. PMMPTE LastPte;
  2681. PFN_NUMBER PagesRequired;
  2682. PFN_NUMBER ResidentPages;
  2683. PMMPTE PointerPte;
  2684. PFN_NUMBER NumberOfPtes;
  2685. PFN_NUMBER RoundedNumberOfPtes;
  2686. PVOID BasedAddress;
  2687. SIZE_T NumberOfBytes;
  2688. LOGICAL MustFree;
  2689. SIZE_T CommittedPages;
  2690. LOGICAL ViewDeleted;
  2691. PIMAGE_ENTRY_IN_SESSION DriverImage;
  2692. NTSTATUS Status;
  2693. PSECTION SectionPointer;
  2694. PKTHREAD CurrentThread;
  2695. ViewDeleted = FALSE;
  2696. DataTableEntry = (PKLDR_DATA_TABLE_ENTRY)ImageHandle;
  2697. BasedAddress = DataTableEntry->DllBase;
  2698. //
  2699. // Arbitrary process context so prevent suspend APCs now.
  2700. //
  2701. CurrentThread = KeGetCurrentThread ();
  2702. KeEnterCriticalRegionThread (CurrentThread);
  2703. KeWaitForSingleObject (&MmSystemLoadLock,
  2704. WrVirtualMemory,
  2705. KernelMode,
  2706. FALSE,
  2707. (PLARGE_INTEGER)NULL);
  2708. if (DataTableEntry->LoadedImports == (PVOID)LOADED_AT_BOOT) {
  2709. //
  2710. // Any driver loaded at boot that did not have its import list
  2711. // and LoadCount reconstructed cannot be unloaded because we don't
  2712. // know how many other drivers may be linked to it.
  2713. //
  2714. KeReleaseMutant (&MmSystemLoadLock, 1, FALSE, FALSE);
  2715. KeLeaveCriticalRegionThread (CurrentThread);
  2716. return STATUS_SUCCESS;
  2717. }
  2718. ASSERT (DataTableEntry->LoadCount != 0);
  2719. if (MI_IS_SESSION_IMAGE_ADDRESS (BasedAddress)) {
  2720. //
  2721. // A printer driver may be referenced multiple times for the
  2722. // same session space. Only unload the last reference.
  2723. //
  2724. DriverImage = MiSessionLookupImage (BasedAddress);
  2725. ASSERT (DriverImage);
  2726. ASSERT (DriverImage->ImageCountInThisSession);
  2727. DriverImage->ImageCountInThisSession -= 1;
  2728. if (DriverImage->ImageCountInThisSession != 0) {
  2729. KeReleaseMutant (&MmSystemLoadLock, 1, FALSE, FALSE);
  2730. KeLeaveCriticalRegionThread (CurrentThread);
  2731. return STATUS_SUCCESS;
  2732. }
  2733. //
  2734. // The reference count for this image has dropped to zero in this
  2735. // session, so we can delete this session's view of the image.
  2736. //
  2737. NumberOfBytes = DataTableEntry->SizeOfImage;
  2738. //
  2739. // Free the session space taken up by the image, unmapping it from
  2740. // the current VA space - note this does not remove page table pages
  2741. // from the session PageTables[]. Each data page is only freed
  2742. // if there are no other references to it (ie: from any other
  2743. // sessions).
  2744. //
  2745. PointerPte = MiGetPteAddress (BasedAddress);
  2746. LastPte = MiGetPteAddress ((PVOID)((ULONG_PTR)BasedAddress + NumberOfBytes));
  2747. PagesRequired = MiDeleteSystemPagableVm (PointerPte,
  2748. (PFN_NUMBER)(LastPte - PointerPte),
  2749. ZeroKernelPte,
  2750. TRUE,
  2751. &ResidentPages);
  2752. //
  2753. // Note resident available is returned here without waiting for load
  2754. // count to reach zero because it is charged each time a session space
  2755. // driver locks down its code or data regardless of whether it is really
  2756. // the same copy-on-write backing page(s) that some other session has
  2757. // already locked down.
  2758. //
  2759. MI_INCREMENT_RESIDENT_AVAILABLE (ResidentPages,
  2760. MM_RESAVAIL_FREE_UNLOAD_SYSTEM_IMAGE);
  2761. SectionPointer = (PSECTION) DataTableEntry->SectionPointer;
  2762. ASSERT (SectionPointer != NULL);
  2763. ASSERT (SectionPointer->Segment->u1.ImageCommitment != 0);
  2764. if (BasedAddress != SectionPointer->Segment->BasedAddress) {
  2765. CommittedPages = SectionPointer->Segment->TotalNumberOfPtes;
  2766. }
  2767. else {
  2768. CommittedPages = SectionPointer->Segment->u1.ImageCommitment;
  2769. }
  2770. InterlockedExchangeAddSizeT (&MmSessionSpace->CommittedPages,
  2771. 0 - CommittedPages);
  2772. MM_BUMP_SESS_COUNTER(MM_DBG_SESSION_COMMIT_IMAGE_UNLOAD,
  2773. (ULONG)CommittedPages);
  2774. ViewDeleted = TRUE;
  2775. //
  2776. // Return the commitment we took out on the pagefile when the
  2777. // image was allocated.
  2778. //
  2779. MiReturnCommitment (CommittedPages);
  2780. MM_TRACK_COMMIT (MM_DBG_COMMIT_RETURN_DRIVER_UNLOAD, CommittedPages);
  2781. //
  2782. // Tell the session space image handler that we are releasing
  2783. // our claim to the image.
  2784. //
  2785. ASSERT (DataTableEntry->LoadCount != 0);
  2786. MiRemoveImageSessionWide (DataTableEntry,
  2787. BasedAddress,
  2788. DataTableEntry->SizeOfImage);
  2789. ASSERT (MiSessionLookupImage (BasedAddress) == NULL);
  2790. }
  2791. ASSERT (DataTableEntry->LoadCount != 0);
  2792. DataTableEntry->LoadCount -= 1;
  2793. if (DataTableEntry->LoadCount != 0) {
  2794. KeReleaseMutant (&MmSystemLoadLock, 1, FALSE, FALSE);
  2795. KeLeaveCriticalRegionThread (CurrentThread);
  2796. return STATUS_SUCCESS;
  2797. }
  2798. if (MmSnapUnloads) {
  2799. #if 0
  2800. PVOID StillQueued;
  2801. StillQueued = KeCheckForTimer (DataTableEntry->DllBase,
  2802. DataTableEntry->SizeOfImage);
  2803. if (StillQueued != NULL) {
  2804. KeBugCheckEx (DRIVER_VERIFIER_DETECTED_VIOLATION,
  2805. 0x18,
  2806. (ULONG_PTR)StillQueued,
  2807. (ULONG_PTR)-1,
  2808. (ULONG_PTR)DataTableEntry->DllBase);
  2809. }
  2810. StillQueued = ExpCheckForResource (DataTableEntry->DllBase,
  2811. DataTableEntry->SizeOfImage);
  2812. if (StillQueued != NULL) {
  2813. KeBugCheckEx (DRIVER_VERIFIER_DETECTED_VIOLATION,
  2814. 0x19,
  2815. (ULONG_PTR)StillQueued,
  2816. (ULONG_PTR)-1,
  2817. (ULONG_PTR)DataTableEntry->DllBase);
  2818. }
  2819. #endif
  2820. }
  2821. if (MmVerifierData.Level & DRIVER_VERIFIER_DEADLOCK_DETECTION) {
  2822. VerifierDeadlockFreePool (DataTableEntry->DllBase, DataTableEntry->SizeOfImage);
  2823. }
  2824. if (DataTableEntry->Flags & LDRP_IMAGE_VERIFYING) {
  2825. MiVerifyingDriverUnloading (DataTableEntry);
  2826. }
  2827. if (MiActiveVerifierThunks != 0) {
  2828. MiVerifierCheckThunks (DataTableEntry);
  2829. }
  2830. //
  2831. // Unload symbols from debugger.
  2832. //
  2833. if (DataTableEntry->Flags & LDRP_DEBUG_SYMBOLS_LOADED) {
  2834. //
  2835. // TEMP TEMP TEMP rip out when debugger converted
  2836. //
  2837. ANSI_STRING AnsiName;
  2838. Status = RtlUnicodeStringToAnsiString (&AnsiName,
  2839. &DataTableEntry->BaseDllName,
  2840. TRUE);
  2841. if (NT_SUCCESS (Status)) {
  2842. DbgUnLoadImageSymbols (&AnsiName, BasedAddress, (ULONG)-1);
  2843. RtlFreeAnsiString (&AnsiName);
  2844. }
  2845. }
  2846. //
  2847. // No unload can happen till after Mm has finished Phase 1 initialization.
  2848. // Therefore, large pages are already in effect (if this platform supports
  2849. // it).
  2850. //
  2851. if (ViewDeleted == FALSE) {
  2852. NumberOfPtes = DataTableEntry->SizeOfImage >> PAGE_SHIFT;
  2853. if (MmSnapUnloads) {
  2854. MiRememberUnloadedDriver (&DataTableEntry->BaseDllName,
  2855. BasedAddress,
  2856. (ULONG)(NumberOfPtes << PAGE_SHIFT));
  2857. }
  2858. if (DataTableEntry->Flags & LDRP_SYSTEM_MAPPED) {
  2859. if (MI_PDE_MAPS_LARGE_PAGE (MiGetPdeAddress (BasedAddress))) {
  2860. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (MiGetPdeAddress (BasedAddress)) + MiGetPteOffset (BasedAddress);
  2861. RoundedNumberOfPtes = MI_ROUND_TO_SIZE (NumberOfPtes,
  2862. MM_MINIMUM_VA_FOR_LARGE_PAGE >> PAGE_SHIFT);
  2863. MiUnmapLargePages (BasedAddress,
  2864. RoundedNumberOfPtes << PAGE_SHIFT);
  2865. //
  2866. // MiFreeContiguousPages is going to return commitment
  2867. // and resident available so don't do it here.
  2868. //
  2869. MiRemoveCachedRange (PageFrameIndex, PageFrameIndex + RoundedNumberOfPtes - 1);
  2870. MiFreeContiguousPages (PageFrameIndex, NumberOfPtes);
  2871. PagesRequired = NumberOfPtes;
  2872. }
  2873. else {
  2874. PointerPte = MiGetPteAddress (BasedAddress);
  2875. PagesRequired = MiDeleteSystemPagableVm (PointerPte,
  2876. NumberOfPtes,
  2877. ZeroKernelPte,
  2878. FALSE,
  2879. &ResidentPages);
  2880. //
  2881. // Note that drivers loaded at boot that have not been relocated
  2882. // have no system PTEs or commit charged.
  2883. //
  2884. MiReleaseSystemPtes (PointerPte,
  2885. (ULONG)NumberOfPtes,
  2886. SystemPteSpace);
  2887. MI_INCREMENT_RESIDENT_AVAILABLE (ResidentPages,
  2888. MM_RESAVAIL_FREE_UNLOAD_SYSTEM_IMAGE1);
  2889. MiReturnCommitment (PagesRequired);
  2890. MM_TRACK_COMMIT (MM_DBG_COMMIT_RETURN_DRIVER_UNLOAD1, PagesRequired);
  2891. InterlockedExchangeAdd (&MmTotalSystemDriverPages,
  2892. 0 - (ULONG)(PagesRequired - ResidentPages));
  2893. }
  2894. if (DataTableEntry->SectionPointer != NULL) {
  2895. InterlockedExchangeAdd ((PLONG)&MmDriverCommit,
  2896. (LONG) (0 - PagesRequired));
  2897. }
  2898. }
  2899. else {
  2900. //
  2901. // This must be a boot driver that was not relocated into
  2902. // system PTEs. If large or super pages are enabled, the
  2903. // image pages must be freed without referencing the
  2904. // non-existent page table pages. If large/super pages are
  2905. // not enabled, note that system PTEs were not used to map the
  2906. // image and thus, cannot be freed.
  2907. //
  2908. // This is further complicated by the fact that the INIT and/or
  2909. // discardable portions of these images may have already been freed.
  2910. //
  2911. MI_INCREMENT_RESIDENT_AVAILABLE (NumberOfPtes,
  2912. MM_RESAVAIL_FREE_UNLOAD_SYSTEM_IMAGE1);
  2913. MiReturnCommitment (NumberOfPtes);
  2914. MM_TRACK_COMMIT (MM_DBG_COMMIT_RETURN_DRIVER_UNLOAD1, NumberOfPtes);
  2915. }
  2916. }
  2917. //
  2918. // Search the loaded module list for the data table entry that describes
  2919. // the DLL that was just unloaded. It is possible an entry is not in the
  2920. // list if a failure occurred at a point in loading the DLL just before
  2921. // the data table entry was generated.
  2922. //
  2923. if (DataTableEntry->InLoadOrderLinks.Flink != NULL) {
  2924. MiProcessLoaderEntry (DataTableEntry, FALSE);
  2925. MustFree = TRUE;
  2926. }
  2927. else {
  2928. MustFree = FALSE;
  2929. }
  2930. //
  2931. // Handle unloading of any dependent DLLs that we loaded automatically
  2932. // for this image.
  2933. //
  2934. MiDereferenceImports ((PLOAD_IMPORTS)DataTableEntry->LoadedImports);
  2935. MiClearImports (DataTableEntry);
  2936. //
  2937. // Free this loader entry.
  2938. //
  2939. if (MustFree == TRUE) {
  2940. if (DataTableEntry->FullDllName.Buffer != NULL) {
  2941. ExFreePool (DataTableEntry->FullDllName.Buffer);
  2942. }
  2943. //
  2944. // Dereference the section object (session images only).
  2945. //
  2946. if (DataTableEntry->SectionPointer != NULL) {
  2947. ObDereferenceObject (DataTableEntry->SectionPointer);
  2948. }
  2949. if (DataTableEntry->PatchInformation) {
  2950. MiRundownHotpatchList (DataTableEntry->PatchInformation);
  2951. }
  2952. ExFreePool (DataTableEntry);
  2953. }
  2954. KeReleaseMutant (&MmSystemLoadLock, 1, FALSE, FALSE);
  2955. KeLeaveCriticalRegionThread (CurrentThread);
  2956. PERFINFO_IMAGE_UNLOAD(BasedAddress);
  2957. return STATUS_SUCCESS;
  2958. }
  2959. NTSTATUS
  2960. MiBuildImportsForBootDrivers (
  2961. VOID
  2962. )
  2963. /*++
  2964. Routine Description:
  2965. Construct an import list chain for boot-loaded drivers.
  2966. If this cannot be done for an entry, its chain is set to LOADED_AT_BOOT.
  2967. If a chain can be successfully built, then this driver's DLLs
  2968. will be automatically unloaded if this driver goes away (provided
  2969. no other driver is also using them). Otherwise, on driver unload,
  2970. its dependent DLLs would have to be explicitly unloaded.
  2971. Note that the incoming LoadCount values are not correct and thus, they
  2972. are reinitialized here.
  2973. Arguments:
  2974. None.
  2975. Return Value:
  2976. Various NTSTATUS codes.
  2977. --*/
  2978. {
  2979. PKLDR_DATA_TABLE_ENTRY DataTableEntry;
  2980. PLIST_ENTRY NextEntry;
  2981. PKLDR_DATA_TABLE_ENTRY DataTableEntry2;
  2982. PLIST_ENTRY NextEntry2;
  2983. ULONG i;
  2984. ULONG j;
  2985. ULONG ImageCount;
  2986. PVOID *ImageReferences;
  2987. PVOID LastImageReference;
  2988. PULONG_PTR ImportThunk;
  2989. ULONG_PTR BaseAddress;
  2990. ULONG_PTR LastAddress;
  2991. ULONG ImportSize;
  2992. ULONG ImportListSize;
  2993. PLOAD_IMPORTS ImportList;
  2994. LOGICAL UndoEverything;
  2995. PKLDR_DATA_TABLE_ENTRY KernelDataTableEntry;
  2996. PKLDR_DATA_TABLE_ENTRY HalDataTableEntry;
  2997. UNICODE_STRING KernelString;
  2998. UNICODE_STRING HalString;
  2999. PAGED_CODE();
  3000. ImageCount = 0;
  3001. KernelDataTableEntry = NULL;
  3002. HalDataTableEntry = NULL;
  3003. #define KERNEL_NAME L"ntoskrnl.exe"
  3004. KernelString.Buffer = (const PUSHORT) KERNEL_NAME;
  3005. KernelString.Length = sizeof (KERNEL_NAME) - sizeof (WCHAR);
  3006. KernelString.MaximumLength = sizeof KERNEL_NAME;
  3007. #define HAL_NAME L"hal.dll"
  3008. HalString.Buffer = (const PUSHORT) HAL_NAME;
  3009. HalString.Length = sizeof (HAL_NAME) - sizeof (WCHAR);
  3010. HalString.MaximumLength = sizeof HAL_NAME;
  3011. NextEntry = PsLoadedModuleList.Flink;
  3012. while (NextEntry != &PsLoadedModuleList) {
  3013. DataTableEntry = CONTAINING_RECORD(NextEntry,
  3014. KLDR_DATA_TABLE_ENTRY,
  3015. InLoadOrderLinks);
  3016. if (RtlEqualUnicodeString (&KernelString,
  3017. &DataTableEntry->BaseDllName,
  3018. TRUE)) {
  3019. KernelDataTableEntry = CONTAINING_RECORD(NextEntry,
  3020. KLDR_DATA_TABLE_ENTRY,
  3021. InLoadOrderLinks);
  3022. }
  3023. else if (RtlEqualUnicodeString (&HalString,
  3024. &DataTableEntry->BaseDllName,
  3025. TRUE)) {
  3026. HalDataTableEntry = CONTAINING_RECORD(NextEntry,
  3027. KLDR_DATA_TABLE_ENTRY,
  3028. InLoadOrderLinks);
  3029. }
  3030. //
  3031. // Initialize these properly so error recovery is simplified.
  3032. //
  3033. if (DataTableEntry->Flags & LDRP_DRIVER_DEPENDENT_DLL) {
  3034. if ((DataTableEntry == HalDataTableEntry) || (DataTableEntry == KernelDataTableEntry)) {
  3035. DataTableEntry->LoadCount = 1;
  3036. }
  3037. else {
  3038. DataTableEntry->LoadCount = 0;
  3039. }
  3040. }
  3041. else {
  3042. DataTableEntry->LoadCount = 1;
  3043. }
  3044. DataTableEntry->LoadedImports = (PVOID)LOADED_AT_BOOT;
  3045. ImageCount += 1;
  3046. NextEntry = NextEntry->Flink;
  3047. }
  3048. if (KernelDataTableEntry == NULL || HalDataTableEntry == NULL) {
  3049. return STATUS_NOT_FOUND;
  3050. }
  3051. ImageReferences = (PVOID *) ExAllocatePoolWithTag (PagedPool | POOL_COLD_ALLOCATION,
  3052. ImageCount * sizeof (PVOID),
  3053. 'TDmM');
  3054. if (ImageReferences == NULL) {
  3055. return STATUS_INSUFFICIENT_RESOURCES;
  3056. }
  3057. UndoEverything = FALSE;
  3058. NextEntry = PsLoadedModuleList.Flink;
  3059. for ( ; NextEntry != &PsLoadedModuleList; NextEntry = NextEntry->Flink) {
  3060. DataTableEntry = CONTAINING_RECORD (NextEntry,
  3061. KLDR_DATA_TABLE_ENTRY,
  3062. InLoadOrderLinks);
  3063. ImportThunk = (PULONG_PTR)RtlImageDirectoryEntryToData (
  3064. DataTableEntry->DllBase,
  3065. TRUE,
  3066. IMAGE_DIRECTORY_ENTRY_IAT,
  3067. &ImportSize);
  3068. if (ImportThunk == NULL) {
  3069. DataTableEntry->LoadedImports = NO_IMPORTS_USED;
  3070. continue;
  3071. }
  3072. RtlZeroMemory (ImageReferences, ImageCount * sizeof (PVOID));
  3073. ImportSize /= sizeof(PULONG_PTR);
  3074. BaseAddress = 0;
  3075. //
  3076. // Initializing these locals is not needed for correctness, but
  3077. // without it the compiler cannot compile this code W4 to check
  3078. // for use of uninitialized variables.
  3079. //
  3080. j = 0;
  3081. LastAddress = 0;
  3082. for (i = 0; i < ImportSize; i += 1, ImportThunk += 1) {
  3083. //
  3084. // Check the hint first.
  3085. //
  3086. if (BaseAddress != 0) {
  3087. if (*ImportThunk >= BaseAddress && *ImportThunk < LastAddress) {
  3088. ASSERT (ImageReferences[j]);
  3089. continue;
  3090. }
  3091. }
  3092. j = 0;
  3093. NextEntry2 = PsLoadedModuleList.Flink;
  3094. while (NextEntry2 != &PsLoadedModuleList) {
  3095. DataTableEntry2 = CONTAINING_RECORD(NextEntry2,
  3096. KLDR_DATA_TABLE_ENTRY,
  3097. InLoadOrderLinks);
  3098. BaseAddress = (ULONG_PTR) DataTableEntry2->DllBase;
  3099. LastAddress = BaseAddress + DataTableEntry2->SizeOfImage;
  3100. if (*ImportThunk >= BaseAddress && *ImportThunk < LastAddress) {
  3101. ImageReferences[j] = DataTableEntry2;
  3102. break;
  3103. }
  3104. NextEntry2 = NextEntry2->Flink;
  3105. j += 1;
  3106. }
  3107. if (*ImportThunk < BaseAddress || *ImportThunk >= LastAddress) {
  3108. if (*ImportThunk) {
  3109. #if DBG
  3110. DbgPrint ("MM: broken import linkage %p %p %p\n",
  3111. DataTableEntry,
  3112. ImportThunk,
  3113. *ImportThunk);
  3114. DbgBreakPoint ();
  3115. #endif
  3116. UndoEverything = TRUE;
  3117. goto finished;
  3118. }
  3119. BaseAddress = 0;
  3120. }
  3121. }
  3122. ImportSize = 0;
  3123. //
  3124. // Initializing LastImageReference is not needed for correctness, but
  3125. // without it the compiler cannot compile this code W4 to check
  3126. // for use of uninitialized variables.
  3127. //
  3128. LastImageReference = NULL;
  3129. for (i = 0; i < ImageCount; i += 1) {
  3130. if ((ImageReferences[i] != NULL) &&
  3131. (ImageReferences[i] != KernelDataTableEntry) &&
  3132. (ImageReferences[i] != HalDataTableEntry)) {
  3133. LastImageReference = ImageReferences[i];
  3134. ImportSize += 1;
  3135. }
  3136. }
  3137. if (ImportSize == 0) {
  3138. DataTableEntry->LoadedImports = NO_IMPORTS_USED;
  3139. }
  3140. else if (ImportSize == 1) {
  3141. #if DBG_SYSLOAD
  3142. DbgPrint("driver %wZ imports %wZ\n",
  3143. &DataTableEntry->FullDllName,
  3144. &((PKLDR_DATA_TABLE_ENTRY)LastImageReference)->FullDllName);
  3145. #endif
  3146. DataTableEntry->LoadedImports = POINTER_TO_SINGLE_ENTRY (LastImageReference);
  3147. ((PKLDR_DATA_TABLE_ENTRY)LastImageReference)->LoadCount += 1;
  3148. }
  3149. else {
  3150. #if DBG_SYSLOAD
  3151. DbgPrint("driver %wZ imports many\n", &DataTableEntry->FullDllName);
  3152. #endif
  3153. ImportListSize = ImportSize * sizeof(PVOID) + sizeof(SIZE_T);
  3154. ImportList = (PLOAD_IMPORTS) ExAllocatePoolWithTag (PagedPool | POOL_COLD_ALLOCATION,
  3155. ImportListSize,
  3156. 'TDmM');
  3157. if (ImportList == NULL) {
  3158. UndoEverything = TRUE;
  3159. break;
  3160. }
  3161. ImportList->Count = ImportSize;
  3162. j = 0;
  3163. for (i = 0; i < ImageCount; i += 1) {
  3164. if ((ImageReferences[i] != NULL) &&
  3165. (ImageReferences[i] != KernelDataTableEntry) &&
  3166. (ImageReferences[i] != HalDataTableEntry)) {
  3167. #if DBG_SYSLOAD
  3168. DbgPrint("driver %wZ imports %wZ\n",
  3169. &DataTableEntry->FullDllName,
  3170. &((PKLDR_DATA_TABLE_ENTRY)ImageReferences[i])->FullDllName);
  3171. #endif
  3172. ImportList->Entry[j] = ImageReferences[i];
  3173. ((PKLDR_DATA_TABLE_ENTRY)ImageReferences[i])->LoadCount += 1;
  3174. j += 1;
  3175. }
  3176. }
  3177. ASSERT (j == ImportSize);
  3178. DataTableEntry->LoadedImports = ImportList;
  3179. }
  3180. #if DBG_SYSLOAD
  3181. DbgPrint("\n");
  3182. #endif
  3183. }
  3184. finished:
  3185. ExFreePool ((PVOID)ImageReferences);
  3186. //
  3187. // The kernel and HAL are never unloaded.
  3188. //
  3189. if ((KernelDataTableEntry->LoadedImports != NO_IMPORTS_USED) &&
  3190. (!POINTER_TO_SINGLE_ENTRY(KernelDataTableEntry->LoadedImports))) {
  3191. ExFreePool ((PVOID)KernelDataTableEntry->LoadedImports);
  3192. }
  3193. if ((HalDataTableEntry->LoadedImports != NO_IMPORTS_USED) &&
  3194. (!POINTER_TO_SINGLE_ENTRY(HalDataTableEntry->LoadedImports))) {
  3195. ExFreePool ((PVOID)HalDataTableEntry->LoadedImports);
  3196. }
  3197. KernelDataTableEntry->LoadedImports = (PVOID)LOADED_AT_BOOT;
  3198. HalDataTableEntry->LoadedImports = (PVOID)LOADED_AT_BOOT;
  3199. if (UndoEverything == TRUE) {
  3200. #if DBG_SYSLOAD
  3201. DbgPrint("driver %wZ import rebuild failed\n",
  3202. &DataTableEntry->FullDllName);
  3203. DbgBreakPoint();
  3204. #endif
  3205. //
  3206. // An error occurred and this is an all or nothing operation so
  3207. // roll everything back.
  3208. //
  3209. NextEntry = PsLoadedModuleList.Flink;
  3210. while (NextEntry != &PsLoadedModuleList) {
  3211. DataTableEntry = CONTAINING_RECORD(NextEntry,
  3212. KLDR_DATA_TABLE_ENTRY,
  3213. InLoadOrderLinks);
  3214. ImportList = DataTableEntry->LoadedImports;
  3215. if (ImportList == LOADED_AT_BOOT || ImportList == NO_IMPORTS_USED ||
  3216. SINGLE_ENTRY(ImportList)) {
  3217. NOTHING;
  3218. }
  3219. else {
  3220. ExFreePool (ImportList);
  3221. }
  3222. DataTableEntry->LoadedImports = (PVOID)LOADED_AT_BOOT;
  3223. DataTableEntry->LoadCount = 1;
  3224. NextEntry = NextEntry->Flink;
  3225. }
  3226. return STATUS_INSUFFICIENT_RESOURCES;
  3227. }
  3228. return STATUS_SUCCESS;
  3229. }
  3230. LOGICAL
  3231. MiCallDllUnloadAndUnloadDll(
  3232. IN PKLDR_DATA_TABLE_ENTRY DataTableEntry
  3233. )
  3234. /*++
  3235. Routine Description:
  3236. All the references from other drivers to this DLL have been cleared.
  3237. The only remaining issue is that this DLL must support being unloaded.
  3238. This means having no outstanding DPCs, allocated pool, etc.
  3239. If the DLL has an unload routine that returns SUCCESS, then we clean
  3240. it up and free up its memory now.
  3241. Note this routine is NEVER called for drivers - only for DLLs that were
  3242. loaded due to import references from various drivers.
  3243. Arguments:
  3244. DataTableEntry - provided for the DLL.
  3245. Return Value:
  3246. TRUE if the DLL was successfully unloaded, FALSE if not.
  3247. --*/
  3248. {
  3249. PMM_DLL_UNLOAD Func;
  3250. NTSTATUS Status;
  3251. LOGICAL Unloaded;
  3252. PAGED_CODE();
  3253. Unloaded = FALSE;
  3254. Func = (PMM_DLL_UNLOAD) (ULONG_PTR) MiLocateExportName (DataTableEntry->DllBase, "DllUnload");
  3255. if (Func) {
  3256. //
  3257. // The unload function was found in the DLL so unload it now.
  3258. //
  3259. Status = Func();
  3260. if (NT_SUCCESS(Status)) {
  3261. //
  3262. // Set up the reference count so the import DLL looks like a regular
  3263. // driver image is being unloaded.
  3264. //
  3265. ASSERT (DataTableEntry->LoadCount == 0);
  3266. DataTableEntry->LoadCount = 1;
  3267. MmUnloadSystemImage ((PVOID)DataTableEntry);
  3268. Unloaded = TRUE;
  3269. }
  3270. }
  3271. return Unloaded;
  3272. }
  3273. PVOID
  3274. MiLocateExportName (
  3275. IN PVOID DllBase,
  3276. IN PCHAR FunctionName
  3277. )
  3278. /*++
  3279. Routine Description:
  3280. This function is invoked to locate a function name in an export directory.
  3281. Arguments:
  3282. DllBase - Supplies the image base.
  3283. FunctionName - Supplies the the name to be located.
  3284. Return Value:
  3285. The address of the located function or NULL.
  3286. --*/
  3287. {
  3288. PVOID Func;
  3289. PULONG NameTableBase;
  3290. PUSHORT NameOrdinalTableBase;
  3291. PIMAGE_EXPORT_DIRECTORY ExportDirectory;
  3292. PULONG Addr;
  3293. ULONG ExportSize;
  3294. ULONG Low;
  3295. ULONG Middle;
  3296. ULONG High;
  3297. LONG Result;
  3298. USHORT OrdinalNumber;
  3299. PAGED_CODE();
  3300. Func = NULL;
  3301. //
  3302. // Locate the DLL's export directory.
  3303. //
  3304. ExportDirectory = (PIMAGE_EXPORT_DIRECTORY) RtlImageDirectoryEntryToData (
  3305. DllBase,
  3306. TRUE,
  3307. IMAGE_DIRECTORY_ENTRY_EXPORT,
  3308. &ExportSize);
  3309. if (ExportDirectory) {
  3310. NameTableBase = (PULONG)((PCHAR)DllBase + (ULONG)ExportDirectory->AddressOfNames);
  3311. NameOrdinalTableBase = (PUSHORT)((PCHAR)DllBase + (ULONG)ExportDirectory->AddressOfNameOrdinals);
  3312. //
  3313. // Look in the export name table for the specified function name.
  3314. //
  3315. Low = 0;
  3316. Middle = 0;
  3317. High = ExportDirectory->NumberOfNames - 1;
  3318. while (High >= Low && (LONG)High >= 0) {
  3319. //
  3320. // Compute the next probe index and compare the export name entry
  3321. // with the specified function name.
  3322. //
  3323. Middle = (Low + High) >> 1;
  3324. Result = strcmp(FunctionName,
  3325. (PCHAR)((PCHAR)DllBase + NameTableBase[Middle]));
  3326. if (Result < 0) {
  3327. High = Middle - 1;
  3328. }
  3329. else if (Result > 0) {
  3330. Low = Middle + 1;
  3331. }
  3332. else {
  3333. break;
  3334. }
  3335. }
  3336. //
  3337. // If the high index is less than the low index, then a matching table
  3338. // entry was not found. Otherwise, get the ordinal number from the
  3339. // ordinal table and location the function address.
  3340. //
  3341. if ((LONG)High >= (LONG)Low) {
  3342. OrdinalNumber = NameOrdinalTableBase[Middle];
  3343. Addr = (PULONG)((PCHAR)DllBase + (ULONG)ExportDirectory->AddressOfFunctions);
  3344. Func = (PVOID)((ULONG_PTR)DllBase + Addr[OrdinalNumber]);
  3345. //
  3346. // If the function address is w/in range of the export directory,
  3347. // then the function is forwarded, which is not allowed, so ignore
  3348. // it.
  3349. //
  3350. if ((ULONG_PTR)Func > (ULONG_PTR)ExportDirectory &&
  3351. (ULONG_PTR)Func < ((ULONG_PTR)ExportDirectory + ExportSize)) {
  3352. Func = NULL;
  3353. }
  3354. }
  3355. }
  3356. return Func;
  3357. }
  3358. NTSTATUS
  3359. MiDereferenceImports (
  3360. IN PLOAD_IMPORTS ImportList
  3361. )
  3362. /*++
  3363. Routine Description:
  3364. Decrement the reference count on each DLL specified in the image import
  3365. list. If any DLL's reference count reaches zero, then free the DLL.
  3366. No locks may be held on entry as MmUnloadSystemImage may be called.
  3367. The parameter list is freed here as well.
  3368. Arguments:
  3369. ImportList - Supplies the list of DLLs to dereference.
  3370. Return Value:
  3371. Status of the dereference operation.
  3372. --*/
  3373. {
  3374. ULONG i;
  3375. LOGICAL Unloaded;
  3376. PVOID SavedImports;
  3377. LOAD_IMPORTS SingleTableEntry;
  3378. PKLDR_DATA_TABLE_ENTRY ImportTableEntry;
  3379. PAGED_CODE();
  3380. if (ImportList == LOADED_AT_BOOT || ImportList == NO_IMPORTS_USED) {
  3381. return STATUS_SUCCESS;
  3382. }
  3383. if (SINGLE_ENTRY(ImportList)) {
  3384. SingleTableEntry.Count = 1;
  3385. SingleTableEntry.Entry[0] = SINGLE_ENTRY_TO_POINTER(ImportList);
  3386. ImportList = &SingleTableEntry;
  3387. }
  3388. for (i = 0; i < ImportList->Count && ImportList->Entry[i]; i += 1) {
  3389. ImportTableEntry = ImportList->Entry[i];
  3390. if (ImportTableEntry->LoadedImports == (PVOID)LOADED_AT_BOOT) {
  3391. //
  3392. // Skip this one - it was loaded by ntldr.
  3393. //
  3394. continue;
  3395. }
  3396. #if DBG
  3397. {
  3398. ULONG ImageCount;
  3399. PLIST_ENTRY NextEntry;
  3400. PKLDR_DATA_TABLE_ENTRY DataTableEntry;
  3401. //
  3402. // Assert that the first 2 entries are never dereferenced as
  3403. // unloading the kernel or HAL would be fatal.
  3404. //
  3405. NextEntry = PsLoadedModuleList.Flink;
  3406. ImageCount = 0;
  3407. while (NextEntry != &PsLoadedModuleList && ImageCount < 2) {
  3408. DataTableEntry = CONTAINING_RECORD(NextEntry,
  3409. KLDR_DATA_TABLE_ENTRY,
  3410. InLoadOrderLinks);
  3411. ASSERT (ImportTableEntry != DataTableEntry);
  3412. ASSERT (DataTableEntry->LoadCount == 1);
  3413. NextEntry = NextEntry->Flink;
  3414. ImageCount += 1;
  3415. }
  3416. }
  3417. #endif
  3418. ASSERT (ImportTableEntry->LoadCount >= 1);
  3419. ImportTableEntry->LoadCount -= 1;
  3420. if (ImportTableEntry->LoadCount == 0) {
  3421. //
  3422. // Unload this dependent DLL - we only do this to non-referenced
  3423. // non-boot-loaded drivers. Stop the import list recursion prior
  3424. // to unloading - we know we're done at this point.
  3425. //
  3426. // Note we can continue on afterwards without restarting
  3427. // regardless of which locks get released and reacquired
  3428. // because this chain is private.
  3429. //
  3430. SavedImports = ImportTableEntry->LoadedImports;
  3431. ImportTableEntry->LoadedImports = (PVOID)NO_IMPORTS_USED;
  3432. Unloaded = MiCallDllUnloadAndUnloadDll ((PVOID)ImportTableEntry);
  3433. if (Unloaded == TRUE) {
  3434. //
  3435. // This DLL was unloaded so recurse through its imports and
  3436. // attempt to unload all of those too.
  3437. //
  3438. MiDereferenceImports ((PLOAD_IMPORTS)SavedImports);
  3439. if ((SavedImports != (PVOID)LOADED_AT_BOOT) &&
  3440. (SavedImports != (PVOID)NO_IMPORTS_USED) &&
  3441. (!SINGLE_ENTRY(SavedImports))) {
  3442. ExFreePool (SavedImports);
  3443. }
  3444. }
  3445. else {
  3446. ImportTableEntry->LoadedImports = SavedImports;
  3447. }
  3448. }
  3449. }
  3450. return STATUS_SUCCESS;
  3451. }
  3452. NTSTATUS
  3453. MiResolveImageReferences (
  3454. PVOID ImageBase,
  3455. IN PUNICODE_STRING ImageFileDirectory,
  3456. IN PUNICODE_STRING NamePrefix OPTIONAL,
  3457. OUT PCHAR *MissingProcedureName,
  3458. OUT PWSTR *MissingDriverName,
  3459. OUT PLOAD_IMPORTS *LoadedImports
  3460. )
  3461. /*++
  3462. Routine Description:
  3463. This routine resolves the references from the newly loaded driver
  3464. to the kernel, HAL and other drivers.
  3465. Arguments:
  3466. ImageBase - Supplies the address of which the image header resides.
  3467. ImageFileDirectory - Supplies the directory to load referenced DLLs.
  3468. Return Value:
  3469. Status of the image reference resolution.
  3470. --*/
  3471. {
  3472. PCHAR MissingProcedureStorageArea;
  3473. PVOID ImportBase;
  3474. ULONG ImportSize;
  3475. ULONG ImportListSize;
  3476. ULONG Count;
  3477. ULONG i;
  3478. PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor;
  3479. PIMAGE_IMPORT_DESCRIPTOR Imp;
  3480. NTSTATUS st;
  3481. ULONG ExportSize;
  3482. PIMAGE_EXPORT_DIRECTORY ExportDirectory;
  3483. PIMAGE_THUNK_DATA NameThunk;
  3484. PIMAGE_THUNK_DATA AddrThunk;
  3485. PSZ ImportName;
  3486. PLIST_ENTRY NextEntry;
  3487. PKLDR_DATA_TABLE_ENTRY DataTableEntry;
  3488. PKLDR_DATA_TABLE_ENTRY SingleEntry;
  3489. ANSI_STRING AnsiString;
  3490. UNICODE_STRING ImportName_U;
  3491. UNICODE_STRING ImportDescriptorName_U;
  3492. UNICODE_STRING DllToLoad;
  3493. UNICODE_STRING DllToLoad2;
  3494. PVOID Section;
  3495. PVOID BaseAddress;
  3496. LOGICAL PrefixedNameAllocated;
  3497. LOGICAL ReferenceImport;
  3498. ULONG LinkWin32k = 0;
  3499. ULONG LinkNonWin32k = 0;
  3500. PLOAD_IMPORTS ImportList;
  3501. PLOAD_IMPORTS CompactedImportList;
  3502. LOGICAL Loaded;
  3503. UNICODE_STRING DriverDirectory;
  3504. PAGED_CODE();
  3505. *LoadedImports = NO_IMPORTS_USED;
  3506. MissingProcedureStorageArea = *MissingProcedureName;
  3507. ImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR) RtlImageDirectoryEntryToData (
  3508. ImageBase,
  3509. TRUE,
  3510. IMAGE_DIRECTORY_ENTRY_IMPORT,
  3511. &ImportSize);
  3512. if (ImportDescriptor == NULL) {
  3513. return STATUS_SUCCESS;
  3514. }
  3515. // Count the number of imports so we can allocate enough room to
  3516. // store them all chained off this module's LDR_DATA_TABLE_ENTRY.
  3517. //
  3518. Count = 0;
  3519. for (Imp = ImportDescriptor; Imp->Name && Imp->OriginalFirstThunk; Imp += 1) {
  3520. Count += 1;
  3521. }
  3522. if (Count != 0) {
  3523. ImportListSize = Count * sizeof(PVOID) + sizeof(SIZE_T);
  3524. ImportList = (PLOAD_IMPORTS) ExAllocatePoolWithTag (PagedPool | POOL_COLD_ALLOCATION,
  3525. ImportListSize,
  3526. 'TDmM');
  3527. //
  3528. // Zero it so we can recover gracefully if we fail in the middle.
  3529. // If the allocation failed, just don't build the import list.
  3530. //
  3531. if (ImportList != NULL) {
  3532. RtlZeroMemory (ImportList, ImportListSize);
  3533. ImportList->Count = Count;
  3534. }
  3535. }
  3536. else {
  3537. ImportList = NULL;
  3538. }
  3539. Count = 0;
  3540. while (ImportDescriptor->Name && ImportDescriptor->OriginalFirstThunk) {
  3541. ImportName = (PSZ)((PCHAR)ImageBase + ImportDescriptor->Name);
  3542. //
  3543. // A driver can link with win32k.sys if and only if it is a GDI
  3544. // driver.
  3545. // Also display drivers can only link to win32k.sys (and lego ...).
  3546. //
  3547. // So if we get a driver that links to win32k.sys and has more
  3548. // than one set of imports, we will fail to load it.
  3549. //
  3550. LinkWin32k = LinkWin32k |
  3551. (!_strnicmp(ImportName, "win32k", sizeof("win32k") - 1));
  3552. //
  3553. // We don't want to count coverage, win32k and irt (lego) since
  3554. // display drivers CAN link against these.
  3555. //
  3556. LinkNonWin32k = LinkNonWin32k |
  3557. ((_strnicmp(ImportName, "win32k", sizeof("win32k") - 1)) &&
  3558. (_strnicmp(ImportName, "dxapi", sizeof("dxapi") - 1)) &&
  3559. (_strnicmp(ImportName, "coverage", sizeof("coverage") - 1)) &&
  3560. (_strnicmp(ImportName, "irt", sizeof("irt") - 1)));
  3561. if (LinkNonWin32k && LinkWin32k) {
  3562. MiDereferenceImports (ImportList);
  3563. if (ImportList) {
  3564. ExFreePool (ImportList);
  3565. }
  3566. return STATUS_PROCEDURE_NOT_FOUND;
  3567. }
  3568. if ((!_strnicmp(ImportName, "ntdll", sizeof("ntdll") - 1)) ||
  3569. (!_strnicmp(ImportName, "winsrv", sizeof("winsrv") - 1)) ||
  3570. (!_strnicmp(ImportName, "advapi32", sizeof("advapi32") - 1)) ||
  3571. (!_strnicmp(ImportName, "kernel32", sizeof("kernel32") - 1)) ||
  3572. (!_strnicmp(ImportName, "user32", sizeof("user32") - 1)) ||
  3573. (!_strnicmp(ImportName, "gdi32", sizeof("gdi32") - 1)) ) {
  3574. MiDereferenceImports (ImportList);
  3575. if (ImportList) {
  3576. ExFreePool (ImportList);
  3577. }
  3578. return STATUS_PROCEDURE_NOT_FOUND;
  3579. }
  3580. if ((!_strnicmp(ImportName, "ntoskrnl", sizeof("ntoskrnl") - 1)) ||
  3581. (!_strnicmp(ImportName, "win32k", sizeof("win32k") - 1)) ||
  3582. (!_strnicmp(ImportName, "hal", sizeof("hal") - 1))) {
  3583. //
  3584. // These imports don't get refcounted because we don't
  3585. // ever want to unload them.
  3586. //
  3587. ReferenceImport = FALSE;
  3588. }
  3589. else {
  3590. ReferenceImport = TRUE;
  3591. }
  3592. RtlInitAnsiString (&AnsiString, ImportName);
  3593. st = RtlAnsiStringToUnicodeString (&ImportName_U, &AnsiString, TRUE);
  3594. if (!NT_SUCCESS(st)) {
  3595. MiDereferenceImports (ImportList);
  3596. if (ImportList != NULL) {
  3597. ExFreePool (ImportList);
  3598. }
  3599. return st;
  3600. }
  3601. if (NamePrefix &&
  3602. (_strnicmp(ImportName, "ntoskrnl", sizeof("ntoskrnl") - 1) &&
  3603. _strnicmp(ImportName, "hal", sizeof("hal") - 1))) {
  3604. ImportDescriptorName_U.MaximumLength = (USHORT)(ImportName_U.Length + NamePrefix->Length);
  3605. ImportDescriptorName_U.Buffer = ExAllocatePoolWithTag (NonPagedPool,
  3606. ImportDescriptorName_U.MaximumLength,
  3607. 'TDmM');
  3608. if (!ImportDescriptorName_U.Buffer) {
  3609. RtlFreeUnicodeString (&ImportName_U);
  3610. MiDereferenceImports (ImportList);
  3611. if (ImportList != NULL) {
  3612. ExFreePool (ImportList);
  3613. }
  3614. return STATUS_INSUFFICIENT_RESOURCES;
  3615. }
  3616. ImportDescriptorName_U.Length = 0;
  3617. RtlAppendUnicodeStringToString(&ImportDescriptorName_U, NamePrefix);
  3618. RtlAppendUnicodeStringToString(&ImportDescriptorName_U, &ImportName_U);
  3619. PrefixedNameAllocated = TRUE;
  3620. }
  3621. else {
  3622. ImportDescriptorName_U = ImportName_U;
  3623. PrefixedNameAllocated = FALSE;
  3624. }
  3625. Loaded = FALSE;
  3626. ReCheck:
  3627. NextEntry = PsLoadedModuleList.Flink;
  3628. ImportBase = NULL;
  3629. //
  3630. // Initializing DataTableEntry is not needed for correctness
  3631. // but without it the compiler cannot compile this code
  3632. // W4 to check for use of uninitialized variables.
  3633. //
  3634. DataTableEntry = NULL;
  3635. while (NextEntry != &PsLoadedModuleList) {
  3636. DataTableEntry = CONTAINING_RECORD(NextEntry,
  3637. KLDR_DATA_TABLE_ENTRY,
  3638. InLoadOrderLinks);
  3639. if (RtlEqualUnicodeString (&ImportDescriptorName_U,
  3640. &DataTableEntry->BaseDllName,
  3641. TRUE)) {
  3642. ImportBase = DataTableEntry->DllBase;
  3643. //
  3644. // Only bump the LoadCount if this thread did not initiate
  3645. // the load below. If this thread initiated the load, then
  3646. // the LoadCount has already been bumped as part of the
  3647. // load - we only want to increment it here if we are
  3648. // "attaching" to a previously loaded DLL.
  3649. //
  3650. if ((Loaded == FALSE) && (ReferenceImport == TRUE)) {
  3651. //
  3652. // Only increment the load count on the import if it is not
  3653. // circular (ie: the import is not from the original
  3654. // caller).
  3655. //
  3656. if ((DataTableEntry->Flags & LDRP_LOAD_IN_PROGRESS) == 0) {
  3657. DataTableEntry->LoadCount += 1;
  3658. }
  3659. }
  3660. break;
  3661. }
  3662. NextEntry = NextEntry->Flink;
  3663. }
  3664. if (ImportBase == NULL) {
  3665. //
  3666. // The DLL name was not located, attempt to load this dll.
  3667. //
  3668. DllToLoad.MaximumLength = (USHORT)(ImportName_U.Length +
  3669. ImageFileDirectory->Length +
  3670. sizeof(WCHAR));
  3671. DllToLoad.Buffer = ExAllocatePoolWithTag (NonPagedPool,
  3672. DllToLoad.MaximumLength,
  3673. 'TDmM');
  3674. if (DllToLoad.Buffer) {
  3675. DllToLoad.Length = ImageFileDirectory->Length;
  3676. RtlCopyMemory (DllToLoad.Buffer,
  3677. ImageFileDirectory->Buffer,
  3678. ImageFileDirectory->Length);
  3679. RtlAppendStringToString ((PSTRING)&DllToLoad,
  3680. (PSTRING)&ImportName_U);
  3681. //
  3682. // Add NULL termination in case the load fails so the name
  3683. // can be returned as the PWSTR MissingDriverName.
  3684. //
  3685. DllToLoad.Buffer[(DllToLoad.MaximumLength - 1) / sizeof (WCHAR)] =
  3686. UNICODE_NULL;
  3687. st = MmLoadSystemImage (&DllToLoad,
  3688. NamePrefix,
  3689. NULL,
  3690. FALSE,
  3691. &Section,
  3692. &BaseAddress);
  3693. if (NT_SUCCESS(st)) {
  3694. //
  3695. // No need to keep the temporary name buffer around now
  3696. // that there is a loaded module list entry for this DLL.
  3697. //
  3698. ExFreePool (DllToLoad.Buffer);
  3699. }
  3700. else {
  3701. if ((st == STATUS_OBJECT_NAME_NOT_FOUND) &&
  3702. (NamePrefix == NULL) &&
  3703. (MI_IS_SESSION_ADDRESS (ImageBase))) {
  3704. #define DRIVERS_SUBDIR_NAME L"drivers\\"
  3705. DriverDirectory.Buffer = (const PUSHORT) DRIVERS_SUBDIR_NAME;
  3706. DriverDirectory.Length = sizeof (DRIVERS_SUBDIR_NAME) - sizeof (WCHAR);
  3707. DriverDirectory.MaximumLength = sizeof DRIVERS_SUBDIR_NAME;
  3708. //
  3709. // The DLL file was not located, attempt to load it
  3710. // from the drivers subdirectory. This makes it
  3711. // possible for drivers like win32k.sys to link to
  3712. // drivers that reside in the drivers subdirectory
  3713. // (like dxapi.sys).
  3714. //
  3715. DllToLoad2.MaximumLength = (USHORT)(ImportName_U.Length +
  3716. DriverDirectory.Length +
  3717. ImageFileDirectory->Length +
  3718. sizeof(WCHAR));
  3719. DllToLoad2.Buffer = ExAllocatePoolWithTag (NonPagedPool,
  3720. DllToLoad2.MaximumLength,
  3721. 'TDmM');
  3722. if (DllToLoad2.Buffer) {
  3723. DllToLoad2.Length = ImageFileDirectory->Length;
  3724. RtlCopyMemory (DllToLoad2.Buffer,
  3725. ImageFileDirectory->Buffer,
  3726. ImageFileDirectory->Length);
  3727. RtlAppendStringToString ((PSTRING)&DllToLoad2,
  3728. (PSTRING)&DriverDirectory);
  3729. RtlAppendStringToString ((PSTRING)&DllToLoad2,
  3730. (PSTRING)&ImportName_U);
  3731. //
  3732. // Add NULL termination in case the load fails
  3733. // so the name can be returned as the PWSTR
  3734. // MissingDriverName.
  3735. //
  3736. DllToLoad2.Buffer[(DllToLoad2.MaximumLength - 1) / sizeof (WCHAR)] =
  3737. UNICODE_NULL;
  3738. st = MmLoadSystemImage (&DllToLoad2,
  3739. NULL,
  3740. NULL,
  3741. FALSE,
  3742. &Section,
  3743. &BaseAddress);
  3744. ExFreePool (DllToLoad.Buffer);
  3745. DllToLoad.Buffer = DllToLoad2.Buffer;
  3746. DllToLoad.Length = DllToLoad2.Length;
  3747. DllToLoad.MaximumLength = DllToLoad2.MaximumLength;
  3748. if (NT_SUCCESS(st)) {
  3749. ExFreePool (DllToLoad.Buffer);
  3750. goto LoadFinished;
  3751. }
  3752. }
  3753. else {
  3754. Section = NULL;
  3755. BaseAddress = NULL;
  3756. st = STATUS_INSUFFICIENT_RESOURCES;
  3757. goto LoadFinished;
  3758. }
  3759. }
  3760. //
  3761. // Return the temporary name buffer to our caller so
  3762. // the name of the DLL that failed to load can be displayed.
  3763. // Set the low bit of the pointer so our caller knows to
  3764. // free this buffer when he's done displaying it (as opposed
  3765. // to loaded module list entries which should not be freed).
  3766. //
  3767. *MissingDriverName = DllToLoad.Buffer;
  3768. *(PULONG)MissingDriverName |= 0x1;
  3769. //
  3770. // Set this to NULL so the hard error prints properly.
  3771. //
  3772. *MissingProcedureName = NULL;
  3773. }
  3774. }
  3775. else {
  3776. //
  3777. // Initializing Section and BaseAddress is not needed for
  3778. // correctness but without it the compiler cannot compile
  3779. // this code W4 to check for use of uninitialized variables.
  3780. //
  3781. Section = NULL;
  3782. BaseAddress = NULL;
  3783. st = STATUS_INSUFFICIENT_RESOURCES;
  3784. }
  3785. LoadFinished:
  3786. //
  3787. // Call any needed DLL initialization now.
  3788. //
  3789. if (NT_SUCCESS(st)) {
  3790. #if DBG
  3791. PLIST_ENTRY Entry;
  3792. #endif
  3793. PKLDR_DATA_TABLE_ENTRY TableEntry;
  3794. Loaded = TRUE;
  3795. TableEntry = (PKLDR_DATA_TABLE_ENTRY) Section;
  3796. ASSERT (BaseAddress == TableEntry->DllBase);
  3797. #if DBG
  3798. //
  3799. // Lookup the dll's table entry in the loaded module list.
  3800. // This is expected to always succeed.
  3801. //
  3802. Entry = PsLoadedModuleList.Blink;
  3803. while (Entry != &PsLoadedModuleList) {
  3804. TableEntry = CONTAINING_RECORD (Entry,
  3805. KLDR_DATA_TABLE_ENTRY,
  3806. InLoadOrderLinks);
  3807. if (BaseAddress == TableEntry->DllBase) {
  3808. ASSERT (TableEntry == (PKLDR_DATA_TABLE_ENTRY) Section);
  3809. break;
  3810. }
  3811. ASSERT (TableEntry != (PKLDR_DATA_TABLE_ENTRY) Section);
  3812. Entry = Entry->Blink;
  3813. }
  3814. ASSERT (Entry != &PsLoadedModuleList);
  3815. #endif
  3816. //
  3817. // Call the dll's initialization routine if it has
  3818. // one. This routine will reapply verifier thunks to
  3819. // any modules that link to this one if necessary.
  3820. //
  3821. st = MmCallDllInitialize (TableEntry, &PsLoadedModuleList);
  3822. //
  3823. // If the module could not be properly initialized,
  3824. // unload it.
  3825. //
  3826. if (!NT_SUCCESS(st)) {
  3827. MmUnloadSystemImage ((PVOID)TableEntry);
  3828. Loaded = FALSE;
  3829. }
  3830. }
  3831. if (!NT_SUCCESS(st)) {
  3832. RtlFreeUnicodeString (&ImportName_U);
  3833. if (PrefixedNameAllocated == TRUE) {
  3834. ExFreePool (ImportDescriptorName_U.Buffer);
  3835. }
  3836. MiDereferenceImports (ImportList);
  3837. if (ImportList != NULL) {
  3838. ExFreePool (ImportList);
  3839. }
  3840. return st;
  3841. }
  3842. goto ReCheck;
  3843. }
  3844. if ((ReferenceImport == TRUE) && (ImportList)) {
  3845. //
  3846. // Only add the image providing satisfying our imports to the
  3847. // import list if the reference is not circular (ie: the import
  3848. // is not from the original caller).
  3849. //
  3850. if ((DataTableEntry->Flags & LDRP_LOAD_IN_PROGRESS) == 0) {
  3851. ImportList->Entry[Count] = DataTableEntry;
  3852. Count += 1;
  3853. }
  3854. }
  3855. RtlFreeUnicodeString (&ImportName_U);
  3856. if (PrefixedNameAllocated) {
  3857. ExFreePool (ImportDescriptorName_U.Buffer);
  3858. }
  3859. *MissingDriverName = DataTableEntry->BaseDllName.Buffer;
  3860. ExportDirectory = (PIMAGE_EXPORT_DIRECTORY) RtlImageDirectoryEntryToData (
  3861. ImportBase,
  3862. TRUE,
  3863. IMAGE_DIRECTORY_ENTRY_EXPORT,
  3864. &ExportSize);
  3865. if (!ExportDirectory) {
  3866. MiDereferenceImports (ImportList);
  3867. if (ImportList) {
  3868. ExFreePool (ImportList);
  3869. }
  3870. return STATUS_DRIVER_ENTRYPOINT_NOT_FOUND;
  3871. }
  3872. //
  3873. // Walk through the IAT and snap all the thunks.
  3874. //
  3875. if (ImportDescriptor->OriginalFirstThunk) {
  3876. NameThunk = (PIMAGE_THUNK_DATA)((PCHAR)ImageBase + (ULONG)ImportDescriptor->OriginalFirstThunk);
  3877. AddrThunk = (PIMAGE_THUNK_DATA)((PCHAR)ImageBase + (ULONG)ImportDescriptor->FirstThunk);
  3878. while (NameThunk->u1.AddressOfData) {
  3879. st = MiSnapThunk (ImportBase,
  3880. ImageBase,
  3881. NameThunk++,
  3882. AddrThunk++,
  3883. ExportDirectory,
  3884. ExportSize,
  3885. FALSE,
  3886. MissingProcedureName);
  3887. if (!NT_SUCCESS(st) ) {
  3888. MiDereferenceImports (ImportList);
  3889. if (ImportList) {
  3890. ExFreePool (ImportList);
  3891. }
  3892. return st;
  3893. }
  3894. *MissingProcedureName = MissingProcedureStorageArea;
  3895. }
  3896. }
  3897. ImportDescriptor += 1;
  3898. }
  3899. //
  3900. // All the imports are successfully loaded so establish and compact
  3901. // the import unload list.
  3902. //
  3903. if (ImportList) {
  3904. //
  3905. // Blank entries occur for things like the kernel, HAL & win32k.sys
  3906. // that we never want to unload. Especially for things like
  3907. // win32k.sys where the reference count can really hit 0.
  3908. //
  3909. //
  3910. // Initializing SingleEntry is not needed for correctness
  3911. // but without it the compiler cannot compile this code
  3912. // W4 to check for use of uninitialized variables.
  3913. //
  3914. SingleEntry = NULL;
  3915. Count = 0;
  3916. for (i = 0; i < ImportList->Count; i += 1) {
  3917. if (ImportList->Entry[i]) {
  3918. SingleEntry = POINTER_TO_SINGLE_ENTRY(ImportList->Entry[i]);
  3919. Count += 1;
  3920. }
  3921. }
  3922. if (Count == 0) {
  3923. ExFreePool(ImportList);
  3924. ImportList = NO_IMPORTS_USED;
  3925. }
  3926. else if (Count == 1) {
  3927. ExFreePool(ImportList);
  3928. ImportList = (PLOAD_IMPORTS)SingleEntry;
  3929. }
  3930. else if (Count != ImportList->Count) {
  3931. ImportListSize = Count * sizeof(PVOID) + sizeof(SIZE_T);
  3932. CompactedImportList = (PLOAD_IMPORTS)
  3933. ExAllocatePoolWithTag (PagedPool | POOL_COLD_ALLOCATION,
  3934. ImportListSize,
  3935. 'TDmM');
  3936. if (CompactedImportList) {
  3937. CompactedImportList->Count = Count;
  3938. Count = 0;
  3939. for (i = 0; i < ImportList->Count; i += 1) {
  3940. if (ImportList->Entry[i]) {
  3941. CompactedImportList->Entry[Count] = ImportList->Entry[i];
  3942. Count += 1;
  3943. }
  3944. }
  3945. ExFreePool(ImportList);
  3946. ImportList = CompactedImportList;
  3947. }
  3948. }
  3949. *LoadedImports = ImportList;
  3950. }
  3951. return STATUS_SUCCESS;
  3952. }
  3953. NTSTATUS
  3954. MiSnapThunk(
  3955. IN PVOID DllBase,
  3956. IN PVOID ImageBase,
  3957. IN PIMAGE_THUNK_DATA NameThunk,
  3958. OUT PIMAGE_THUNK_DATA AddrThunk,
  3959. IN PIMAGE_EXPORT_DIRECTORY ExportDirectory,
  3960. IN ULONG ExportSize,
  3961. IN LOGICAL SnapForwarder,
  3962. OUT PCHAR *MissingProcedureName
  3963. )
  3964. /*++
  3965. Routine Description:
  3966. This function snaps a thunk using the specified Export Section data.
  3967. If the section data does not support the thunk, then the thunk is
  3968. partially snapped (Dll field is still non-null, but snap address is
  3969. set).
  3970. Arguments:
  3971. DllBase - Base of DLL being snapped to.
  3972. ImageBase - Base of image that contains the thunks to snap.
  3973. Thunk - On input, supplies the thunk to snap. When successfully
  3974. snapped, the function field is set to point to the address in
  3975. the DLL, and the DLL field is set to NULL.
  3976. ExportDirectory - Supplies the Export Section data from a DLL.
  3977. SnapForwarder - Supplies TRUE if the snap is for a forwarder, and therefore
  3978. Address of Data is already setup.
  3979. Return Value:
  3980. STATUS_SUCCESS or STATUS_DRIVER_ENTRYPOINT_NOT_FOUND or
  3981. STATUS_DRIVER_ORDINAL_NOT_FOUND
  3982. --*/
  3983. {
  3984. BOOLEAN Ordinal;
  3985. USHORT OrdinalNumber;
  3986. PULONG NameTableBase;
  3987. PUSHORT NameOrdinalTableBase;
  3988. PULONG Addr;
  3989. USHORT HintIndex;
  3990. ULONG High;
  3991. ULONG Low;
  3992. ULONG Middle;
  3993. LONG Result;
  3994. NTSTATUS Status;
  3995. PCHAR MissingProcedureName2;
  3996. CHAR NameBuffer[ MAXIMUM_FILENAME_LENGTH ];
  3997. PAGED_CODE();
  3998. //
  3999. // Determine if snap is by name, or by ordinal
  4000. //
  4001. Ordinal = (BOOLEAN)IMAGE_SNAP_BY_ORDINAL(NameThunk->u1.Ordinal);
  4002. if (Ordinal && !SnapForwarder) {
  4003. OrdinalNumber = (USHORT)(IMAGE_ORDINAL(NameThunk->u1.Ordinal) -
  4004. ExportDirectory->Base);
  4005. *MissingProcedureName = (PCHAR)(ULONG_PTR)OrdinalNumber;
  4006. }
  4007. else {
  4008. //
  4009. // Change AddressOfData from an RVA to a VA.
  4010. //
  4011. if (!SnapForwarder) {
  4012. NameThunk->u1.AddressOfData = (ULONG_PTR)ImageBase + NameThunk->u1.AddressOfData;
  4013. }
  4014. strncpy (*MissingProcedureName,
  4015. (const PCHAR)&((PIMAGE_IMPORT_BY_NAME)NameThunk->u1.AddressOfData)->Name[0],
  4016. MAXIMUM_FILENAME_LENGTH - 1);
  4017. //
  4018. // Lookup Name in NameTable
  4019. //
  4020. NameTableBase = (PULONG)((PCHAR)DllBase + (ULONG)ExportDirectory->AddressOfNames);
  4021. NameOrdinalTableBase = (PUSHORT)((PCHAR)DllBase + (ULONG)ExportDirectory->AddressOfNameOrdinals);
  4022. //
  4023. // Before dropping into binary search, see if
  4024. // the hint index results in a successful
  4025. // match. If the hint index is zero, then
  4026. // drop into binary search.
  4027. //
  4028. HintIndex = ((PIMAGE_IMPORT_BY_NAME)NameThunk->u1.AddressOfData)->Hint;
  4029. if ((ULONG)HintIndex < ExportDirectory->NumberOfNames &&
  4030. !strcmp((PSZ)((PIMAGE_IMPORT_BY_NAME)NameThunk->u1.AddressOfData)->Name,
  4031. (PSZ)((PCHAR)DllBase + NameTableBase[HintIndex]))) {
  4032. OrdinalNumber = NameOrdinalTableBase[HintIndex];
  4033. }
  4034. else {
  4035. //
  4036. // Lookup the import name in the name table using a binary search.
  4037. //
  4038. Low = 0;
  4039. Middle = 0;
  4040. High = ExportDirectory->NumberOfNames - 1;
  4041. while (High >= Low) {
  4042. //
  4043. // Compute the next probe index and compare the import name
  4044. // with the export name entry.
  4045. //
  4046. Middle = (Low + High) >> 1;
  4047. Result = strcmp((const PCHAR)&((PIMAGE_IMPORT_BY_NAME)NameThunk->u1.AddressOfData)->Name[0],
  4048. (PCHAR)((PCHAR)DllBase + NameTableBase[Middle]));
  4049. if (Result < 0) {
  4050. High = Middle - 1;
  4051. }
  4052. else if (Result > 0) {
  4053. Low = Middle + 1;
  4054. }
  4055. else {
  4056. break;
  4057. }
  4058. }
  4059. //
  4060. // If the high index is less than the low index, then a matching
  4061. // table entry was not found. Otherwise, get the ordinal number
  4062. // from the ordinal table.
  4063. //
  4064. if ((LONG)High < (LONG)Low) {
  4065. return STATUS_DRIVER_ENTRYPOINT_NOT_FOUND;
  4066. }
  4067. else {
  4068. OrdinalNumber = NameOrdinalTableBase[Middle];
  4069. }
  4070. }
  4071. }
  4072. //
  4073. // If OrdinalNumber is not within the Export Address Table,
  4074. // then DLL does not implement function. Snap to LDRP_BAD_DLL.
  4075. //
  4076. if ((ULONG)OrdinalNumber >= ExportDirectory->NumberOfFunctions) {
  4077. Status = STATUS_DRIVER_ORDINAL_NOT_FOUND;
  4078. }
  4079. else {
  4080. MissingProcedureName2 = NameBuffer;
  4081. Addr = (PULONG)((PCHAR)DllBase + (ULONG)ExportDirectory->AddressOfFunctions);
  4082. *(PULONG_PTR)&AddrThunk->u1.Function = (ULONG_PTR)DllBase + Addr[OrdinalNumber];
  4083. // AddrThunk s/b used from here on.
  4084. Status = STATUS_SUCCESS;
  4085. if (((ULONG_PTR)AddrThunk->u1.Function > (ULONG_PTR)ExportDirectory) &&
  4086. ((ULONG_PTR)AddrThunk->u1.Function < ((ULONG_PTR)ExportDirectory + ExportSize)) ) {
  4087. UNICODE_STRING UnicodeString;
  4088. ANSI_STRING ForwardDllName;
  4089. PLIST_ENTRY NextEntry;
  4090. PKLDR_DATA_TABLE_ENTRY DataTableEntry;
  4091. ULONG LocalExportSize;
  4092. PIMAGE_EXPORT_DIRECTORY LocalExportDirectory;
  4093. Status = STATUS_DRIVER_ENTRYPOINT_NOT_FOUND;
  4094. //
  4095. // Include the dot in the length so we can do prefix later on.
  4096. //
  4097. ForwardDllName.Buffer = (PCHAR)AddrThunk->u1.Function;
  4098. ForwardDllName.Length = (USHORT)(strchr(ForwardDllName.Buffer, '.') -
  4099. ForwardDllName.Buffer + 1);
  4100. ForwardDllName.MaximumLength = ForwardDllName.Length;
  4101. if (NT_SUCCESS(RtlAnsiStringToUnicodeString(&UnicodeString,
  4102. &ForwardDllName,
  4103. TRUE))) {
  4104. NextEntry = PsLoadedModuleList.Flink;
  4105. while (NextEntry != &PsLoadedModuleList) {
  4106. DataTableEntry = CONTAINING_RECORD(NextEntry,
  4107. KLDR_DATA_TABLE_ENTRY,
  4108. InLoadOrderLinks);
  4109. //
  4110. // We have to do a case INSENSITIVE comparison for
  4111. // forwarder because the linker just took what is in the
  4112. // def file, as opposed to looking in the exporting
  4113. // image for the name.
  4114. // we also use the prefix function to ignore the .exe or
  4115. // .sys or .dll at the end.
  4116. //
  4117. if (RtlPrefixString((PSTRING)&UnicodeString,
  4118. (PSTRING)&DataTableEntry->BaseDllName,
  4119. TRUE)) {
  4120. LocalExportDirectory = (PIMAGE_EXPORT_DIRECTORY)
  4121. RtlImageDirectoryEntryToData (DataTableEntry->DllBase,
  4122. TRUE,
  4123. IMAGE_DIRECTORY_ENTRY_EXPORT,
  4124. &LocalExportSize);
  4125. if (LocalExportDirectory != NULL) {
  4126. IMAGE_THUNK_DATA thunkData;
  4127. PIMAGE_IMPORT_BY_NAME addressOfData;
  4128. SIZE_T length;
  4129. //
  4130. // One extra byte for NULL termination.
  4131. //
  4132. length = strlen(ForwardDllName.Buffer +
  4133. ForwardDllName.Length) + 1;
  4134. addressOfData = (PIMAGE_IMPORT_BY_NAME)
  4135. ExAllocatePoolWithTag (PagedPool,
  4136. length +
  4137. sizeof(IMAGE_IMPORT_BY_NAME),
  4138. ' mM');
  4139. if (addressOfData) {
  4140. RtlCopyMemory(&(addressOfData->Name[0]),
  4141. ForwardDllName.Buffer +
  4142. ForwardDllName.Length,
  4143. length);
  4144. addressOfData->Hint = 0;
  4145. *(PULONG_PTR)&thunkData.u1.AddressOfData =
  4146. (ULONG_PTR)addressOfData;
  4147. Status = MiSnapThunk (DataTableEntry->DllBase,
  4148. ImageBase,
  4149. &thunkData,
  4150. &thunkData,
  4151. LocalExportDirectory,
  4152. LocalExportSize,
  4153. TRUE,
  4154. &MissingProcedureName2);
  4155. ExFreePool (addressOfData);
  4156. AddrThunk->u1 = thunkData.u1;
  4157. }
  4158. }
  4159. break;
  4160. }
  4161. NextEntry = NextEntry->Flink;
  4162. }
  4163. RtlFreeUnicodeString (&UnicodeString);
  4164. }
  4165. }
  4166. }
  4167. return Status;
  4168. }
  4169. NTSTATUS
  4170. MmCheckSystemImage (
  4171. IN HANDLE ImageFileHandle,
  4172. IN LOGICAL PurgeSection
  4173. )
  4174. /*++
  4175. Routine Description:
  4176. This function ensures the checksum for a system image is correct
  4177. and matches the data in the image.
  4178. Arguments:
  4179. ImageFileHandle - Supplies the file handle of the image. This is a kernel
  4180. handle (ie: cannot be tampered with by the user).
  4181. PurgeSection - Supplies TRUE if the data section mapping the image should
  4182. be purged prior to returning. Note that the first page
  4183. could be used to speed up subsequent image section creation,
  4184. but generally the cost of useless data pages sitting in
  4185. transition is costly. Better to put the pages immediately
  4186. on the free list to preserve the transition cache for more
  4187. useful pages.
  4188. Return Value:
  4189. Status value.
  4190. --*/
  4191. {
  4192. NTSTATUS Status;
  4193. NTSTATUS Status2;
  4194. HANDLE Section;
  4195. PVOID ViewBase;
  4196. SIZE_T ViewSize;
  4197. IO_STATUS_BLOCK IoStatusBlock;
  4198. PIMAGE_FILE_HEADER FileHeader;
  4199. PIMAGE_NT_HEADERS NtHeaders;
  4200. FILE_STANDARD_INFORMATION StandardInfo;
  4201. PSECTION SectionPointer;
  4202. OBJECT_ATTRIBUTES ObjectAttributes;
  4203. KAPC_STATE ApcState;
  4204. PAGED_CODE();
  4205. InitializeObjectAttributes (&ObjectAttributes,
  4206. NULL,
  4207. (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE),
  4208. NULL,
  4209. NULL);
  4210. Status = ZwCreateSection (&Section,
  4211. SECTION_MAP_EXECUTE,
  4212. &ObjectAttributes,
  4213. NULL,
  4214. PAGE_EXECUTE,
  4215. SEC_COMMIT,
  4216. ImageFileHandle);
  4217. if (!NT_SUCCESS (Status)) {
  4218. return Status;
  4219. }
  4220. ViewBase = NULL;
  4221. ViewSize = 0;
  4222. //
  4223. // Since callees are not always in the context of the system process,
  4224. // attach here when necessary to guarantee the driver load occurs in a
  4225. // known safe address space to prevent security holes.
  4226. //
  4227. KeStackAttachProcess (&PsInitialSystemProcess->Pcb, &ApcState);
  4228. Status = ZwMapViewOfSection (Section,
  4229. NtCurrentProcess (),
  4230. (PVOID *)&ViewBase,
  4231. 0L,
  4232. 0L,
  4233. NULL,
  4234. &ViewSize,
  4235. ViewShare,
  4236. 0L,
  4237. PAGE_EXECUTE);
  4238. if (!NT_SUCCESS(Status)) {
  4239. KeUnstackDetachProcess (&ApcState);
  4240. ZwClose (Section);
  4241. return Status;
  4242. }
  4243. //
  4244. // Now the image is mapped as a data file... Calculate its size and then
  4245. // check its checksum.
  4246. //
  4247. Status = ZwQueryInformationFile (ImageFileHandle,
  4248. &IoStatusBlock,
  4249. &StandardInfo,
  4250. sizeof(StandardInfo),
  4251. FileStandardInformation);
  4252. if (NT_SUCCESS(Status)) {
  4253. try {
  4254. if (!LdrVerifyMappedImageMatchesChecksum (ViewBase, StandardInfo.EndOfFile.LowPart)) {
  4255. Status = STATUS_IMAGE_CHECKSUM_MISMATCH;
  4256. goto out;
  4257. }
  4258. NtHeaders = RtlImageNtHeader (ViewBase);
  4259. if (NtHeaders == NULL) {
  4260. Status = STATUS_IMAGE_CHECKSUM_MISMATCH;
  4261. goto out;
  4262. }
  4263. FileHeader = &NtHeaders->FileHeader;
  4264. //
  4265. // Detect configurations inadvertently trying to load 32-bit
  4266. // drivers on NT64 or mismatched platform architectures, etc.
  4267. //
  4268. if ((FileHeader->Machine != IMAGE_FILE_MACHINE_NATIVE) ||
  4269. (NtHeaders->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC)) {
  4270. Status = STATUS_INVALID_IMAGE_PROTECT;
  4271. goto out;
  4272. }
  4273. #if !defined(NT_UP)
  4274. if (!MmVerifyImageIsOkForMpUse (ViewBase)) {
  4275. Status = STATUS_IMAGE_MP_UP_MISMATCH;
  4276. goto out;
  4277. }
  4278. #endif
  4279. } except (EXCEPTION_EXECUTE_HANDLER) {
  4280. Status = STATUS_IMAGE_CHECKSUM_MISMATCH;
  4281. }
  4282. }
  4283. out:
  4284. ZwUnmapViewOfSection (NtCurrentProcess (), ViewBase);
  4285. KeUnstackDetachProcess (&ApcState);
  4286. if (PurgeSection == TRUE) {
  4287. Status2 = ObReferenceObjectByHandle (Section,
  4288. SECTION_MAP_EXECUTE,
  4289. MmSectionObjectType,
  4290. KernelMode,
  4291. (PVOID *) &SectionPointer,
  4292. (POBJECT_HANDLE_INFORMATION) NULL);
  4293. if (NT_SUCCESS (Status2)) {
  4294. MmPurgeSection (SectionPointer->Segment->ControlArea->FilePointer->SectionObjectPointer,
  4295. NULL,
  4296. 0,
  4297. FALSE);
  4298. ObDereferenceObject (SectionPointer);
  4299. }
  4300. }
  4301. ZwClose (Section);
  4302. return Status;
  4303. }
  4304. #if !defined(NT_UP)
  4305. BOOLEAN
  4306. MmVerifyImageIsOkForMpUse (
  4307. IN PVOID BaseAddress
  4308. )
  4309. {
  4310. PIMAGE_NT_HEADERS NtHeaders;
  4311. PAGED_CODE();
  4312. NtHeaders = RtlImageNtHeader (BaseAddress);
  4313. if ((NtHeaders != NULL) &&
  4314. (KeNumberProcessors > 1) &&
  4315. (NtHeaders->FileHeader.Characteristics & IMAGE_FILE_UP_SYSTEM_ONLY)) {
  4316. return FALSE;
  4317. }
  4318. return TRUE;
  4319. }
  4320. #endif
  4321. PFN_NUMBER
  4322. MiDeleteSystemPagableVm (
  4323. IN PMMPTE PointerPte,
  4324. IN PFN_NUMBER NumberOfPtes,
  4325. IN MMPTE NewPteValue,
  4326. IN LOGICAL SessionAllocation,
  4327. OUT PPFN_NUMBER ResidentPages OPTIONAL
  4328. )
  4329. /*++
  4330. Routine Description:
  4331. This function deletes pagable system address space (paged pool
  4332. or driver pagable sections).
  4333. Arguments:
  4334. PointerPte - Supplies the start of the PTE range to delete.
  4335. NumberOfPtes - Supplies the number of PTEs in the range.
  4336. NewPteValue - Supplies the new value for the PTE.
  4337. SessionAllocation - Supplies TRUE if this is a range in session space. If
  4338. TRUE is specified, it is assumed that the caller has
  4339. already attached to the relevant session.
  4340. If FALSE is supplied, then it is assumed that the range
  4341. is in the systemwide global space instead.
  4342. ResidentPages - If not NULL, the number of resident pages freed is
  4343. returned here.
  4344. Return Value:
  4345. Returns the number of pages actually freed.
  4346. --*/
  4347. {
  4348. PMMSUPPORT Ws;
  4349. PVOID VirtualAddress;
  4350. PFN_NUMBER PageFrameIndex;
  4351. MMPTE PteContents;
  4352. PMMPFN Pfn1;
  4353. PMMPFN Pfn2;
  4354. PFN_NUMBER ValidPages;
  4355. PFN_NUMBER PagesRequired;
  4356. MMPTE NewContents;
  4357. WSLE_NUMBER WsIndex;
  4358. KIRQL OldIrql;
  4359. MMPTE_FLUSH_LIST PteFlushList;
  4360. MMWSLENTRY Locked;
  4361. PFN_NUMBER PageTableFrameIndex;
  4362. LOGICAL WsHeld;
  4363. ASSERT (KeGetCurrentIrql() <= APC_LEVEL);
  4364. ValidPages = 0;
  4365. PagesRequired = 0;
  4366. PteFlushList.Count = 0;
  4367. WsHeld = FALSE;
  4368. NewContents = NewPteValue;
  4369. if (SessionAllocation == TRUE) {
  4370. Ws = &MmSessionSpace->GlobalVirtualAddress->Vm;
  4371. }
  4372. else {
  4373. Ws = &MmSystemCacheWs;
  4374. }
  4375. while (NumberOfPtes != 0) {
  4376. PteContents = *PointerPte;
  4377. if (PteContents.u.Long != ZeroKernelPte.u.Long) {
  4378. if (PteContents.u.Hard.Valid == 1) {
  4379. //
  4380. // Once the working set mutex is acquired, it is deliberately
  4381. // held until all the pages have been freed. This is because
  4382. // when paged pool is running low on large servers, we need the
  4383. // segment dereference thread to be able to free large amounts
  4384. // quickly. Typically this thread will free 64k chunks and we
  4385. // don't want to have to contend for the mutex 16 times to do
  4386. // this as there may be thousands of other threads also trying
  4387. // for it.
  4388. //
  4389. if (WsHeld == FALSE) {
  4390. WsHeld = TRUE;
  4391. LOCK_WORKING_SET (Ws);
  4392. }
  4393. PteContents = *PointerPte;
  4394. if (PteContents.u.Hard.Valid == 0) {
  4395. continue;
  4396. }
  4397. //
  4398. // Delete the page.
  4399. //
  4400. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (&PteContents);
  4401. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  4402. //
  4403. // Check to see if this is a pagable page in which
  4404. // case it needs to be removed from the working set list.
  4405. //
  4406. WsIndex = Pfn1->u1.WsIndex;
  4407. if (WsIndex == 0) {
  4408. ValidPages += 1;
  4409. if (SessionAllocation == TRUE) {
  4410. MM_BUMP_SESS_COUNTER (MM_DBG_SESSION_NP_DELVA, 1);
  4411. InterlockedExchangeAddSizeT (&MmSessionSpace->NonPagablePages, -1);
  4412. }
  4413. }
  4414. else {
  4415. if (SessionAllocation == FALSE) {
  4416. MiRemoveWsle (WsIndex, MmSystemCacheWorkingSetList);
  4417. MiReleaseWsle (WsIndex, &MmSystemCacheWs);
  4418. }
  4419. else {
  4420. VirtualAddress = MiGetVirtualAddressMappedByPte (PointerPte);
  4421. WsIndex = MiLocateWsle (VirtualAddress,
  4422. MmSessionSpace->Vm.VmWorkingSetList,
  4423. WsIndex);
  4424. ASSERT (WsIndex != WSLE_NULL_INDEX);
  4425. //
  4426. // Check to see if this entry is locked in
  4427. // the working set or locked in memory.
  4428. //
  4429. Locked = MmSessionSpace->Wsle[WsIndex].u1.e1;
  4430. MiRemoveWsle (WsIndex, MmSessionSpace->Vm.VmWorkingSetList);
  4431. MiReleaseWsle (WsIndex, &MmSessionSpace->Vm);
  4432. if (Locked.LockedInWs == 1 || Locked.LockedInMemory == 1) {
  4433. //
  4434. // This entry is locked.
  4435. //
  4436. MM_BUMP_SESS_COUNTER (MM_DBG_SESSION_NP_DELVA, 1);
  4437. InterlockedExchangeAddSizeT (&MmSessionSpace->NonPagablePages, -1);
  4438. ValidPages += 1;
  4439. ASSERT (WsIndex < MmSessionSpace->Vm.VmWorkingSetList->FirstDynamic);
  4440. MmSessionSpace->Vm.VmWorkingSetList->FirstDynamic -= 1;
  4441. if (WsIndex != MmSessionSpace->Vm.VmWorkingSetList->FirstDynamic) {
  4442. WSLE_NUMBER Entry;
  4443. PVOID SwapVa;
  4444. Entry = MmSessionSpace->Vm.VmWorkingSetList->FirstDynamic;
  4445. ASSERT (MmSessionSpace->Wsle[Entry].u1.e1.Valid);
  4446. SwapVa = MmSessionSpace->Wsle[Entry].u1.VirtualAddress;
  4447. SwapVa = PAGE_ALIGN (SwapVa);
  4448. MiSwapWslEntries (Entry,
  4449. WsIndex,
  4450. &MmSessionSpace->Vm,
  4451. FALSE);
  4452. }
  4453. }
  4454. else {
  4455. ASSERT (WsIndex >= MmSessionSpace->Vm.VmWorkingSetList->FirstDynamic);
  4456. }
  4457. }
  4458. }
  4459. LOCK_PFN (OldIrql);
  4460. #if DBG0
  4461. if ((Pfn1->u3.e2.ReferenceCount > 1) &&
  4462. (Pfn1->u3.e1.WriteInProgress == 0)) {
  4463. DbgPrint ("MM:SYSLOAD - deleting pool locked for I/O PTE %p, pfn %p, share=%x, refcount=%x, wsindex=%x\n",
  4464. PointerPte,
  4465. PageFrameIndex,
  4466. Pfn1->u2.ShareCount,
  4467. Pfn1->u3.e2.ReferenceCount,
  4468. Pfn1->u1.WsIndex);
  4469. //
  4470. // This case is valid only if the page being deleted
  4471. // contained a lookaside free list entry that wasn't mapped
  4472. // and multiple threads faulted on it and waited together.
  4473. // Some of the faulted threads are still on the ready
  4474. // list but haven't run yet, and so still have references
  4475. // to this page that they picked up during the fault.
  4476. // But this current thread has already allocated the
  4477. // lookaside entry and is now freeing the entire page.
  4478. //
  4479. // BUT - if it is NOT the above case, we really should
  4480. // trap here. However, we don't have a good way to
  4481. // distinguish between the two cases. Note
  4482. // that this complication was inserted when we went to
  4483. // cmpxchg8 because using locks would prevent anyone from
  4484. // accessing the lookaside freelist flinks like this.
  4485. //
  4486. // So, the ASSERT below comes out, but we leave the print
  4487. // above in (with more data added) for the case where it's
  4488. // not a lookaside contender with the reference count, but
  4489. // is instead a truly bad reference that needs to be
  4490. // debugged. The system should crash shortly thereafter
  4491. // and we'll at least have the above print to help us out.
  4492. //
  4493. // ASSERT (Pfn1->u3.e2.ReferenceCount == 1);
  4494. }
  4495. #endif //DBG
  4496. //
  4497. // Check if this is a prototype PTE.
  4498. //
  4499. if (Pfn1->u3.e1.PrototypePte == 1) {
  4500. PMMPTE PointerPde;
  4501. ASSERT (SessionAllocation == TRUE);
  4502. //
  4503. // Capture the state of the modified bit for this
  4504. // PTE.
  4505. //
  4506. MI_CAPTURE_DIRTY_BIT_TO_PFN (PointerPte, Pfn1);
  4507. //
  4508. // Decrement the share and valid counts of the page table
  4509. // page which maps this PTE.
  4510. //
  4511. PointerPde = MiGetPteAddress (PointerPte);
  4512. if (PointerPde->u.Hard.Valid == 0) {
  4513. #if (_MI_PAGING_LEVELS < 3)
  4514. if (!NT_SUCCESS(MiCheckPdeForPagedPool (PointerPte))) {
  4515. #endif
  4516. KeBugCheckEx (MEMORY_MANAGEMENT,
  4517. 0x61940,
  4518. (ULONG_PTR)PointerPte,
  4519. (ULONG_PTR)PointerPde->u.Long,
  4520. (ULONG_PTR)MiGetVirtualAddressMappedByPte(PointerPte));
  4521. #if (_MI_PAGING_LEVELS < 3)
  4522. }
  4523. #endif
  4524. }
  4525. PageTableFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (PointerPde);
  4526. Pfn2 = MI_PFN_ELEMENT (PageTableFrameIndex);
  4527. MiDecrementShareCountInline (Pfn2, PageTableFrameIndex);
  4528. //
  4529. // Decrement the share count for the physical page.
  4530. //
  4531. MiDecrementShareCount (Pfn1, PageFrameIndex);
  4532. //
  4533. // No need to worry about fork prototype PTEs
  4534. // for kernel addresses.
  4535. //
  4536. ASSERT (PointerPte > MiHighestUserPte);
  4537. }
  4538. else {
  4539. PageTableFrameIndex = Pfn1->u4.PteFrame;
  4540. Pfn2 = MI_PFN_ELEMENT (PageTableFrameIndex);
  4541. MiDecrementShareCountInline (Pfn2, PageTableFrameIndex);
  4542. MI_SET_PFN_DELETED (Pfn1);
  4543. MiDecrementShareCount (Pfn1, PageFrameIndex);
  4544. }
  4545. MI_WRITE_INVALID_PTE (PointerPte, NewContents);
  4546. UNLOCK_PFN (OldIrql);
  4547. //
  4548. // Flush the TB for this virtual address.
  4549. //
  4550. if (PteFlushList.Count != MM_MAXIMUM_FLUSH_COUNT) {
  4551. PteFlushList.FlushVa[PteFlushList.Count] =
  4552. MiGetVirtualAddressMappedByPte (PointerPte);
  4553. PteFlushList.Count += 1;
  4554. }
  4555. }
  4556. else if (PteContents.u.Soft.Prototype) {
  4557. ASSERT (SessionAllocation == TRUE);
  4558. //
  4559. // No need to worry about fork prototype PTEs
  4560. // for kernel addresses.
  4561. //
  4562. ASSERT (PointerPte >= MiHighestUserPte);
  4563. MI_WRITE_INVALID_PTE (PointerPte, NewContents);
  4564. //
  4565. // We currently commit for all prototype kernel mappings since
  4566. // we could copy-on-write.
  4567. //
  4568. }
  4569. else if (PteContents.u.Soft.Transition == 1) {
  4570. LOCK_PFN (OldIrql);
  4571. PteContents = *PointerPte;
  4572. if (PteContents.u.Soft.Transition == 0) {
  4573. UNLOCK_PFN (OldIrql);
  4574. continue;
  4575. }
  4576. //
  4577. // Transition, release page.
  4578. //
  4579. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE (&PteContents);
  4580. //
  4581. // Set the pointer to PTE as empty so the page
  4582. // is deleted when the reference count goes to zero.
  4583. //
  4584. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  4585. MI_SET_PFN_DELETED (Pfn1);
  4586. PageTableFrameIndex = Pfn1->u4.PteFrame;
  4587. Pfn2 = MI_PFN_ELEMENT (PageTableFrameIndex);
  4588. MiDecrementShareCountInline (Pfn2, PageTableFrameIndex);
  4589. //
  4590. // Check the reference count for the page, if the reference
  4591. // count is zero, move the page to the free list, if the
  4592. // reference count is not zero, ignore this page. When the
  4593. // reference count goes to zero, it will be placed on the
  4594. // free list.
  4595. //
  4596. if (Pfn1->u3.e2.ReferenceCount == 0) {
  4597. MiUnlinkPageFromList (Pfn1);
  4598. MiReleasePageFileSpace (Pfn1->OriginalPte);
  4599. MiInsertPageInFreeList (PageFrameIndex);
  4600. }
  4601. #if 0
  4602. //
  4603. // This assert is not valid since pool may now be the deferred
  4604. // MmUnlockPages queue in which case the reference count
  4605. // will be nonzero with no write in progress pending.
  4606. //
  4607. if ((Pfn1->u3.e2.ReferenceCount > 1) &&
  4608. (Pfn1->u3.e1.WriteInProgress == 0)) {
  4609. DbgPrint ("MM:SYSLOAD - deleting pool locked for I/O %p\n",
  4610. PageFrameIndex);
  4611. DbgBreakPoint();
  4612. }
  4613. #endif //DBG
  4614. MI_WRITE_INVALID_PTE (PointerPte, NewContents);
  4615. UNLOCK_PFN (OldIrql);
  4616. }
  4617. else {
  4618. //
  4619. // Demand zero, release page file space.
  4620. //
  4621. if (PteContents.u.Soft.PageFileHigh != 0) {
  4622. LOCK_PFN (OldIrql);
  4623. MiReleasePageFileSpace (PteContents);
  4624. UNLOCK_PFN (OldIrql);
  4625. }
  4626. MI_WRITE_INVALID_PTE (PointerPte, NewContents);
  4627. }
  4628. PagesRequired += 1;
  4629. }
  4630. NumberOfPtes -= 1;
  4631. PointerPte += 1;
  4632. }
  4633. if (WsHeld == TRUE) {
  4634. UNLOCK_WORKING_SET (Ws);
  4635. }
  4636. if (PteFlushList.Count != 0) {
  4637. if (SessionAllocation == TRUE) {
  4638. //
  4639. // Session space has no ASN - flush the entire TB.
  4640. //
  4641. MI_FLUSH_ENTIRE_SESSION_TB (TRUE, TRUE);
  4642. }
  4643. MiFlushPteList (&PteFlushList, TRUE);
  4644. }
  4645. if (ARGUMENT_PRESENT(ResidentPages)) {
  4646. *ResidentPages = ValidPages;
  4647. }
  4648. return PagesRequired;
  4649. }
  4650. VOID
  4651. MiMarkSectionWritable (
  4652. IN PIMAGE_SECTION_HEADER SectionTableEntry
  4653. )
  4654. /*++
  4655. Routine Description:
  4656. This function is a nonpaged helper routine that updates the characteristics
  4657. field of the argument section table entry and marks the page dirty so
  4658. that subsequent session loads share the same copy.
  4659. Arguments:
  4660. SectionTableEntry - Supplies the relevant section table entry.
  4661. Return Value:
  4662. None.
  4663. --*/
  4664. {
  4665. PEPROCESS Process;
  4666. PMMPTE PointerPte;
  4667. ULONG FreeBit;
  4668. PMMPFN Pfn1;
  4669. PFN_NUMBER PageFrameIndex;
  4670. KIRQL OldIrql;
  4671. PULONG Characteristics;
  4672. //
  4673. // Modify the PE header through hyperspace and mark the header page
  4674. // dirty so subsequent sections pick up the same copy.
  4675. //
  4676. // Note this makes the entire .rdata (.sdata on IA64) writable
  4677. // instead of just the import tables.
  4678. //
  4679. Process = PsGetCurrentProcess ();
  4680. PointerPte = MiGetPteAddress (&SectionTableEntry->Characteristics);
  4681. LOCK_PFN (OldIrql);
  4682. MiMakeSystemAddressValidPfn (&SectionTableEntry->Characteristics, OldIrql);
  4683. ASSERT (PointerPte->u.Hard.Valid == 1);
  4684. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte);
  4685. Characteristics = MiMapPageInHyperSpaceAtDpc (Process, PageFrameIndex);
  4686. Characteristics = (PULONG)((PCHAR)Characteristics + MiGetByteOffset (&SectionTableEntry->Characteristics));
  4687. *Characteristics |= IMAGE_SCN_MEM_WRITE;
  4688. MiUnmapPageInHyperSpaceFromDpc (Process, Characteristics);
  4689. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  4690. MI_SET_MODIFIED (Pfn1, 1, 0x7);
  4691. if ((Pfn1->OriginalPte.u.Soft.Prototype == 0) &&
  4692. (Pfn1->u3.e1.WriteInProgress == 0)) {
  4693. FreeBit = GET_PAGING_FILE_OFFSET (Pfn1->OriginalPte);
  4694. if ((FreeBit != 0) && (FreeBit != MI_PTE_LOOKUP_NEEDED)) {
  4695. MiReleaseConfirmedPageFileSpace (Pfn1->OriginalPte);
  4696. }
  4697. Pfn1->OriginalPte.u.Soft.PageFileHigh = 0;
  4698. }
  4699. UNLOCK_PFN (OldIrql);
  4700. return;
  4701. }
  4702. VOID
  4703. MiMakeEntireImageCopyOnWrite (
  4704. IN PSUBSECTION Subsection
  4705. )
  4706. /*++
  4707. Routine Description:
  4708. This function sets the protection of all prototype PTEs to copy on write.
  4709. Arguments:
  4710. Subsection - Supplies the base subsection for the entire image.
  4711. Return Value:
  4712. None.
  4713. --*/
  4714. {
  4715. PMMPTE PointerPte;
  4716. PMMPTE ProtoPte;
  4717. PMMPTE LastProtoPte;
  4718. MMPTE PteContents;
  4719. //
  4720. // Note this is only called for image control areas that have at least
  4721. // PAGE_SIZE subsection alignment, and so the first
  4722. // subsection which maps the header can always be skipped.
  4723. //
  4724. while (Subsection = Subsection->NextSubsection) {
  4725. //
  4726. // Don't mark global subsections as copy on write even when the
  4727. // image is relocated. This is easily distinguishable because
  4728. // it is the only subsection that is marked readwrite.
  4729. //
  4730. if (Subsection->u.SubsectionFlags.Protection == MM_READWRITE) {
  4731. continue;
  4732. }
  4733. ProtoPte = Subsection->SubsectionBase;
  4734. LastProtoPte = Subsection->SubsectionBase + Subsection->PtesInSubsection;
  4735. PointerPte = ProtoPte;
  4736. MmLockPagedPool (ProtoPte, Subsection->PtesInSubsection * sizeof (MMPTE));
  4737. do {
  4738. PteContents = *PointerPte;
  4739. ASSERT (PteContents.u.Hard.Valid == 0);
  4740. if (PteContents.u.Long != ZeroPte.u.Long) {
  4741. if ((PteContents.u.Soft.Prototype == 0) &&
  4742. (PteContents.u.Soft.Transition == 1)) {
  4743. if (MiSetProtectionOnTransitionPte (PointerPte, MM_EXECUTE_WRITECOPY)) {
  4744. continue;
  4745. }
  4746. }
  4747. else {
  4748. PointerPte->u.Soft.Protection = MM_EXECUTE_WRITECOPY;
  4749. }
  4750. }
  4751. PointerPte += 1;
  4752. } while (PointerPte < LastProtoPte);
  4753. MmUnlockPagedPool (ProtoPte, Subsection->PtesInSubsection * sizeof (MMPTE));
  4754. Subsection->u.SubsectionFlags.Protection = MM_EXECUTE_WRITECOPY;
  4755. }
  4756. return;
  4757. }
  4758. VOID
  4759. MiSetSystemCodeProtection (
  4760. IN PMMPTE FirstPte,
  4761. IN PMMPTE LastPte,
  4762. IN ULONG ProtectionMask
  4763. )
  4764. /*++
  4765. Routine Description:
  4766. This function sets the protection of system code to read only.
  4767. Arguments:
  4768. FirstPte - Supplies the starting PTE.
  4769. LastPte - Supplies the ending PTE.
  4770. ProtectionMask - Supplies the desired protection mask.
  4771. Return Value:
  4772. None.
  4773. Environment:
  4774. Kernel Mode, APC_LEVEL or below.
  4775. --*/
  4776. {
  4777. KIRQL OldIrql;
  4778. MMPTE PteContents;
  4779. MMPTE TempPte;
  4780. PMMPTE PointerPte;
  4781. PMMPTE PointerPde;
  4782. PMMPTE PointerProtoPte;
  4783. PMMPFN Pfn1;
  4784. LOGICAL SessionAddress;
  4785. PVOID VirtualAddress;
  4786. MMPTE_FLUSH_LIST PteFlushList;
  4787. PETHREAD CurrentThread;
  4788. PMMSUPPORT Ws;
  4789. ASSERT (KeGetCurrentIrql () <= APC_LEVEL);
  4790. PteFlushList.Count = 0;
  4791. #if defined(_X86_)
  4792. ASSERT (MI_IS_PHYSICAL_ADDRESS(MiGetVirtualAddressMappedByPte(FirstPte)) == 0);
  4793. #endif
  4794. CurrentThread = PsGetCurrentThread ();
  4795. PointerPte = FirstPte;
  4796. if (MI_IS_SESSION_ADDRESS (MiGetVirtualAddressMappedByPte(FirstPte))) {
  4797. Ws = &MmSessionSpace->GlobalVirtualAddress->Vm;
  4798. SessionAddress = TRUE;
  4799. }
  4800. else {
  4801. Ws = &MmSystemCacheWs;
  4802. SessionAddress = FALSE;
  4803. }
  4804. LOCK_WORKING_SET (Ws);
  4805. //
  4806. // Set these PTEs to the specified protection.
  4807. //
  4808. // Note that the write bit may already be off (in the valid PTE) if the
  4809. // page has already been inpaged from the paging file and has not since
  4810. // been dirtied.
  4811. //
  4812. LOCK_PFN (OldIrql);
  4813. while (PointerPte <= LastPte) {
  4814. PteContents = *PointerPte;
  4815. if ((PteContents.u.Long == 0) ||
  4816. ((!*MiPteStr) &&
  4817. ((ProtectionMask == MM_READONLY) || (ProtectionMask == MM_EXECUTE_READ)))) {
  4818. PointerPte += 1;
  4819. continue;
  4820. }
  4821. if (PteContents.u.Hard.Valid == 1) {
  4822. Pfn1 = MI_PFN_ELEMENT (PteContents.u.Hard.PageFrameNumber);
  4823. if (Pfn1->u3.e1.PrototypePte == 1) {
  4824. //
  4825. // This must be a session address. The prototype PTE contains
  4826. // the protection that is pushed out to the real PTE after
  4827. // it's been trimmed so update that too.
  4828. //
  4829. PointerProtoPte = Pfn1->PteAddress;
  4830. PointerPde = MiGetPteAddress (PointerProtoPte);
  4831. if (PointerPde->u.Hard.Valid == 0) {
  4832. if (SessionAddress == TRUE) {
  4833. //
  4834. // Unlock the session working set and lock the
  4835. // system working set as we need to make the backing
  4836. // prototype PTE valid.
  4837. //
  4838. UNLOCK_PFN (OldIrql);
  4839. UNLOCK_WORKING_SET (Ws);
  4840. LOCK_WORKING_SET (&MmSystemCacheWs);
  4841. LOCK_PFN (OldIrql);
  4842. }
  4843. MiMakeSystemAddressValidPfnSystemWs (PointerProtoPte,
  4844. OldIrql);
  4845. if (SessionAddress == TRUE) {
  4846. //
  4847. // Unlock the system working set and lock the
  4848. // session working set as we have made the backing
  4849. // prototype PTE valid and can now handle the
  4850. // original session PTE.
  4851. //
  4852. UNLOCK_PFN (OldIrql);
  4853. UNLOCK_WORKING_SET (&MmSystemCacheWs);
  4854. LOCK_WORKING_SET (Ws);
  4855. LOCK_PFN (OldIrql);
  4856. }
  4857. //
  4858. // The world may have changed while we waited.
  4859. //
  4860. continue;
  4861. }
  4862. }
  4863. else {
  4864. PointerProtoPte = NULL;
  4865. }
  4866. Pfn1->OriginalPte.u.Soft.Protection = ProtectionMask;
  4867. MI_MAKE_VALID_PTE (TempPte,
  4868. PteContents.u.Hard.PageFrameNumber,
  4869. ProtectionMask,
  4870. PointerPte);
  4871. //
  4872. // Note the dirty and write bits get turned off here.
  4873. // Any existing pagefile addresses for clean pages are preserved.
  4874. //
  4875. if (MI_IS_PTE_DIRTY (PteContents)) {
  4876. MI_CAPTURE_DIRTY_BIT_TO_PFN (&PteContents, Pfn1);
  4877. }
  4878. MI_WRITE_VALID_PTE_NEW_PROTECTION (PointerPte, TempPte);
  4879. if (PointerProtoPte != NULL) {
  4880. MI_WRITE_VALID_PTE_NEW_PROTECTION (PointerProtoPte, TempPte);
  4881. }
  4882. if (PteFlushList.Count < MM_MAXIMUM_FLUSH_COUNT) {
  4883. VirtualAddress = MiGetVirtualAddressMappedByPte (PointerPte);
  4884. PteFlushList.FlushVa[PteFlushList.Count] = VirtualAddress;
  4885. PteFlushList.Count += 1;
  4886. }
  4887. }
  4888. else if (PteContents.u.Soft.Prototype == 1) {
  4889. //
  4890. // WITH REGARDS TO SESSION SPACE :
  4891. //
  4892. // Nothing needs to be done if the image was linked with
  4893. // greater than or equal to PAGE_SIZE subsection alignment
  4894. // because image section creation assigned proper protections
  4895. // to each subsection.
  4896. //
  4897. // However, if the image had less than PAGE_SIZE subsection
  4898. // alignment, then image creation uses a single copyonwrite
  4899. // subsection to control the entire image, so individual
  4900. // protections need to be applied now. Note well - this must
  4901. // only be done *ONCE* when the image is first loaded - subsequent
  4902. // loads of this image in other sessions do not need to update
  4903. // the common prototype PTEs.
  4904. //
  4905. PointerProtoPte = MiPteToProto (PointerPte);
  4906. ASSERT (!MI_IS_PHYSICAL_ADDRESS (PointerProtoPte));
  4907. PointerPde = MiGetPteAddress (PointerProtoPte);
  4908. if (PointerPde->u.Hard.Valid == 0) {
  4909. if (SessionAddress == TRUE) {
  4910. UNLOCK_PFN (OldIrql);
  4911. UNLOCK_WORKING_SET (Ws);
  4912. LOCK_WORKING_SET (&MmSystemCacheWs);
  4913. LOCK_PFN (OldIrql);
  4914. }
  4915. MiMakeSystemAddressValidPfnSystemWs (PointerProtoPte, OldIrql);
  4916. if (SessionAddress == TRUE) {
  4917. UNLOCK_PFN (OldIrql);
  4918. UNLOCK_WORKING_SET (&MmSystemCacheWs);
  4919. LOCK_WORKING_SET (Ws);
  4920. LOCK_PFN (OldIrql);
  4921. }
  4922. //
  4923. // The world may have changed while we waited.
  4924. //
  4925. continue;
  4926. }
  4927. PteContents = *PointerProtoPte;
  4928. if (PteContents.u.Long != ZeroPte.u.Long) {
  4929. ASSERT (PteContents.u.Hard.Valid == 0);
  4930. PointerProtoPte->u.Soft.Protection = ProtectionMask;
  4931. if ((PteContents.u.Soft.Prototype == 0) &&
  4932. (PteContents.u.Soft.Transition == 1)) {
  4933. Pfn1 = MI_PFN_ELEMENT (PteContents.u.Trans.PageFrameNumber);
  4934. Pfn1->OriginalPte.u.Soft.Protection = ProtectionMask;
  4935. }
  4936. }
  4937. }
  4938. else if (PteContents.u.Soft.Transition == 1) {
  4939. Pfn1 = MI_PFN_ELEMENT (PteContents.u.Trans.PageFrameNumber);
  4940. Pfn1->OriginalPte.u.Soft.Protection = ProtectionMask;
  4941. PointerPte->u.Soft.Protection = ProtectionMask;
  4942. }
  4943. else {
  4944. //
  4945. // Must be page file space or demand zero.
  4946. //
  4947. PointerPte->u.Soft.Protection = ProtectionMask;
  4948. }
  4949. PointerPte += 1;
  4950. }
  4951. if (PteFlushList.Count != 0) {
  4952. if (SessionAddress == TRUE) {
  4953. //
  4954. // Session space has no ASN - flush the entire TB.
  4955. //
  4956. MI_FLUSH_ENTIRE_SESSION_TB (TRUE, TRUE);
  4957. }
  4958. MiFlushPteList (&PteFlushList, TRUE);
  4959. }
  4960. UNLOCK_PFN (OldIrql);
  4961. UNLOCK_WORKING_SET (Ws);
  4962. return;
  4963. }
  4964. VOID
  4965. MiWriteProtectSystemImage (
  4966. IN PVOID DllBase
  4967. )
  4968. /*++
  4969. Routine Description:
  4970. This function sets the protection of a system component to read only.
  4971. Arguments:
  4972. DllBase - Supplies the base address of the system component.
  4973. Return Value:
  4974. None.
  4975. --*/
  4976. {
  4977. ULONG SectionProtection;
  4978. ULONG NumberOfSubsections;
  4979. ULONG SectionVirtualSize;
  4980. ULONG OffsetToSectionTable;
  4981. PFN_NUMBER NumberOfPtes;
  4982. ULONG_PTR VirtualAddress;
  4983. PVOID LastVirtualAddress;
  4984. PMMPTE PointerPte;
  4985. PMMPTE FirstPte;
  4986. PMMPTE LastPte;
  4987. PMMPTE LastImagePte;
  4988. PMMPTE WritablePte;
  4989. PIMAGE_NT_HEADERS NtHeader;
  4990. PIMAGE_FILE_HEADER FileHeader;
  4991. PIMAGE_SECTION_HEADER SectionTableEntry;
  4992. LOGICAL SessionAddress;
  4993. PAGED_CODE();
  4994. if (MI_IS_PHYSICAL_ADDRESS (DllBase)) {
  4995. return;
  4996. }
  4997. NtHeader = RtlImageNtHeader (DllBase);
  4998. if (NtHeader == NULL) {
  4999. return;
  5000. }
  5001. //
  5002. // All session drivers must be one way or the other - no mixing is allowed
  5003. // within multiple copy-on-write drivers.
  5004. //
  5005. if (MI_IS_SESSION_ADDRESS (DllBase) == 0) {
  5006. //
  5007. // Images prior to Win2000 were not protected from stepping all over
  5008. // their (and others) code and readonly data. Here we somewhat
  5009. // preserve that behavior, but don't allow them to step on anyone else.
  5010. //
  5011. if (NtHeader->OptionalHeader.MajorOperatingSystemVersion < 5) {
  5012. return;
  5013. }
  5014. if (NtHeader->OptionalHeader.MajorImageVersion < 5) {
  5015. return;
  5016. }
  5017. SessionAddress = FALSE;
  5018. }
  5019. else {
  5020. SessionAddress = TRUE;
  5021. }
  5022. //
  5023. // If the image has section alignment of at least PAGE_SIZE, then
  5024. // the image section was created with individual subsections and
  5025. // proper permissions already applied to the prototype PTEs. However,
  5026. // our caller may have been changing the individual PTE protections
  5027. // in order to relocate the image, so march on regardless of section
  5028. // alignment.
  5029. //
  5030. NumberOfPtes = BYTES_TO_PAGES (NtHeader->OptionalHeader.SizeOfImage);
  5031. FileHeader = &NtHeader->FileHeader;
  5032. NumberOfSubsections = FileHeader->NumberOfSections;
  5033. ASSERT (NumberOfSubsections != 0);
  5034. OffsetToSectionTable = sizeof(ULONG) +
  5035. sizeof(IMAGE_FILE_HEADER) +
  5036. FileHeader->SizeOfOptionalHeader;
  5037. SectionTableEntry = (PIMAGE_SECTION_HEADER)((PCHAR)NtHeader +
  5038. OffsetToSectionTable);
  5039. //
  5040. // Verify the image contains subsections ordered by increasing virtual
  5041. // address and that there are no overlaps.
  5042. //
  5043. FirstPte = NULL;
  5044. LastVirtualAddress = DllBase;
  5045. for ( ; NumberOfSubsections > 0; NumberOfSubsections -= 1, SectionTableEntry += 1) {
  5046. if (SectionTableEntry->Misc.VirtualSize == 0) {
  5047. SectionVirtualSize = SectionTableEntry->SizeOfRawData;
  5048. }
  5049. else {
  5050. SectionVirtualSize = SectionTableEntry->Misc.VirtualSize;
  5051. }
  5052. VirtualAddress = (ULONG_PTR)DllBase + SectionTableEntry->VirtualAddress;
  5053. if ((PVOID)VirtualAddress <= LastVirtualAddress) {
  5054. //
  5055. // Subsections are not in an increasing virtual address ordering.
  5056. // No protection is provided for such a poorly linked image.
  5057. //
  5058. KdPrint (("MM:sysload - Image at %p is badly linked\n", DllBase));
  5059. return;
  5060. }
  5061. LastVirtualAddress = (PVOID)((PCHAR)VirtualAddress + SectionVirtualSize - 1);
  5062. }
  5063. NumberOfSubsections = FileHeader->NumberOfSections;
  5064. ASSERT (NumberOfSubsections != 0);
  5065. SectionTableEntry = (PIMAGE_SECTION_HEADER)((PCHAR)NtHeader +
  5066. OffsetToSectionTable);
  5067. LastVirtualAddress = NULL;
  5068. //
  5069. // Set writable PTE here so the image headers are excluded. This is
  5070. // needed so that locking down of sections can continue to edit the
  5071. // image headers for counts.
  5072. //
  5073. WritablePte = MiGetPteAddress ((PVOID)((ULONG_PTR)(SectionTableEntry + NumberOfSubsections) - 1));
  5074. LastImagePte = MiGetPteAddress(DllBase) + NumberOfPtes;
  5075. for ( ; NumberOfSubsections > 0; NumberOfSubsections -= 1, SectionTableEntry += 1) {
  5076. if (SectionTableEntry->Misc.VirtualSize == 0) {
  5077. SectionVirtualSize = SectionTableEntry->SizeOfRawData;
  5078. }
  5079. else {
  5080. SectionVirtualSize = SectionTableEntry->Misc.VirtualSize;
  5081. }
  5082. VirtualAddress = (ULONG_PTR)DllBase + SectionTableEntry->VirtualAddress;
  5083. PointerPte = MiGetPteAddress ((PVOID)VirtualAddress);
  5084. if (PointerPte >= LastImagePte) {
  5085. //
  5086. // Skip relocation subsections (which aren't given VA space).
  5087. //
  5088. break;
  5089. }
  5090. SectionProtection = (SectionTableEntry->Characteristics & (IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_EXECUTE));
  5091. if (SectionProtection & IMAGE_SCN_MEM_WRITE) {
  5092. //
  5093. // This is a writable subsection, skip it. Make sure if it's
  5094. // sharing a PTE (and update the linker so this doesn't happen
  5095. // for the kernel at least) that the last PTE isn't made
  5096. // read only.
  5097. //
  5098. WritablePte = MiGetPteAddress ((PVOID)(VirtualAddress + SectionVirtualSize - 1));
  5099. if (LastVirtualAddress != NULL) {
  5100. LastPte = (PVOID) MiGetPteAddress (LastVirtualAddress);
  5101. if (LastPte == PointerPte) {
  5102. LastPte -= 1;
  5103. }
  5104. if (FirstPte <= LastPte) {
  5105. ASSERT (PointerPte < LastImagePte);
  5106. if (LastPte >= LastImagePte) {
  5107. LastPte = LastImagePte - 1;
  5108. }
  5109. MiSetSystemCodeProtection (FirstPte, LastPte, MM_EXECUTE_READ);
  5110. }
  5111. LastVirtualAddress = NULL;
  5112. }
  5113. continue;
  5114. }
  5115. if (LastVirtualAddress == NULL) {
  5116. //
  5117. // There is no previous subsection or the previous
  5118. // subsection was writable. Thus the current starting PTE
  5119. // could be mapping both a readonly and a readwrite
  5120. // subsection if the image alignment is less than PAGE_SIZE.
  5121. // These cases (in either order) are handled here.
  5122. //
  5123. if (PointerPte == WritablePte) {
  5124. LastPte = MiGetPteAddress ((PVOID)(VirtualAddress + SectionVirtualSize - 1));
  5125. if (PointerPte == LastPte) {
  5126. //
  5127. // Nothing can be protected in this subsection
  5128. // due to the image alignment specified for the executable.
  5129. //
  5130. continue;
  5131. }
  5132. PointerPte += 1;
  5133. }
  5134. FirstPte = PointerPte;
  5135. }
  5136. LastVirtualAddress = (PVOID)((PCHAR)VirtualAddress + SectionVirtualSize - 1);
  5137. }
  5138. if (LastVirtualAddress != NULL) {
  5139. LastPte = (PVOID) MiGetPteAddress (LastVirtualAddress);
  5140. if ((FirstPte <= LastPte) && (FirstPte < LastImagePte)) {
  5141. if (LastPte >= LastImagePte) {
  5142. LastPte = LastImagePte - 1;
  5143. }
  5144. MiSetSystemCodeProtection (FirstPte, LastPte, MM_EXECUTE_READ);
  5145. }
  5146. }
  5147. return;
  5148. }
  5149. VOID
  5150. MiSessionProcessGlobalSubsections (
  5151. IN PKLDR_DATA_TABLE_ENTRY DataTableEntry
  5152. )
  5153. /*++
  5154. Routine Description:
  5155. This function sets the protection of a session driver's subsections
  5156. to globally shared if their PE header specifies them as such.
  5157. Arguments:
  5158. DataTableEntry - Supplies the loaded module list entry for the driver.
  5159. Return Value:
  5160. None.
  5161. --*/
  5162. {
  5163. PVOID DllBase;
  5164. PSUBSECTION Subsection;
  5165. PMMPTE RealPteBase;
  5166. PMMPTE PrototypePteBase;
  5167. PCONTROL_AREA ControlArea;
  5168. PIMAGE_NT_HEADERS NtHeader;
  5169. PMMPTE PointerPte;
  5170. PMMPTE LastPte;
  5171. LOGICAL GlobalSubsectionSupport;
  5172. PIMAGE_ENTRY_IN_SESSION Image;
  5173. ULONG Count;
  5174. PAGED_CODE();
  5175. Image = MiSessionLookupImage (DataTableEntry->DllBase);
  5176. if (Image != NULL) {
  5177. ASSERT (MmSessionSpace->ImageLoadingCount >= 0);
  5178. if (Image->ImageLoading == TRUE) {
  5179. Image->ImageLoading = FALSE;
  5180. ASSERT (MmSessionSpace->ImageLoadingCount > 0);
  5181. InterlockedDecrement (&MmSessionSpace->ImageLoadingCount);
  5182. }
  5183. }
  5184. else {
  5185. ASSERT (FALSE);
  5186. }
  5187. DllBase = DataTableEntry->DllBase;
  5188. ControlArea = ((PSECTION)DataTableEntry->SectionPointer)->Segment->ControlArea;
  5189. ASSERT (MI_IS_PHYSICAL_ADDRESS(DllBase) == FALSE);
  5190. ASSERT (MI_IS_SESSION_ADDRESS(DllBase));
  5191. NtHeader = RtlImageNtHeader (DllBase);
  5192. ASSERT (NtHeader);
  5193. if (NtHeader->OptionalHeader.SectionAlignment < PAGE_SIZE) {
  5194. if (Image->GlobalSubs != NULL) {
  5195. ExFreePool (Image->GlobalSubs);
  5196. Image->GlobalSubs = NULL;
  5197. }
  5198. return;
  5199. }
  5200. //
  5201. // Win XP and Win2000 did not support global shared subsections
  5202. // for session images. To ensure backwards compatibility for existing
  5203. // drivers, ensure that only newer ones get this feature.
  5204. //
  5205. GlobalSubsectionSupport = FALSE;
  5206. if (NtHeader->OptionalHeader.MajorOperatingSystemVersion > 5) {
  5207. GlobalSubsectionSupport = TRUE;
  5208. }
  5209. else if (NtHeader->OptionalHeader.MajorOperatingSystemVersion == 5) {
  5210. if (NtHeader->OptionalHeader.MinorOperatingSystemVersion > 1) {
  5211. GlobalSubsectionSupport = TRUE;
  5212. }
  5213. else if (NtHeader->OptionalHeader.MinorOperatingSystemVersion == 1) {
  5214. if (NtHeader->OptionalHeader.MajorImageVersion > 5) {
  5215. GlobalSubsectionSupport = TRUE;
  5216. }
  5217. else if (NtHeader->OptionalHeader.MajorImageVersion == 5) {
  5218. if (NtHeader->OptionalHeader.MinorImageVersion > 1) {
  5219. GlobalSubsectionSupport = TRUE;
  5220. }
  5221. else if (NtHeader->OptionalHeader.MinorImageVersion == 1) {
  5222. if (NtHeader->OptionalHeader.MajorSubsystemVersion > 5) {
  5223. GlobalSubsectionSupport = TRUE;
  5224. }
  5225. else if (NtHeader->OptionalHeader.MajorSubsystemVersion == 5) {
  5226. if (NtHeader->OptionalHeader.MinorSubsystemVersion >= 2) {
  5227. GlobalSubsectionSupport = TRUE;
  5228. }
  5229. }
  5230. }
  5231. }
  5232. }
  5233. }
  5234. if (GlobalSubsectionSupport == FALSE) {
  5235. if (Image->GlobalSubs != NULL) {
  5236. ExFreePool (Image->GlobalSubs);
  5237. Image->GlobalSubs = NULL;
  5238. }
  5239. return;
  5240. }
  5241. if ((ControlArea->u.Flags.GlobalOnlyPerSession == 0) &&
  5242. (ControlArea->u.Flags.Rom == 0)) {
  5243. Subsection = (PSUBSECTION)(ControlArea + 1);
  5244. }
  5245. else {
  5246. Subsection = (PSUBSECTION)((PLARGE_CONTROL_AREA)ControlArea + 1);
  5247. }
  5248. RealPteBase = MiGetPteAddress (DllBase);
  5249. PrototypePteBase = Subsection->SubsectionBase;
  5250. //
  5251. // Loop through all the subsections.
  5252. //
  5253. if (ControlArea->u.Flags.Image == 1) {
  5254. do {
  5255. if (Subsection->u.SubsectionFlags.GlobalMemory == 1) {
  5256. PointerPte = RealPteBase + (Subsection->SubsectionBase - PrototypePteBase);
  5257. LastPte = PointerPte + Subsection->PtesInSubsection - 1;
  5258. MiSetSystemCodeProtection (PointerPte,
  5259. LastPte,
  5260. Subsection->u.SubsectionFlags.Protection);
  5261. }
  5262. Subsection = Subsection->NextSubsection;
  5263. } while (Subsection != NULL);
  5264. }
  5265. else if (Image->GlobalSubs != NULL) {
  5266. Count = 0;
  5267. ASSERT (Subsection->NextSubsection == NULL);
  5268. while (Image->GlobalSubs[Count].PteCount != 0) {
  5269. PointerPte = RealPteBase + Image->GlobalSubs[Count].PteIndex;
  5270. LastPte = PointerPte + Image->GlobalSubs[Count].PteCount - 1;
  5271. MiSetSystemCodeProtection (PointerPte,
  5272. LastPte,
  5273. Image->GlobalSubs[Count].Protection);
  5274. Count += 1;
  5275. }
  5276. ExFreePool (Image->GlobalSubs);
  5277. Image->GlobalSubs = NULL;
  5278. }
  5279. return;
  5280. }
  5281. VOID
  5282. MiUpdateThunks (
  5283. IN PLOADER_PARAMETER_BLOCK LoaderBlock,
  5284. IN PVOID OldAddress,
  5285. IN PVOID NewAddress,
  5286. IN ULONG NumberOfBytes
  5287. )
  5288. /*++
  5289. Routine Description:
  5290. This function updates the IATs of all the loaded modules in the system
  5291. to handle a newly relocated image.
  5292. Arguments:
  5293. LoaderBlock - Supplies a pointer to the system loader block.
  5294. OldAddress - Supplies the old address of the DLL which was just relocated.
  5295. NewAddress - Supplies the new address of the DLL which was just relocated.
  5296. NumberOfBytes - Supplies the number of bytes spanned by the DLL.
  5297. Return Value:
  5298. None.
  5299. --*/
  5300. {
  5301. PULONG_PTR ImportThunk;
  5302. ULONG_PTR OldAddressHigh;
  5303. ULONG_PTR AddressDifference;
  5304. PKLDR_DATA_TABLE_ENTRY DataTableEntry;
  5305. PLIST_ENTRY NextEntry;
  5306. ULONG_PTR i;
  5307. ULONG ImportSize;
  5308. //
  5309. // Note this routine must not call any modules outside the kernel.
  5310. // This is because that module may itself be the one being relocated right
  5311. // now.
  5312. //
  5313. OldAddressHigh = (ULONG_PTR)((PCHAR)OldAddress + NumberOfBytes - 1);
  5314. AddressDifference = (ULONG_PTR)NewAddress - (ULONG_PTR)OldAddress;
  5315. NextEntry = LoaderBlock->LoadOrderListHead.Flink;
  5316. for ( ; NextEntry != &LoaderBlock->LoadOrderListHead; NextEntry = NextEntry->Flink) {
  5317. DataTableEntry = CONTAINING_RECORD(NextEntry,
  5318. KLDR_DATA_TABLE_ENTRY,
  5319. InLoadOrderLinks);
  5320. ImportThunk = (PULONG_PTR) RtlImageDirectoryEntryToData (
  5321. DataTableEntry->DllBase,
  5322. TRUE,
  5323. IMAGE_DIRECTORY_ENTRY_IAT,
  5324. &ImportSize);
  5325. if (ImportThunk == NULL) {
  5326. continue;
  5327. }
  5328. ImportSize /= sizeof(PULONG_PTR);
  5329. for (i = 0; i < ImportSize; i += 1, ImportThunk += 1) {
  5330. if (*ImportThunk >= (ULONG_PTR)OldAddress && *ImportThunk <= OldAddressHigh) {
  5331. *ImportThunk += AddressDifference;
  5332. }
  5333. }
  5334. }
  5335. }
  5336. VOID
  5337. MiReloadBootLoadedDrivers (
  5338. IN PLOADER_PARAMETER_BLOCK LoaderBlock
  5339. )
  5340. /*++
  5341. Routine Description:
  5342. The kernel, HAL and boot drivers are relocated by the loader.
  5343. All the boot drivers are then relocated again here.
  5344. This function relocates osloader-loaded images into system PTEs. This
  5345. gives these images the benefits that all other drivers already enjoy,
  5346. including :
  5347. 1. Paging of the drivers (this is more than 500K today).
  5348. 2. Write-protection of their text sections.
  5349. 3. Automatic unload of drivers on last dereference.
  5350. Note care must be taken when processing HIGHADJ relocations more than once.
  5351. Arguments:
  5352. LoaderBlock - Supplies a pointer to the system loader block.
  5353. Return Value:
  5354. None.
  5355. Environment:
  5356. Kernel mode, Phase 0 Initialization.
  5357. --*/
  5358. {
  5359. LOGICAL UsedLargePage;
  5360. LOGICAL HasRelocations;
  5361. PKLDR_DATA_TABLE_ENTRY DataTableEntry;
  5362. PLIST_ENTRY NextEntry;
  5363. PIMAGE_FILE_HEADER FileHeader;
  5364. PIMAGE_NT_HEADERS NtHeader;
  5365. PIMAGE_DATA_DIRECTORY DataDirectory;
  5366. ULONG_PTR i;
  5367. ULONG RoundedNumberOfPtes;
  5368. ULONG NumberOfPtes;
  5369. ULONG NumberOfLoaderPtes;
  5370. PMMPTE PointerPte;
  5371. PMMPTE LastPte;
  5372. PMMPTE LoaderPte;
  5373. MMPTE PteContents;
  5374. MMPTE TempPte;
  5375. PVOID LoaderImageAddress;
  5376. PVOID NewImageAddress;
  5377. NTSTATUS Status;
  5378. PFN_NUMBER PageFrameIndex;
  5379. PFN_NUMBER PteFramePage;
  5380. PMMPTE PteFramePointer;
  5381. PMMPFN Pfn1;
  5382. PMMPFN Pfn2;
  5383. KIRQL OldIrql;
  5384. PCHAR RelocatedVa;
  5385. PCHAR NonRelocatedVa;
  5386. LOGICAL StopMoving;
  5387. #if !defined (_X86_)
  5388. //
  5389. // Only try to preserve low memory on x86 machines.
  5390. //
  5391. MmMakeLowMemory = FALSE;
  5392. #endif
  5393. StopMoving = FALSE;
  5394. i = 0;
  5395. NextEntry = LoaderBlock->LoadOrderListHead.Flink;
  5396. for ( ; NextEntry != &LoaderBlock->LoadOrderListHead; NextEntry = NextEntry->Flink) {
  5397. //
  5398. // Skip the kernel and the HAL. Note their relocation sections will
  5399. // be automatically reclaimed.
  5400. //
  5401. i += 1;
  5402. if (i <= 2) {
  5403. continue;
  5404. }
  5405. DataTableEntry = CONTAINING_RECORD (NextEntry,
  5406. KLDR_DATA_TABLE_ENTRY,
  5407. InLoadOrderLinks);
  5408. NtHeader = RtlImageNtHeader (DataTableEntry->DllBase);
  5409. //
  5410. // Ensure that the relocation section exists and that the loader
  5411. // hasn't freed it already.
  5412. //
  5413. if (NtHeader == NULL) {
  5414. continue;
  5415. }
  5416. FileHeader = &NtHeader->FileHeader;
  5417. if (FileHeader->Characteristics & IMAGE_FILE_RELOCS_STRIPPED) {
  5418. continue;
  5419. }
  5420. if (IMAGE_DIRECTORY_ENTRY_BASERELOC >= NtHeader->OptionalHeader.NumberOfRvaAndSizes) {
  5421. continue;
  5422. }
  5423. DataDirectory = &NtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
  5424. if (DataDirectory->VirtualAddress == 0) {
  5425. HasRelocations = FALSE;
  5426. }
  5427. else {
  5428. if (DataDirectory->VirtualAddress + DataDirectory->Size > DataTableEntry->SizeOfImage) {
  5429. //
  5430. // The relocation section has already been freed, the user must
  5431. // be using an old loader that didn't save the relocations.
  5432. //
  5433. continue;
  5434. }
  5435. HasRelocations = TRUE;
  5436. }
  5437. LoaderImageAddress = DataTableEntry->DllBase;
  5438. LoaderPte = MiGetPteAddress(DataTableEntry->DllBase);
  5439. NumberOfLoaderPtes = (ULONG)((ROUND_TO_PAGES(DataTableEntry->SizeOfImage)) >> PAGE_SHIFT);
  5440. LOCK_PFN (OldIrql);
  5441. PointerPte = LoaderPte;
  5442. LastPte = PointerPte + NumberOfLoaderPtes;
  5443. while (PointerPte < LastPte) {
  5444. ASSERT (PointerPte->u.Hard.Valid == 1);
  5445. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte);
  5446. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  5447. //
  5448. // Mark the page as modified so boot drivers that call
  5449. // MmPageEntireDriver don't lose their unmodified data !
  5450. //
  5451. MI_SET_MODIFIED (Pfn1, 1, 0x14);
  5452. PointerPte += 1;
  5453. }
  5454. UNLOCK_PFN (OldIrql);
  5455. NumberOfPtes = NumberOfLoaderPtes;
  5456. NewImageAddress = LoaderImageAddress;
  5457. UsedLargePage = MiUseLargeDriverPage (NumberOfPtes,
  5458. &NewImageAddress,
  5459. &DataTableEntry->BaseDllName,
  5460. 0);
  5461. if (UsedLargePage == TRUE) {
  5462. //
  5463. // This image has been loaded into a large page mapping.
  5464. //
  5465. RelocatedVa = NewImageAddress;
  5466. NonRelocatedVa = (PCHAR) DataTableEntry->DllBase;
  5467. PointerPte -= NumberOfPtes;
  5468. goto Fixup;
  5469. }
  5470. //
  5471. // Extra PTEs are allocated here to map the relocation section at the
  5472. // new address so the image can be relocated.
  5473. //
  5474. PointerPte = MiReserveSystemPtes (NumberOfPtes, SystemPteSpace);
  5475. if (PointerPte == NULL) {
  5476. continue;
  5477. }
  5478. LastPte = PointerPte + NumberOfPtes;
  5479. NewImageAddress = MiGetVirtualAddressMappedByPte (PointerPte);
  5480. #if DBG_SYSLOAD
  5481. DbgPrint ("Relocating %wZ from %p to %p, %x bytes\n",
  5482. &DataTableEntry->FullDllName,
  5483. DataTableEntry->DllBase,
  5484. NewImageAddress,
  5485. DataTableEntry->SizeOfImage
  5486. );
  5487. #endif
  5488. //
  5489. // This assert is important because the assumption is made that PTEs
  5490. // (not superpages) are mapping these drivers.
  5491. //
  5492. ASSERT (InitializationPhase == 0);
  5493. //
  5494. // If the system is configured to make low memory available for ISA
  5495. // type drivers, then copy the boot loaded drivers now. Otherwise
  5496. // only PTE adjustment is done. Presumably some day when ISA goes
  5497. // away this code can be removed.
  5498. //
  5499. RelocatedVa = NewImageAddress;
  5500. NonRelocatedVa = (PCHAR) DataTableEntry->DllBase;
  5501. while (PointerPte < LastPte) {
  5502. PteContents = *LoaderPte;
  5503. ASSERT (PteContents.u.Hard.Valid == 1);
  5504. if (MmMakeLowMemory == TRUE) {
  5505. #if DBG
  5506. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (LoaderPte);
  5507. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  5508. ASSERT (Pfn1->u1.WsIndex == 0);
  5509. #endif
  5510. LOCK_PFN (OldIrql);
  5511. if (MmAvailablePages < MM_HIGH_LIMIT) {
  5512. MiEnsureAvailablePageOrWait (NULL, NULL, OldIrql);
  5513. }
  5514. PageFrameIndex = MiRemoveAnyPage(
  5515. MI_GET_PAGE_COLOR_FROM_PTE (PointerPte));
  5516. if (PageFrameIndex < (16*1024*1024)/PAGE_SIZE) {
  5517. //
  5518. // If the frames cannot be replaced with high pages
  5519. // then stop copying.
  5520. //
  5521. #if defined (_MI_MORE_THAN_4GB_)
  5522. if (MiNoLowMemory == 0)
  5523. #endif
  5524. StopMoving = TRUE;
  5525. }
  5526. MI_MAKE_VALID_PTE (TempPte,
  5527. PageFrameIndex,
  5528. MM_EXECUTE_READWRITE,
  5529. PointerPte);
  5530. MI_SET_PTE_DIRTY (TempPte);
  5531. MI_SET_ACCESSED_IN_PTE (&TempPte, 1);
  5532. MI_WRITE_VALID_PTE (PointerPte, TempPte);
  5533. MiInitializePfn (PageFrameIndex, PointerPte, 1);
  5534. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  5535. MI_SET_MODIFIED (Pfn1, 1, 0x15);
  5536. //
  5537. // Initialize the WsIndex just like the original page had it.
  5538. //
  5539. Pfn1->u1.WsIndex = 0;
  5540. UNLOCK_PFN (OldIrql);
  5541. RtlCopyMemory (RelocatedVa, NonRelocatedVa, PAGE_SIZE);
  5542. RelocatedVa += PAGE_SIZE;
  5543. NonRelocatedVa += PAGE_SIZE;
  5544. }
  5545. else {
  5546. MI_MAKE_VALID_PTE (TempPte,
  5547. PteContents.u.Hard.PageFrameNumber,
  5548. MM_EXECUTE_READWRITE,
  5549. PointerPte);
  5550. MI_SET_PTE_DIRTY (TempPte);
  5551. MI_WRITE_VALID_PTE (PointerPte, TempPte);
  5552. }
  5553. PointerPte += 1;
  5554. LoaderPte += 1;
  5555. }
  5556. PointerPte -= NumberOfPtes;
  5557. Fixup:
  5558. ASSERT (*(PULONG)NewImageAddress == *(PULONG)LoaderImageAddress);
  5559. //
  5560. // Image is now mapped at the new address. Relocate it (again).
  5561. //
  5562. NtHeader->OptionalHeader.ImageBase = (ULONG_PTR)LoaderImageAddress;
  5563. if ((MmMakeLowMemory == TRUE) || (UsedLargePage == TRUE)) {
  5564. PIMAGE_NT_HEADERS NtHeader2;
  5565. NtHeader2 = (PIMAGE_NT_HEADERS)((PCHAR)NtHeader + (RelocatedVa - NonRelocatedVa));
  5566. NtHeader2->OptionalHeader.ImageBase = (ULONG_PTR)LoaderImageAddress;
  5567. }
  5568. if (HasRelocations == TRUE) {
  5569. Status = (NTSTATUS)LdrRelocateImage(NewImageAddress,
  5570. (CONST PCHAR)"SYSLDR",
  5571. (ULONG)STATUS_SUCCESS,
  5572. (ULONG)STATUS_CONFLICTING_ADDRESSES,
  5573. (ULONG)STATUS_INVALID_IMAGE_FORMAT
  5574. );
  5575. if (!NT_SUCCESS(Status)) {
  5576. if (UsedLargePage == TRUE) {
  5577. ASSERT (MI_PDE_MAPS_LARGE_PAGE (MiGetPdeAddress (NewImageAddress)));
  5578. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (MiGetPdeAddress (NewImageAddress)) + MiGetPteOffset (NewImageAddress);
  5579. RoundedNumberOfPtes = MI_ROUND_TO_SIZE (NumberOfPtes,
  5580. MM_MINIMUM_VA_FOR_LARGE_PAGE >> PAGE_SHIFT);
  5581. MiUnmapLargePages (NewImageAddress,
  5582. RoundedNumberOfPtes << PAGE_SHIFT);
  5583. MiRemoveCachedRange (PageFrameIndex, PageFrameIndex + RoundedNumberOfPtes - 1);
  5584. MiFreeContiguousPages (PageFrameIndex, NumberOfPtes);
  5585. }
  5586. if (MmMakeLowMemory == TRUE) {
  5587. while (PointerPte < LastPte) {
  5588. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte);
  5589. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  5590. Pfn2 = MI_PFN_ELEMENT (Pfn1->u4.PteFrame);
  5591. MiDecrementShareCount (Pfn2, Pfn1->u4.PteFrame);
  5592. MI_SET_PFN_DELETED (Pfn1);
  5593. MiDecrementShareCount (Pfn1, PageFrameIndex);
  5594. PointerPte += 1;
  5595. }
  5596. PointerPte -= NumberOfPtes;
  5597. }
  5598. MiReleaseSystemPtes (PointerPte, NumberOfPtes, SystemPteSpace);
  5599. if (StopMoving == TRUE) {
  5600. MmMakeLowMemory = FALSE;
  5601. }
  5602. continue;
  5603. }
  5604. }
  5605. //
  5606. // Update the IATs for all other loaded modules that reference this one.
  5607. //
  5608. NonRelocatedVa = (PCHAR) DataTableEntry->DllBase;
  5609. DataTableEntry->DllBase = NewImageAddress;
  5610. MiUpdateThunks (LoaderBlock,
  5611. LoaderImageAddress,
  5612. NewImageAddress,
  5613. DataTableEntry->SizeOfImage);
  5614. //
  5615. // Update the loaded module list entry.
  5616. //
  5617. DataTableEntry->Flags |= LDRP_SYSTEM_MAPPED;
  5618. DataTableEntry->DllBase = NewImageAddress;
  5619. DataTableEntry->EntryPoint =
  5620. (PVOID)((PCHAR)NewImageAddress + NtHeader->OptionalHeader.AddressOfEntryPoint);
  5621. DataTableEntry->SizeOfImage = NumberOfPtes << PAGE_SHIFT;
  5622. //
  5623. // Update the exception table data info
  5624. //
  5625. MiCaptureImageExceptionValues (DataTableEntry);
  5626. //
  5627. // Update the PFNs of the image to support trimming.
  5628. // Note that the loader addresses are freed now so no references
  5629. // to it are permitted after this point.
  5630. //
  5631. LoaderPte = MiGetPteAddress (NonRelocatedVa);
  5632. LOCK_PFN (OldIrql);
  5633. while (PointerPte < LastPte) {
  5634. ASSERT ((UsedLargePage == TRUE) || (PointerPte->u.Hard.Valid == 1));
  5635. if ((MmMakeLowMemory == TRUE) || (UsedLargePage == TRUE)) {
  5636. ASSERT (LoaderPte->u.Hard.Valid == 1);
  5637. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (LoaderPte);
  5638. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  5639. Pfn2 = MI_PFN_ELEMENT (Pfn1->u4.PteFrame);
  5640. //
  5641. // Decrement the share count on the original page table
  5642. // page so it can be freed.
  5643. //
  5644. MiDecrementShareCount (Pfn2, Pfn1->u4.PteFrame);
  5645. MI_SET_PFN_DELETED (Pfn1);
  5646. MiDecrementShareCount (Pfn1, PageFrameIndex);
  5647. LoaderPte += 1;
  5648. }
  5649. else {
  5650. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte);
  5651. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  5652. Pfn2 = MI_PFN_ELEMENT (Pfn1->u4.PteFrame);
  5653. //
  5654. // Decrement the share count on the original page table
  5655. // page so it can be freed.
  5656. //
  5657. MiDecrementShareCount (Pfn2, Pfn1->u4.PteFrame);
  5658. *Pfn1->PteAddress = ZeroPte;
  5659. //
  5660. // Chain the PFN entry to its new page table.
  5661. //
  5662. PteFramePointer = MiGetPteAddress(PointerPte);
  5663. PteFramePage = MI_GET_PAGE_FRAME_FROM_PTE (PteFramePointer);
  5664. Pfn1->u4.PteFrame = PteFramePage;
  5665. Pfn1->PteAddress = PointerPte;
  5666. //
  5667. // Increment the share count for the page table page that now
  5668. // contains the PTE that was copied.
  5669. //
  5670. Pfn2 = MI_PFN_ELEMENT (PteFramePage);
  5671. Pfn2->u2.ShareCount += 1;
  5672. }
  5673. PointerPte += 1;
  5674. }
  5675. UNLOCK_PFN (OldIrql);
  5676. //
  5677. // The physical pages mapping the relocation section are freed
  5678. // later with the rest of the initialization code spanned by the
  5679. // DataTableEntry->SizeOfImage.
  5680. //
  5681. if (StopMoving == TRUE) {
  5682. MmMakeLowMemory = FALSE;
  5683. }
  5684. }
  5685. }
  5686. #if defined(_X86_) || defined(_AMD64_)
  5687. PMMPTE MiKernelResourceStartPte;
  5688. PMMPTE MiKernelResourceEndPte;
  5689. #endif
  5690. VOID
  5691. MiLocateKernelSections (
  5692. IN PKLDR_DATA_TABLE_ENTRY DataTableEntry
  5693. )
  5694. /*++
  5695. Routine Description:
  5696. This function locates the resource section in the kernel so it can be
  5697. made readwrite if we bugcheck later, as the bugcheck code will write
  5698. into it.
  5699. Arguments:
  5700. DataTableEntry - Supplies the kernel's data table entry.
  5701. Return Value:
  5702. None.
  5703. Environment:
  5704. Kernel mode, Phase 0 Initialization.
  5705. --*/
  5706. {
  5707. ULONG Span;
  5708. PVOID CurrentBase;
  5709. PIMAGE_NT_HEADERS NtHeader;
  5710. PIMAGE_SECTION_HEADER SectionTableEntry;
  5711. LONG i;
  5712. PMMPTE PointerPte;
  5713. PVOID SectionBaseAddress;
  5714. CurrentBase = (PVOID) DataTableEntry->DllBase;
  5715. NtHeader = RtlImageNtHeader (CurrentBase);
  5716. SectionTableEntry = (PIMAGE_SECTION_HEADER)((PCHAR)NtHeader +
  5717. sizeof(ULONG) +
  5718. sizeof(IMAGE_FILE_HEADER) +
  5719. NtHeader->FileHeader.SizeOfOptionalHeader);
  5720. //
  5721. // From the image header, locate the section named '.rsrc'.
  5722. //
  5723. i = NtHeader->FileHeader.NumberOfSections;
  5724. PointerPte = NULL;
  5725. while (i > 0) {
  5726. SectionBaseAddress = SECTION_BASE_ADDRESS(SectionTableEntry);
  5727. //
  5728. // Generally, SizeOfRawData is larger than VirtualSize for each
  5729. // section because it includes the padding to get to the subsection
  5730. // alignment boundary. However, if the image is linked with
  5731. // subsection alignment == native page alignment, the linker will
  5732. // have VirtualSize be much larger than SizeOfRawData because it
  5733. // will account for all the bss.
  5734. //
  5735. Span = SectionTableEntry->SizeOfRawData;
  5736. if (Span < SectionTableEntry->Misc.VirtualSize) {
  5737. Span = SectionTableEntry->Misc.VirtualSize;
  5738. }
  5739. #if defined(_X86_) || defined(_AMD64_)
  5740. if (*(PULONG)SectionTableEntry->Name == 'rsr.') {
  5741. MiKernelResourceStartPte = MiGetPteAddress ((ULONG_PTR)CurrentBase +
  5742. SectionTableEntry->VirtualAddress);
  5743. MiKernelResourceEndPte = MiGetPteAddress (ROUND_TO_PAGES((ULONG_PTR)CurrentBase +
  5744. SectionTableEntry->VirtualAddress + Span));
  5745. break;
  5746. }
  5747. #endif
  5748. if (*(PULONG)SectionTableEntry->Name == 'LOOP') {
  5749. if (*(PULONG)&SectionTableEntry->Name[4] == 'EDOC') {
  5750. ExPoolCodeStart = (PVOID)((ULONG_PTR)CurrentBase +
  5751. SectionTableEntry->VirtualAddress);
  5752. ExPoolCodeEnd = (PVOID)((ULONG_PTR)CurrentBase +
  5753. SectionTableEntry->VirtualAddress +
  5754. Span);
  5755. }
  5756. else if (*(PUSHORT)&SectionTableEntry->Name[4] == 'IM') {
  5757. MmPoolCodeStart = (PVOID)((ULONG_PTR)CurrentBase +
  5758. SectionTableEntry->VirtualAddress);
  5759. MmPoolCodeEnd = (PVOID)((ULONG_PTR)CurrentBase +
  5760. SectionTableEntry->VirtualAddress +
  5761. Span);
  5762. }
  5763. }
  5764. else if ((*(PULONG)SectionTableEntry->Name == 'YSIM') &&
  5765. (*(PULONG)&SectionTableEntry->Name[4] == 'ETPS')) {
  5766. MmPteCodeStart = (PVOID)((ULONG_PTR)CurrentBase +
  5767. SectionTableEntry->VirtualAddress);
  5768. MmPteCodeEnd = (PVOID)((ULONG_PTR)CurrentBase +
  5769. SectionTableEntry->VirtualAddress +
  5770. Span);
  5771. }
  5772. i -= 1;
  5773. SectionTableEntry += 1;
  5774. }
  5775. }
  5776. VOID
  5777. MmMakeKernelResourceSectionWritable (
  5778. VOID
  5779. )
  5780. /*++
  5781. Routine Description:
  5782. This function makes the kernel's resource section readwrite so the bugcheck
  5783. code can write into it.
  5784. Arguments:
  5785. None.
  5786. Return Value:
  5787. None.
  5788. Environment:
  5789. Kernel mode. Any IRQL.
  5790. --*/
  5791. {
  5792. #if defined(_X86_) || defined(_AMD64_)
  5793. MMPTE TempPte;
  5794. MMPTE PteContents;
  5795. PMMPTE PointerPte;
  5796. if (MiKernelResourceStartPte == NULL) {
  5797. return;
  5798. }
  5799. PointerPte = MiKernelResourceStartPte;
  5800. if (MI_IS_PHYSICAL_ADDRESS (MiGetVirtualAddressMappedByPte (PointerPte))) {
  5801. //
  5802. // Mapped physically, doesn't need to be made readwrite.
  5803. //
  5804. return;
  5805. }
  5806. //
  5807. // Since the entry state and IRQL are unknown, just go through the
  5808. // PTEs without a lock and make them all readwrite.
  5809. //
  5810. do {
  5811. PteContents = *PointerPte;
  5812. #if defined(NT_UP)
  5813. if (PteContents.u.Hard.Write == 0)
  5814. #else
  5815. if (PteContents.u.Hard.Writable == 0)
  5816. #endif
  5817. {
  5818. MI_MAKE_VALID_PTE (TempPte,
  5819. PteContents.u.Hard.PageFrameNumber,
  5820. MM_READWRITE,
  5821. PointerPte);
  5822. #if !defined(NT_UP)
  5823. TempPte.u.Hard.Writable = 1;
  5824. #endif
  5825. MI_WRITE_VALID_PTE_NEW_PROTECTION (PointerPte, TempPte);
  5826. }
  5827. PointerPte += 1;
  5828. } while (PointerPte < MiKernelResourceEndPte);
  5829. //
  5830. // Don't do this more than once.
  5831. //
  5832. MiKernelResourceStartPte = NULL;
  5833. //
  5834. // Only flush this processor as the state of the others is unknown.
  5835. //
  5836. KeFlushCurrentTb ();
  5837. #endif
  5838. }
  5839. #ifdef i386
  5840. PVOID PsNtosImageBase = (PVOID)0x80100000;
  5841. #else
  5842. PVOID PsNtosImageBase;
  5843. #endif
  5844. #if DBG
  5845. PVOID PsNtosImageEnd;
  5846. #endif
  5847. #if defined (_WIN64)
  5848. INVERTED_FUNCTION_TABLE PsInvertedFunctionTable = {
  5849. 0, MAXIMUM_INVERTED_FUNCTION_TABLE_SIZE, FALSE};
  5850. #endif
  5851. LIST_ENTRY PsLoadedModuleList;
  5852. ERESOURCE PsLoadedModuleResource;
  5853. LOGICAL
  5854. MiInitializeLoadedModuleList (
  5855. IN PLOADER_PARAMETER_BLOCK LoaderBlock
  5856. )
  5857. /*++
  5858. Routine Description:
  5859. This function initializes the loaded module list based on the LoaderBlock.
  5860. Arguments:
  5861. LoaderBlock - Supplies a pointer to the system loader block.
  5862. Return Value:
  5863. None.
  5864. Environment:
  5865. Kernel mode, Phase 0 Initialization.
  5866. --*/
  5867. {
  5868. SIZE_T CommittedPages;
  5869. SIZE_T DataTableEntrySize;
  5870. PLIST_ENTRY NextEntry;
  5871. PLIST_ENTRY NextEntryEnd;
  5872. PKLDR_DATA_TABLE_ENTRY DataTableEntry1;
  5873. PKLDR_DATA_TABLE_ENTRY DataTableEntry2;
  5874. CommittedPages = 0;
  5875. //
  5876. // Initialize the loaded module list executive resource and spin lock.
  5877. //
  5878. ExInitializeResourceLite (&PsLoadedModuleResource);
  5879. KeInitializeSpinLock (&PsLoadedModuleSpinLock);
  5880. InitializeListHead (&PsLoadedModuleList);
  5881. //
  5882. // Scan the loaded module list and allocate and initialize a data table
  5883. // entry for each module. The data table entry is inserted in the loaded
  5884. // module list and the initialization order list in the order specified
  5885. // in the loader parameter block. The data table entry is inserted in the
  5886. // memory order list in memory order.
  5887. //
  5888. NextEntry = LoaderBlock->LoadOrderListHead.Flink;
  5889. NextEntryEnd = &LoaderBlock->LoadOrderListHead;
  5890. DataTableEntry2 = CONTAINING_RECORD (NextEntry,
  5891. KLDR_DATA_TABLE_ENTRY,
  5892. InLoadOrderLinks);
  5893. PsNtosImageBase = DataTableEntry2->DllBase;
  5894. #if DBG
  5895. PsNtosImageEnd = (PVOID) ((ULONG_PTR) DataTableEntry2->DllBase + DataTableEntry2->SizeOfImage);
  5896. #endif
  5897. MiLocateKernelSections (DataTableEntry2);
  5898. #if defined (_IA64_)
  5899. ExamineList:
  5900. #endif
  5901. while (NextEntry != NextEntryEnd) {
  5902. DataTableEntry2 = CONTAINING_RECORD(NextEntry,
  5903. KLDR_DATA_TABLE_ENTRY,
  5904. InLoadOrderLinks);
  5905. //
  5906. // Allocate a data table entry.
  5907. //
  5908. DataTableEntrySize = sizeof (KLDR_DATA_TABLE_ENTRY) +
  5909. DataTableEntry2->BaseDllName.MaximumLength + sizeof(UNICODE_NULL);
  5910. DataTableEntry1 = ExAllocatePoolWithTag (NonPagedPool,
  5911. DataTableEntrySize,
  5912. 'dLmM');
  5913. if (DataTableEntry1 == NULL) {
  5914. return FALSE;
  5915. }
  5916. //
  5917. // Copy the data table entry.
  5918. //
  5919. *DataTableEntry1 = *DataTableEntry2;
  5920. //
  5921. // Clear fields we may use later so they don't inherit irrelevant
  5922. // loader values.
  5923. //
  5924. ((PKLDR_DATA_TABLE_ENTRY)DataTableEntry1)->NonPagedDebugInfo = NULL;
  5925. DataTableEntry1->PatchInformation = NULL;
  5926. DataTableEntry1->FullDllName.Buffer = ExAllocatePoolWithTag (PagedPool,
  5927. DataTableEntry2->FullDllName.MaximumLength + sizeof(UNICODE_NULL),
  5928. 'TDmM');
  5929. if (DataTableEntry1->FullDllName.Buffer == NULL) {
  5930. ExFreePool (DataTableEntry1);
  5931. return FALSE;
  5932. }
  5933. DataTableEntry1->BaseDllName.Buffer = (PWSTR)((ULONG_PTR)DataTableEntry1 + sizeof (KLDR_DATA_TABLE_ENTRY));
  5934. //
  5935. // Copy the strings.
  5936. //
  5937. RtlCopyMemory (DataTableEntry1->FullDllName.Buffer,
  5938. DataTableEntry2->FullDllName.Buffer,
  5939. DataTableEntry1->FullDllName.MaximumLength);
  5940. RtlCopyMemory (DataTableEntry1->BaseDllName.Buffer,
  5941. DataTableEntry2->BaseDllName.Buffer,
  5942. DataTableEntry1->BaseDllName.MaximumLength);
  5943. DataTableEntry1->BaseDllName.Buffer[DataTableEntry1->BaseDllName.Length/sizeof(WCHAR)] = UNICODE_NULL;
  5944. //
  5945. // Always charge commitment regardless of whether we were able to
  5946. // relocate the driver, use large pages, etc.
  5947. //
  5948. CommittedPages += (DataTableEntry1->SizeOfImage >> PAGE_SHIFT);
  5949. #if defined (_IA64_)
  5950. //
  5951. // Don't calculate exception values for IA64 firmware modules.
  5952. //
  5953. if (NextEntryEnd != &LoaderBlock->Extension->FirmwareDescriptorListHead)
  5954. #endif
  5955. //
  5956. // Calculate exception pointers
  5957. //
  5958. MiCaptureImageExceptionValues(DataTableEntry1);
  5959. //
  5960. // Insert the data table entry in the load order list in the order
  5961. // they are specified.
  5962. //
  5963. InsertTailList (&PsLoadedModuleList,
  5964. &DataTableEntry1->InLoadOrderLinks);
  5965. #if defined (_WIN64)
  5966. #if defined (_IA64_)
  5967. //
  5968. // Don't add IA64 firmware modules to the exception handling list.
  5969. //
  5970. if (NextEntryEnd != &LoaderBlock->Extension->FirmwareDescriptorListHead)
  5971. #endif
  5972. RtlInsertInvertedFunctionTable (&PsInvertedFunctionTable,
  5973. DataTableEntry1->DllBase,
  5974. DataTableEntry1->SizeOfImage);
  5975. #endif
  5976. NextEntry = NextEntry->Flink;
  5977. }
  5978. #if defined (_IA64_)
  5979. //
  5980. // Go pick up the firmware modules if we haven't already.
  5981. //
  5982. if (NextEntryEnd != &LoaderBlock->Extension->FirmwareDescriptorListHead) {
  5983. NextEntry = LoaderBlock->Extension->FirmwareDescriptorListHead.Flink;
  5984. NextEntryEnd = &LoaderBlock->Extension->FirmwareDescriptorListHead;
  5985. goto ExamineList;
  5986. }
  5987. #endif
  5988. //
  5989. // Charge commitment for each boot loaded driver so that if unloads
  5990. // later, the return will balance. Note that the actual number of
  5991. // free pages is not changing now so the commit limits need to be
  5992. // bumped by the same amount.
  5993. //
  5994. // Resident available does not need to be charged here because it
  5995. // has been already (by virtue of being snapped from available pages).
  5996. //
  5997. MM_TRACK_COMMIT (MM_DBG_COMMIT_LOAD_SYSTEM_IMAGE_TEMP, CommittedPages);
  5998. MmTotalCommittedPages += CommittedPages;
  5999. MmTotalCommitLimit += CommittedPages;
  6000. MmTotalCommitLimitMaximum += CommittedPages;
  6001. MiBuildImportsForBootDrivers ();
  6002. return TRUE;
  6003. }
  6004. NTSTATUS
  6005. MmCallDllInitialize (
  6006. IN PKLDR_DATA_TABLE_ENTRY DataTableEntry,
  6007. IN PLIST_ENTRY ModuleListHead
  6008. )
  6009. /*++
  6010. Routine Description:
  6011. This function calls the DLL's initialize routine.
  6012. Arguments:
  6013. DataTableEntry - Supplies the kernel's data table entry.
  6014. Return Value:
  6015. Various NTSTATUS error codes.
  6016. Environment:
  6017. Kernel mode.
  6018. --*/
  6019. {
  6020. NTSTATUS st;
  6021. PWCHAR Dot;
  6022. PMM_DLL_INITIALIZE Func;
  6023. UNICODE_STRING RegistryPath;
  6024. UNICODE_STRING ImportName;
  6025. ULONG ThunksAdded;
  6026. Func = (PMM_DLL_INITIALIZE)(ULONG_PTR)MiLocateExportName (DataTableEntry->DllBase, "DllInitialize");
  6027. if (!Func) {
  6028. return STATUS_SUCCESS;
  6029. }
  6030. ImportName.MaximumLength = DataTableEntry->BaseDllName.Length;
  6031. ImportName.Buffer = ExAllocatePoolWithTag (NonPagedPool,
  6032. ImportName.MaximumLength,
  6033. 'TDmM');
  6034. if (ImportName.Buffer == NULL) {
  6035. return STATUS_INSUFFICIENT_RESOURCES;
  6036. }
  6037. ImportName.Length = DataTableEntry->BaseDllName.Length;
  6038. RtlCopyMemory (ImportName.Buffer,
  6039. DataTableEntry->BaseDllName.Buffer,
  6040. ImportName.Length);
  6041. RegistryPath.MaximumLength = (USHORT)(CmRegistryMachineSystemCurrentControlSetServices.Length +
  6042. ImportName.Length +
  6043. 2*sizeof(WCHAR));
  6044. RegistryPath.Buffer = ExAllocatePoolWithTag (NonPagedPool,
  6045. RegistryPath.MaximumLength,
  6046. 'TDmM');
  6047. if (RegistryPath.Buffer == NULL) {
  6048. ExFreePool (ImportName.Buffer);
  6049. return STATUS_INSUFFICIENT_RESOURCES;
  6050. }
  6051. RegistryPath.Length = CmRegistryMachineSystemCurrentControlSetServices.Length;
  6052. RtlCopyMemory (RegistryPath.Buffer,
  6053. CmRegistryMachineSystemCurrentControlSetServices.Buffer,
  6054. CmRegistryMachineSystemCurrentControlSetServices.Length);
  6055. RtlAppendUnicodeToString (&RegistryPath, (const PUSHORT)L"\\");
  6056. Dot = wcschr (ImportName.Buffer, L'.');
  6057. if (Dot) {
  6058. ImportName.Length = (USHORT)((Dot - ImportName.Buffer) * sizeof(WCHAR));
  6059. }
  6060. RtlAppendUnicodeStringToString (&RegistryPath, &ImportName);
  6061. ExFreePool (ImportName.Buffer);
  6062. //
  6063. // Save the number of verifier thunks currently added so we know
  6064. // if this activation adds any. To extend the thunk list, the module
  6065. // performs an NtSetSystemInformation call which calls back to the
  6066. // verifier's MmAddVerifierThunks, which increments MiVerifierThunksAdded.
  6067. //
  6068. ThunksAdded = MiVerifierThunksAdded;
  6069. //
  6070. // Invoke the DLL's initialization routine.
  6071. //
  6072. st = Func (&RegistryPath);
  6073. ExFreePool (RegistryPath.Buffer);
  6074. //
  6075. // If the module's initialization routine succeeded, and if it extended
  6076. // the verifier thunk list, and this is boot time, reapply the verifier
  6077. // to the loaded modules.
  6078. //
  6079. // Note that boot time is the special case because after boot time, Mm
  6080. // loads all the DLLs itself and a DLL initialize is thus guaranteed to
  6081. // complete and add its thunks before the importing driver load finishes.
  6082. // Since the importing driver is only thunked after its load finishes,
  6083. // ordering implicitly guarantees that all DLL-registered thunks are
  6084. // properly factored in to the importing driver.
  6085. //
  6086. // Boot time is special because the loader (not the Mm) already loaded
  6087. // the DLLs *AND* the importing drivers so we have to look over our
  6088. // shoulder and make it all right after the fact.
  6089. //
  6090. if ((NT_SUCCESS(st)) &&
  6091. (MiFirstDriverLoadEver == 0) &&
  6092. (MiVerifierThunksAdded != ThunksAdded)) {
  6093. MiReApplyVerifierToLoadedModules (ModuleListHead);
  6094. }
  6095. return st;
  6096. }
  6097. NTKERNELAPI
  6098. PVOID
  6099. MmGetSystemRoutineAddress (
  6100. IN PUNICODE_STRING SystemRoutineName
  6101. )
  6102. /*++
  6103. Routine Description:
  6104. This function returns the address of the argument function pointer if
  6105. it is in the kernel or HAL, NULL if it is not.
  6106. Arguments:
  6107. SystemRoutineName - Supplies the name of the desired routine.
  6108. Return Value:
  6109. Non-NULL function pointer if successful. NULL if not.
  6110. Environment:
  6111. Kernel mode, PASSIVE_LEVEL, arbitrary process context.
  6112. --*/
  6113. {
  6114. PKTHREAD CurrentThread;
  6115. NTSTATUS Status;
  6116. PKLDR_DATA_TABLE_ENTRY DataTableEntry;
  6117. ANSI_STRING AnsiString;
  6118. PLIST_ENTRY NextEntry;
  6119. UNICODE_STRING KernelString;
  6120. UNICODE_STRING HalString;
  6121. PVOID FunctionAddress;
  6122. LOGICAL Found;
  6123. ULONG EntriesChecked;
  6124. ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL);
  6125. EntriesChecked = 0;
  6126. FunctionAddress = NULL;
  6127. KernelString.Buffer = (const PUSHORT) KERNEL_NAME;
  6128. KernelString.Length = sizeof (KERNEL_NAME) - sizeof (WCHAR);
  6129. KernelString.MaximumLength = sizeof KERNEL_NAME;
  6130. HalString.Buffer = (const PUSHORT) HAL_NAME;
  6131. HalString.Length = sizeof (HAL_NAME) - sizeof (WCHAR);
  6132. HalString.MaximumLength = sizeof HAL_NAME;
  6133. do {
  6134. Status = RtlUnicodeStringToAnsiString (&AnsiString,
  6135. SystemRoutineName,
  6136. TRUE);
  6137. if (NT_SUCCESS (Status)) {
  6138. break;
  6139. }
  6140. KeDelayExecutionThread (KernelMode, FALSE, (PLARGE_INTEGER)&MmShortTime);
  6141. } while (TRUE);
  6142. //
  6143. // Arbitrary process context so prevent suspend APCs now.
  6144. //
  6145. CurrentThread = KeGetCurrentThread ();
  6146. KeEnterCriticalRegionThread (CurrentThread);
  6147. ExAcquireResourceSharedLite (&PsLoadedModuleResource, TRUE);
  6148. //
  6149. // Check only the kernel and the HAL for exports.
  6150. //
  6151. NextEntry = PsLoadedModuleList.Flink;
  6152. while (NextEntry != &PsLoadedModuleList) {
  6153. Found = FALSE;
  6154. DataTableEntry = CONTAINING_RECORD(NextEntry,
  6155. KLDR_DATA_TABLE_ENTRY,
  6156. InLoadOrderLinks);
  6157. if (RtlEqualUnicodeString (&KernelString,
  6158. &DataTableEntry->BaseDllName,
  6159. TRUE)) {
  6160. Found = TRUE;
  6161. EntriesChecked += 1;
  6162. }
  6163. else if (RtlEqualUnicodeString (&HalString,
  6164. &DataTableEntry->BaseDllName,
  6165. TRUE)) {
  6166. Found = TRUE;
  6167. EntriesChecked += 1;
  6168. }
  6169. if (Found == TRUE) {
  6170. FunctionAddress = MiFindExportedRoutineByName (DataTableEntry->DllBase,
  6171. &AnsiString);
  6172. if (FunctionAddress != NULL) {
  6173. break;
  6174. }
  6175. if (EntriesChecked == 2) {
  6176. break;
  6177. }
  6178. }
  6179. NextEntry = NextEntry->Flink;
  6180. }
  6181. ExReleaseResourceLite (&PsLoadedModuleResource);
  6182. KeLeaveCriticalRegionThread (CurrentThread);
  6183. RtlFreeAnsiString (&AnsiString);
  6184. return FunctionAddress;
  6185. }
  6186. PVOID
  6187. MiFindExportedRoutineByName (
  6188. IN PVOID DllBase,
  6189. IN PANSI_STRING AnsiImageRoutineName
  6190. )
  6191. /*++
  6192. Routine Description:
  6193. This function searches the argument module looking for the requested
  6194. exported function name.
  6195. Arguments:
  6196. DllBase - Supplies the base address of the requested module.
  6197. AnsiImageRoutineName - Supplies the ANSI routine name being searched for.
  6198. Return Value:
  6199. The virtual address of the requested routine or NULL if not found.
  6200. --*/
  6201. {
  6202. USHORT OrdinalNumber;
  6203. PULONG NameTableBase;
  6204. PUSHORT NameOrdinalTableBase;
  6205. PULONG Addr;
  6206. ULONG High;
  6207. ULONG Low;
  6208. ULONG Middle;
  6209. LONG Result;
  6210. ULONG ExportSize;
  6211. PVOID FunctionAddress;
  6212. PIMAGE_EXPORT_DIRECTORY ExportDirectory;
  6213. PAGED_CODE();
  6214. ExportDirectory = (PIMAGE_EXPORT_DIRECTORY) RtlImageDirectoryEntryToData (
  6215. DllBase,
  6216. TRUE,
  6217. IMAGE_DIRECTORY_ENTRY_EXPORT,
  6218. &ExportSize);
  6219. if (ExportDirectory == NULL) {
  6220. return NULL;
  6221. }
  6222. //
  6223. // Initialize the pointer to the array of RVA-based ansi export strings.
  6224. //
  6225. NameTableBase = (PULONG)((PCHAR)DllBase + (ULONG)ExportDirectory->AddressOfNames);
  6226. //
  6227. // Initialize the pointer to the array of USHORT ordinal numbers.
  6228. //
  6229. NameOrdinalTableBase = (PUSHORT)((PCHAR)DllBase + (ULONG)ExportDirectory->AddressOfNameOrdinals);
  6230. //
  6231. // Lookup the desired name in the name table using a binary search.
  6232. //
  6233. Low = 0;
  6234. Middle = 0;
  6235. High = ExportDirectory->NumberOfNames - 1;
  6236. while (High >= Low) {
  6237. //
  6238. // Compute the next probe index and compare the import name
  6239. // with the export name entry.
  6240. //
  6241. Middle = (Low + High) >> 1;
  6242. Result = strcmp (AnsiImageRoutineName->Buffer,
  6243. (PCHAR)DllBase + NameTableBase[Middle]);
  6244. if (Result < 0) {
  6245. High = Middle - 1;
  6246. }
  6247. else if (Result > 0) {
  6248. Low = Middle + 1;
  6249. }
  6250. else {
  6251. break;
  6252. }
  6253. }
  6254. //
  6255. // If the high index is less than the low index, then a matching
  6256. // table entry was not found. Otherwise, get the ordinal number
  6257. // from the ordinal table.
  6258. //
  6259. if ((LONG)High < (LONG)Low) {
  6260. return NULL;
  6261. }
  6262. OrdinalNumber = NameOrdinalTableBase[Middle];
  6263. //
  6264. // If the OrdinalNumber is not within the Export Address Table,
  6265. // then this image does not implement the function. Return not found.
  6266. //
  6267. if ((ULONG)OrdinalNumber >= ExportDirectory->NumberOfFunctions) {
  6268. return NULL;
  6269. }
  6270. //
  6271. // Index into the array of RVA export addresses by ordinal number.
  6272. //
  6273. Addr = (PULONG)((PCHAR)DllBase + (ULONG)ExportDirectory->AddressOfFunctions);
  6274. FunctionAddress = (PVOID)((PCHAR)DllBase + Addr[OrdinalNumber]);
  6275. //
  6276. // Forwarders are not used by the kernel and HAL to each other.
  6277. //
  6278. ASSERT ((FunctionAddress <= (PVOID)ExportDirectory) ||
  6279. (FunctionAddress >= (PVOID)((PCHAR)ExportDirectory + ExportSize)));
  6280. return FunctionAddress;
  6281. }
  6282. #if _MI_DEBUG_RONLY
  6283. PMMPTE MiSessionDataStartPte;
  6284. PMMPTE MiSessionDataEndPte;
  6285. VOID
  6286. MiAssertNotSessionData (
  6287. IN PMMPTE PointerPte
  6288. )
  6289. {
  6290. if (MI_IS_SESSION_IMAGE_PTE (PointerPte)) {
  6291. if ((PointerPte >= MiSessionDataStartPte) &&
  6292. (PointerPte <= MiSessionDataEndPte)) {
  6293. KeBugCheckEx (MEMORY_MANAGEMENT,
  6294. 0x41287,
  6295. (ULONG_PTR) PointerPte,
  6296. 0,
  6297. 0);
  6298. }
  6299. }
  6300. }
  6301. VOID
  6302. MiLogSessionDataStart (
  6303. IN PKLDR_DATA_TABLE_ENTRY DataTableEntry
  6304. )
  6305. {
  6306. LONG i;
  6307. PVOID CurrentBase;
  6308. PIMAGE_NT_HEADERS NtHeader;
  6309. PIMAGE_SECTION_HEADER SectionTableEntry;
  6310. PVOID DataStart;
  6311. PVOID DataEnd;
  6312. if (MiSessionDataStartPte != NULL) {
  6313. return;
  6314. }
  6315. //
  6316. // Crack the image header to mark the data.
  6317. //
  6318. CurrentBase = (PVOID)DataTableEntry->DllBase;
  6319. NtHeader = RtlImageNtHeader (CurrentBase);
  6320. SectionTableEntry = (PIMAGE_SECTION_HEADER)((PCHAR)NtHeader +
  6321. sizeof(ULONG) +
  6322. sizeof(IMAGE_FILE_HEADER) +
  6323. NtHeader->FileHeader.SizeOfOptionalHeader);
  6324. i = NtHeader->FileHeader.NumberOfSections;
  6325. while (i > 0) {
  6326. //
  6327. // Save the start and end of the data section.
  6328. //
  6329. if ((*(PULONG)SectionTableEntry->Name == 0x7461642e) &&
  6330. (*(PULONG)&SectionTableEntry->Name[4] == 0x61)) {
  6331. DataStart = (PVOID)((PCHAR)CurrentBase + SectionTableEntry->VirtualAddress);
  6332. //
  6333. // Generally, SizeOfRawData is larger than VirtualSize for each
  6334. // section because it includes the padding to get to the subsection
  6335. // alignment boundary. However, if the image is linked with
  6336. // subsection alignment == native page alignment, the linker will
  6337. // have VirtualSize be much larger than SizeOfRawData because it
  6338. // will account for all the bss.
  6339. //
  6340. Span = SectionTableEntry->SizeOfRawData;
  6341. if (Span < SectionTableEntry->Misc.VirtualSize) {
  6342. Span = SectionTableEntry->Misc.VirtualSize;
  6343. }
  6344. DataEnd = (PVOID)((PCHAR)DataStart + Span - 1);
  6345. MiSessionDataStartPte = MiGetPteAddress (DataStart);
  6346. MiSessionDataEndPte = MiGetPteAddress (DataEnd);
  6347. break;
  6348. }
  6349. i -= 1;
  6350. SectionTableEntry += 1;
  6351. }
  6352. }
  6353. #endif