Author: akorotaev
Date: Thu Jan 3 00:49:19 2008
New Revision: 31572
URL:
http://svn.reactos.org/svn/reactos?rev=31572&view=rev
Log:
Fix some bugs. Now it bugchecks.
Added:
branches/cache_manager_rewrite/mm/sysldr.c (with props)
Modified:
branches/cache_manager_rewrite/mm/mminit.c
branches/cache_manager_rewrite/mm/section.c
Modified: branches/cache_manager_rewrite/mm/mminit.c
URL:
http://svn.reactos.org/svn/reactos/branches/cache_manager_rewrite/mm/mminit…
==============================================================================
--- branches/cache_manager_rewrite/mm/mminit.c (original)
+++ branches/cache_manager_rewrite/mm/mminit.c Thu Jan 3 00:49:19 2008
@@ -493,29 +493,10 @@
return TRUE;
}
-#if 0
-
-VOID static
-MiFreeInitMemoryPage(PVOID Context, MEMORY_AREA* MemoryArea, PVOID Address,
- PFN_TYPE Page, SWAPENTRY SwapEntry,
- BOOLEAN Dirty)
-{
- ASSERT(SwapEntry == 0);
- if (Page != 0)
- {
- MmReleasePageMemoryConsumer(MC_NPPOOL, Page);
- }
-}
-
VOID
NTAPI
MiFreeInitMemory(VOID)
{
MmLockAddressSpace(MmGetKernelAddressSpace());
- MmFreeMemoryAreaByPtr(MmGetKernelAddressSpace(),
- (PVOID)&_init_start__,
- MiFreeInitMemoryPage,
- NULL);
MmUnlockAddressSpace(MmGetKernelAddressSpace());
}
-#endif
Modified: branches/cache_manager_rewrite/mm/section.c
URL:
http://svn.reactos.org/svn/reactos/branches/cache_manager_rewrite/mm/sectio…
==============================================================================
--- branches/cache_manager_rewrite/mm/section.c (original)
+++ branches/cache_manager_rewrite/mm/section.c Thu Jan 3 00:49:19 2008
@@ -3197,6 +3197,7 @@
OBJECT_ATTRIBUTES Obj;
UNICODE_STRING Name = RTL_CONSTANT_STRING(L"\\Device\\PhysicalMemory");
LARGE_INTEGER SectionSize;
+ HANDLE Handle;
/*
* Create the section mapping physical memory
@@ -3225,11 +3226,12 @@
SECTION_ALL_ACCESS,
0,
NULL,
- NULL);
+ &Handle);
if (!NT_SUCCESS(Status))
{
ObDereferenceObject(PhysSection);
}
+ ObCloseHandle(Handle, KernelMode);
PhysSection->AllocationAttributes |= SEC_PHYSICALMEMORY;
PhysSection->Segment->Flags &= ~MM_PAGEFILE_SEGMENT;
Added: branches/cache_manager_rewrite/mm/sysldr.c
URL:
http://svn.reactos.org/svn/reactos/branches/cache_manager_rewrite/mm/sysldr…
==============================================================================
--- branches/cache_manager_rewrite/mm/sysldr.c (added)
+++ branches/cache_manager_rewrite/mm/sysldr.c Thu Jan 3 00:49:19 2008
@@ -1,0 +1,2009 @@
+/*
+* PROJECT: ReactOS Kernel
+* LICENSE: GPL - See COPYING in the top level directory
+* FILE: ntoskrnl/mm/sysldr.c
+* PURPOSE: Contains the Kernel Loader (SYSLDR) for loading PE files.
+* PROGRAMMERS: Alex Ionescu (alex.ionescu(a)reactos.org)
+*/
+
+/* INCLUDES ******************************************************************/
+
+#include <ntoskrnl.h>
+#define NDEBUG
+#include <debug.h>
+
+/* GCC's incompetence strikes again */
+FORCEINLINE
+VOID
+sprintf_nt(IN PCHAR Buffer,
+ IN PCHAR Format,
+ IN ...)
+{
+ va_list ap;
+ va_start(ap, Format);
+ vsprintf(Buffer, Format, ap);
+ va_end(ap);
+}
+
+/* GLOBALS *******************************************************************/
+
+LIST_ENTRY PsLoadedModuleList;
+KSPIN_LOCK PsLoadedModuleSpinLock;
+ULONG PsNtosImageBase;
+KMUTANT MmSystemLoadLock;
+extern ULONG NtGlobalFlag;
+
+/* FUNCTIONS *****************************************************************/
+
+PVOID
+NTAPI
+MiCacheImageSymbols(IN PVOID BaseAddress)
+{
+ ULONG DebugSize;
+ PVOID DebugDirectory = NULL;
+ PAGED_CODE();
+
+ /* Make sure it's safe to access the image */
+ _SEH_TRY
+ {
+ /* Get the debug directory */
+ DebugDirectory = RtlImageDirectoryEntryToData(BaseAddress,
+ TRUE,
+ IMAGE_DIRECTORY_ENTRY_DEBUG,
+ &DebugSize);
+ }
+ _SEH_HANDLE
+ {
+ /* Nothing */
+ }
+ _SEH_END;
+
+ /* Return the directory */
+ return DebugDirectory;
+}
+
+VOID
+NTAPI
+MiFreeBootDriverMemory(PVOID BaseAddress,
+ ULONG Length)
+{
+ ULONG i;
+
+ /* Loop each page */
+ for (i = 0; i < PAGE_ROUND_UP(Length) / PAGE_SIZE; i++)
+ {
+ /* Free the page */
+ MmDeleteVirtualMapping(NULL,
+ (PVOID)((ULONG_PTR)BaseAddress + i * PAGE_SIZE),
+ TRUE,
+ NULL,
+ NULL);
+ }
+}
+
+NTSTATUS
+NTAPI
+MiLoadImageSection(IN OUT PVOID *SectionPtr,
+ OUT PVOID *ImageBase,
+ IN PUNICODE_STRING FileName,
+ IN BOOLEAN SessionLoad,
+ IN PLDR_DATA_TABLE_ENTRY LdrEntry)
+{
+ PROS_SECTION_OBJECT Section = *SectionPtr;
+ NTSTATUS Status;
+ PEPROCESS Process;
+ PVOID Base = NULL;
+ SIZE_T ViewSize = 0;
+ KAPC_STATE ApcState;
+ LARGE_INTEGER SectionOffset = {{0}};
+ BOOLEAN LoadSymbols = FALSE;
+ ULONG DriverSize;
+ PVOID DriverBase;
+ PAGED_CODE();
+
+ /* Detect session load */
+ if (SessionLoad)
+ {
+ /* Fail */
+ DPRINT1("Session loading not yet supported!\n");
+ while (TRUE);
+ }
+
+ /* Not session load, shouldn't have an entry */
+ ASSERT(LdrEntry == NULL);
+
+ /* Attach to the system process */
+ KeStackAttachProcess(&PsInitialSystemProcess->Pcb, &ApcState);
+
+ /* Check if we need to load symbols */
+ if (NtGlobalFlag & FLG_ENABLE_KDEBUG_SYMBOL_LOAD)
+ {
+ /* Yes we do */
+ LoadSymbols = TRUE;
+ NtGlobalFlag &= ~FLG_ENABLE_KDEBUG_SYMBOL_LOAD;
+ }
+
+ /* Map the driver */
+ Process = PsGetCurrentProcess();
+ Status = MmMapViewOfSection(Section,
+ Process,
+ &Base,
+ 0,
+ 0,
+ &SectionOffset,
+ &ViewSize,
+ ViewUnmap,
+ 0,
+ PAGE_EXECUTE);
+
+ /* Re-enable the flag */
+ if (LoadSymbols) NtGlobalFlag |= FLG_ENABLE_KDEBUG_SYMBOL_LOAD;
+
+ /* Check if we failed with distinguished status code */
+ if (Status == STATUS_IMAGE_MACHINE_TYPE_MISMATCH)
+ {
+ /* Change it to something more generic */
+ Status = STATUS_INVALID_IMAGE_FORMAT;
+ }
+
+ /* Now check if we failed */
+ if (!NT_SUCCESS(Status))
+ {
+ /* Detach and return */
+ KeUnstackDetachProcess(&ApcState);
+ return Status;
+ }
+
+ /* Get the driver size */
+ DriverSize = Section->ImageSection->ImageSize;
+
+ /* Allocate a virtual section for the module */
+ DriverBase = MmAllocateSection(DriverSize, NULL);
+ *ImageBase = DriverBase;
+
+ /* Copy the image */
+ RtlCopyMemory(DriverBase, Base, DriverSize);
+
+ /* Now unmap the view */
+ Status = MmUnmapViewOfSection(Process, Base);
+ ASSERT(NT_SUCCESS(Status));
+
+ /* Detach and return status */
+ KeUnstackDetachProcess(&ApcState);
+ return Status;
+}
+
+NTSTATUS
+NTAPI
+MiDereferenceImports(IN PLOAD_IMPORTS ImportList)
+{
+ SIZE_T i;
+
+ /* Check if there's no imports or if we're a boot driver */
+ if ((ImportList == (PVOID)-1) ||
+ (ImportList == (PVOID)-2) ||
+ (ImportList->Count == 0))
+ {
+ /* Then there's nothing to do */
+ return STATUS_SUCCESS;
+ }
+
+ /* Otherwise, FIXME */
+ DPRINT1("%u imports not dereferenced!\n", ImportList->Count);
+ for (i = 0; i < ImportList->Count; i++)
+ {
+ DPRINT1("%wZ <%wZ>\n",
&ImportList->Entry[i]->FullDllName,
&ImportList->Entry[i]->BaseDllName);
+ }
+ return STATUS_UNSUCCESSFUL;
+}
+
+VOID
+NTAPI
+MiClearImports(IN PLDR_DATA_TABLE_ENTRY LdrEntry)
+{
+ PAGED_CODE();
+
+ /* Check if there's no imports or we're a boot driver or only one entry */
+ if ((LdrEntry->LoadedImports == (PVOID)-1) ||
+ (LdrEntry->LoadedImports == (PVOID)-2) ||
+ ((ULONG_PTR)LdrEntry->LoadedImports & 1))
+ {
+ /* Nothing to do */
+ return;
+ }
+
+ /* Otherwise, free the import list */
+ ExFreePool(LdrEntry->LoadedImports);
+}
+
+PVOID
+NTAPI
+MiFindExportedRoutineByName(IN PVOID DllBase,
+ IN PANSI_STRING ExportName)
+{
+ PULONG NameTable;
+ PUSHORT OrdinalTable;
+ PIMAGE_EXPORT_DIRECTORY ExportDirectory;
+ LONG Low = 0, Mid = 0, High, Ret;
+ USHORT Ordinal;
+ PVOID Function;
+ ULONG ExportSize;
+ PULONG ExportTable;
+ PAGED_CODE();
+
+ /* Get the export directory */
+ ExportDirectory = RtlImageDirectoryEntryToData(DllBase,
+ TRUE,
+ IMAGE_DIRECTORY_ENTRY_EXPORT,
+ &ExportSize);
+ if (!ExportDirectory) return NULL;
+
+ /* Setup name tables */
+ NameTable = (PULONG)((ULONG_PTR)DllBase +
+ ExportDirectory->AddressOfNames);
+ OrdinalTable = (PUSHORT)((ULONG_PTR)DllBase +
+ ExportDirectory->AddressOfNameOrdinals);
+
+ /* Do a binary search */
+ High = ExportDirectory->NumberOfNames - 1;
+ while (High >= Low)
+ {
+ /* Get new middle value */
+ Mid = (Low + High) >> 1;
+
+ /* Compare name */
+ Ret = strcmp(ExportName->Buffer, (PCHAR)DllBase + NameTable[Mid]);
+ if (Ret < 0)
+ {
+ /* Update high */
+ High = Mid - 1;
+ }
+ else if (Ret > 0)
+ {
+ /* Update low */
+ Low = Mid + 1;
+ }
+ else
+ {
+ /* We got it */
+ break;
+ }
+ }
+
+ /* Check if we couldn't find it */
+ if (High < Low) return NULL;
+
+ /* Otherwise, this is the ordinal */
+ Ordinal = OrdinalTable[Mid];
+
+ /* Resolve the address and write it */
+ ExportTable = (PULONG)((ULONG_PTR)DllBase +
+ ExportDirectory->AddressOfFunctions);
+ Function = (PVOID)((ULONG_PTR)DllBase + ExportTable[Ordinal]);
+
+ /* We found it! */
+ ASSERT((Function > (PVOID)ExportDirectory) &&
+ (Function < (PVOID)((ULONG_PTR)ExportDirectory + ExportSize)));
+ return Function;
+}
+
+PVOID
+NTAPI
+MiLocateExportName(IN PVOID DllBase,
+ IN PCHAR ExportName)
+{
+ PULONG NameTable;
+ PUSHORT OrdinalTable;
+ PIMAGE_EXPORT_DIRECTORY ExportDirectory;
+ LONG Low = 0, Mid = 0, High, Ret;
+ USHORT Ordinal;
+ PVOID Function;
+ ULONG ExportSize;
+ PULONG ExportTable;
+ PAGED_CODE();
+
+ /* Get the export directory */
+ ExportDirectory = RtlImageDirectoryEntryToData(DllBase,
+ TRUE,
+ IMAGE_DIRECTORY_ENTRY_EXPORT,
+ &ExportSize);
+ if (!ExportDirectory) return NULL;
+
+ /* Setup name tables */
+ NameTable = (PULONG)((ULONG_PTR)DllBase +
+ ExportDirectory->AddressOfNames);
+ OrdinalTable = (PUSHORT)((ULONG_PTR)DllBase +
+ ExportDirectory->AddressOfNameOrdinals);
+
+ /* Do a binary search */
+ High = ExportDirectory->NumberOfNames - 1;
+ while (High >= Low)
+ {
+ /* Get new middle value */
+ Mid = (Low + High) >> 1;
+
+ /* Compare name */
+ Ret = strcmp(ExportName, (PCHAR)DllBase + NameTable[Mid]);
+ if (Ret < 0)
+ {
+ /* Update high */
+ High = Mid - 1;
+ }
+ else if (Ret > 0)
+ {
+ /* Update low */
+ Low = Mid + 1;
+ }
+ else
+ {
+ /* We got it */
+ break;
+ }
+ }
+
+ /* Check if we couldn't find it */
+ if (High < Low) return NULL;
+
+ /* Otherwise, this is the ordinal */
+ Ordinal = OrdinalTable[Mid];
+
+ /* Resolve the address and write it */
+ ExportTable = (PULONG)((ULONG_PTR)DllBase +
+ ExportDirectory->AddressOfFunctions);
+ Function = (PVOID)((ULONG_PTR)DllBase + ExportTable[Ordinal]);
+
+ /* Check if the function is actually a forwarder */
+ if (((ULONG_PTR)Function > (ULONG_PTR)ExportDirectory) &&
+ ((ULONG_PTR)Function < ((ULONG_PTR)ExportDirectory + ExportSize)))
+ {
+ /* It is, fail */
+ return NULL;
+ }
+
+ /* We found it */
+ return Function;
+}
+
+NTSTATUS
+NTAPI
+MmCallDllInitialize(IN PLDR_DATA_TABLE_ENTRY LdrEntry,
+ IN PLIST_ENTRY ListHead)
+{
+ PMM_DLL_INITIALIZE DllInit;
+
+ /* Try to see if the image exports a DllInitialize routine */
+ DllInit = (PMM_DLL_INITIALIZE)MiLocateExportName(LdrEntry->DllBase,
+ "DllInitialize");
+ if (!DllInit) return STATUS_SUCCESS;
+
+ /* FIXME: TODO */
+ DPRINT1("DllInitialize not called!\n");
+ return STATUS_UNSUCCESSFUL;
+}
+
+VOID
+NTAPI
+MiProcessLoaderEntry(IN PLDR_DATA_TABLE_ENTRY LdrEntry,
+ IN BOOLEAN Insert)
+{
+ KIRQL OldIrql;
+
+ /* Acquire the lock */
+ KeAcquireSpinLock(&PsLoadedModuleSpinLock, &OldIrql);
+
+ /* Insert or remove from the list */
+ Insert ? InsertTailList(&PsLoadedModuleList, &LdrEntry->InLoadOrderLinks)
:
+ RemoveEntryList(&LdrEntry->InLoadOrderLinks);
+
+ /* Release the lock */
+ KeReleaseSpinLock(&PsLoadedModuleSpinLock, OldIrql);
+}
+
+VOID
+NTAPI
+MiUpdateThunks(IN PLOADER_PARAMETER_BLOCK LoaderBlock,
+ IN PVOID OldBase,
+ IN PVOID NewBase,
+ IN ULONG Size)
+{
+ ULONG_PTR OldBaseTop, Delta;
+ PLDR_DATA_TABLE_ENTRY LdrEntry;
+ PLIST_ENTRY NextEntry;
+ ULONG ImportSize;
+ PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor;
+ PULONG ImageThunk;
+
+ /* Calculate the top and delta */
+ OldBaseTop = (ULONG_PTR)OldBase + Size - 1;
+ Delta = (ULONG_PTR)NewBase - (ULONG_PTR)OldBase;
+
+ /* Loop the loader block */
+ for (NextEntry = LoaderBlock->LoadOrderListHead.Flink;
+ NextEntry != &LoaderBlock->LoadOrderListHead;
+ NextEntry = NextEntry->Flink)
+ {
+ /* Get the loader entry */
+ LdrEntry = CONTAINING_RECORD(NextEntry,
+ LDR_DATA_TABLE_ENTRY,
+ InLoadOrderLinks);
+
+ /* Get the import table */
+ ImportDescriptor = RtlImageDirectoryEntryToData(LdrEntry->DllBase,
+ TRUE,
+ IMAGE_DIRECTORY_ENTRY_IMPORT,
+ &ImportSize);
+ if (!ImportDescriptor) continue;
+
+ /* Make sure we have an IAT */
+ DPRINT("[Mm0]: Updating thunks in: %wZ\n",
&LdrEntry->BaseDllName);
+ while ((ImportDescriptor->Name) &&
+ (ImportDescriptor->OriginalFirstThunk))
+ {
+ /* Get the image thunk */
+ ImageThunk = (PVOID)((ULONG_PTR)LdrEntry->DllBase +
+ ImportDescriptor->FirstThunk);
+ while (*ImageThunk)
+ {
+ /* Check if it's within this module */
+ if ((*ImageThunk >= (ULONG_PTR)OldBase) && (*ImageThunk <=
OldBaseTop))
+ {
+ /* Relocate it */
+ DPRINT("[Mm0]: Updating IAT at: %p. Old Entry: %p. New Entry:
%p.\n",
+ ImageThunk, *ImageThunk, *ImageThunk + Delta);
+ *ImageThunk += Delta;
+ }
+
+ /* Go to the next thunk */
+ ImageThunk++;
+ }
+
+ /* Go to the next import */
+ ImportDescriptor++;
+ }
+ }
+}
+
+NTSTATUS
+NTAPI
+MiSnapThunk(IN PVOID DllBase,
+ IN PVOID ImageBase,
+ IN PIMAGE_THUNK_DATA Name,
+ IN PIMAGE_THUNK_DATA Address,
+ IN PIMAGE_EXPORT_DIRECTORY ExportDirectory,
+ IN ULONG ExportSize,
+ IN BOOLEAN SnapForwarder,
+ OUT PCHAR *MissingApi)
+{
+ BOOLEAN IsOrdinal;
+ USHORT Ordinal;
+ PULONG NameTable;
+ PUSHORT OrdinalTable;
+ PIMAGE_IMPORT_BY_NAME NameImport;
+ USHORT Hint;
+ ULONG Low = 0, Mid = 0, High;
+ LONG Ret;
+ NTSTATUS Status;
+ PCHAR MissingForwarder;
+ CHAR NameBuffer[MAXIMUM_FILENAME_LENGTH];
+ PULONG ExportTable;
+ ANSI_STRING DllName;
+ UNICODE_STRING ForwarderName;
+ PLIST_ENTRY NextEntry;
+ PLDR_DATA_TABLE_ENTRY LdrEntry;
+ ULONG ForwardExportSize;
+ PIMAGE_EXPORT_DIRECTORY ForwardExportDirectory;
+ PIMAGE_IMPORT_BY_NAME ForwardName;
+ ULONG ForwardLength;
+ IMAGE_THUNK_DATA ForwardThunk;
+ PAGED_CODE();
+
+ /* Check if this is an ordinal */
+ IsOrdinal = IMAGE_SNAP_BY_ORDINAL(Name->u1.Ordinal);
+ if ((IsOrdinal) && !(SnapForwarder))
+ {
+ /* Get the ordinal number and set it as missing */
+ Ordinal = (USHORT)(IMAGE_ORDINAL(Name->u1.Ordinal) -
+ ExportDirectory->Base);
+ *MissingApi = (PCHAR)(ULONG_PTR)Ordinal;
+ }
+ else
+ {
+ /* Get the VA if we don't have to snap */
+ if (!SnapForwarder) Name->u1.AddressOfData += (ULONG_PTR)ImageBase;
+ NameImport = (PIMAGE_IMPORT_BY_NAME)Name->u1.AddressOfData;
+
+ /* Copy the procedure name */
+ strncpy(*MissingApi,
+ (PCHAR)&NameImport->Name[0],
+ MAXIMUM_FILENAME_LENGTH - 1);
+
+ /* Setup name tables */
+ DPRINT("Import name: %s\n", NameImport->Name);
+ NameTable = (PULONG)((ULONG_PTR)DllBase +
+ ExportDirectory->AddressOfNames);
+ OrdinalTable = (PUSHORT)((ULONG_PTR)DllBase +
+ ExportDirectory->AddressOfNameOrdinals);
+
+ /* Get the hint and check if it's valid */
+ Hint = NameImport->Hint;
+ if ((Hint < ExportDirectory->NumberOfNames) &&
+ !(strcmp((PCHAR) NameImport->Name, (PCHAR)DllBase + NameTable[Hint])))
+ {
+ /* We have a match, get the ordinal number from here */
+ Ordinal = OrdinalTable[Hint];
+ }
+ else
+ {
+ /* Do a binary search */
+ High = ExportDirectory->NumberOfNames - 1;
+ while (High >= Low)
+ {
+ /* Get new middle value */
+ Mid = (Low + High) >> 1;
+
+ /* Compare name */
+ Ret = strcmp((PCHAR)NameImport->Name, (PCHAR)DllBase +
NameTable[Mid]);
+ if (Ret < 0)
+ {
+ /* Update high */
+ High = Mid - 1;
+ }
+ else if (Ret > 0)
+ {
+ /* Update low */
+ Low = Mid + 1;
+ }
+ else
+ {
+ /* We got it */
+ break;
+ }
+ }
+
+ /* Check if we couldn't find it */
+ if (High < Low) return STATUS_DRIVER_ENTRYPOINT_NOT_FOUND;
+
+ /* Otherwise, this is the ordinal */
+ Ordinal = OrdinalTable[Mid];
+ }
+ }
+
+ /* Check if the ordinal is invalid */
+ if (Ordinal >= ExportDirectory->NumberOfFunctions)
+ {
+ /* Fail */
+ Status = STATUS_DRIVER_ORDINAL_NOT_FOUND;
+ }
+ else
+ {
+ /* In case the forwarder is missing */
+ MissingForwarder = NameBuffer;
+
+ /* Resolve the address and write it */
+ ExportTable = (PULONG)((ULONG_PTR)DllBase +
+ ExportDirectory->AddressOfFunctions);
+ Address->u1.Function = (ULONG_PTR)DllBase + ExportTable[Ordinal];
+
+ /* Assume success from now on */
+ Status = STATUS_SUCCESS;
+
+ /* Check if the function is actually a forwarder */
+ if ((Address->u1.Function > (ULONG_PTR)ExportDirectory) &&
+ (Address->u1.Function < ((ULONG_PTR)ExportDirectory + ExportSize)))
+ {
+ /* Now assume failure in case the forwarder doesn't exist */
+ Status = STATUS_DRIVER_ENTRYPOINT_NOT_FOUND;
+
+ /* Build the forwarder name */
+ DllName.Buffer = (PCHAR)Address->u1.Function;
+ DllName.Length = strchr(DllName.Buffer, '.') -
+ DllName.Buffer +
+ sizeof(ANSI_NULL);
+ DllName.MaximumLength = DllName.Length;
+
+ /* Convert it */
+ if (!NT_SUCCESS(RtlAnsiStringToUnicodeString(&ForwarderName,
+ &DllName,
+ TRUE)))
+ {
+ /* We failed, just return an error */
+ return Status;
+ }
+
+ /* Loop the module list */
+ NextEntry = PsLoadedModuleList.Flink;
+ while (NextEntry != &PsLoadedModuleList)
+ {
+ /* Get the loader entry */
+ LdrEntry = CONTAINING_RECORD(NextEntry,
+ LDR_DATA_TABLE_ENTRY,
+ InLoadOrderLinks);
+
+ /* Check if it matches */
+ if (RtlPrefixString((PSTRING)&ForwarderName,
+ (PSTRING)&LdrEntry->BaseDllName,
+ TRUE))
+ {
+ /* Get the forwarder export directory */
+ ForwardExportDirectory =
+ RtlImageDirectoryEntryToData(LdrEntry->DllBase,
+ TRUE,
+ IMAGE_DIRECTORY_ENTRY_EXPORT,
+ &ForwardExportSize);
+ if (!ForwardExportDirectory) break;
+
+ /* Allocate a name entry */
+ ForwardLength = strlen(DllName.Buffer + DllName.Length) +
+ sizeof(ANSI_NULL);
+ ForwardName = ExAllocatePoolWithTag(PagedPool,
+ sizeof(*ForwardName) +
+ ForwardLength,
+ TAG_LDR_WSTR);
+ if (!ForwardName) break;
+
+ /* Copy the data */
+ RtlCopyMemory(&ForwardName->Name[0],
+ DllName.Buffer + DllName.Length,
+ ForwardLength);
+ ForwardName->Hint = 0;
+
+ /* Set the new address */
+ *(PULONG)&ForwardThunk.u1.AddressOfData = (ULONG)ForwardName;
+
+ /* Snap the forwarder */
+ Status = MiSnapThunk(LdrEntry->DllBase,
+ ImageBase,
+ &ForwardThunk,
+ &ForwardThunk,
+ ForwardExportDirectory,
+ ForwardExportSize,
+ TRUE,
+ &MissingForwarder);
+
+ /* Free the forwarder name and set the thunk */
+ ExFreePool(ForwardName);
+ Address->u1 = ForwardThunk.u1;
+ break;
+ }
+
+ /* Go to the next entry */
+ NextEntry = NextEntry->Flink;
+ }
+
+ /* Free the name */
+ RtlFreeUnicodeString(&ForwarderName);
+ }
+ }
+
+ /* Return status */
+ return Status;
+}
+
+NTSTATUS
+NTAPI
+MmUnloadSystemImage(IN PVOID ImageHandle)
+{
+ PLDR_DATA_TABLE_ENTRY LdrEntry = ImageHandle;
+ PVOID BaseAddress = LdrEntry->DllBase;
+ NTSTATUS Status;
+ ANSI_STRING TempName;
+ BOOLEAN HadEntry = FALSE;
+
+ /* Acquire the loader lock */
+ KeEnterCriticalRegion();
+ KeWaitForSingleObject(&MmSystemLoadLock,
+ WrVirtualMemory,
+ KernelMode,
+ FALSE,
+ NULL);
+
+ /* Check if this driver was loaded at boot and didn't get imports parsed */
+ if (LdrEntry->LoadedImports == (PVOID)-1) goto Done;
+
+ /* We should still be alive */
+ ASSERT(LdrEntry->LoadCount != 0);
+ LdrEntry->LoadCount--;
+
+ /* Check if we're still loaded */
+ if (LdrEntry->LoadCount) goto Done;
+
+ /* We should cleanup... are symbols loaded */
+ if (LdrEntry->Flags & LDRP_DEBUG_SYMBOLS_LOADED)
+ {
+ /* Create the ANSI name */
+ Status = RtlUnicodeStringToAnsiString(&TempName,
+ &LdrEntry->BaseDllName,
+ TRUE);
+ if (NT_SUCCESS(Status))
+ {
+ /* Unload the symbols */
+ DbgUnLoadImageSymbols(&TempName, BaseAddress, -1);
+ RtlFreeAnsiString(&TempName);
+ }
+ }
+
+ /* FIXME: Free the driver */
+ //MmFreeSection(LdrEntry->DllBase);
+
+ /* Check if we're linked in */
+ if (LdrEntry->InLoadOrderLinks.Flink)
+ {
+ /* Remove us */
+ MiProcessLoaderEntry(LdrEntry, FALSE);
+ HadEntry = TRUE;
+ }
+
+ /* Dereference and clear the imports */
+ MiDereferenceImports(LdrEntry->LoadedImports);
+ MiClearImports(LdrEntry);
+
+ /* Check if the entry needs to go away */
+ if (HadEntry)
+ {
+ /* Check if it had a name */
+ if (LdrEntry->FullDllName.Buffer)
+ {
+ /* Free it */
+ ExFreePool(LdrEntry->FullDllName.Buffer);
+ }
+
+ /* Check if we had a section */
+ if (LdrEntry->SectionPointer)
+ {
+ /* Dereference it */
+ ObDereferenceObject(LdrEntry->SectionPointer);
+ }
+
+ /* Free the entry */
+ ExFreePool(LdrEntry);
+ }
+
+ /* Release the system lock and return */
+Done:
+ KeReleaseMutant(&MmSystemLoadLock, 1, FALSE, FALSE);
+ KeLeaveCriticalRegion();
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+NTAPI
+MiResolveImageReferences(IN PVOID ImageBase,
+ IN PUNICODE_STRING ImageFileDirectory,
+ IN PUNICODE_STRING NamePrefix OPTIONAL,
+ OUT PCHAR *MissingApi,
+ OUT PWCHAR *MissingDriver,
+ OUT PLOAD_IMPORTS *LoadImports)
+{
+ PCHAR MissingApiBuffer = *MissingApi, ImportName;
+ PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor, CurrentImport;
+ ULONG ImportSize, ImportCount = 0, LoadedImportsSize, ExportSize;
+ PLOAD_IMPORTS LoadedImports, NewImports;
+ ULONG GdiLink, NormalLink, i;
+ BOOLEAN ReferenceNeeded, Loaded;
+ ANSI_STRING TempString;
+ UNICODE_STRING NameString, DllName;
+ PLDR_DATA_TABLE_ENTRY LdrEntry = NULL, DllEntry, ImportEntry = NULL;
+ PVOID ImportBase, DllBase;
+ PLIST_ENTRY NextEntry;
+ PIMAGE_EXPORT_DIRECTORY ExportDirectory;
+ NTSTATUS Status;
+ PIMAGE_THUNK_DATA OrigThunk, FirstThunk;
+ PAGED_CODE();
+ DPRINT("%s - ImageBase: %p. ImageFileDirectory: %wZ\n",
+ __FUNCTION__, ImageBase, ImageFileDirectory);
+
+ /* Assume no imports */
+ *LoadImports = (PVOID)-2;
+
+ /* Get the import descriptor */
+ ImportDescriptor = RtlImageDirectoryEntryToData(ImageBase,
+ TRUE,
+ IMAGE_DIRECTORY_ENTRY_IMPORT,
+ &ImportSize);
+ if (!ImportDescriptor) return STATUS_SUCCESS;
+
+ /* Loop all imports to count them */
+ for (CurrentImport = ImportDescriptor;
+ (CurrentImport->Name) && (CurrentImport->OriginalFirstThunk);
+ CurrentImport++)
+ {
+ /* One more */
+ ImportCount++;
+ }
+
+ /* Make sure we have non-zero imports */
+ if (ImportCount)
+ {
+ /* Calculate and allocate the list we'll need */
+ LoadedImportsSize = ImportCount * sizeof(PVOID) + sizeof(SIZE_T);
+ LoadedImports = ExAllocatePoolWithTag(PagedPool,
+ LoadedImportsSize,
+ TAG_LDR_WSTR);
+ if (LoadedImports)
+ {
+ /* Zero it */
+ RtlZeroMemory(LoadedImports, LoadedImportsSize);
+ }
+ }
+ else
+ {
+ /* No table */
+ LoadedImports = NULL;
+ }
+
+ /* Reset the import count and loop descriptors again */
+ ImportCount = GdiLink = NormalLink = 0;
+ while ((ImportDescriptor->Name) &&
(ImportDescriptor->OriginalFirstThunk))
+ {
+ /* Get the name */
+ ImportName = (PCHAR)((ULONG_PTR)ImageBase + ImportDescriptor->Name);
+
+ /* Check if this is a GDI driver */
+ GdiLink = GdiLink |
+ !(_strnicmp(ImportName, "win32k", sizeof("win32k")
- 1));
+
+ /* We can also allow dxapi */
+ NormalLink = NormalLink |
+ ((_strnicmp(ImportName, "win32k",
sizeof("win32k") - 1)) &&
+ (_strnicmp(ImportName, "dxapi", sizeof("dxapi")
- 1)));
+
+ /* Check if this is a valid GDI driver */
+ if ((GdiLink) && (NormalLink))
+ {
+ /* It's not, it's importing stuff it shouldn't be! */
+ MiDereferenceImports(LoadedImports);
+ if (LoadedImports) ExFreePool(LoadedImports);
+ return STATUS_PROCEDURE_NOT_FOUND;
+ }
+
+ /* Check if this is a "core" import, which doesn't get referenced
*/
+ if (!(_strnicmp(ImportName, "ntoskrnl", sizeof("ntoskrnl") -
1)) ||
+ !(_strnicmp(ImportName, "win32k", sizeof("win32k") - 1))
||
+ !(_strnicmp(ImportName, "hal", sizeof("hal") - 1)))
+ {
+ /* Don't reference this */
+ ReferenceNeeded = FALSE;
+ }
+ else
+ {
+ /* Reference these modules */
+ ReferenceNeeded = TRUE;
+ }
+
+ /* Now setup a unicode string for the import */
+ RtlInitAnsiString(&TempString, ImportName);
+ Status = RtlAnsiStringToUnicodeString(&NameString, &TempString, TRUE);
+ if (!NT_SUCCESS(Status))
+ {
+ /* Failed */
+ MiDereferenceImports(LoadedImports);
+ if (LoadedImports) ExFreePool(LoadedImports);
+ return Status;
+ }
+
+ /* We don't support name prefixes yet */
+ if (NamePrefix) DPRINT1("Name Prefix not yet supported!\n");
+
+ /* Remember that we haven't loaded the import at this point */
+CheckDllState:
+ Loaded = FALSE;
+ ImportBase = NULL;
+
+ /* Loop the driver list */
+ NextEntry = PsLoadedModuleList.Flink;
+ while (NextEntry != &PsLoadedModuleList)
+ {
+ /* Get the loader entry and compare the name */
+ LdrEntry = CONTAINING_RECORD(NextEntry,
+ LDR_DATA_TABLE_ENTRY,
+ InLoadOrderLinks);
+ if (RtlEqualUnicodeString(&NameString,
+ &LdrEntry->BaseDllName,
+ TRUE))
+ {
+ /* Get the base address */
+ ImportBase = LdrEntry->DllBase;
+
+ /* Check if we haven't loaded yet, and we need references */
+ if (!(Loaded) && (ReferenceNeeded))
+ {
+ /* Make sure we're not already loading */
+ if (!(LdrEntry->Flags & LDRP_LOAD_IN_PROGRESS))
+ {
+ /* Increase the load count */
+ LdrEntry->LoadCount++;
+ }
+ }
+
+ /* Done, break out */
+ break;
+ }
+
+ /* Go to the next entry */
+ NextEntry = NextEntry->Flink;
+ }
+
+ /* Check if we haven't loaded the import yet */
+ if (!ImportBase)
+ {
+ /* Setup the import DLL name */
+ DllName.MaximumLength = NameString.Length +
+ ImageFileDirectory->Length +
+ sizeof(UNICODE_NULL);
+ DllName.Buffer = ExAllocatePoolWithTag(NonPagedPool,
+ DllName.MaximumLength,
+ TAG_LDR_WSTR);
+ if (DllName.Buffer)
+ {
+ /* Setup the base length and copy it */
+ DllName.Length = ImageFileDirectory->Length;
+ RtlCopyMemory(DllName.Buffer,
+ ImageFileDirectory->Buffer,
+ ImageFileDirectory->Length);
+
+ /* Now add the import name and null-terminate it */
+ RtlAppendStringToString((PSTRING)&DllName,
+ (PSTRING)&NameString);
+ DllName.Buffer[(DllName.MaximumLength - 1) / 2] = UNICODE_NULL;
+
+ /* Load the image */
+ Status = MmLoadSystemImage(&DllName,
+ NamePrefix,
+ NULL,
+ 0,
+ (PVOID)&DllEntry,
+ &DllBase);
+ if (NT_SUCCESS(Status))
+ {
+ /* We can free the DLL Name */
+ ExFreePool(DllName.Buffer);
+ }
+ else
+ {
+ /* Fill out the information for the error */
+ *MissingDriver = DllName.Buffer;
+ *(PULONG)MissingDriver |= 1;
+ *MissingApi = NULL;
+ }
+ }
+ else
+ {
+ /* We're out of resources */
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ /* Check if we're OK until now */
+ if (NT_SUCCESS(Status))
+ {
+ /* We're now loaded */
+ Loaded = TRUE;
+
+ /* Sanity check */
+ ASSERT(DllBase = DllEntry->DllBase);
+
+ /* Call the initialization routines */
+ Status = MmCallDllInitialize(DllEntry, &PsLoadedModuleList);
+ if (!NT_SUCCESS(Status))
+ {
+ /* We failed, unload the image */
+ MmUnloadSystemImage(DllEntry);
+ while (TRUE);
+ Loaded = FALSE;
+ }
+ }
+
+ /* Check if we failed by here */
+ if (!NT_SUCCESS(Status))
+ {
+ /* Cleanup and return */
+ RtlFreeUnicodeString(&NameString);
+ MiDereferenceImports(LoadedImports);
+ if (LoadedImports) ExFreePool(LoadedImports);
+ return Status;
+ }
+
+ /* Loop again to make sure that everything is OK */
+ goto CheckDllState;
+ }
+
+ /* Check if we're support to reference this import */
+ if ((ReferenceNeeded) && (LoadedImports))
+ {
+ /* Make sure we're not already loading */
+ if (!(LdrEntry->Flags & LDRP_LOAD_IN_PROGRESS))
+ {
+ /* Add the entry */
+ LoadedImports->Entry[LoadedImports->Count] = LdrEntry;
+ LoadedImports->Count++;
+ }
+ }
+
+ /* Free the import name */
+ RtlFreeUnicodeString(&NameString);
+
+ /* Set the missing driver name and get the export directory */
+ *MissingDriver = LdrEntry->BaseDllName.Buffer;
+ ExportDirectory = RtlImageDirectoryEntryToData(ImportBase,
+ TRUE,
+ IMAGE_DIRECTORY_ENTRY_EXPORT,
+ &ExportSize);
+ if (!ExportDirectory)
+ {
+ /* Cleanup and return */
+ MiDereferenceImports(LoadedImports);
+ if (LoadedImports) ExFreePool(LoadedImports);
+ return STATUS_DRIVER_ENTRYPOINT_NOT_FOUND;
+ }
+
+ /* Make sure we have an IAT */
+ if (ImportDescriptor->OriginalFirstThunk)
+ {
+ /* Get the first thunks */
+ OrigThunk = (PVOID)((ULONG_PTR)ImageBase +
+ ImportDescriptor->OriginalFirstThunk);
+ FirstThunk = (PVOID)((ULONG_PTR)ImageBase +
+ ImportDescriptor->FirstThunk);
+
+ /* Loop the IAT */
+ while (OrigThunk->u1.AddressOfData)
+ {
+ /* Snap thunk */
+ Status = MiSnapThunk(ImportBase,
+ ImageBase,
+ OrigThunk++,
+ FirstThunk++,
+ ExportDirectory,
+ ExportSize,
+ FALSE,
+ MissingApi);
+ if (!NT_SUCCESS(Status))
+ {
+ /* Cleanup and return */
+ MiDereferenceImports(LoadedImports);
+ if (LoadedImports) ExFreePool(LoadedImports);
+ return Status;
+ }
+
+ /* Reset the buffer */
+ *MissingApi = MissingApiBuffer;
+ }
+ }
+
+ /* Go to the next import */
+ ImportDescriptor++;
+ }
+
+ /* Check if we have an import list */
+ if (LoadedImports)
+ {
+ /* Reset the count again, and loop entries*/
+ ImportCount = 0;
+ for (i = 0; i < LoadedImports->Count; i++)
+ {
+ if (LoadedImports->Entry[i])
+ {
+ /* Got an entry, OR it with 1 in case it's the single entry */
+ ImportEntry = (PVOID)((ULONG_PTR)LoadedImports->Entry[i] | 1);
+ ImportCount++;
+ }
+ }
+
+ /* Check if we had no imports */
+ if (!ImportCount)
+ {
+ /* Free the list and set it to no imports */
+ ExFreePool(LoadedImports);
+ LoadedImports = (PVOID)-2;
+ }
+ else if (ImportCount == 1)
+ {
+ /* Just one entry, we can free the table and only use our entry */
+ ExFreePool(LoadedImports);
+ LoadedImports = (PLOAD_IMPORTS)ImportEntry;
+ }
+ else if (ImportCount != LoadedImports->Count)
+ {
+ /* Allocate a new list */
+ LoadedImportsSize = ImportCount * sizeof(PVOID) + sizeof(SIZE_T);
+ NewImports = ExAllocatePoolWithTag(PagedPool,
+ LoadedImportsSize,
+ TAG_LDR_WSTR);
+ if (NewImports)
+ {
+ /* Set count */
+ NewImports->Count = 0;
+
+ /* Loop all the imports */
+ for (i = 0; i < LoadedImports->Count; i++)
+ {
+ /* Make sure it's valid */
+ if (LoadedImports->Entry[i])
+ {
+ /* Copy it */
+ NewImports->Entry[NewImports->Count] =
LoadedImports->Entry[i];
+ NewImports->Count++;
+ }
+ }
+
+ /* Free the old copy */
+ ExFreePool(LoadedImports);
+ LoadedImports = NewImports;
+ }
+ }
+
+ /* Return the list */
+ *LoadImports = LoadedImports;
+ }
+
+ /* Return success */
+ return STATUS_SUCCESS;
+}
+
+VOID
+NTAPI
+MiReloadBootLoadedDrivers(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
+{
+ PLIST_ENTRY NextEntry;
+ ULONG i = 0;
+ PIMAGE_NT_HEADERS NtHeader;
+ PLDR_DATA_TABLE_ENTRY LdrEntry;
+ PIMAGE_FILE_HEADER FileHeader;
+ BOOLEAN ValidRelocs;
+ PIMAGE_DATA_DIRECTORY DataDirectory;
+ PVOID DllBase, NewImageAddress;
+ NTSTATUS Status;
+
+ /* Loop driver list */
+ for (NextEntry = LoaderBlock->LoadOrderListHead.Flink;
+ NextEntry != &LoaderBlock->LoadOrderListHead;
+ NextEntry = NextEntry->Flink)
+ {
+ /* Get the loader entry and NT header */
+ LdrEntry = CONTAINING_RECORD(NextEntry,
+ LDR_DATA_TABLE_ENTRY,
+ InLoadOrderLinks);
+ NtHeader = RtlImageNtHeader(LdrEntry->DllBase);
+
+ /* Debug info */
+ DPRINT("[Mm0]: Driver at: %p ending at: %p for module: %wZ\n",
+ LdrEntry->DllBase,
+ (ULONG_PTR)LdrEntry->DllBase+ LdrEntry->SizeOfImage,
+ &LdrEntry->FullDllName);
+
+ /* Skip kernel and HAL */
+ /* ROS HACK: Skip BOOTVID/KDCOM too */
+ i++;
+ if (i <= 4) continue;
+
+ /* Skip non-drivers */
+ if (!NtHeader) continue;
+
+ /* Get the file header and make sure we can relocate */
+ FileHeader = &NtHeader->FileHeader;
+ if (FileHeader->Characteristics & IMAGE_FILE_RELOCS_STRIPPED) continue;
+ if (NtHeader->OptionalHeader.NumberOfRvaAndSizes <
+ IMAGE_DIRECTORY_ENTRY_BASERELOC) continue;
+
+ /* Everything made sense until now, check the relocation section too */
+ DataDirectory = &NtHeader->OptionalHeader.
+ DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
+ if (!DataDirectory->VirtualAddress)
+ {
+ /* We don't really have relocations */
+ ValidRelocs = FALSE;
+ }
+ else
+ {
+ /* Make sure the size is valid */
+ if ((DataDirectory->VirtualAddress + DataDirectory->Size) >
+ LdrEntry->SizeOfImage)
+ {
+ /* They're not, skip */
+ continue;
+ }
+
+ /* We have relocations */
+ ValidRelocs = TRUE;
+ }
+
+ /* Remember the original address */
+ DllBase = LdrEntry->DllBase;
+
+ /* Allocate a virtual section for the module */
+ NewImageAddress = MmAllocateSection(LdrEntry->SizeOfImage, NULL);
+ if (!NewImageAddress)
+ {
+ /* Shouldn't happen */
+ DPRINT1("[Mm0]: Couldn't allocate driver section!\n");
+ while (TRUE);
+ }
+
+ /* Sanity check */
+ DPRINT("[Mm0]: Copying from: %p to: %p\n", DllBase, NewImageAddress);
+ ASSERT(ExpInitializationPhase == 0);
+
+ /* Now copy the entire driver over */
+ RtlCopyMemory(NewImageAddress, DllBase, LdrEntry->SizeOfImage);
+
+ /* Sanity check */
+ ASSERT(*(PULONG)NewImageAddress == *(PULONG)DllBase);
+
+ /* Set the image base to the address where the loader put it */
+ NtHeader->OptionalHeader.ImageBase = (ULONG_PTR)DllBase;
+ NtHeader = RtlImageNtHeader(NewImageAddress);
+ NtHeader->OptionalHeader.ImageBase = (ULONG_PTR)DllBase;
+
+ /* Check if we had relocations */
+ if (ValidRelocs)
+ {
+ /* Relocate the image */
+ Status = LdrRelocateImageWithBias(NewImageAddress,
+ 0,
+ "SYSLDR",
+ STATUS_SUCCESS,
+ STATUS_CONFLICTING_ADDRESSES,
+ STATUS_INVALID_IMAGE_FORMAT);
+ if (!NT_SUCCESS(Status))
+ {
+ /* This shouldn't happen */
+ DPRINT1("Relocations failed!\n");
+ while (TRUE);
+ }
+ }
+
+ /* Update the loader entry */
+ LdrEntry->DllBase = NewImageAddress;
+
+ /* Update the thunks */
+ DPRINT("[Mm0]: Updating thunks to: %wZ\n",
&LdrEntry->BaseDllName);
+ MiUpdateThunks(LoaderBlock,
+ DllBase,
+ NewImageAddress,
+ LdrEntry->SizeOfImage);
+
+ /* Update the loader entry */
+ LdrEntry->Flags |= 0x01000000;
+ LdrEntry->EntryPoint = (PVOID)((ULONG_PTR)NewImageAddress +
+ NtHeader->OptionalHeader.AddressOfEntryPoint);
+ LdrEntry->SizeOfImage = LdrEntry->SizeOfImage;
+
+ /* Free the old copy */
+ MiFreeBootDriverMemory(DllBase, LdrEntry->SizeOfImage);
+ }
+}
+
+BOOLEAN
+NTAPI
+MiInitializeLoadedModuleList(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
+{
+ PLDR_DATA_TABLE_ENTRY LdrEntry, NewEntry;
+ PLIST_ENTRY ListHead, NextEntry;
+ ULONG EntrySize;
+
+ /* Setup the loaded module list and lock */
+ KeInitializeSpinLock(&PsLoadedModuleSpinLock);
+ InitializeListHead(&PsLoadedModuleList);
+
+ /* Get loop variables and the kernel entry */
+ ListHead = &LoaderBlock->LoadOrderListHead;
+ NextEntry = ListHead->Flink;
+ LdrEntry = CONTAINING_RECORD(NextEntry,
+ LDR_DATA_TABLE_ENTRY,
+ InLoadOrderLinks);
+ PsNtosImageBase = (ULONG)LdrEntry->DllBase;
+
+ /* Loop the loader block */
+ while (NextEntry != ListHead)
+ {
+ /* Get the loader entry */
+ LdrEntry = CONTAINING_RECORD(NextEntry,
+ LDR_DATA_TABLE_ENTRY,
+ InLoadOrderLinks);
+
+ /* FIXME: ROS HACK. Make sure this is a driver */
+ if (!RtlImageNtHeader(LdrEntry->DllBase))
+ {
+ /* Skip this entry */
+ NextEntry= NextEntry->Flink;
+ continue;
+ }
+
+ /* Calculate the size we'll need and allocate a copy */
+ EntrySize = sizeof(LDR_DATA_TABLE_ENTRY) +
+ LdrEntry->BaseDllName.MaximumLength +
+ sizeof(UNICODE_NULL);
+ NewEntry = ExAllocatePoolWithTag(NonPagedPool, EntrySize, TAG_LDR_WSTR);
+ if (!NewEntry) return FALSE;
+
+ /* Copy the entry over */
+ *NewEntry = *LdrEntry;
+
+ /* Allocate the name */
+ NewEntry->FullDllName.Buffer =
+ ExAllocatePoolWithTag(PagedPool,
+ LdrEntry->FullDllName.MaximumLength +
+ sizeof(UNICODE_NULL),
+ TAG_LDR_WSTR);
+ if (!NewEntry->FullDllName.Buffer) return FALSE;
+
+ /* Set the base name */
+ NewEntry->BaseDllName.Buffer = (PVOID)(NewEntry + 1);
+
+ /* Copy the full and base name */
+ RtlCopyMemory(NewEntry->FullDllName.Buffer,
+ LdrEntry->FullDllName.Buffer,
+ LdrEntry->FullDllName.MaximumLength);
+ RtlCopyMemory(NewEntry->BaseDllName.Buffer,
+ LdrEntry->BaseDllName.Buffer,
+ LdrEntry->BaseDllName.MaximumLength);
+
+ /* Null-terminate the base name */
+ NewEntry->BaseDllName.Buffer[NewEntry->BaseDllName.Length /
+ sizeof(WCHAR)] = UNICODE_NULL;
+
+ /* Insert the entry into the list */
+ InsertTailList(&PsLoadedModuleList, &NewEntry->InLoadOrderLinks);
+ NextEntry = NextEntry->Flink;
+ }
+
+ /* Build the import lists for the boot drivers */
+ //MiBuildImportsForBootDrivers();
+
+ /* We're done */
+ return TRUE;
+}
+
+#if 0
+
+BOOLEAN
+NTAPI
+MmVerifyImageIsOkForMpUse(IN PVOID BaseAddress)
+{
+ PIMAGE_NT_HEADERS NtHeader;
+ PAGED_CODE();
+
+ /* Get NT Headers */
+ NtHeader = RtlImageNtHeader(BaseAddress);
+ if (NtHeader)
+ {
+ /* Check if this image is only safe for UP while we have 2+ CPUs */
+ if ((KeNumberProcessors > 1) &&
+ (NtHeader->FileHeader.Characteristics & IMAGE_FILE_UP_SYSTEM_ONLY))
+ {
+ /* Fail */
+ return FALSE;
+ }
+ }
+
+ /* Otherwise, it's safe */
+ return TRUE;
+}
+
+NTSTATUS
+NTAPI
+MmCheckSystemImage(IN HANDLE ImageHandle,
+ IN BOOLEAN PurgeSection)
+{
+ NTSTATUS Status;
+ HANDLE SectionHandle;
+ PVOID ViewBase = NULL;
+ SIZE_T ViewSize = 0;
+ IO_STATUS_BLOCK IoStatusBlock;
+ FILE_STANDARD_INFORMATION FileStandardInfo;
+ KAPC_STATE ApcState;
+ PAGED_CODE();
+
+ /* Create a section for the DLL */
+ Status = ZwCreateSection(&SectionHandle,
+ SECTION_MAP_EXECUTE,
+ NULL,
+ NULL,
+ PAGE_EXECUTE,
+ SEC_COMMIT,
+ ImageHandle);
+ if (!NT_SUCCESS(Status)) return Status;
+
+ /* Make sure we're in the system process */
+ KeStackAttachProcess(&PsInitialSystemProcess->Pcb, &ApcState);
+
+ /* Map it */
+ Status = ZwMapViewOfSection(SectionHandle,
+ NtCurrentProcess(),
+ &ViewBase,
+ 0,
+ 0,
+ NULL,
+ &ViewSize,
+ ViewShare,
+ 0,
+ PAGE_EXECUTE);
+ if (!NT_SUCCESS(Status))
+ {
+ /* We failed, close the handle and return */
+ KeUnstackDetachProcess(&ApcState);
+ ZwClose(SectionHandle);
+ return Status;
+ }
+
+ /* Now query image information */
+ Status = ZwQueryInformationFile(ImageHandle,
+ &IoStatusBlock,
+ &FileStandardInfo,
+ sizeof(FileStandardInfo),
+ FileStandardInformation);
+ if ( NT_SUCCESS(Status) )
+ {
+ /* First, verify the checksum */
+ if (!LdrVerifyMappedImageMatchesChecksum(ViewBase,
+ FileStandardInfo.
+ EndOfFile.LowPart,
+ FileStandardInfo.
+ EndOfFile.LowPart))
+ {
+ /* Set checksum failure */
+ Status = STATUS_IMAGE_CHECKSUM_MISMATCH;
+ }
+
+ /* Check that it's a valid SMP image if we have more then one CPU */
+ if (!MmVerifyImageIsOkForMpUse(ViewBase))
+ {
+ /* Otherwise it's not the right image */
+ Status = STATUS_IMAGE_MP_UP_MISMATCH;
+ }
+ }
+
+ /* Unmap the section, close the handle, and return status */
+ ZwUnmapViewOfSection(NtCurrentProcess(), ViewBase);
+ KeUnstackDetachProcess(&ApcState);
+ ZwClose(SectionHandle);
+ return Status;
+}
+
+#endif
+
+NTSTATUS
+NTAPI
+MmLoadSystemImage(IN PUNICODE_STRING FileName,
+ IN PUNICODE_STRING NamePrefix OPTIONAL,
+ IN PUNICODE_STRING LoadedName OPTIONAL,
+ IN ULONG Flags,
+ OUT PVOID *ModuleObject,
+ OUT PVOID *ImageBaseAddress)
+{
+ PVOID ModuleLoadBase = NULL;
+ NTSTATUS Status;
+ HANDLE FileHandle = NULL;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ IO_STATUS_BLOCK IoStatusBlock;
+ PIMAGE_NT_HEADERS NtHeader;
+ UNICODE_STRING BaseName, BaseDirectory, PrefixName, UnicodeTemp;
+ PLDR_DATA_TABLE_ENTRY LdrEntry = NULL;
+ ULONG EntrySize, DriverSize;
+ PLOAD_IMPORTS LoadedImports = (PVOID)-2;
+ PCHAR MissingApiName, Buffer;
+ PWCHAR MissingDriverName;
+ HANDLE SectionHandle;
+ ACCESS_MASK DesiredAccess;
+ PVOID Section = NULL;
+ BOOLEAN LockOwned = FALSE;
+ PLIST_ENTRY NextEntry;
+ IMAGE_INFO ImageInfo;
+ ANSI_STRING AnsiTemp;
+ PAGED_CODE();
+
+ /* Detect session-load */
+ if (Flags)
+ {
+ /* Sanity checks */
+ ASSERT(NamePrefix == NULL);
+ ASSERT(LoadedName == NULL);
+
+ /* Make sure the process is in session too */
+ if (!PsGetCurrentProcess()->ProcessInSession) return STATUS_NO_MEMORY;
+ }
+
+ if (ModuleObject) *ModuleObject = NULL;
+ if (ImageBaseAddress) *ImageBaseAddress = NULL;
+
+ /* Allocate a buffer we'll use for names */
+ Buffer = ExAllocatePoolWithTag(NonPagedPool, MAX_PATH, TAG_LDR_WSTR);
+ if (!Buffer) return STATUS_INSUFFICIENT_RESOURCES;
+
+ /* Check for a separator */
+ if (FileName->Buffer[0] == OBJ_NAME_PATH_SEPARATOR)
+ {
+ PWCHAR p;
+ ULONG BaseLength;
+
+ /* Loop the path until we get to the base name */
+ p = &FileName->Buffer[FileName->Length / sizeof(WCHAR)];
+ while (*(p - 1) != OBJ_NAME_PATH_SEPARATOR) p--;
+
+ /* Get the length */
+ BaseLength = (ULONG)(&FileName->Buffer[FileName->Length /
sizeof(WCHAR)] - p);
+ BaseLength *= sizeof(WCHAR);
+
+ /* Setup the string */
+ BaseName.Length = (USHORT)BaseLength;
+ BaseName.Buffer = p;
+ }
+ else
+ {
+ /* Otherwise, we already have a base name */
+ BaseName.Length = FileName->Length;
+ BaseName.Buffer = FileName->Buffer;
+ }
+
+ /* Setup the maximum length */
+ BaseName.MaximumLength = BaseName.Length;
+
+ /* Now compute the base directory */
+ BaseDirectory = *FileName;
+ BaseDirectory.Length -= BaseName.Length;
+ BaseDirectory.MaximumLength = BaseDirectory.Length;
+
+ /* And the prefix, which for now is just the name itself */
+ PrefixName = *FileName;
+
+ /* Check if we have a prefix */
+ if (NamePrefix) DPRINT1("Prefixed images are not yet supported!\n");
+
+ /* Check if we already have a name, use it instead */
+ if (LoadedName) BaseName = *LoadedName;
+
+ /* Acquire the load lock */
+LoaderScan:
+ ASSERT(LockOwned == FALSE);
+ LockOwned = TRUE;
+ KeEnterCriticalRegion();
+ KeWaitForSingleObject(&MmSystemLoadLock,
+ WrVirtualMemory,
+ KernelMode,
+ FALSE,
+ NULL);
+
+ /* Scan the module list */
+ NextEntry = PsLoadedModuleList.Flink;
+ while (NextEntry != &PsLoadedModuleList)
+ {
+ /* Get the entry and compare the names */
+ LdrEntry = CONTAINING_RECORD(NextEntry,
+ LDR_DATA_TABLE_ENTRY,
+ InLoadOrderLinks);
+ if (RtlEqualUnicodeString(&PrefixName, &LdrEntry->FullDllName, TRUE))
+ {
+ /* Found it, break out */
+ break;
+ }
+
+ /* Keep scanning */
+ NextEntry = NextEntry->Flink;
+ }
+
+ /* Check if we found the image */
+ if (NextEntry != &PsLoadedModuleList)
+ {
+ /* Check if we had already mapped a section */
+ if (Section)
+ {
+ /* Dereference and clear */
+ ObDereferenceObject(Section);
+ Section = NULL;
+ }
+
+ /* Check if this was supposed to be a session load */
+ if (!Flags)
+ {
+ /* It wasn't, so just return the data */
+ if (ModuleObject) *ModuleObject = LdrEntry;
+ if (ImageBaseAddress) *ImageBaseAddress = LdrEntry->DllBase;
+ Status = STATUS_IMAGE_ALREADY_LOADED;
+ }
+ else
+ {
+ /* We don't support session loading yet */
+ DPRINT1("Unsupported Session-Load!\n");
+ while (TRUE);
+ }
+
+ /* Do cleanup */
+ goto Quickie;
+ }
+ else if (!Section)
+ {
+ /* It wasn't loaded, and we didn't have a previous attempt */
+ KeReleaseMutant(&MmSystemLoadLock, 1, FALSE, FALSE);
+ KeLeaveCriticalRegion();
+ LockOwned = FALSE;
+
+ /* Check if KD is enabled */
+ if ((KdDebuggerEnabled) && !(KdDebuggerNotPresent))
+ {
+ /* FIXME: Attempt to get image from KD */
+ }
+
+ /* We don't have a valid entry */
+ LdrEntry = NULL;
+
+ /* Setup image attributes */
+ InitializeObjectAttributes(&ObjectAttributes,
+ FileName,
+ OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+ NULL,
+ NULL);
+
+ /* Open the image */
+ Status = ZwOpenFile(&FileHandle,
+ FILE_EXECUTE,
+ &ObjectAttributes,
+ &IoStatusBlock,
+ FILE_SHARE_READ | FILE_SHARE_DELETE,
+ 0);
+ if (!NT_SUCCESS(Status)) goto Quickie;
+
+ /* Validate it */
+ Status = MmCheckSystemImage(FileHandle, FALSE);
+ if ((Status == STATUS_IMAGE_CHECKSUM_MISMATCH) ||
+ (Status == STATUS_IMAGE_MP_UP_MISMATCH) ||
+ (Status == STATUS_INVALID_IMAGE_FORMAT))
+ {
+ /* Fail loading */
+ goto Quickie;
+ }
+
+ /* Check if this is a session-load */
+ if (Flags)
+ {
+ /* Then we only need read and execute */
+ DesiredAccess = SECTION_MAP_READ | SECTION_MAP_EXECUTE;
+ }
+ else
+ {
+ /* Otherwise, we can allow write access */
+ DesiredAccess = SECTION_ALL_ACCESS;
+ }
+
+ /* Initialize the attributes for the section */
+ InitializeObjectAttributes(&ObjectAttributes,
+ NULL,
+ OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+ NULL,
+ NULL);
+
+ /* Create the section */
+ Status = ZwCreateSection(&SectionHandle,
+ DesiredAccess,
+ &ObjectAttributes,
+ NULL,
+ PAGE_EXECUTE,
+ SEC_IMAGE,
+ FileHandle);
+ if (!NT_SUCCESS(Status)) goto Quickie;
+
+ /* Now get the section pointer */
+ Status = ObReferenceObjectByHandle(SectionHandle,
+ SECTION_MAP_EXECUTE,
+ MmSectionObjectType,
+ KernelMode,
+ &Section,
+ NULL);
+ ZwClose(SectionHandle);
+ if (!NT_SUCCESS(Status)) goto Quickie;
+
+ /* Check if this was supposed to be a session-load */
+ if (Flags)
+ {
+ /* We don't support session loading yet */
+ DPRINT1("Unsupported Session-Load!\n");
+ while (TRUE);
+ }
+
+ /* Check the loader list again, we should end up in the path below */
+ goto LoaderScan;
+ }
+ else
+ {
+ /* We don't have a valid entry */
+ LdrEntry = NULL;
+ }
+
+ /* Load the image */
+ Status = MiLoadImageSection(&Section,
+ &ModuleLoadBase,
+ FileName,
+ FALSE,
+ NULL);
+ ASSERT(Status != STATUS_ALREADY_COMMITTED);
+
+ /* Get the size of the driver */
+ DriverSize = ((PROS_SECTION_OBJECT)Section)->ImageSection->ImageSize;
+
+ /* Make sure we're not being loaded into session space */
+ if (!Flags)
+ {
+ /* Check for success */
+ if (NT_SUCCESS(Status))
+ {
+ /* FIXME: Support large pages for drivers */
+ }
+
+ /* Dereference the section */
+ ObDereferenceObject(Section);
+ Section = NULL;
+ }
+
+ /* Get the NT Header */
+ NtHeader = RtlImageNtHeader(ModuleLoadBase);
+
+ /* Relocate the driver */
+ Status = LdrRelocateImageWithBias(ModuleLoadBase,
+ 0,
+ "SYSLDR",
+ STATUS_SUCCESS,
+ STATUS_CONFLICTING_ADDRESSES,
+ STATUS_INVALID_IMAGE_FORMAT);
+ if (!NT_SUCCESS(Status)) goto Quickie;
+
+ /* Calculate the size we'll need for the entry and allocate it */
+ EntrySize = sizeof(LDR_DATA_TABLE_ENTRY) +
+ BaseName.Length +
+ sizeof(UNICODE_NULL);
+
+ /* Allocate the entry */
+ LdrEntry = ExAllocatePoolWithTag(NonPagedPool, EntrySize, TAG_MODULE_OBJECT);
+ if (!LdrEntry)
+ {
+ /* Fail */
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Quickie;
+ }
+
+ /* Setup the entry */
+ LdrEntry->Flags = LDRP_LOAD_IN_PROGRESS;
+ LdrEntry->LoadCount = 1;
+ LdrEntry->LoadedImports = LoadedImports;
+ LdrEntry->PatchInformation = NULL;
+
+ /* Check the version */
+ if ((NtHeader->OptionalHeader.MajorOperatingSystemVersion >= 5) &&
+ (NtHeader->OptionalHeader.MajorImageVersion >= 5))
+ {
+ /* Mark this image as a native image */
+ LdrEntry->Flags |= 0x80000000;
+ }
+
+ /* Setup the rest of the entry */
+ LdrEntry->DllBase = ModuleLoadBase;
+ LdrEntry->EntryPoint = (PVOID)((ULONG_PTR)ModuleLoadBase +
+ NtHeader->OptionalHeader.AddressOfEntryPoint);
+ LdrEntry->SizeOfImage = DriverSize;
+ LdrEntry->CheckSum = NtHeader->OptionalHeader.CheckSum;
+ LdrEntry->SectionPointer = Section;
+
+ /* Now write the DLL name */
+ LdrEntry->BaseDllName.Buffer = (PVOID)(LdrEntry + 1);
+ LdrEntry->BaseDllName.Length = BaseName.Length;
+ LdrEntry->BaseDllName.MaximumLength = BaseName.Length;
+
+ /* Copy and null-terminate it */
+ RtlCopyMemory(LdrEntry->BaseDllName.Buffer,
+ BaseName.Buffer,
+ BaseName.Length);
+ LdrEntry->BaseDllName.Buffer[BaseName.Length / 2] = UNICODE_NULL;
+
+ /* Now allocate the full name */
+ LdrEntry->FullDllName.Buffer = ExAllocatePoolWithTag(PagedPool,
+ PrefixName.Length +
+ sizeof(UNICODE_NULL),
+ TAG_LDR_WSTR);
+ if (!LdrEntry->FullDllName.Buffer)
+ {
+ /* Don't fail, just set it to zero */
+ LdrEntry->FullDllName.Length = 0;
+ LdrEntry->FullDllName.MaximumLength = 0;
+ }
+ else
+ {
+ /* Set it up */
+ LdrEntry->FullDllName.Length = PrefixName.Length;
+ LdrEntry->FullDllName.MaximumLength = PrefixName.Length;
+
+ /* Copy and null-terminate */
+ RtlCopyMemory(LdrEntry->FullDllName.Buffer,
+ PrefixName.Buffer,
+ PrefixName.Length);
+ LdrEntry->FullDllName.Buffer[PrefixName.Length / 2] = UNICODE_NULL;
+ }
+
+ /* Add the entry */
+ MiProcessLoaderEntry(LdrEntry, TRUE);
+
+ /* Resolve imports */
+ MissingApiName = Buffer;
+ Status = MiResolveImageReferences(ModuleLoadBase,
+ &BaseDirectory,
+ NULL,
+ &MissingApiName,
+ &MissingDriverName,
+ &LoadedImports);
+ if (!NT_SUCCESS(Status))
+ {
+ /* Fail */
+ MiProcessLoaderEntry(LdrEntry, FALSE);
+
+ /* Check if we need to free the name */
+ if (LdrEntry->FullDllName.Buffer)
+ {
+ /* Free it */
+ ExFreePool(LdrEntry->FullDllName.Buffer);
+ }
+
+ /* Free the entry itself */
+ ExFreePool(LdrEntry);
+ LdrEntry = NULL;
+ goto Quickie;
+ }
+
+ /* Update the loader entry */
+ LdrEntry->Flags |= (LDRP_SYSTEM_MAPPED |
+ LDRP_ENTRY_PROCESSED |
+ LDRP_MM_LOADED);
+ LdrEntry->Flags &= ~LDRP_LOAD_IN_PROGRESS;
+ LdrEntry->LoadedImports = LoadedImports;
+
+ /* FIXME: Apply driver verifier */
+
+ /* FIXME: Write-protect the system image */
+
+ /* Check if notifications are enabled */
+ if (PsImageNotifyEnabled)
+ {
+ /* Fill out the notification data */
+ ImageInfo.Properties = 0;
+ ImageInfo.ImageAddressingMode = IMAGE_ADDRESSING_MODE_32BIT;
+ ImageInfo.SystemModeImage = TRUE;
+ ImageInfo.ImageSize = LdrEntry->SizeOfImage;
+ ImageInfo.ImageBase = LdrEntry->DllBase;
+ ImageInfo.ImageSectionNumber = ImageInfo.ImageSelector = 0;
+
+ /* Send the notification */
+ PspRunLoadImageNotifyRoutines(FileName, NULL, &ImageInfo);
+ }
+
+ /* Check if there's symbols */
+#ifdef KDBG
+ /* If KDBG is defined, then we always have symbols */
+ if (TRUE)
+#else
+ if (MiCacheImageSymbols(LdrEntry->DllBase))
+#endif
+ {
+ /* Check if the system root is present */
+ if ((PrefixName.Length > (11 * sizeof(WCHAR))) &&
+ !(_wcsnicmp(PrefixName.Buffer, L"\\SystemRoot", 11)))
+ {
+ /* Add the system root */
+ UnicodeTemp = PrefixName;
+ UnicodeTemp.Buffer += 11;
+ UnicodeTemp.Length -= (11 * sizeof(WCHAR));
+ sprintf_nt(Buffer,
+ "%ws%wZ",
+ &SharedUserData->NtSystemRoot[2],
+ &UnicodeTemp);
+ }
+ else
+ {
+ /* Build the name */
+ sprintf_nt(Buffer, "%wZ", &BaseName);
+ }
+
+ /* Setup the ansi string */
+ RtlInitString(&AnsiTemp, Buffer);
+
+ /* Notify the debugger */
+ DbgLoadImageSymbols(&AnsiTemp, LdrEntry->DllBase, -1);
+ LdrEntry->Flags |= LDRP_DEBUG_SYMBOLS_LOADED;
+ }
+
+ /* FIXME: Page the driver */
+ ASSERT(Section == NULL);
+
+ /* Return pointers */
+ if (ModuleObject) *ModuleObject = LdrEntry;
+ if (ImageBaseAddress) *ImageBaseAddress = LdrEntry->DllBase;
+
+Quickie:
+ /* If we have a file handle, close it */
+ if (FileHandle) ZwClose(FileHandle);
+
+ /* Check if we have the lock acquired */
+ if (LockOwned)
+ {
+ /* Release the lock */
+ KeReleaseMutant(&MmSystemLoadLock, 1, FALSE, FALSE);
+ KeLeaveCriticalRegion();
+ LockOwned = FALSE;
+ }
+
+ /* Check if we had a prefix */
+ if (NamePrefix) ExFreePool(PrefixName.Buffer);
+
+ /* Free the name buffer and return status */
+ ExFreePool(Buffer);
+ return Status;
+}
+
+/*
+ * @implemented
+ */
+PVOID
+NTAPI
+MmGetSystemRoutineAddress(IN PUNICODE_STRING SystemRoutineName)
+{
+ PVOID ProcAddress = NULL;
+ ANSI_STRING AnsiRoutineName;
+ NTSTATUS Status;
+ PLIST_ENTRY NextEntry;
+ extern LIST_ENTRY PsLoadedModuleList;
+ PLDR_DATA_TABLE_ENTRY LdrEntry;
+ BOOLEAN Found = FALSE;
+ UNICODE_STRING KernelName = RTL_CONSTANT_STRING(L"ntoskrnl.exe");
+ UNICODE_STRING HalName = RTL_CONSTANT_STRING(L"hal.dll");
+ ULONG Modules = 0;
+
+ /* Convert routine to ansi name */
+ Status = RtlUnicodeStringToAnsiString(&AnsiRoutineName,
+ SystemRoutineName,
+ TRUE);
+ if (!NT_SUCCESS(Status)) return NULL;
+
+ /* Lock the list */
+ KeEnterCriticalRegion();
+
+ /* Loop the loaded module list */
+ NextEntry = PsLoadedModuleList.Flink;
+ while (NextEntry != &PsLoadedModuleList)
+ {
+ /* Get the entry */
+ LdrEntry = CONTAINING_RECORD(NextEntry,
+ LDR_DATA_TABLE_ENTRY,
+ InLoadOrderLinks);
+
+ /* Check if it's the kernel or HAL */
+ if (RtlEqualUnicodeString(&KernelName, &LdrEntry->BaseDllName, TRUE))
+ {
+ /* Found it */
+ Found = TRUE;
+ Modules++;
+ }
+ else if (RtlEqualUnicodeString(&HalName, &LdrEntry->BaseDllName,
TRUE))
+ {
+ /* Found it */
+ Found = TRUE;
+ Modules++;
+ }
+
+ /* Check if we found a valid binary */
+ if (Found)
+ {
+ /* Find the procedure name */
+ ProcAddress = MiFindExportedRoutineByName(LdrEntry->DllBase,
+ &AnsiRoutineName);
+
+ /* Break out if we found it or if we already tried both modules */
+ if (ProcAddress) break;
+ if (Modules == 2) break;
+ }
+
+ /* Keep looping */
+ NextEntry = NextEntry->Flink;
+ }
+
+ /* Release the lock */
+ KeLeaveCriticalRegion();
+
+ /* Free the string and return */
+ RtlFreeAnsiString(&AnsiRoutineName);
+ return ProcAddress;
+}
+
Propchange: branches/cache_manager_rewrite/mm/sysldr.c
------------------------------------------------------------------------------
svn:eol-style = native