Xamarin Public Jenkins ea5caba957 Imported Upstream version 4.2.1.36
Former-commit-id: f3008ca867fe7e4b7ae9b9a8844c0ad5798925a9
2015-11-10 14:54:41 +00:00

922 lines
25 KiB
C#

/*
Copyright (C) 2008-2013 Jeroen Frijters
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
Jeroen Frijters
jeroen@frijters.net
*/
using System;
using System.Diagnostics;
using System.IO;
using System.Collections.Generic;
using System.Text;
using IKVM.Reflection.Emit;
using IKVM.Reflection.Impl;
using IKVM.Reflection.Metadata;
namespace IKVM.Reflection.Writer
{
sealed class TextSection
{
private readonly PEWriter peWriter;
private readonly CliHeader cliHeader;
private readonly ModuleBuilder moduleBuilder;
private readonly uint strongNameSignatureLength;
private readonly uint manifestResourcesLength;
private readonly ExportTables exportTables;
private readonly List<RelocationBlock> relocations = new List<RelocationBlock>();
internal TextSection(PEWriter peWriter, CliHeader cliHeader, ModuleBuilder moduleBuilder, int strongNameSignatureLength)
{
this.peWriter = peWriter;
this.cliHeader = cliHeader;
this.moduleBuilder = moduleBuilder;
this.strongNameSignatureLength = (uint)strongNameSignatureLength;
this.manifestResourcesLength = (uint)moduleBuilder.GetManifestResourcesLength();
if (moduleBuilder.unmanagedExports.Count != 0)
{
this.exportTables = new ExportTables(this);
}
}
internal uint PointerToRawData
{
get { return peWriter.ToFileAlignment(peWriter.HeaderSize); }
}
internal uint BaseRVA
{
get { return peWriter.Headers.OptionalHeader.SectionAlignment; }
}
internal uint ImportAddressTableRVA
{
get { return BaseRVA; }
}
internal uint ImportAddressTableLength
{
get { return peWriter.Is32Bit ? 8u : 16u; }
}
internal uint ComDescriptorRVA
{
get { return ImportAddressTableRVA + ImportAddressTableLength; }
}
internal uint ComDescriptorLength
{
get { return cliHeader.Cb; }
}
internal uint MethodBodiesRVA
{
get { return (ComDescriptorRVA + ComDescriptorLength + 7) & ~7U; }
}
private uint MethodBodiesLength
{
get { return (uint)moduleBuilder.methodBodies.Length; }
}
private uint ResourcesRVA
{
get
{
switch (peWriter.Headers.FileHeader.Machine)
{
case IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_I386:
case IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_ARM:
return (MethodBodiesRVA + MethodBodiesLength + 3) & ~3U;
default:
return (MethodBodiesRVA + MethodBodiesLength + 15) & ~15U;
}
}
}
private uint ResourcesLength
{
get { return manifestResourcesLength; }
}
internal uint StrongNameSignatureRVA
{
get
{
return (ResourcesRVA + ResourcesLength + 3) & ~3U;
}
}
internal uint StrongNameSignatureLength
{
get
{
return strongNameSignatureLength;
}
}
private uint MetadataRVA
{
get
{
return (StrongNameSignatureRVA + StrongNameSignatureLength + 3) & ~3U;
}
}
private uint MetadataLength
{
get { return (uint)moduleBuilder.MetadataLength; }
}
private uint VTableFixupsRVA
{
get { return (MetadataRVA + MetadataLength + 7) & ~7U; }
}
private uint VTableFixupsLength
{
get { return (uint)moduleBuilder.vtablefixups.Count * 8; }
}
internal uint DebugDirectoryRVA
{
get { return VTableFixupsRVA + VTableFixupsLength; }
}
internal uint DebugDirectoryLength
{
get
{
if (DebugDirectoryContentsLength != 0)
{
return 28;
}
return 0;
}
}
private uint DebugDirectoryContentsLength
{
get
{
if (moduleBuilder.symbolWriter != null)
{
IMAGE_DEBUG_DIRECTORY idd = new IMAGE_DEBUG_DIRECTORY();
return (uint)SymbolSupport.GetDebugInfo(moduleBuilder.symbolWriter, ref idd).Length;
}
return 0;
}
}
internal uint ExportDirectoryRVA
{
get { return (DebugDirectoryRVA + DebugDirectoryLength + DebugDirectoryContentsLength + 15) & ~15U; }
}
internal uint ExportDirectoryLength
{
get { return moduleBuilder.unmanagedExports.Count == 0 ? 0U : 40U; }
}
private uint ExportTablesRVA
{
get { return ExportDirectoryRVA + ExportDirectoryLength; }
}
private uint ExportTablesLength
{
get { return exportTables == null ? 0U : exportTables.Length; }
}
internal uint ImportDirectoryRVA
{
// on AMD64 (and probably IA64) the import directory needs to be 16 byte aligned (on I386 4 byte alignment is sufficient)
get { return (ExportTablesRVA + ExportTablesLength + 15) & ~15U; }
}
internal uint ImportDirectoryLength
{
get { return (ImportHintNameTableRVA - ImportDirectoryRVA) + 27; }
}
private uint ImportHintNameTableRVA
{
get
{
return peWriter.Is32Bit
? (ImportDirectoryRVA + 48 + 15) & ~15U
: (ImportDirectoryRVA + 52 + 15) & ~15U;
}
}
internal uint StartupStubRVA
{
get
{
if (peWriter.Headers.FileHeader.Machine == IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_IA64)
{
// note that the alignment is driven by the requirement that the two relocation fixups are in a single page
return (ImportDirectoryRVA + ImportDirectoryLength + 15U) & ~15U;
}
else
{
// the additional 2 bytes padding are to align the address in the jump (which is a relocation fixup)
return 2 + ((ImportDirectoryRVA + ImportDirectoryLength + 3U) & ~3U);
}
}
}
internal uint StartupStubLength
{
get
{
switch (peWriter.Headers.FileHeader.Machine)
{
case IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_I386:
return 6;
case IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_AMD64:
case IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_ARM:
return 12;
case IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_IA64:
return 48;
default:
throw new NotSupportedException();
}
}
}
private void WriteRVA(MetadataWriter mw, uint rva)
{
switch (peWriter.Headers.FileHeader.Machine)
{
case IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_I386:
case IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_ARM:
mw.Write(rva);
break;
default:
mw.Write((ulong)rva);
break;
}
}
internal void Write(MetadataWriter mw, uint sdataRVA, out uint guidHeapOffset)
{
// Now that we're ready to start writing, we need to do some fix ups
moduleBuilder.TypeRef.Fixup(moduleBuilder);
moduleBuilder.MethodDef.Fixup(this);
moduleBuilder.MethodImpl.Fixup(moduleBuilder);
moduleBuilder.MethodSemantics.Fixup(moduleBuilder);
moduleBuilder.InterfaceImpl.Fixup();
moduleBuilder.ResolveInterfaceImplPseudoTokens();
moduleBuilder.MemberRef.Fixup(moduleBuilder);
moduleBuilder.Constant.Fixup(moduleBuilder);
moduleBuilder.FieldMarshal.Fixup(moduleBuilder);
moduleBuilder.DeclSecurity.Fixup(moduleBuilder);
moduleBuilder.GenericParam.Fixup(moduleBuilder);
moduleBuilder.CustomAttribute.Fixup(moduleBuilder);
moduleBuilder.FieldLayout.Fixup(moduleBuilder);
moduleBuilder.FieldRVA.Fixup(moduleBuilder, (int)sdataRVA, (int)this.MethodBodiesRVA);
moduleBuilder.ImplMap.Fixup(moduleBuilder);
moduleBuilder.ExportedType.Fixup(moduleBuilder);
moduleBuilder.ManifestResource.Fixup(moduleBuilder);
moduleBuilder.MethodSpec.Fixup(moduleBuilder);
moduleBuilder.GenericParamConstraint.Fixup(moduleBuilder);
// Import Address Table
AssertRVA(mw, ImportAddressTableRVA);
if (ImportAddressTableLength != 0)
{
WriteRVA(mw, ImportHintNameTableRVA);
WriteRVA(mw, 0);
}
// CLI Header
AssertRVA(mw, ComDescriptorRVA);
cliHeader.MetaData.VirtualAddress = MetadataRVA;
cliHeader.MetaData.Size = MetadataLength;
if (ResourcesLength != 0)
{
cliHeader.Resources.VirtualAddress = ResourcesRVA;
cliHeader.Resources.Size = ResourcesLength;
}
if (StrongNameSignatureLength != 0)
{
cliHeader.StrongNameSignature.VirtualAddress = StrongNameSignatureRVA;
cliHeader.StrongNameSignature.Size = StrongNameSignatureLength;
}
if (VTableFixupsLength != 0)
{
cliHeader.VTableFixups.VirtualAddress = VTableFixupsRVA;
cliHeader.VTableFixups.Size = VTableFixupsLength;
}
cliHeader.Write(mw);
// alignment padding
for (int i = (int)(MethodBodiesRVA - (ComDescriptorRVA + ComDescriptorLength)); i > 0; i--)
{
mw.Write((byte)0);
}
// Method Bodies
mw.Write(moduleBuilder.methodBodies);
// alignment padding
for (int i = (int)(ResourcesRVA - (MethodBodiesRVA + MethodBodiesLength)); i > 0; i--)
{
mw.Write((byte)0);
}
// Resources
moduleBuilder.WriteResources(mw);
// The strong name signature live here (if it exists), but it will written later
// and the following alignment padding will take care of reserving the space.
// alignment padding
for (int i = (int)(MetadataRVA - (ResourcesRVA + ResourcesLength)); i > 0; i--)
{
mw.Write((byte)0);
}
// Metadata
AssertRVA(mw, MetadataRVA);
moduleBuilder.WriteMetadata(mw, out guidHeapOffset);
// alignment padding
for (int i = (int)(VTableFixupsRVA - (MetadataRVA + MetadataLength)); i > 0; i--)
{
mw.Write((byte)0);
}
// VTableFixups
AssertRVA(mw, VTableFixupsRVA);
WriteVTableFixups(mw, sdataRVA);
// Debug Directory
AssertRVA(mw, DebugDirectoryRVA);
WriteDebugDirectory(mw);
// alignment padding
for (int i = (int)(ExportDirectoryRVA - (DebugDirectoryRVA + DebugDirectoryLength + DebugDirectoryContentsLength)); i > 0; i--)
{
mw.Write((byte)0);
}
// Export Directory
AssertRVA(mw, ExportDirectoryRVA);
WriteExportDirectory(mw);
// Export Tables
AssertRVA(mw, ExportTablesRVA);
WriteExportTables(mw, sdataRVA);
// alignment padding
for (int i = (int)(ImportDirectoryRVA - (ExportTablesRVA + ExportTablesLength)); i > 0; i--)
{
mw.Write((byte)0);
}
// Import Directory
AssertRVA(mw, ImportDirectoryRVA);
if (ImportDirectoryLength != 0)
{
WriteImportDirectory(mw);
}
// alignment padding
for (int i = (int)(StartupStubRVA - (ImportDirectoryRVA + ImportDirectoryLength)); i > 0; i--)
{
mw.Write((byte)0);
}
// Startup Stub
AssertRVA(mw, StartupStubRVA);
if (peWriter.Headers.FileHeader.Machine == IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_AMD64)
{
/*
* 48 A1 00 20 40 00 00 00 00 00 mov rax,qword ptr [0000000000402000h]
* FF E0 jmp rax
*/
mw.Write((ushort)0xA148);
mw.Write(peWriter.Headers.OptionalHeader.ImageBase + ImportAddressTableRVA);
mw.Write((ushort)0xE0FF);
}
else if (peWriter.Headers.FileHeader.Machine == IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_IA64)
{
mw.Write(new byte[] {
0x0B, 0x48, 0x00, 0x02, 0x18, 0x10, 0xA0, 0x40, 0x24, 0x30, 0x28, 0x00, 0x00, 0x00, 0x04, 0x00,
0x10, 0x08, 0x00, 0x12, 0x18, 0x10, 0x60, 0x50, 0x04, 0x80, 0x03, 0x00, 0x60, 0x00, 0x80, 0x00
});
mw.Write(peWriter.Headers.OptionalHeader.ImageBase + StartupStubRVA);
mw.Write(peWriter.Headers.OptionalHeader.ImageBase + BaseRVA);
}
else if (peWriter.Headers.FileHeader.Machine == IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_I386)
{
mw.Write((ushort)0x25FF);
mw.Write((uint)peWriter.Headers.OptionalHeader.ImageBase + ImportAddressTableRVA);
}
else if (peWriter.Headers.FileHeader.Machine == IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_ARM)
{
uint rva = (uint)peWriter.Headers.OptionalHeader.ImageBase + ImportAddressTableRVA;
ushort lo = (ushort)rva;
ushort hi = (ushort)(rva >> 16);
mw.Write((ushort)(0xF240 + (lo >> 12)));
mw.Write((ushort)(0x0C00 + ((lo << 4) & 0xF000) + (lo & 0xFF)));
mw.Write((ushort)(0xF2C0 + (hi >> 12)));
mw.Write((ushort)(0x0C00 + ((hi << 4) & 0xF000) + (hi & 0xFF)));
mw.Write((ushort)0xF8DC);
mw.Write((ushort)0xF000);
}
else
{
throw new NotSupportedException();
}
}
[Conditional("DEBUG")]
private void AssertRVA(MetadataWriter mw, uint rva)
{
Debug.Assert(mw.Position - PointerToRawData + BaseRVA == rva);
}
private void WriteVTableFixups(MetadataWriter mw, uint sdataRVA)
{
foreach (ModuleBuilder.VTableFixups fixups in moduleBuilder.vtablefixups)
{
mw.Write(fixups.initializedDataOffset + sdataRVA);
mw.Write(fixups.count);
mw.Write(fixups.type);
}
}
private void WriteDebugDirectory(MetadataWriter mw)
{
if (DebugDirectoryLength != 0)
{
IMAGE_DEBUG_DIRECTORY idd = new IMAGE_DEBUG_DIRECTORY();
idd.Characteristics = 0;
idd.TimeDateStamp = peWriter.Headers.FileHeader.TimeDateStamp;
byte[] buf = SymbolSupport.GetDebugInfo(moduleBuilder.symbolWriter, ref idd);
idd.PointerToRawData = (DebugDirectoryRVA - BaseRVA) + DebugDirectoryLength + PointerToRawData;
idd.AddressOfRawData = DebugDirectoryRVA + DebugDirectoryLength;
mw.Write(idd.Characteristics);
mw.Write(idd.TimeDateStamp);
mw.Write(idd.MajorVersion);
mw.Write(idd.MinorVersion);
mw.Write(idd.Type);
mw.Write(idd.SizeOfData);
mw.Write(idd.AddressOfRawData);
mw.Write(idd.PointerToRawData);
mw.Write(buf);
}
}
private sealed class ExportTables
{
private readonly TextSection text;
internal readonly uint entries;
internal readonly uint ordinalBase;
internal readonly uint nameCount;
internal readonly uint namesLength;
internal readonly uint exportAddressTableRVA;
internal readonly uint exportNamePointerTableRVA;
internal readonly uint exportOrdinalTableRVA;
internal readonly uint namesRVA;
internal readonly uint stubsRVA;
private readonly uint stubLength;
internal ExportTables(TextSection text)
{
this.text = text;
ordinalBase = GetOrdinalBase(out entries);
namesLength = GetExportNamesLength(out nameCount);
exportAddressTableRVA = text.ExportTablesRVA;
exportNamePointerTableRVA = exportAddressTableRVA + 4 * entries;
exportOrdinalTableRVA = exportNamePointerTableRVA + 4 * nameCount;
namesRVA = exportOrdinalTableRVA + 2 * nameCount;
stubsRVA = (namesRVA + namesLength + 15) & ~15U;
// note that we align the stubs to avoid having to deal with the relocation crossing a page boundary
switch (text.peWriter.Headers.FileHeader.Machine)
{
case IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_I386:
stubLength = 8;
break;
case IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_AMD64:
case IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_ARM:
stubLength = 16;
break;
default:
throw new NotSupportedException();
}
}
internal uint Length
{
get { return (stubsRVA + stubLength * (uint)text.moduleBuilder.unmanagedExports.Count) - text.ExportTablesRVA; }
}
private uint GetOrdinalBase(out uint entries)
{
uint min = uint.MaxValue;
uint max = uint.MinValue;
foreach (UnmanagedExport exp in text.moduleBuilder.unmanagedExports)
{
uint ordinal = (uint)exp.ordinal;
min = Math.Min(min, ordinal);
max = Math.Max(max, ordinal);
}
entries = 1 + (max - min);
return min;
}
private uint GetExportNamesLength(out uint nameCount)
{
nameCount = 0;
// the first name in the names list is the module name (the Export Directory contains a name of the current module)
uint length = (uint)text.moduleBuilder.fileName.Length + 1;
foreach (UnmanagedExport exp in text.moduleBuilder.unmanagedExports)
{
if (exp.name != null)
{
nameCount++;
length += (uint)exp.name.Length + 1;
}
}
return length;
}
internal void Write(MetadataWriter mw, uint sdataRVA)
{
// sort the exports by ordinal
text.moduleBuilder.unmanagedExports.Sort(CompareUnmanagedExportOrdinals);
// Now write the Export Address Table
text.AssertRVA(mw, exportAddressTableRVA);
for (int i = 0, pos = 0; i < entries; i++)
{
if (text.moduleBuilder.unmanagedExports[pos].ordinal == i + ordinalBase)
{
mw.Write(text.peWriter.Thumb + stubsRVA + (uint)pos * stubLength);
pos++;
}
else
{
mw.Write(0);
}
}
// sort the exports by name
text.moduleBuilder.unmanagedExports.Sort(CompareUnmanagedExportNames);
// Now write the Export Name Pointer Table
text.AssertRVA(mw, exportNamePointerTableRVA);
uint nameOffset = (uint)text.moduleBuilder.fileName.Length + 1;
foreach (UnmanagedExport exp in text.moduleBuilder.unmanagedExports)
{
if (exp.name != null)
{
mw.Write(namesRVA + nameOffset);
nameOffset += (uint)exp.name.Length + 1;
}
}
// Now write the Export Ordinal Table
text.AssertRVA(mw, exportOrdinalTableRVA);
foreach (UnmanagedExport exp in text.moduleBuilder.unmanagedExports)
{
if (exp.name != null)
{
mw.Write((ushort)(exp.ordinal - ordinalBase));
}
}
// Now write the actual names
text.AssertRVA(mw, namesRVA);
mw.WriteAsciiz(text.moduleBuilder.fileName);
foreach (UnmanagedExport exp in text.moduleBuilder.unmanagedExports)
{
if (exp.name != null)
{
mw.WriteAsciiz(exp.name);
}
}
text.AssertRVA(mw, namesRVA + namesLength);
// alignment padding
for (int i = (int)(stubsRVA - (namesRVA + namesLength)); i > 0; i--)
{
mw.Write((byte)0);
}
// sort the exports by ordinal
text.moduleBuilder.unmanagedExports.Sort(CompareUnmanagedExportOrdinals);
// Now write the stubs
text.AssertRVA(mw, stubsRVA);
for (int i = 0, pos = 0; i < entries; i++)
{
if (text.moduleBuilder.unmanagedExports[pos].ordinal == i + ordinalBase)
{
switch (text.peWriter.Headers.FileHeader.Machine)
{
case IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_I386:
mw.Write((byte)0xFF);
mw.Write((byte)0x25);
mw.Write((uint)text.peWriter.Headers.OptionalHeader.ImageBase + text.moduleBuilder.unmanagedExports[pos].rva.initializedDataOffset + sdataRVA);
mw.Write((short)0); // alignment
break;
case IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_AMD64:
mw.Write((byte)0x48);
mw.Write((byte)0xA1);
mw.Write(text.peWriter.Headers.OptionalHeader.ImageBase + text.moduleBuilder.unmanagedExports[pos].rva.initializedDataOffset + sdataRVA);
mw.Write((byte)0xFF);
mw.Write((byte)0xE0);
mw.Write(0); // alignment
break;
case IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_ARM:
mw.Write((ushort)0xF8DF);
mw.Write((ushort)0xC008);
mw.Write((ushort)0xF8DC);
mw.Write((ushort)0xC000);
mw.Write((ushort)0x4760);
mw.Write((ushort)0xDEFE);
mw.Write((uint)text.peWriter.Headers.OptionalHeader.ImageBase + text.moduleBuilder.unmanagedExports[pos].rva.initializedDataOffset + sdataRVA);
break;
default:
throw new NotSupportedException();
}
pos++;
}
}
}
private static int CompareUnmanagedExportNames(UnmanagedExport x, UnmanagedExport y)
{
if (x.name == null)
{
return y.name == null ? 0 : 1;
}
if (y.name == null)
{
return -1;
}
return String.CompareOrdinal(x.name, y.name);
}
private static int CompareUnmanagedExportOrdinals(UnmanagedExport x, UnmanagedExport y)
{
return x.ordinal.CompareTo(y.ordinal);
}
internal void GetRelocations(List<Relocation> list)
{
ushort type;
uint rva;
switch (text.peWriter.Headers.FileHeader.Machine)
{
case IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_I386:
type = 0x3000;
rva = stubsRVA + 2;
break;
case IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_AMD64:
type = 0xA000;
rva = stubsRVA + 2;
break;
case IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_ARM:
type = 0x3000;
rva = stubsRVA + 12;
break;
default:
throw new NotSupportedException();
}
// we assume that unmanagedExports is still sorted by ordinal
for (int i = 0, pos = 0; i < entries; i++)
{
if (text.moduleBuilder.unmanagedExports[pos].ordinal == i + ordinalBase)
{
list.Add(new Relocation(type, rva + (uint)pos * stubLength));
pos++;
}
}
}
}
private uint GetOrdinalBase(out uint entries)
{
uint min = uint.MaxValue;
uint max = uint.MinValue;
foreach (UnmanagedExport exp in moduleBuilder.unmanagedExports)
{
uint ordinal = (uint)exp.ordinal;
min = Math.Min(min, ordinal);
max = Math.Max(max, ordinal);
}
entries = 1 + (max - min);
return min;
}
private uint GetExportNamesLength(out uint nameCount)
{
nameCount = 0;
uint length = 0;
foreach (UnmanagedExport exp in moduleBuilder.unmanagedExports)
{
if (exp.name != null)
{
nameCount++;
length += (uint)exp.name.Length + 1;
}
}
return length;
}
private void WriteExportDirectory(MetadataWriter mw)
{
if (ExportDirectoryLength != 0)
{
// Flags
mw.Write(0);
// Date/Time Stamp
mw.Write(peWriter.Headers.FileHeader.TimeDateStamp);
// Major Version
mw.Write((short)0);
// Minor Version
mw.Write((short)0);
// Name RVA
mw.Write(exportTables.namesRVA);
// Ordinal Base
mw.Write(exportTables.ordinalBase);
// Address Table Entries
mw.Write(exportTables.entries);
// Number of Name Pointers
mw.Write(exportTables.nameCount);
// Export Address Table RVA
mw.Write(exportTables.exportAddressTableRVA);
// Name Pointer RVA
mw.Write(exportTables.exportNamePointerTableRVA);
// Ordinal Table RVA
mw.Write(exportTables.exportOrdinalTableRVA);
}
}
private void WriteExportTables(MetadataWriter mw, uint sdataRVA)
{
if (exportTables != null)
{
exportTables.Write(mw, sdataRVA);
}
}
private void WriteImportDirectory(MetadataWriter mw)
{
mw.Write(ImportDirectoryRVA + 40); // ImportLookupTable
mw.Write(0); // DateTimeStamp
mw.Write(0); // ForwarderChain
mw.Write(ImportHintNameTableRVA + 14); // Name
mw.Write(ImportAddressTableRVA);
mw.Write(new byte[20]);
// Import Lookup Table
mw.Write(ImportHintNameTableRVA); // Hint/Name Table RVA
int size = 48;
if (!peWriter.Is32Bit)
{
size += 4;
mw.Write(0);
}
mw.Write(0);
// alignment padding
for (int i = (int)(ImportHintNameTableRVA - (ImportDirectoryRVA + size)); i > 0; i--)
{
mw.Write((byte)0);
}
// Hint/Name Table
AssertRVA(mw, ImportHintNameTableRVA);
mw.Write((ushort)0); // Hint
if ((peWriter.Headers.FileHeader.Characteristics & IMAGE_FILE_HEADER.IMAGE_FILE_DLL) != 0)
{
mw.WriteAsciiz("_CorDllMain");
}
else
{
mw.WriteAsciiz("_CorExeMain");
}
// Name
mw.WriteAsciiz("mscoree.dll");
mw.Write((byte)0);
}
internal int Length
{
get { return (int)(StartupStubRVA - BaseRVA + StartupStubLength); }
}
struct Relocation : IComparable<Relocation>
{
internal readonly uint rva;
internal readonly ushort type;
internal Relocation(ushort type, uint rva)
{
this.type = type;
this.rva = rva;
}
int IComparable<Relocation>.CompareTo(Relocation other)
{
return rva.CompareTo(other.rva);
}
}
struct RelocationBlock
{
internal readonly uint PageRVA;
internal readonly ushort[] TypeOffset;
internal RelocationBlock(uint pageRva, ushort[] typeOffset)
{
this.PageRVA = pageRva;
this.TypeOffset = typeOffset;
}
}
internal void WriteRelocations(MetadataWriter mw)
{
foreach (RelocationBlock block in relocations)
{
mw.Write(block.PageRVA);
mw.Write(8 + block.TypeOffset.Length * 2);
foreach (ushort typeOffset in block.TypeOffset)
{
mw.Write(typeOffset);
}
}
}
internal uint PackRelocations()
{
List<Relocation> list = new List<Relocation>();
switch (peWriter.Headers.FileHeader.Machine)
{
case IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_I386:
list.Add(new Relocation(0x3000, this.StartupStubRVA + 2));
break;
case IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_AMD64:
list.Add(new Relocation(0xA000, this.StartupStubRVA + 2));
break;
case IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_IA64:
list.Add(new Relocation(0xA000, this.StartupStubRVA + 0x20));
list.Add(new Relocation(0xA000, this.StartupStubRVA + 0x28));
break;
case IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_ARM:
list.Add(new Relocation(0x7000, this.StartupStubRVA));
break;
default:
throw new NotSupportedException();
}
if (exportTables != null)
{
exportTables.GetRelocations(list);
}
list.Sort();
uint size = 0;
for (int i = 0; i < list.Count; )
{
uint pageRVA = list[i].rva & ~0xFFFU;
int count = 1;
while (i + count < list.Count && (list[i + count].rva & ~0xFFFU) == pageRVA)
{
count++;
}
ushort[] typeOffset = new ushort[(count + 1) & ~1];
for (int j = 0; j < count; j++, i++)
{
typeOffset[j] = (ushort)(list[i].type + (list[i].rva - pageRVA));
}
relocations.Add(new RelocationBlock(pageRVA, typeOffset));
size += (uint)(8 + typeOffset.Length * 2);
}
return size;
}
}
}