зеркало из https://github.com/mono/ikvm-fork.git
Added API extension ModuleBuilder.__AddUnmanagedExportStub().
This commit is contained in:
Родитель
1d75ea1fe9
Коммит
72ca5c45b3
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче