singrdk/base/Kernel/Singularity/Loader/PEImage.cs

605 lines
24 KiB
C#

///////////////////////////////////////////////////////////////////////////////
//
// Microsoft Research Singularity
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
//
// Microsoft Research Singularity
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
///////////////////////////////////////////////////////////////////////////////
//
// Simple PE Loader for Singularity
//
// Currently does not support:
// x64
// sections loaded at separate addresses
// loading at preferred address
// #define verbose
// haryadi
// #define SINGULARITY_ASMP
namespace Microsoft.Singularity.Loader
{
using Microsoft.Singularity;
using Microsoft.Singularity.Io;
using Microsoft.Singularity.Memory;
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using System.Threading;
using PageType = System.GCs.PageType;
[CLSCompliant(false)]
internal class PEImage
{
// PE Signature
internal readonly uint signature;
// IMAGE_FILE_HEADER
internal readonly ushort machine;
internal readonly ushort numberOfSections;
internal readonly uint timeDateStamp;
internal readonly ushort sizeOfOptionalHeader;
internal readonly ushort characteristics;
// IMAGE_OPTIONAL_HEADER32
internal readonly ushort magic;
internal readonly uint addressOfEntryPoint;
internal readonly uint imageBase;
internal readonly uint sectionAlignment;
internal readonly uint fileAlignment;
private readonly uint sizeOfImage; // use loadedSize (this is rnd'd)
internal readonly uint sizeOfHeaders;
internal readonly uint checkSum;
internal readonly ushort dllCharacteristics;
internal readonly uint loaderFlags;
internal readonly uint numberOfRvaAndSizes;
// IMAGE_DATA_DIRECTORY and SECTION_HEADERs
internal readonly DirectoryEntry[] directory;
internal readonly SectionHeader[] sections;
internal readonly uint loadedSize;
internal const ushort IMAGE_FILE_32BIT_MACHINE = 0x0100; // 32 bit machine.
internal const ushort IMAGE_FILE_DLL = 0x2000; // File is a DLL.
internal const ushort IMAGE_DOS_SIGNATURE = 0x5A4D; // MZ
internal const uint IMAGE_NT_SIGNATURE = 0x00004550; // PE00
internal const ushort IMAGE_NT_OPTIONAL_HDR32_MAGIC = 0x010b;
enum Machine : ushort {
UNKNOWN = 0,
I386 = 0x014c, // Intel 386.
R3000 = 0x0162, // MIPS little-endian, = 0x160 big-endian
R4000 = 0x0166, // MIPS little-endian
R10000 = 0x0168, // MIPS little-endian
WCEMIPSV2 = 0x0169, // MIPS little-endian WCE v2
ALPHA = 0x0184, // Alpha_AXP
SH3 = 0x01a2, // SH3 little-endian
SH3DSP = 0x01a3,
SH3E = 0x01a4, // SH3E little-endian
SH4 = 0x01a6, // SH4 little-endian
SH5 = 0x01a8, // SH5
ARM = 0x01c0, // ARM Little-Endian
THUMB = 0x01c2,
AM33 = 0x01d3,
POWERPC = 0x01F0, // IBM PowerPC Little-Endian
POWERPCFP = 0x01f1,
IA64 = 0x0200, // Intel 64
MIPS16 = 0x0266, // MIPS
ALPHA64 = 0x0284, // ALPHA64
MIPSFPU = 0x0366, // MIPS
MIPSFPU16 = 0x0466, // MIPS
TRICORE = 0x0520, // Infineon
EBC = 0x0EBC, // EFI Byte Code
X64 = 0x8664, // AMD64, etc.
M32R = 0x9041, // M32R little-endian
CEE = 0xC0EE,
};
// Directory Entries
enum DirEntry {
Export = 0, // Export Directory
Import = 1, // Import Directory
Security = 4, // Security Directory
BaseReloc = 5, // Base Relocation Table
Debug = 6, // Debug Directory
Copyright = 7, // Description String
};
// Corresponds to the WinNT IMAGE_NT_HEADERS data structure
internal static PEImage kernel = null;
internal static ExportTable kernelExports = null;
internal static PEImage mpAbi = null;
internal static ExportTable mpAbiExports = null;
internal PEImage(IoMemory mem)
{
Kernel.Waypoint(601);
if (mem.Length < 64) {
throw new BadImageFormatException("Invalid MZ header");
}
magic = mem.Read16Unchecked(0);
Kernel.Waypoint(602);
int offset = (int)mem.Read32Unchecked(60);
if (magic != IMAGE_DOS_SIGNATURE || offset < 64 || offset + 120 > mem.Length) {
throw new BadImageFormatException("Invalid MZ header");
}
signature = mem.Read32Unchecked(offset + 0);
machine = mem.Read16Unchecked(offset + 4);
numberOfSections = mem.Read16Unchecked(offset + 6);
timeDateStamp = mem.Read16Unchecked(offset + 8);
sizeOfOptionalHeader = mem.Read16Unchecked(offset + 20);
characteristics = mem.Read16Unchecked(offset + 22);
magic = mem.Read16Unchecked(offset + 24);
addressOfEntryPoint = mem.Read32Unchecked(offset + 40);
imageBase = mem.Read32Unchecked(offset + 52);
sectionAlignment = mem.Read32Unchecked(offset + 56);
fileAlignment = mem.Read32Unchecked(offset + 60);
sizeOfImage = mem.Read32Unchecked(offset + 80);
sizeOfHeaders = mem.Read32Unchecked(offset + 84);
checkSum = mem.Read32Unchecked(offset + 88);
dllCharacteristics = mem.Read16Unchecked(offset + 94);
loaderFlags = mem.Read32Unchecked(offset + 112);
numberOfRvaAndSizes = mem.Read32Unchecked(offset + 116);
offset += 120;
Kernel.Waypoint(603);
// Verify that we have a valid NT header
if (signature != IMAGE_NT_SIGNATURE ||
sizeOfOptionalHeader != 224 ||
magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC ||
numberOfRvaAndSizes != 16) {
throw new BadImageFormatException("Invalid PE header");
}
Kernel.Waypoint(693);
directory = new DirectoryEntry[numberOfRvaAndSizes];
for (uint i = 0; i < numberOfRvaAndSizes; i++) {
directory[i] = new DirectoryEntry(mem, ref offset);
}
sections = new SectionHeader[numberOfSections];
for (uint i = 0; i < numberOfSections; i++) {
sections[i] = new SectionHeader(mem, ref offset);
}
Kernel.Waypoint(694);
// Calc the sizes of the loaded regions according to starting virtual address
loadedSize = sizeOfImage;
for (uint i = 0; i < numberOfSections; i++) {
if (!sections[i].IsDiscardable) {
uint addr = sections[i].virtualAddress;
uint size = sections[i].virtualSize;
uint last = addr + size;
if (loadedSize < last) {
loadedSize = last;
}
}
}
Kernel.Waypoint(604);
}
internal ExportTable GetExportTable(IoMemory mem)
{
if (directory[(int)DirEntry.Export].virtualAddress == 0) {
return null;
}
return new ExportTable(mem, mem.VirtualAddress,
directory[(int)DirEntry.Export]);
}
internal ImportTable GetImportTable(IoMemory mem)
{
if (directory[(int)DirEntry.Import].virtualAddress == 0) {
return null;
}
return new ImportTable(mem, directory[(int)DirEntry.Import]);
}
[NoHeapAllocation]
internal uint GetRelocationsRaw()
{
uint relocationAddr = directory[(int)DirEntry.BaseReloc].virtualAddress;
for (uint i = 0; i < numberOfSections; i++) {
if (sections[i].virtualAddress == relocationAddr) {
return sections[i].pointerToRawData;
}
}
return 0;
}
////////////////////////////////////////////////////// Static Methods.
//
public static void Initialize()
{
IoMemory pages = IoMemory.MapPhysicalMemory(BootInfo.KERNEL_BASE, 4096, true, false);
kernel = new PEImage(pages);
pages = IoMemory.MapPhysicalMemory(BootInfo.KERNEL_BASE, kernel.loadedSize, true, false);
kernelExports = kernel.GetExportTable(pages);
DebugStub.LoadedBinary(pages.VirtualAddress,
kernel.sizeOfImage,
"kernel.x86",
kernel.checkSum,
kernel.timeDateStamp,
false);
// This breakpoint is triggered by a "-d" to windbg.
if (DebugStub.PollForBreak()) {
DebugStub.Break();
}
#if GENERATE_ABI_SHIM
DebugStub.WriteLine(" InitializeMpAbi() *****");
InitializeMpAbi();
#endif
}
// haryadi
public static void InitializeMpAbi()
{
// Note: When mapping the abi stub (MpSyscalls.x86), we must
// map it with writable flag! as we want to resolve the stub's
// imports
IoMemory mpPages = IoMemory.MapPhysicalMemory(BootInfo.MP_ABI_BASE, 4096, true, true);
mpAbi = new PEImage(mpPages);
mpPages = IoMemory.MapPhysicalMemory(BootInfo.MP_ABI_BASE, mpAbi.loadedSize, true, true);
mpAbiExports = mpAbi.GetExportTable(mpPages);
// Resolve imports in abi shim dll with the kernel exports
// which are the real stub that will do IPI stuffs
ImportTable mpAbiIt = mpAbi.GetImportTable(mpPages);
DebugStub.WriteLine("HSG: ** Resolving Imports");
mpAbiIt.ResolveImports(kernelExports);
}
public static void Finalize()
{
// Uncomment the following line to notify debugger when we shutdown.
// DebugStub.UnloadedBinary(UIntPtr.MaxValue, false);
}
internal static PEImage KernelImage
{
[NoHeapAllocation]
get { return kernel; }
}
public static PEImage Load(Process process, IoMemory rawMemory,
out IoMemory loadedMemory, bool isForMp)
{
return Load(process, rawMemory, out loadedMemory,
isForMp, process == Process.kernelProcess);
}
public static PEImage Load(Process process, IoMemory rawMemory,
out IoMemory loadedMemory, bool isForMp,
bool inKernelSpace)
{
UIntPtr entryPoint = UIntPtr.Zero;
loadedMemory = null;
if (null == rawMemory || 0 == rawMemory.Length) {
DebugStub.WriteLine("No PXE image to load!");
return null;
}
//
// Allocate memory and copy the PXE image from memory to memory
//
#if verbose
Tracing.Log(Tracing.Debug, " PXE at {0:x8}, Length ={1:x8}",
(uint)rawMemory.VirtualAddress,
rawMemory.Length);
#endif
Kernel.Waypoint(580);
Tracing.Log(Tracing.Debug, "Loading:");
PEImage image = new PEImage(rawMemory);
image.DumpLimitedToStream();
#if verbose
Tracing.Log(Tracing.Debug, " Loaded Size={0:x8}", image.loadedSize);
#endif
Kernel.Waypoint(581);
if (0 == image.loadedSize) {
throw new BadImageFormatException("Invalid PE, no content");
}
try {
if (inKernelSpace) {
loadedMemory = IoMemory.AllocateFixed(
(UIntPtr)image.loadedSize,
process, PageType.System);
} else {
// Situate the process image in the user range
loadedMemory = IoMemory.AllocateUserFixed(
(UIntPtr)image.loadedSize,
process, PageType.System);
}
Kernel.Waypoint(582);
#if verbose
Tracing.Log(Tracing.Debug, " loaded at {0:x8}, Length ={1:x8}",
(uint)loadedMemory.VirtualAddress,
loadedMemory.Length);
#endif
// Copy the header so the debugger can find it.
IoMemory.Copy(rawMemory, 0, loadedMemory, 0, (int)image.sizeOfHeaders);
#if verbose
image.DumpLimitedToStream();
#endif
Kernel.Waypoint(583);
// load sections into memory where they belong.
for (int i = 0; i < image.numberOfSections; i++)
{
if (image.sections[i].IsDiscardable ||
image.sections[i].sizeOfRawData == 0) {
continue;
}
int targetOffset = (int)image.sections[i].virtualAddress;
int sourceOffset = (int)image.sections[i].pointerToRawData;
uint rawSize = Math.Min(image.sections[i].sizeOfRawData,
image.sections[i].virtualSize);
#if verbose
Tracing.Log(Tracing.Debug, "section[{0}] source={1:x8}..{2:x8} target={3:x8}..{4:x8}",
i,
sourceOffset, sourceOffset + rawSize,
targetOffset, targetOffset + rawSize);
#endif
IoMemory.Copy(rawMemory, sourceOffset, loadedMemory, targetOffset, (int)rawSize);
}
Kernel.Waypoint(584);
//
// Handle Relocations
//
int relocationTableOffset = (int)image.GetRelocationsRaw();
uint diff = (uint)loadedMemory.VirtualAddress - (uint) image.imageBase;
#if verbose
Tracing.Log(Tracing.Debug, " Base loaded={0:x8}, relocated ={1:x8} diff={2:x8}",
image.imageBase, (uint)loadedMemory.VirtualAddress, diff);
Tracing.Log(Tracing.Debug, " relocationTableOffset ={0:x8} ", relocationTableOffset);
#endif
Relocations.FixupBlocks(rawMemory, (int)relocationTableOffset,
loadedMemory, diff);
// We should probably zero the relocation table.
Kernel.Waypoint(585);
//
// Resolve Imports
//
ImportTable it = image.GetImportTable(loadedMemory);
Kernel.Waypoint(586);
#if !PAGING
#if GENERATE_ABI_SHIM
if (it != null) {
#endif
//it.DumpIAT("Import directory");
// haryadi -- if this is loading for remote processor
// then solve the imports with abi stub
if (isForMp) {
it.ResolveImports(mpAbiExports);
}
else {
it.ResolveImports(kernelExports);
}
//DumpIAT(loadedMemory, "Import Address Table",
// ref image.directory[12]);
#if GENERATE_ABI_SHIM
}
#endif
#else
if (it != null) {
// Ring-3 protection domains come with a set of
// stubs that accomplish the ring3-to-ring0 ABI
// transitions. Use these when called for.
ExportTable abiStubs = process.Domain.ABIStubs;
if (abiStubs != null) {
it.ResolveImports(abiStubs);
} else {
it.ResolveImports(kernelExports);
}
}
#endif
Kernel.Waypoint(587);
//
// Dump Exports
//
ExportTable et = image.GetExportTable(loadedMemory);
if (et != null) {
et.Dump();
}
Kernel.Waypoint(588);
entryPoint = (UIntPtr)loadedMemory.VirtualAddress + image.addressOfEntryPoint;
Tracing.Log(Tracing.Debug, " Loaded: {0:x8}..{1:x8}, Entry: {2:x8}",
(ulong)loadedMemory.VirtualAddress,
(ulong)(loadedMemory.VirtualAddress + loadedMemory.Length),
(ulong)(entryPoint));
}
finally {
if (entryPoint == UIntPtr.Zero && loadedMemory != null) {
// Need to dispose of target Range.
loadedMemory = null;
}
}
return image;
}
// haryadi -- HelloMpLoad skips several procedures to be able to
// load HelloMp.x86 properly
public static PEImage HelloMpLoad(Process process, IoMemory rawMemory,
out IoMemory loadedMemory)
{
UIntPtr entryPoint = UIntPtr.Zero;
loadedMemory = null;
if (null == rawMemory || 0 == rawMemory.Length) {
DebugStub.WriteLine("No PXE image to load!");
return null;
}
Kernel.Waypoint(580);
Tracing.Log(Tracing.Debug, "Loading:");
PEImage image = new PEImage(rawMemory);
image.DumpLimitedToStream();
Kernel.Waypoint(581);
if (0 == image.loadedSize) {
throw new BadImageFormatException("Invalid PE, no content");
}
try {
// The loadedImage is current loaded to physical memory
// directly
loadedMemory = IoMemory.AllocatePhysical
((UIntPtr)image.loadedSize);
Kernel.Waypoint(582);
// Copy the header so the debugger can find it.
IoMemory.Copy(rawMemory, 0, loadedMemory, 0,
(int)image.sizeOfHeaders);
Kernel.Waypoint(583);
// load sections into memory where they belong.
for (int i = 0; i < image.numberOfSections; i++)
{
if (image.sections[i].IsDiscardable ||
image.sections[i].sizeOfRawData == 0) {
continue;
}
int targetOffset = (int)image.sections[i].virtualAddress;
int sourceOffset = (int)image.sections[i].pointerToRawData;
uint rawSize = Math.Min(image.sections[i].sizeOfRawData,
image.sections[i].virtualSize);
IoMemory.Copy(rawMemory, sourceOffset, loadedMemory,
targetOffset, (int)rawSize);
}
Kernel.Waypoint(584);
int relocationTableOffset = (int)image.GetRelocationsRaw();
uint diff = (uint)loadedMemory.VirtualAddress -
(uint) image.imageBase;
entryPoint = (UIntPtr)loadedMemory.VirtualAddress +
image.addressOfEntryPoint;
}
finally {
if (entryPoint == UIntPtr.Zero && loadedMemory != null) {
// Need to dispose of target Range.
loadedMemory = null;
}
}
return image;
}
[NoHeapAllocation]
public UIntPtr GetEntryPoint(IoMemory loadedImage)
{
return loadedImage.VirtualAddress + addressOfEntryPoint;
}
[AccessedByRuntime("output to header : defined in PEImage.cpp")]
[OutsideGCDomain]
[StackBound(64)]
[NoInline]
[NoStackLinkCheck]
[NoStackOverflowCheck]
[MethodImpl(MethodImplOptions.InternalCall)]
[NoHeapAllocation]
internal static extern int HalCallEntryPoint(UIntPtr entry, int threadIndex,
bool runAtRing3);
[NoHeapAllocation]
internal static int CallEntryPoint(UIntPtr entry, int threadIndex,
bool runAtRing3)
{
return HalCallEntryPoint(entry, threadIndex, runAtRing3);
}
//
// Output Routines
//
[Conditional("DEBUG")]
internal void DumpLimitedToStream()
{
Tracing.Log(Tracing.Debug, " Entry point: {0:x8}", addressOfEntryPoint);
Tracing.Log(Tracing.Debug, " Image base: {0:x8}", imageBase);
#if verbose
Tracing.Log(Tracing.Debug, " Section alignment: {0:x8}", sectionAlignment);
Tracing.Log(Tracing.Debug, " File alignment: {0:x8}", fileAlignment);
Tracing.Log(Tracing.Debug, " Directories: {0:x8}", numberOfRvaAndSizes);
#endif
Tracing.Log(Tracing.Debug, " Loaded Size: {0:x8}", loadedSize);
if (directory[0].virtualAddress != 0) {
directory[0].DumpToStream("Export directory ");
}
if (directory[1].virtualAddress != 0) {
directory[1].DumpToStream("Import directory ");
}
if (directory[5].virtualAddress != 0) {
directory[5].DumpToStream("Relocation Table ");
}
if (directory[6].virtualAddress != 0) {
directory[6].DumpToStream("Debug Directory ");
}
if (directory[11].virtualAddress != 0) {
directory[11].DumpToStream("Bound Import Table ");
}
if (directory[12].virtualAddress != 0) {
directory[12].DumpToStream("Import Address Table ");
}
for (uint i = 0; i < numberOfSections; i++) {
sections[i].DumpToStream();
}
}
}
}