/*
-* PROJECT: ReactOS Kernel
-* LICENSE: BSD - See COPYING.ARM in the top level directory
-* FILE: ntoskrnl/mm/ARM3/sysldr.c
-* PURPOSE: Contains the Kernel Loader (SYSLDR) for loading PE files.
-* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
-* ReactOS Portable Systems Group
-*/
+ * PROJECT: ReactOS Kernel
+ * LICENSE: BSD - See COPYING.ARM in the top level directory
+ * FILE: ntoskrnl/mm/ARM3/sysldr.c
+ * PURPOSE: Contains the Kernel Loader (SYSLDR) for loading PE files.
+ * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
+ * ReactOS Portable Systems Group
+ */
/* INCLUDES *******************************************************************/
KAPC_STATE ApcState;
LARGE_INTEGER SectionOffset = {{0, 0}};
BOOLEAN LoadSymbols = FALSE;
- PFN_NUMBER PteCount;
+ PFN_COUNT PteCount;
PMMPTE PointerPte, LastPte;
PVOID DriverBase;
MMPTE TempPte;
if (!NT_SUCCESS(Status))
{
/* Detach and return */
+ DPRINT1("MmMapViewOfSection failed with status 0x%x\n", Status);
KeUnstackDetachProcess(&ApcState);
return Status;
}
/* Reserve system PTEs needed */
PteCount = ROUND_TO_PAGES(Section->ImageSection->ImageSize) >> PAGE_SHIFT;
PointerPte = MiReserveSystemPtes(PteCount, SystemPteSpace);
- if (!PointerPte) return STATUS_INSUFFICIENT_RESOURCES;
+ if (!PointerPte)
+ {
+ DPRINT1("MiReserveSystemPtes failed\n");
+ KeUnstackDetachProcess(&ApcState);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
/* New driver base */
LastPte = PointerPte + PteCount;
if (wcschr(ImportName.Buffer, L'.'))
{
/* Remove the extension */
- ImportName.Length = (wcschr(ImportName.Buffer, L'.') -
+ ImportName.Length = (USHORT)(wcschr(ImportName.Buffer, L'.') -
ImportName.Buffer) * sizeof(WCHAR);
}
Status = DllInit(&RegPath);
/* Clean up */
- ExFreePool(RegPath.Buffer);
+ ExFreePoolWithTag(RegPath.Buffer, TAG_LDR_WSTR);
/* Return status value which DllInitialize returned */
return Status;
!((ULONG_PTR)LdrEntry->LoadedImports & MM_SYSLDR_SINGLE_ENTRY))
{
/* Free them */
- ExFreePool(CurrentImports);
+ ExFreePoolWithTag(CurrentImports, TAG_LDR_IMPORTS);
}
}
else
}
/* Otherwise, free the import list */
- ExFreePool(LdrEntry->LoadedImports);
+ ExFreePoolWithTag(LdrEntry->LoadedImports, TAG_LDR_IMPORTS);
LdrEntry->LoadedImports = MM_SYSLDR_BOOT_LOADED;
}
ULONG ForwardExportSize;
PIMAGE_EXPORT_DIRECTORY ForwardExportDirectory;
PIMAGE_IMPORT_BY_NAME ForwardName;
- ULONG ForwardLength;
+ SIZE_T ForwardLength;
IMAGE_THUNK_DATA ForwardThunk;
PAGED_CODE();
/* Copy the procedure name */
RtlStringCbCopyA(*MissingApi,
MAXIMUM_FILENAME_LENGTH,
- (PCHAR)&NameImport->Name[0]);
+ (PCHAR)&NameImport->Name[0]);
/* Setup name tables */
DPRINT("Import name: %s\n", NameImport->Name);
/* Build the forwarder name */
DllName.Buffer = (PCHAR)Address->u1.Function;
- DllName.Length = strchr(DllName.Buffer, '.') -
- DllName.Buffer +
- sizeof(ANSI_NULL);
+ DllName.Length = (USHORT)(strchr(DllName.Buffer, '.') -
+ DllName.Buffer) +
+ sizeof(ANSI_NULL);
DllName.MaximumLength = DllName.Length;
/* Convert it */
if (LdrEntry->FullDllName.Buffer)
{
/* Free it */
- ExFreePool(LdrEntry->FullDllName.Buffer);
+ ExFreePoolWithTag(LdrEntry->FullDllName.Buffer, TAG_LDR_WSTR);
}
/* Check if we had a section */
}
/* Free the entry */
- ExFreePool(LdrEntry);
+ ExFreePoolWithTag(LdrEntry, TAG_MODULE_OBJECT);
}
/* Release the system lock and return */
LoadedImportsSize = ImportCount * sizeof(PVOID) + sizeof(SIZE_T);
LoadedImports = ExAllocatePoolWithTag(PagedPool,
LoadedImportsSize,
- 'TDmM');
+ TAG_LDR_IMPORTS);
if (LoadedImports)
{
/* Zero it */
{
/* It's not, it's importing stuff it shouldn't be! */
MiDereferenceImports(LoadedImports);
- if (LoadedImports) ExFreePoolWithTag(LoadedImports, 'TDmM');
+ if (LoadedImports) ExFreePoolWithTag(LoadedImports, TAG_LDR_IMPORTS);
return STATUS_PROCEDURE_NOT_FOUND;
}
{
/* This is not kernel code */
MiDereferenceImports(LoadedImports);
- if (LoadedImports) ExFreePoolWithTag(LoadedImports, 'TDmM');
+ if (LoadedImports) ExFreePoolWithTag(LoadedImports, TAG_LDR_IMPORTS);
return STATUS_PROCEDURE_NOT_FOUND;
}
{
/* Failed */
MiDereferenceImports(LoadedImports);
- if (LoadedImports) ExFreePoolWithTag(LoadedImports, 'TDmM');
+ if (LoadedImports) ExFreePoolWithTag(LoadedImports, TAG_LDR_IMPORTS);
return Status;
}
sizeof(UNICODE_NULL);
DllName.Buffer = ExAllocatePoolWithTag(NonPagedPool,
DllName.MaximumLength,
- 'TDmM');
+ TAG_LDR_WSTR);
if (DllName.Buffer)
{
/* Setup the base length and copy it */
if (NT_SUCCESS(Status))
{
/* We can free the DLL Name */
- ExFreePool(DllName.Buffer);
+ ExFreePoolWithTag(DllName.Buffer, TAG_LDR_WSTR);
}
else
{
*MissingDriver = DllName.Buffer;
*(PULONG)MissingDriver |= 1;
*MissingApi = NULL;
+
+ DPRINT1("Failed to load dependency: %wZ\n", &DllName);
}
}
else
Loaded = TRUE;
/* Sanity check */
- ASSERT(DllBase = DllEntry->DllBase);
+ ASSERT(DllBase == DllEntry->DllBase);
/* Call the initialization routines */
Status = MmCallDllInitialize(DllEntry, &PsLoadedModuleList);
{
/* We failed, unload the image */
MmUnloadSystemImage(DllEntry);
+ DPRINT1("MmCallDllInitialize failed with status 0x%x\n", Status);
while (TRUE);
Loaded = FALSE;
}
/* Cleanup and return */
RtlFreeUnicodeString(&NameString);
MiDereferenceImports(LoadedImports);
- if (LoadedImports) ExFreePoolWithTag(LoadedImports, 'TDmM');
+ if (LoadedImports) ExFreePoolWithTag(LoadedImports, TAG_LDR_IMPORTS);
return Status;
}
{
/* Cleanup and return */
MiDereferenceImports(LoadedImports);
- if (LoadedImports) ExFreePoolWithTag(LoadedImports, 'TDmM');
+ if (LoadedImports) ExFreePoolWithTag(LoadedImports, TAG_LDR_IMPORTS);
DPRINT1("Warning: Driver failed to load, %S not found\n", *MissingDriver);
return STATUS_DRIVER_ENTRYPOINT_NOT_FOUND;
}
{
/* Cleanup and return */
MiDereferenceImports(LoadedImports);
- if (LoadedImports) ExFreePoolWithTag(LoadedImports, 'TDmM');
+ if (LoadedImports) ExFreePoolWithTag(LoadedImports, TAG_LDR_IMPORTS);
return Status;
}
if (!ImportCount)
{
/* Free the list and set it to no imports */
- ExFreePoolWithTag(LoadedImports, 'TDmM');
+ ExFreePoolWithTag(LoadedImports, TAG_LDR_IMPORTS);
LoadedImports = MM_SYSLDR_NO_IMPORTS;
}
else if (ImportCount == 1)
{
/* Just one entry, we can free the table and only use our entry */
- ExFreePoolWithTag(LoadedImports, 'TDmM');
+ ExFreePoolWithTag(LoadedImports, TAG_LDR_IMPORTS);
LoadedImports = (PLOAD_IMPORTS)ImportEntry;
}
else if (ImportCount != LoadedImports->Count)
LoadedImportsSize = ImportCount * sizeof(PVOID) + sizeof(SIZE_T);
NewImports = ExAllocatePoolWithTag(PagedPool,
LoadedImportsSize,
- 'TDmM');
+ TAG_LDR_IMPORTS);
if (NewImports)
{
/* Set count */
}
/* Free the old copy */
- ExFreePoolWithTag(LoadedImports, 'TDmM');
+ ExFreePoolWithTag(LoadedImports, TAG_LDR_IMPORTS);
LoadedImports = NewImports;
}
}
return STATUS_SUCCESS;
}
+VOID
+NTAPI
+MiFreeInitializationCode(IN PVOID InitStart,
+ IN PVOID InitEnd)
+{
+ PMMPTE PointerPte;
+ PFN_NUMBER PagesFreed;
+
+ /* Get the start PTE */
+ PointerPte = MiAddressToPte(InitStart);
+ ASSERT(MI_IS_PHYSICAL_ADDRESS(InitStart) == FALSE);
+
+ /* Compute the number of pages we expect to free */
+ PagesFreed = (PFN_NUMBER)(MiAddressToPte(InitEnd) - PointerPte + 1);
+
+ /* Try to actually free them */
+ PagesFreed = MiDeleteSystemPageableVm(PointerPte,
+ PagesFreed,
+ 0,
+ NULL);
+}
+
+VOID
+NTAPI
+INIT_FUNCTION
+MiFindInitializationCode(OUT PVOID *StartVa,
+ OUT PVOID *EndVa)
+{
+ ULONG Size, SectionCount, Alignment;
+ PLDR_DATA_TABLE_ENTRY LdrEntry;
+ ULONG_PTR DllBase, InitStart, InitEnd, ImageEnd, InitCode;
+ PLIST_ENTRY NextEntry;
+ PIMAGE_NT_HEADERS NtHeader;
+ PIMAGE_SECTION_HEADER Section, LastSection;
+ BOOLEAN InitFound;
+
+ /* So we don't free our own code yet */
+ InitCode = (ULONG_PTR)&MiFindInitializationCode;
+
+ /* Assume failure */
+ *StartVa = NULL;
+
+ /* Enter a critical region while we loop the list */
+ KeEnterCriticalRegion();
+
+ /* Loop all loaded modules */
+ NextEntry = PsLoadedModuleList.Flink;
+ while (NextEntry != &PsLoadedModuleList)
+ {
+ /* Get the loader entry and its DLL base */
+ LdrEntry = CONTAINING_RECORD(NextEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
+ DllBase = (ULONG_PTR)LdrEntry->DllBase;
+
+ /* Get the NT header */
+ NtHeader = RtlImageNtHeader((PVOID)DllBase);
+ if (!NtHeader)
+ {
+ /* Keep going */
+ NextEntry = NextEntry->Flink;
+ continue;
+ }
+
+ /* Get the first section, the section count, and scan them all */
+ Section = IMAGE_FIRST_SECTION(NtHeader);
+ SectionCount = NtHeader->FileHeader.NumberOfSections;
+ InitStart = 0;
+ while (SectionCount > 0)
+ {
+ /* Assume failure */
+ InitFound = FALSE;
+
+ /* Is this the INIT section or a discardable section? */
+ if ((*(PULONG)Section->Name == 'TINI') ||
+ ((Section->Characteristics & IMAGE_SCN_MEM_DISCARDABLE)))
+ {
+ /* Remember this */
+ InitFound = TRUE;
+ }
+
+ if (InitFound)
+ {
+ /* Pick the biggest size -- either raw or virtual */
+ Size = max(Section->SizeOfRawData, Section->Misc.VirtualSize);
+
+ /* Read the section alignment */
+ Alignment = NtHeader->OptionalHeader.SectionAlignment;
+
+ /* Align the start and end addresses appropriately */
+ InitStart = DllBase + Section->VirtualAddress;
+ InitEnd = ((Alignment + InitStart + Size - 2) & 0xFFFFF000) - 1;
+ InitStart = (InitStart + (PAGE_SIZE - 1)) & 0xFFFFF000;
+
+ /* Have we reached the last section? */
+ if (SectionCount == 1)
+ {
+ /* Remember this */
+ LastSection = Section;
+ }
+ else
+ {
+ /* We have not, loop all the sections */
+ LastSection = NULL;
+ do
+ {
+ /* Keep going until we find a non-discardable section range */
+ SectionCount--;
+ Section++;
+ if (Section->Characteristics & IMAGE_SCN_MEM_DISCARDABLE)
+ {
+ /* Discardable, so record it, then keep going */
+ LastSection = Section;
+ }
+ else
+ {
+ /* Non-contigous discard flag, or no flag, break out */
+ break;
+ }
+ }
+ while (SectionCount > 1);
+ }
+
+ /* Have we found a discardable or init section? */
+ if (LastSection)
+ {
+ /* Pick the biggest size -- either raw or virtual */
+ Size = max(LastSection->SizeOfRawData, LastSection->Misc.VirtualSize);
+
+ /* Use this as the end of the section address */
+ InitEnd = DllBase + LastSection->VirtualAddress + Size - 1;
+
+ /* Have we reached the last section yet? */
+ if (SectionCount != 1)
+ {
+ /* Then align this accross the session boundary */
+ InitEnd = ((Alignment + InitEnd - 1) & 0XFFFFF000) - 1;
+ }
+ }
+
+ /* Make sure we don't let the init section go past the image */
+ ImageEnd = DllBase + LdrEntry->SizeOfImage;
+ if (InitEnd > ImageEnd) InitEnd = (ImageEnd - 1) | (PAGE_SIZE - 1);
+
+ /* Make sure we have a valid, non-zero init section */
+ if (InitStart <= InitEnd)
+ {
+ /* Make sure we are not within this code itself */
+ if ((InitCode >= InitStart) && (InitCode <= InitEnd))
+ {
+ /* Return it, we can't free ourselves now */
+ ASSERT(*StartVa == 0);
+ *StartVa = (PVOID)InitStart;
+ *EndVa = (PVOID)InitEnd;
+ }
+ else
+ {
+ /* This isn't us -- go ahead and free it */
+ ASSERT(MI_IS_PHYSICAL_ADDRESS((PVOID)InitStart) == FALSE);
+ MiFreeInitializationCode((PVOID)InitStart, (PVOID)InitEnd);
+ }
+ }
+ }
+
+ /* Move to the next section */
+ SectionCount--;
+ Section++;
+ }
+
+ /* Move to the next module */
+ NextEntry = NextEntry->Flink;
+ }
+
+ /* Leave the critical region and return */
+ KeLeaveCriticalRegion();
+}
+
+/*
+ * Note: This function assumes that all discardable sections are at the end of
+ * the PE file. It searches backwards until it finds the non-discardable section
+ */
+VOID
+NTAPI
+MmFreeDriverInitialization(IN PLDR_DATA_TABLE_ENTRY LdrEntry)
+{
+ PMMPTE StartPte, EndPte;
+ PFN_NUMBER PageCount;
+ PVOID DllBase;
+ ULONG i;
+ PIMAGE_NT_HEADERS NtHeader;
+ PIMAGE_SECTION_HEADER Section, DiscardSection;
+ ULONG PagesDeleted;
+
+ /* Get the base address and the page count */
+ DllBase = LdrEntry->DllBase;
+ PageCount = LdrEntry->SizeOfImage >> PAGE_SHIFT;
+
+ /* Get the last PTE in this image */
+ EndPte = MiAddressToPte(DllBase) + PageCount;
+
+ /* Get the NT header */
+ NtHeader = RtlImageNtHeader(DllBase);
+ if (!NtHeader) return;
+
+ /* Get the last section and loop each section backwards */
+ Section = IMAGE_FIRST_SECTION(NtHeader) + NtHeader->FileHeader.NumberOfSections;
+ DiscardSection = NULL;
+ for (i = 0; i < NtHeader->FileHeader.NumberOfSections; i++)
+ {
+ /* Go back a section and check if it's discardable */
+ Section--;
+ if (Section->Characteristics & IMAGE_SCN_MEM_DISCARDABLE)
+ {
+ /* It is, select it for freeing */
+ DiscardSection = Section;
+ }
+ else
+ {
+ /* No more discardable sections exist, bail out */
+ break;
+ }
+ }
+
+ /* Bail out if there's nothing to free */
+ if (!DiscardSection) return;
+
+ /* Push the DLL base to the first disacrable section, and get its PTE */
+ DllBase = (PVOID)ROUND_TO_PAGES((ULONG_PTR)DllBase + DiscardSection->VirtualAddress);
+ ASSERT(MI_IS_PHYSICAL_ADDRESS(DllBase) == FALSE);
+ StartPte = MiAddressToPte(DllBase);
+
+ /* Check how many pages to free total */
+ PageCount = (PFN_NUMBER)(EndPte - StartPte);
+ if (!PageCount) return;
+
+ /* Delete this many PTEs */
+ PagesDeleted = MiDeleteSystemPageableVm(StartPte, PageCount, 0, NULL);
+}
+
VOID
NTAPI
INIT_FUNCTION
PVOID DllBase, NewImageAddress;
NTSTATUS Status;
PMMPTE PointerPte, StartPte, LastPte;
- PFN_NUMBER PteCount;
+ PFN_COUNT PteCount;
PMMPFN Pfn1;
MMPTE TempPte, OldPte;
if (!(HalEntry) || (!KernelEntry)) return STATUS_NOT_FOUND;
/* Allocate the list */
- EntryArray = ExAllocatePoolWithTag(PagedPool, Modules * sizeof(PVOID), 'TDmM');
+ EntryArray = ExAllocatePoolWithTag(PagedPool, Modules * sizeof(PVOID), TAG_LDR_IMPORTS);
if (!EntryArray) return STATUS_INSUFFICIENT_RESOURCES;
/* Loop the loaded module list again */
/* Scan the thunks */
for (i = 0, DllBase = 0, DllEnd = 0; i < ImportSize; i++, ImageThunk++)
#else
- i = DllBase = DllEnd = 0;
+ DllBase = DllEnd = i = 0;
while ((ImportDescriptor->Name) &&
(ImportDescriptor->OriginalFirstThunk))
{
LoadedImportsSize = ImportSize * sizeof(PVOID) + sizeof(SIZE_T);
LoadedImports = ExAllocatePoolWithTag(PagedPool,
LoadedImportsSize,
- 'TDmM');
+ TAG_LDR_IMPORTS);
ASSERT(LoadedImports);
/* Save the count */
}
/* Free the initial array */
- ExFreePool(EntryArray);
+ ExFreePoolWithTag(EntryArray, TAG_LDR_IMPORTS);
/* FIXME: Might not need to keep the HAL/Kernel imports around */
EntrySize = sizeof(LDR_DATA_TABLE_ENTRY) +
LdrEntry->BaseDllName.MaximumLength +
sizeof(UNICODE_NULL);
- NewEntry = ExAllocatePoolWithTag(NonPagedPool, EntrySize, TAG_LDR_WSTR);
+ NewEntry = ExAllocatePoolWithTag(NonPagedPool, EntrySize, TAG_MODULE_OBJECT);
if (!NewEntry) return FALSE;
/* Copy the entry over */
NewEntry->FullDllName.Buffer =
ExAllocatePoolWithTag(PagedPool,
LdrEntry->FullDllName.MaximumLength +
- sizeof(UNICODE_NULL),
+ sizeof(UNICODE_NULL),
TAG_LDR_WSTR);
- if (!NewEntry->FullDllName.Buffer) return FALSE;
+ if (!NewEntry->FullDllName.Buffer)
+ {
+ ExFreePoolWithTag(NewEntry, TAG_MODULE_OBJECT);
+ return FALSE;
+ }
/* Set the base name */
NewEntry->BaseDllName.Buffer = (PVOID)(NewEntry + 1);
PIMAGE_NT_HEADERS NtHeaders;
PIMAGE_SECTION_HEADER Section;
PFN_NUMBER DriverPages;
- ULONG CurrentProtection, SectionProtection, CombinedProtection, ProtectionMask;
+ ULONG CurrentProtection, SectionProtection, CombinedProtection = 0, ProtectionMask;
ULONG Sections, Size;
ULONG_PTR BaseAddress, CurrentAddress;
PMMPTE PointerPte, StartPte, LastPte, CurrentPte, ComboPte = NULL;
{
PVOID ImageBase;
PETHREAD CurrentThread = PsGetCurrentThread();
- PFN_NUMBER PageCount = 0, PageFrameIndex;
+ PFN_COUNT PageCount = 0;
+ PFN_NUMBER PageFrameIndex;
PMMPFN Pfn1;
PAGED_CODE();
PAGE_EXECUTE,
SEC_IMAGE,
ImageHandle);
- if (!NT_SUCCESS(Status)) return Status;
+ if (!NT_SUCCESS(Status))
+ {
+ DPRINT1("ZwCreateSection failed with status 0x%x\n", Status);
+ return Status;
+ }
/* Make sure we're in the system process */
KeStackAttachProcess(&PsInitialSystemProcess->Pcb, &ApcState);
if (!NT_SUCCESS(Status))
{
/* We failed, close the handle and return */
+ DPRINT1("ZwMapViewOfSection failed with status 0x%x\n", Status);
KeUnstackDetachProcess(&ApcState);
ZwClose(SectionHandle);
return Status;
}
/* Allocate a buffer we'll use for names */
- Buffer = ExAllocatePoolWithTag(NonPagedPool, MAX_PATH, 'nLmM');
+ Buffer = ExAllocatePoolWithTag(NonPagedPool, MAX_PATH, TAG_LDR_WSTR);
if (!Buffer) return STATUS_INSUFFICIENT_RESOURCES;
/* Check for a separator */
&IoStatusBlock,
FILE_SHARE_READ | FILE_SHARE_DELETE,
0);
- if (!NT_SUCCESS(Status)) goto Quickie;
+ if (!NT_SUCCESS(Status))
+ {
+ DPRINT1("ZwOpenFile failed with status 0x%x\n", Status);
+ goto Quickie;
+ }
/* Validate it */
Status = MmCheckSystemImage(FileHandle, FALSE);
PAGE_EXECUTE,
SEC_IMAGE,
FileHandle);
- if (!NT_SUCCESS(Status)) goto Quickie;
+ if (!NT_SUCCESS(Status))
+ {
+ DPRINT1("ZwCreateSection failed with status 0x%x\n", Status);
+ goto Quickie;
+ }
/* Now get the section pointer */
Status = ObReferenceObjectByHandle(SectionHandle,
}
/* Check for failure of the load earlier */
- if (!NT_SUCCESS(Status)) goto Quickie;
+ if (!NT_SUCCESS(Status))
+ {
+ DPRINT1("MiLoadImageSection failed with status 0x%x\n", Status);
+ goto Quickie;
+ }
/* Relocate the driver */
Status = LdrRelocateImageWithBias(ModuleLoadBase,
STATUS_SUCCESS,
STATUS_CONFLICTING_ADDRESSES,
STATUS_INVALID_IMAGE_FORMAT);
- if (!NT_SUCCESS(Status)) goto Quickie;
-
+ if (!NT_SUCCESS(Status))
+ {
+ DPRINT1("LdrRelocateImageWithBias failed with status 0x%x\n", Status);
+ goto Quickie;
+ }
/* Get the NT Header */
NtHeader = RtlImageNtHeader(ModuleLoadBase);
&LoadedImports);
if (!NT_SUCCESS(Status))
{
+ DPRINT1("MiResolveImageReferences failed with status 0x%x\n", Status);
+
/* Fail */
MiProcessLoaderEntry(LdrEntry, FALSE);
if (LdrEntry->FullDllName.Buffer)
{
/* Free it */
- ExFreePool(LdrEntry->FullDllName.Buffer);
+ ExFreePoolWithTag(LdrEntry->FullDllName.Buffer, TAG_LDR_WSTR);
}
/* Free the entry itself */
/* if (NamePrefix) ExFreePool(PrefixName.Buffer); */
/* Free the name buffer and return status */
- ExFreePoolWithTag(Buffer, 'nLmM');
+ ExFreePoolWithTag(Buffer, TAG_LDR_WSTR);
return Status;
}
return ProcAddress;
}
+/* EOF */