500 lines
12 KiB
C
500 lines
12 KiB
C
/*++
|
||
|
||
Copyright (c) 1991 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
peldr.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:
|
||
|
||
--*/
|
||
|
||
#include "bldr.h"
|
||
#include "string.h"
|
||
#include "ntimage.h"
|
||
|
||
extern ULONG BlConsoleOutDeviceId;
|
||
|
||
//
|
||
// Define forward referenced prototypes.
|
||
//
|
||
|
||
USHORT
|
||
ChkSum(
|
||
ULONG PartialSum,
|
||
PUSHORT Source,
|
||
ULONG Length
|
||
);
|
||
|
||
|
||
ARC_STATUS
|
||
BlLoadImage(
|
||
IN ULONG DeviceId,
|
||
IN TYPE_OF_MEMORY MemoryType,
|
||
IN PCHAR LoadFile,
|
||
IN USHORT ImageType,
|
||
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.
|
||
|
||
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;
|
||
PVOID 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;
|
||
|
||
//
|
||
// 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) {
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// Read the first two sectors of the image header from the file.
|
||
//
|
||
|
||
Status = BlRead(FileId, LocalPointer, SECTOR_SIZE * 2, &Count);
|
||
if (Status != ESUCCESS) {
|
||
BlClose(FileId);
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// 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) {
|
||
BlClose(FileId);
|
||
return EBADF;
|
||
}
|
||
|
||
MachineType = NtHeaders->FileHeader.Machine;
|
||
if (MachineType == IMAGE_FILE_MACHINE_R4000 &&
|
||
ImageType == IMAGE_FILE_MACHINE_R3000) {
|
||
ImageType = IMAGE_FILE_MACHINE_R4000;
|
||
}
|
||
|
||
if ((MachineType != ImageType) ||
|
||
((NtHeaders->FileHeader.Characteristics & IMAGE_FILE_EXECUTABLE_IMAGE) == 0)) {
|
||
|
||
BlClose(FileId);
|
||
return EBADF;
|
||
}
|
||
|
||
//
|
||
// 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 );
|
||
|
||
BasePage = (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;
|
||
}
|
||
|
||
Status = BlAllocateDescriptor(MemoryType,
|
||
BasePage,
|
||
PageCount,
|
||
&ActualBase);
|
||
|
||
if (Status != ESUCCESS) {
|
||
BlClose(FileId);
|
||
return EBADF;
|
||
}
|
||
|
||
//
|
||
// Compute the address of the file header.
|
||
//
|
||
|
||
NewImageBase = (PVOID)(KSEG0_BASE | (ActualBase << PAGE_SHIFT));
|
||
|
||
//
|
||
// Read the entire image header from the file.
|
||
//
|
||
|
||
SeekPosition.QuadPart = 0;
|
||
Status = BlSeek(FileId, &SeekPosition, SeekAbsolute);
|
||
if (Status != ESUCCESS) {
|
||
BlClose(FileId);
|
||
return Status;
|
||
}
|
||
|
||
Status = BlRead(FileId,
|
||
NewImageBase,
|
||
NtHeaders->OptionalHeader.SizeOfHeaders,
|
||
&Count);
|
||
|
||
if (Status != ESUCCESS) {
|
||
BlClose(FileId);
|
||
return Status;
|
||
}
|
||
|
||
NtHeaders = RtlImageNtHeader(NewImageBase);
|
||
|
||
//
|
||
// Compute the address of the section headers, set the
|
||
// image base address.
|
||
//
|
||
|
||
SectionHeader = IMAGE_FIRST_SECTION( NtHeaders );
|
||
|
||
*ImageBase = NewImageBase;
|
||
|
||
//
|
||
// Compute the check sum on the image.
|
||
//
|
||
|
||
PartialSum = ChkSum(0, 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;
|
||
|
||
if ((VirtualSize & 1) == 1) {
|
||
//
|
||
// Round to even so that checksum works
|
||
//
|
||
|
||
VirtualSize++;
|
||
}
|
||
|
||
if ((SizeOfRawData & 1) == 1) {
|
||
//
|
||
// Round to even so that checksum works
|
||
//
|
||
|
||
SizeOfRawData++;
|
||
}
|
||
|
||
if (VirtualSize == 0) {
|
||
VirtualSize = SizeOfRawData;
|
||
}
|
||
|
||
if (SectionHeader->PointerToRawData == 0) {
|
||
//
|
||
// SizeOfRawData can be non-zero even if PointerToRawData is zero
|
||
//
|
||
|
||
SizeOfRawData = 0;
|
||
} else if (SizeOfRawData > VirtualSize) {
|
||
//
|
||
// Don't load more from image than is expected in memory
|
||
//
|
||
|
||
SizeOfRawData = VirtualSize;
|
||
}
|
||
|
||
if (SizeOfRawData != 0) {
|
||
SeekPosition.LowPart = SectionHeader->PointerToRawData;
|
||
Status = BlSeek(FileId,
|
||
&SeekPosition,
|
||
SeekAbsolute);
|
||
|
||
if (Status != ESUCCESS) {
|
||
break;
|
||
}
|
||
|
||
Status = BlRead(FileId,
|
||
(PVOID)(SectionHeader->VirtualAddress + (ULONG)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 + (ULONG)NewImageBase),
|
||
SizeOfRawData / sizeof(USHORT));
|
||
}
|
||
|
||
if (SizeOfRawData < VirtualSize) {
|
||
//
|
||
// Zero the portion not loaded from the image
|
||
//
|
||
|
||
RtlZeroMemory((PVOID)(KSEG0_BASE | SectionHeader->VirtualAddress + (ULONG)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 an check sum it.
|
||
//
|
||
|
||
Length = Count < SECTOR_SIZE * 2 ? Count : SECTOR_SIZE * 2;
|
||
if (BlRead(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;
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// Close the image file.
|
||
//
|
||
|
||
BlClose(FileId);
|
||
|
||
//
|
||
// If the specified image was successfully loaded, then perform image
|
||
// relocation if necessary.
|
||
//
|
||
|
||
if (Status == ESUCCESS) {
|
||
|
||
//
|
||
// Compute relocation value.
|
||
//
|
||
|
||
if ((ULONG)NewImageBase != NtHeaders->OptionalHeader.ImageBase) {
|
||
Status = (ARC_STATUS)LdrRelocateImage(NewImageBase,
|
||
"OS Loader",
|
||
ESUCCESS,
|
||
EBADF,
|
||
EBADF
|
||
);
|
||
}
|
||
}
|
||
|
||
//
|
||
// 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.
|
||
//
|
||
|
||
RelocDirectory = (PIMAGE_BASE_RELOCATION)
|
||
RtlImageDirectoryEntryToData(NewImageBase,
|
||
TRUE,
|
||
IMAGE_DIRECTORY_ENTRY_BASERELOC,
|
||
&RelocSize );
|
||
|
||
if (RelocDirectory != NULL) {
|
||
RelocPage = ((ULONG)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 );
|
||
|
||
}
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
USHORT
|
||
ChkSum(
|
||
ULONG PartialSum,
|
||
PUSHORT Source,
|
||
ULONG Length
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Compute a partial checksum on a portion of an imagefile.
|
||
|
||
Arguments:
|
||
|
||
PartialSum - Supplies the initial checksum value.
|
||
|
||
Sources - Supplies a pointer to the array of words for which the
|
||
checksum is computed.
|
||
|
||
Length - Supplies the length of the array in words.
|
||
|
||
Return Value:
|
||
|
||
The computed checksum value is returned as the function value.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
//
|
||
// Compute the word wise checksum allowing carries to occur into the
|
||
// high order half of the checksum longword.
|
||
//
|
||
|
||
while (Length--) {
|
||
PartialSum += *Source++;
|
||
PartialSum = (PartialSum >> 16) + (PartialSum & 0xffff);
|
||
}
|
||
|
||
//
|
||
// Fold final carry into a single word result and return the resultant
|
||
// value.
|
||
//
|
||
|
||
return (USHORT)(((PartialSum >> 16) + PartialSum) & 0xffff);
|
||
}
|