|
|
/*++
Copyright (c) 1991 Microsoft Corporation
Module Name:
peldrt.c
Abstract:
This module implements the code to load a PE format image into memory and relocate it if necessary.
Author:
David N. Cutler (davec) 10-May-1991
Environment:
Kernel mode only.
Revision History:
Forrest Foltz (forrestf) 10-Jun-2000
Broke out x86 32/64 code into this module
--*/
extern BOOLEAN BlBootingFromNet;
ARC_STATUS BlLoadImageEx( IN ULONG DeviceId, IN TYPE_OF_MEMORY MemoryType, IN PCHAR LoadFile, IN USHORT ImageType, IN OPTIONAL ULONG PreferredAlignment, IN OPTIONAL ULONG PreferredBasePage, OUT PVOID *ImageBase )
/*++
Routine Description:
This routine attempts to load the specified file from the specified device.
Arguments:
DeviceId - Supplies the file table index of the device to load the specified image file from.
MemoryType - Supplies the type of memory to to be assigned to the allocated memory descriptor.
BootFile - Supplies a pointer to string descriptor for the name of the file to load.
ImageType - Supplies the type of image that is expected.
PreferredAlignment - If present, supplies the preferred image alignment.
PreferredBasePage - If present, supplies the preferred base page which will override the image base address
ImageBase - Supplies a pointer to a variable that receives the address of the image base.
Return Value:
ESUCCESS is returned if the specified image file is loaded successfully. Otherwise, an unsuccessful status is returned that describes the reason for failure.
--*/
{
ULONG ActualBase; ULONG BasePage; ULONG Count; ULONG FileId; ULONG_PTR NewImageBase; ULONG Index; UCHAR LocalBuffer[(SECTOR_SIZE * 2) + 256]; PUCHAR LocalPointer; ULONG NumberOfSections; ULONG PageCount; USHORT MachineType; ARC_STATUS Status; PIMAGE_NT_HEADERS NtHeaders; PIMAGE_SECTION_HEADER SectionHeader; LARGE_INTEGER SeekPosition; ULONG RelocSize; PIMAGE_BASE_RELOCATION RelocDirectory; ULONG RelocPage; ULONG RelocPageCount; PMEMORY_ALLOCATION_DESCRIPTOR MemoryDescriptor; FILE_INFORMATION FileInfo; PUSHORT AdjustSum; USHORT PartialSum; ULONG CheckSum; ULONG VirtualSize; ULONG SizeOfRawData; BOOLEAN bCloseFile = FALSE; BOOLEAN bFreeCache = FALSE; IMAGE_PREFETCH_CACHE ImgCache = {0};
if (PreferredAlignment == 0) { PreferredAlignment = 1; } //
// Align the buffer on a Dcache fill boundary.
//
LocalPointer = ALIGN_BUFFER(LocalBuffer);
//
// Attempt to open the image file.
//
Status = BlOpen(DeviceId, LoadFile, ArcOpenReadOnly, &FileId); if (Status != ESUCCESS) { goto cleanup; } bCloseFile = TRUE;
//
// Try to prefetch the whole file into a prefetch buffer. The file
// must have been opened read-only and must not be modified until
// the cache is freed by the BlImageFreeCache call. The file position
// of FileId is reset to the beginning of the file.
//
if ((BlBootingFromNet) || (BlImageInitCache(&ImgCache, FileId) != ESUCCESS) ) { //
// Make sure file position is at the beginning of the file.
// BlImageInitCache leaves file position undefined under failure.
//
SeekPosition.QuadPart = 0; Status = BlSeek(FileId, &SeekPosition, SeekAbsolute); if (Status != ESUCCESS) { goto cleanup; } } else { //
// We got a cache. Make a note to free it.
//
bFreeCache = TRUE; }
//
// Read the first two sectors of the image header from the file.
//
Status = BlImageRead(&ImgCache, FileId, LocalPointer, SECTOR_SIZE * 2, &Count); if (Status != ESUCCESS) { goto cleanup; }
//
// If the image file is not the specified type, is not executable, or is
// not a NT image, then return bad image type status.
//
NtHeaders = RtlImageNtHeader(LocalPointer); if (NtHeaders == NULL) { Status = EBADF; goto cleanup; }
MachineType = NtHeaders->FileHeader.Machine; if ((MachineType != ImageType) || ((NtHeaders->FileHeader.Characteristics & IMAGE_FILE_EXECUTABLE_IMAGE) == 0)) { Status = EBADF; goto cleanup; }
//
// Compute the starting page and the number of pages that are consumed
// by the entire image, and then allocate a memory descriptor for the
// allocated region.
//
NumberOfSections = NtHeaders->FileHeader.NumberOfSections; SectionHeader = IMAGE_FIRST_SECTION( NtHeaders );
//
// If a preferred alignment was specified or the image is not located in KSEG0,
// then don't bother trying to put it at its specified image base.
//
if (PreferredBasePage != 0) { BasePage = PreferredBasePage; } else if ((PreferredAlignment != 1) || ((NtHeaders->OptionalHeader.ImageBase & KSEG0_BASE) == 0)) { BasePage = 0; } else { BasePage = (ULONG)((NtHeaders->OptionalHeader.ImageBase & 0x1fffffff) >> PAGE_SHIFT); } if (strcmp((PCHAR)&SectionHeader[NumberOfSections - 1].Name, ".debug") == 0) { NumberOfSections -= 1; PageCount = (NtHeaders->OptionalHeader.SizeOfImage - SectionHeader[NumberOfSections].SizeOfRawData + PAGE_SIZE - 1) >> PAGE_SHIFT;
} else { PageCount = (NtHeaders->OptionalHeader.SizeOfImage + PAGE_SIZE - 1) >> PAGE_SHIFT; }
//
// If we fail to allocate memory descriptor here, we will try again
// below after freeing the cache if we have one.
//
Status = BlAllocateAlignedDescriptor(MemoryType, BasePage, PageCount, PreferredAlignment, &ActualBase);
if (Status != ESUCCESS) {
//
// Free the memory we have allocated for caching the image and
// try again.
//
if (bFreeCache) { BlImageFreeCache(&ImgCache, FileId); bFreeCache = FALSE;
Status = BlAllocateDescriptor(MemoryType, BasePage, PageCount, &ActualBase); }
//
// Check to see if we were able to allocate memory after freeing
// cache if we had one.
//
if (Status != ESUCCESS) { Status = ENOMEM; goto cleanup; } }
//
// Compute the address of the file header.
//
NewImageBase = KSEG0_BASE | (ActualBase << PAGE_SHIFT);
//
// Read the entire image header from the file.
//
SeekPosition.QuadPart = 0; Status = BlImageSeek(&ImgCache, FileId, &SeekPosition, SeekAbsolute); if (Status != ESUCCESS) { goto cleanup; }
Status = BlImageRead(&ImgCache, FileId, (PVOID)NewImageBase, NtHeaders->OptionalHeader.SizeOfHeaders, &Count);
if (Status != ESUCCESS) { goto cleanup; BlClose(FileId); return Status; }
NtHeaders = RtlImageNtHeader((PVOID)NewImageBase);
//
// Compute the address of the section headers, set the image base address.
//
SectionHeader = IMAGE_FIRST_SECTION(NtHeaders);
//
// Compute the check sum on the image.
//
PartialSum = ChkSum(0, (PVOID)NewImageBase, NtHeaders->OptionalHeader.SizeOfHeaders / sizeof(USHORT));
//
// Scan through the sections and either read them into memory or clear
// the memory as appropriate.
//
for (Index = 0; Index < NumberOfSections; Index += 1) { VirtualSize = SectionHeader->Misc.VirtualSize; SizeOfRawData = SectionHeader->SizeOfRawData; VirtualSize = (VirtualSize + 1) & ~1; SizeOfRawData = (SizeOfRawData + 1) & ~1; if (VirtualSize == 0) { VirtualSize = SizeOfRawData; }
//
// Compute the size of the raw data.
//
// N.B. The size ofthe raw data can be non-zero even when the pointer
// to the raw data is zero. The size of the raw data can also be
// larger than the virtual size.
//
if (SectionHeader->PointerToRawData == 0) { SizeOfRawData = 0;
} else if (SizeOfRawData > VirtualSize) { SizeOfRawData = VirtualSize; }
//
// If the size of the raw data is not zero, then load the raw data
// into memory.
//
if (SizeOfRawData != 0) { SeekPosition.LowPart = SectionHeader->PointerToRawData; Status = BlImageSeek(&ImgCache, FileId, &SeekPosition, SeekAbsolute);
if (Status != ESUCCESS) { break; }
Status = BlImageRead(&ImgCache, FileId, (PVOID)(SectionHeader->VirtualAddress + NewImageBase), SizeOfRawData, &Count);
if (Status != ESUCCESS) { break; }
//
// Remember how far we have read.
//
RelocSize = SectionHeader->PointerToRawData + SizeOfRawData;
//
// Compute the check sum on the section.
//
PartialSum = ChkSum(PartialSum, (PVOID)(SectionHeader->VirtualAddress + NewImageBase), SizeOfRawData / sizeof(USHORT)); }
//
// If the size of the raw data is less than the virtual size, then zero
// the remaining memory.
//
if (SizeOfRawData < VirtualSize) { RtlZeroMemory((PVOID)(KSEG0_BASE | SectionHeader->VirtualAddress + NewImageBase + SizeOfRawData), VirtualSize - SizeOfRawData);
}
SectionHeader += 1; }
//
// Only do the check sum if the image loaded properly and is stripped.
//
if ((Status == ESUCCESS) && (NtHeaders->FileHeader.Characteristics & IMAGE_FILE_DEBUG_STRIPPED)) {
//
// Get the length of the file for check sum validation.
//
Status = BlGetFileInformation(FileId, &FileInfo); if (Status != ESUCCESS) {
//
// Set the length to current end of file.
//
Count = RelocSize; FileInfo.EndingAddress.LowPart = RelocSize;
} else { Count = FileInfo.EndingAddress.LowPart; }
Count -= RelocSize; while (Count != 0) { ULONG Length;
//
// Read in the rest of the image and check sum it.
//
Length = Count < SECTOR_SIZE * 2 ? Count : SECTOR_SIZE * 2; if (BlImageRead(&ImgCache, FileId, LocalBuffer, Length, &Length) != ESUCCESS) { break; }
if (Length == 0) { break;
}
PartialSum = ChkSum(PartialSum, (PUSHORT) LocalBuffer, Length / 2); Count -= Length; }
AdjustSum = (PUSHORT)(&NtHeaders->OptionalHeader.CheckSum); PartialSum -= (PartialSum < AdjustSum[0]); PartialSum -= AdjustSum[0]; PartialSum -= (PartialSum < AdjustSum[1]); PartialSum -= AdjustSum[1]; CheckSum = (ULONG)PartialSum + FileInfo.EndingAddress.LowPart;
if (CheckSum != NtHeaders->OptionalHeader.CheckSum) { Status = EBADF; } }
//
// If the specified image was successfully loaded, then perform image
// relocation if necessary.
//
if (Status == ESUCCESS) {
//
// If a virtual bias is specified, then attempt to relocate the
// image to its biased address. If the image cannot be relocated,
// then turn off the virtual bias, and attempt to relocate the
// image again as its allocated base. Otherwise, just attempt to
// relocate the image to its allocated base.
//
// N.B. The loaded image is double mapped at the biased address.
//
// N.B. It is assumed that the only possibly nonrelocatable image
// is the kernel image which is the first image that is loaded.
// Therefore, if a biased address is specified and the kernel
// cannot be relocated, then the biased loading of the kernel
// image is turned off.
//
if (BlVirtualBias != 0) { Status = LdrRelocateImage((PVOID)(NewImageBase + BlVirtualBias), "OS Loader", ESUCCESS, 0xffff0000 + EBADF, EBADF);
if (Status == (0xffff0000 + EBADF)) { BlVirtualBias = 0; if (NewImageBase != NtHeaders->OptionalHeader.ImageBase) { Status = (ARC_STATUS)LdrRelocateImage((PVOID)NewImageBase, "OS Loader", ESUCCESS, EBADF, EBADF); } else { Status = ESUCCESS; }
}
} else { if (NewImageBase != NtHeaders->OptionalHeader.ImageBase) { Status = (ARC_STATUS)LdrRelocateImage((PVOID)NewImageBase, "OS Loader", ESUCCESS, EBADF, EBADF); } }
*ImageBase = (PVOID)(NewImageBase + BlVirtualBias);
if(BdDebuggerEnabled) { DbgPrint("BD: %s base address %p\n", LoadFile, *ImageBase); { STRING string;
RtlInitString(&string, LoadFile); DbgLoadImageSymbols(&string, *ImageBase, (ULONG_PTR)-1); } } }
#if 0
//
// Mark the pages from the relocation information to the end of the
// image as MemoryFree and adjust the size of the image so table
// based structured exception handling will work properly.
//
// Relocation sections are no longer deleted here because memory
// management will be relocating them again in Phase 0.
//
RelocDirectory = (PIMAGE_BASE_RELOCATION) RtlImageDirectoryEntryToData((PVOID)NewImageBase, TRUE, IMAGE_DIRECTORY_ENTRY_BASERELOC, &RelocSize );
if (RelocDirectory != NULL) { RelocPage = (ULONG)(((ULONG_PTR)RelocDirectory + PAGE_SIZE - 1) >> PAGE_SHIFT); RelocPage &= ~(KSEG0_BASE >> PAGE_SHIFT); MemoryDescriptor = BlFindMemoryDescriptor(RelocPage); if ((MemoryDescriptor != NULL) && (RelocPage < (ActualBase + PageCount))) { RelocPageCount = MemoryDescriptor->PageCount + MemoryDescriptor->BasePage - RelocPage;
NtHeaders->OptionalHeader.SizeOfImage = (RelocPage - ActualBase) << PAGE_SHIFT;
BlGenerateDescriptor(MemoryDescriptor, MemoryFree, RelocPage, RelocPageCount ); } }
#endif
#if defined(_GAMBIT_)
{ SSC_IMAGE_INFO ImageInfo;
ImageInfo.LoadBase = *ImageBase; ImageInfo.ImageSize = NtHeaders->OptionalHeader.SizeOfImage; ImageInfo.ImageType = NtHeaders->FileHeader.Machine; ImageInfo.ProcessID.QuadPart = 0; ImageInfo.LoadCount = 1;
if (memcmp(LoadFile, "\\ntdetect.exe", 13) != 0) { SscLoadImage64( LoadFile, &ImageInfo ); } } #endif // _GAMBIT_
cleanup:
if (bFreeCache) { BlImageFreeCache(&ImgCache, FileId); }
if (bCloseFile) { BlClose(FileId); }
return Status; }
|