Added API extension ModuleBuilder.__AddUnmanagedExportStub().

This commit is contained in:
jfrijters 2011-03-22 09:48:49 +00:00
Родитель 1d75ea1fe9
Коммит 72ca5c45b3
3 изменённых файлов: 383 добавлений и 33 удалений

Просмотреть файл

@ -66,6 +66,7 @@ namespace IKVM.Reflection.Emit
internal readonly GuidHeap Guids = new GuidHeap();
internal readonly BlobHeap Blobs = new BlobHeap();
internal readonly List<VTableFixups> vtablefixups = new List<VTableFixups>();
internal readonly List<UnmanagedExport> unmanagedExports = new List<UnmanagedExport>();
internal struct VTableFixups
{
@ -1429,6 +1430,22 @@ namespace IKVM.Reflection.Emit
vtablefixups.Add(fixups);
return new RelativeVirtualAddress(fixups.initializedDataOffset);
}
public void __AddUnmanagedExportStub(string name, int ordinal, RelativeVirtualAddress rva)
{
UnmanagedExport export;
export.name = name;
export.ordinal = ordinal;
export.rva = rva;
unmanagedExports.Add(export);
}
}
struct UnmanagedExport
{
internal string name;
internal int ordinal;
internal RelativeVirtualAddress rva;
}
public struct RelativeVirtualAddress

Просмотреть файл

@ -172,6 +172,13 @@ namespace IKVM.Reflection.Writer
moduleBuilder.Tables.Freeze(mw);
TextSection code = new TextSection(writer, cliHeader, moduleBuilder, ComputeStrongNameSignatureLength(publicKey));
// Export Directory
if (code.ExportDirectoryLength != 0)
{
writer.Headers.OptionalHeader.DataDirectory[0].VirtualAddress = code.ExportDirectoryRVA;
writer.Headers.OptionalHeader.DataDirectory[0].Size = code.ExportDirectoryLength;
}
// Import Directory
writer.Headers.OptionalHeader.DataDirectory[1].VirtualAddress = code.ImportDirectoryRVA;
writer.Headers.OptionalHeader.DataDirectory[1].Size = code.ImportDirectoryLength;
@ -237,7 +244,7 @@ namespace IKVM.Reflection.Writer
SectionHeader reloc = new SectionHeader();
reloc.Name = ".reloc";
reloc.VirtualAddress = rsrc.VirtualAddress + writer.ToSectionAlignment(rsrc.VirtualSize);
reloc.VirtualSize = 12;
reloc.VirtualSize = ((uint)moduleBuilder.unmanagedExports.Count + 1) * 12;
reloc.PointerToRawData = rsrc.PointerToRawData + rsrc.SizeOfRawData;
reloc.SizeOfRawData = writer.ToFileAlignment(reloc.VirtualSize);
reloc.Characteristics = SectionHeader.IMAGE_SCN_MEM_READ | SectionHeader.IMAGE_SCN_CNT_INITIALIZED_DATA | SectionHeader.IMAGE_SCN_MEM_DISCARDABLE;
@ -292,37 +299,10 @@ namespace IKVM.Reflection.Writer
stream.Seek(reloc.PointerToRawData, SeekOrigin.Begin);
// .reloc section
uint relocAddress = code.StartupStubRVA;
switch (imageFileMachine)
{
case ImageFileMachine.I386:
case ImageFileMachine.AMD64:
relocAddress += 2;
break;
case ImageFileMachine.IA64:
relocAddress += 0x20;
break;
}
uint pageRVA = relocAddress & ~0xFFFU;
mw.Write(pageRVA); // PageRVA
mw.Write(0x000C); // Block Size
if (imageFileMachine == ImageFileMachine.I386)
{
mw.Write(0x3000 + relocAddress - pageRVA); // Type / Offset
}
else if (imageFileMachine == ImageFileMachine.AMD64)
{
mw.Write(0xA000 + relocAddress - pageRVA); // Type / Offset
}
else if (imageFileMachine == ImageFileMachine.IA64)
{
// on IA64 the StartupStubRVA is 16 byte aligned, so these two addresses won't cross a page boundary
mw.Write((short)(0xA000 + relocAddress - pageRVA)); // Type / Offset
mw.Write((short)(0xA000 + relocAddress - pageRVA + 8)); // Type / Offset
}
code.WriteRelocations(mw);
// file alignment
mw.Write(new byte[writer.Headers.OptionalHeader.FileAlignment - reloc.VirtualSize]);
mw.Write(new byte[writer.Headers.OptionalHeader.FileAlignment - (reloc.VirtualSize & (writer.Headers.OptionalHeader.FileAlignment - 1))]);
// do the strong naming
if (keyPair != null)

Просмотреть файл

@ -1,5 +1,5 @@
/*
Copyright (C) 2008 Jeroen Frijters
Copyright (C) 2008-2011 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
@ -38,6 +38,7 @@ namespace IKVM.Reflection.Writer
private readonly CliHeader cliHeader;
private readonly ModuleBuilder moduleBuilder;
private readonly uint strongNameSignatureLength;
private readonly ExportTables exportTables;
internal TextSection(PEWriter peWriter, CliHeader cliHeader, ModuleBuilder moduleBuilder, int strongNameSignatureLength)
{
@ -45,6 +46,10 @@ namespace IKVM.Reflection.Writer
this.cliHeader = cliHeader;
this.moduleBuilder = moduleBuilder;
this.strongNameSignatureLength = (uint)strongNameSignatureLength;
if (moduleBuilder.unmanagedExports.Count != 0)
{
this.exportTables = new ExportTables(this);
}
}
internal uint PointerToRawData
@ -186,10 +191,30 @@ namespace IKVM.Reflection.Writer
}
}
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 (DebugDirectoryRVA + DebugDirectoryLength + DebugDirectoryContentsLength + 15) & ~15U; }
get { return (ExportTablesRVA + ExportTablesLength + 15) & ~15U; }
}
internal uint ImportDirectoryLength
@ -351,7 +376,21 @@ namespace IKVM.Reflection.Writer
WriteDebugDirectory(mw);
// alignment padding
for (int i = (int)(ImportDirectoryRVA - (DebugDirectoryRVA + DebugDirectoryLength + DebugDirectoryContentsLength)); i > 0; i--)
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);
}
@ -431,6 +470,277 @@ namespace IKVM.Reflection.Writer
}
}
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:
stubLength = 16;
break;
default:
throw new NotImplementedException();
}
}
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(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.Write(Encoding.ASCII.GetBytes(text.moduleBuilder.fileName));
mw.Write((byte)0);
foreach (UnmanagedExport exp in text.moduleBuilder.unmanagedExports)
{
if (exp.name != null)
{
mw.Write(Encoding.ASCII.GetBytes(exp.name));
mw.Write((byte)0);
}
}
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;
default:
throw new NotImplementedException();
}
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 x.name.CompareTo(y.name);
}
private static int CompareUnmanagedExportOrdinals(UnmanagedExport x, UnmanagedExport y)
{
return x.ordinal.CompareTo(y.ordinal);
}
internal void WriteRelocations(MetadataWriter mw)
{
// 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)
{
// both I386 and AMD64 have the address at offset 2
text.WriteRelocationBlock(mw, stubsRVA + 2 + (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
@ -476,5 +786,48 @@ namespace IKVM.Reflection.Writer
{
get { return (int)(StartupStubRVA - BaseRVA + StartupStubLength); }
}
internal void WriteRelocations(MetadataWriter mw)
{
uint relocAddress = this.StartupStubRVA;
switch (peWriter.Headers.FileHeader.Machine)
{
case IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_I386:
case IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_AMD64:
relocAddress += 2;
break;
case IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_IA64:
relocAddress += 0x20;
break;
}
WriteRelocationBlock(mw, relocAddress);
if (exportTables != null)
{
exportTables.WriteRelocations(mw);
}
}
// note that we're lazy and write a new relocation block for every relocation
// even if they are in the same page (since there is typically only one anyway)
private void WriteRelocationBlock(MetadataWriter mw, uint relocAddress)
{
uint pageRVA = relocAddress & ~0xFFFU;
mw.Write(pageRVA); // PageRVA
mw.Write(0x000C); // Block Size
switch (peWriter.Headers.FileHeader.Machine)
{
case IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_I386:
mw.Write(0x3000 + relocAddress - pageRVA); // Type / Offset
break;
case IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_AMD64:
mw.Write(0xA000 + relocAddress - pageRVA); // Type / Offset
break;
case IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_IA64:
// on IA64 the StartupStubRVA is 16 byte aligned, so these two addresses won't cross a page boundary
mw.Write((short)(0xA000 + relocAddress - pageRVA)); // Type / Offset
mw.Write((short)(0xA000 + relocAddress - pageRVA + 8)); // Type / Offset
break;
}
}
}
}