obfuz/Plugins/dnlib/DotNet/Writer/PreserveTokensMetadata.cs

1233 lines
40 KiB
C#

// dnlib: See LICENSE.txt for more info
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using dnlib.DotNet.MD;
namespace dnlib.DotNet.Writer {
/// <summary>
/// Preserves metadata tokens
/// </summary>
sealed class PreserveTokensMetadata : Metadata {
readonly ModuleDefMD mod;
readonly Rows<TypeRef> typeRefInfos = new Rows<TypeRef>();
readonly Dictionary<TypeDef, uint> typeToRid = new Dictionary<TypeDef, uint>();
MemberDefDict<FieldDef> fieldDefInfos;
MemberDefDict<MethodDef> methodDefInfos;
MemberDefDict<ParamDef> paramDefInfos;
readonly Rows<MemberRef> memberRefInfos = new Rows<MemberRef>();
readonly Rows<StandAloneSig> standAloneSigInfos = new Rows<StandAloneSig>();
MemberDefDict<EventDef> eventDefInfos;
MemberDefDict<PropertyDef> propertyDefInfos;
readonly Rows<TypeSpec> typeSpecInfos = new Rows<TypeSpec>();
readonly Rows<MethodSpec> methodSpecInfos = new Rows<MethodSpec>();
readonly Dictionary<uint, uint> callConvTokenToSignature = new Dictionary<uint, uint>();
[DebuggerDisplay("{Rid} -> {NewRid} {Def}")]
sealed class MemberDefInfo<T> where T : IMDTokenProvider {
public readonly T Def;
/// <summary>
/// Its real rid
/// </summary>
public uint Rid;
/// <summary>
/// Its logical rid or real rid. If the ptr table exists (eg. MethodPtr), then it's
/// an index into it, else it's the real rid.
/// </summary>
public uint NewRid;
public MemberDefInfo(T def, uint rid) {
Def = def;
Rid = rid;
NewRid = rid;
}
}
[DebuggerDisplay("Count = {Count}")]
sealed class MemberDefDict<T> where T : IMDTokenProvider {
readonly Type defMDType;
uint userRid = 0x01000000;
uint newRid = 1;
int numDefMDs;
int numDefUsers;
int tableSize;
bool wasSorted;
readonly bool preserveRids;
readonly bool enableRidToInfo;
readonly Dictionary<T, MemberDefInfo<T>> defToInfo = new Dictionary<T, MemberDefInfo<T>>();
Dictionary<uint, MemberDefInfo<T>> ridToInfo;
readonly List<MemberDefInfo<T>> defs = new List<MemberDefInfo<T>>();
List<MemberDefInfo<T>> sortedDefs;
readonly Dictionary<T, int> collectionPositions = new Dictionary<T, int>();
/// <summary>
/// Gets total number of defs in the list. It does <c>not</c> necessarily return
/// the table size. Use <see cref="TableSize"/> for that.
/// </summary>
public int Count => defs.Count;
/// <summary>
/// Gets the number of rows that need to be created in the table
/// </summary>
public int TableSize => tableSize;
/// <summary>
/// Returns <c>true</c> if the ptr table (eg. <c>MethodPtr</c>) is needed
/// </summary>
public bool NeedPtrTable => preserveRids && !wasSorted;
public MemberDefDict(Type defMDType, bool preserveRids)
: this(defMDType, preserveRids, false) {
}
public MemberDefDict(Type defMDType, bool preserveRids, bool enableRidToInfo) {
this.defMDType = defMDType;
this.preserveRids = preserveRids;
this.enableRidToInfo = enableRidToInfo;
}
public uint Rid(T def) => defToInfo[def].Rid;
public bool TryGetRid(T def, out uint rid) {
if (def == null || !defToInfo.TryGetValue(def, out var info)) {
rid = 0;
return false;
}
rid = info.Rid;
return true;
}
/// <summary>
/// Sorts the table
/// </summary>
/// <param name="comparer">Comparer</param>
public void Sort(Comparison<MemberDefInfo<T>> comparer) {
if (!preserveRids) {
// It's already sorted
sortedDefs = defs;
return;
}
sortedDefs = new List<MemberDefInfo<T>>(defs);
sortedDefs.Sort(comparer);
wasSorted = true;
for (int i = 0; i < sortedDefs.Count; i++) {
var def = sortedDefs[i];
uint newRid = (uint)i + 1;
def.NewRid = newRid;
if (def.Rid != newRid)
wasSorted = false;
}
}
public MemberDefInfo<T> Get(int i) => defs[i];
public MemberDefInfo<T> GetSorted(int i) => sortedDefs[i];
public MemberDefInfo<T> GetByRid(uint rid) {
ridToInfo.TryGetValue(rid, out var info);
return info;
}
/// <summary>
/// Adds a def. <see cref="SortDefs()"/> must be called after adding the last def.
/// </summary>
/// <param name="def">The def</param>
/// <param name="collPos">Collection position</param>
public void Add(T def, int collPos) {
uint rid;
if (def.GetType() == defMDType) {
numDefMDs++;
rid = preserveRids ? def.Rid : newRid++;
}
else {
numDefUsers++;
rid = preserveRids ? userRid++ : newRid++;
}
var info = new MemberDefInfo<T>(def, rid);
defToInfo[def] = info;
defs.Add(info);
collectionPositions.Add(def, collPos);
}
/// <summary>
/// Must be called after <see cref="Add"/>'ing the last def
/// </summary>
public void SortDefs() {
// It's already sorted if we don't preserve rids
if (preserveRids) {
// Sort all def MDs before user defs
defs.Sort((a, b) => a.Rid.CompareTo(b.Rid));
// Fix user created defs' rids
uint newRid = numDefMDs == 0 ? 1 : defs[numDefMDs - 1].Rid + 1;
for (int i = numDefMDs; i < defs.Count; i++)
defs[i].Rid = newRid++;
// Now we know total table size
tableSize = (int)newRid - 1;
}
else
tableSize = defs.Count;
if (enableRidToInfo) {
ridToInfo = new Dictionary<uint, MemberDefInfo<T>>(defs.Count);
foreach (var info in defs)
ridToInfo.Add(info.Rid, info);
}
if ((uint)tableSize > 0x00FFFFFF)
throw new ModuleWriterException("Table is too big");
}
public int GetCollectionPosition(T def) => collectionPositions[def];
}
protected override int NumberOfMethods => methodDefInfos.Count;
public PreserveTokensMetadata(ModuleDef module, UniqueChunkList<ByteArrayChunk> constants, MethodBodyChunks methodBodies, NetResources netResources, MetadataOptions options, DebugMetadataKind debugKind, bool isStandaloneDebugMetadata)
: base(module, constants, methodBodies, netResources, options, debugKind, isStandaloneDebugMetadata) {
mod = module as ModuleDefMD;
if (mod is null)
throw new ModuleWriterException("Not a ModuleDefMD");
}
/// <inheritdoc/>
public override uint GetRid(TypeRef tr) {
typeRefInfos.TryGetRid(tr, out uint rid);
return rid;
}
/// <inheritdoc/>
public override uint GetRid(TypeDef td) {
if (td is null) {
Error("TypeDef is null");
return 0;
}
if (typeToRid.TryGetValue(td, out uint rid))
return rid;
Error("TypeDef '{0}' (0x{1:X8}) is not defined in this module '{2}'. A type was removed that is still referenced by this module.", td, td.MDToken.Raw, module);
return 0;
}
/// <inheritdoc/>
public override uint GetRid(FieldDef fd) {
if (fieldDefInfos.TryGetRid(fd, out uint rid))
return rid;
if (fd is null)
Error("Field is null");
else
Error("Field '{0}' (0x{1:X8}) is not defined in this module '{2}'. A field was removed that is still referenced by this module.", fd, fd.MDToken.Raw, module);
return 0;
}
/// <inheritdoc/>
public override uint GetRid(MethodDef md) {
if (methodDefInfos.TryGetRid(md, out uint rid))
return rid;
if (md is null)
Error("Method is null");
else
Error("Method '{0}' (0x{1:X8}) is not defined in this module '{2}'. A method was removed that is still referenced by this module.", md, md.MDToken.Raw, module);
return 0;
}
/// <inheritdoc/>
public override uint GetRid(ParamDef pd) {
if (paramDefInfos.TryGetRid(pd, out uint rid))
return rid;
if (pd is null)
Error("Param is null");
else
Error("Param '{0}' (0x{1:X8}) is not defined in this module '{2}'. A parameter was removed that is still referenced by this module.", pd, pd.MDToken.Raw, module);
return 0;
}
/// <inheritdoc/>
public override uint GetRid(MemberRef mr) {
memberRefInfos.TryGetRid(mr, out uint rid);
return rid;
}
/// <inheritdoc/>
public override uint GetRid(StandAloneSig sas) {
standAloneSigInfos.TryGetRid(sas, out uint rid);
return rid;
}
/// <inheritdoc/>
public override uint GetRid(EventDef ed) {
if (eventDefInfos.TryGetRid(ed, out uint rid))
return rid;
if (ed is null)
Error("Event is null");
else
Error("Event '{0}' (0x{1:X8}) is not defined in this module '{2}'. An event was removed that is still referenced by this module.", ed, ed.MDToken.Raw, module);
return 0;
}
/// <inheritdoc/>
public override uint GetRid(PropertyDef pd) {
if (propertyDefInfos.TryGetRid(pd, out uint rid))
return rid;
if (pd is null)
Error("Property is null");
else
Error("Property '{0}' (0x{1:X8}) is not defined in this module '{2}'. A property was removed that is still referenced by this module.", pd, pd.MDToken.Raw, module);
return 0;
}
/// <inheritdoc/>
public override uint GetRid(TypeSpec ts) {
typeSpecInfos.TryGetRid(ts, out uint rid);
return rid;
}
/// <inheritdoc/>
public override uint GetRid(MethodSpec ms) {
methodSpecInfos.TryGetRid(ms, out uint rid);
return rid;
}
/// <inheritdoc/>
protected override void Initialize() {
fieldDefInfos = new MemberDefDict<FieldDef>(typeof(FieldDefMD), PreserveFieldRids);
methodDefInfos = new MemberDefDict<MethodDef>(typeof(MethodDefMD), PreserveMethodRids, true);
paramDefInfos = new MemberDefDict<ParamDef>(typeof(ParamDefMD), PreserveParamRids);
eventDefInfos = new MemberDefDict<EventDef>(typeof(EventDefMD), PreserveEventRids);
propertyDefInfos = new MemberDefDict<PropertyDef>(typeof(PropertyDefMD), PreservePropertyRids);
CreateEmptyTableRows();
}
/// <inheritdoc/>
protected override TypeDef[] GetAllTypeDefs() {
if (!PreserveTypeDefRids) {
var types2 = module.GetTypes().ToArray();
InitializeTypeToRid(types2);
return types2;
}
var typeToIndex = new Dictionary<TypeDef, uint>();
var types = new List<TypeDef>();
uint index = 0;
const uint IS_TYPEDEFMD = 0x80000000;
const uint INDEX_BITS = 0x00FFFFFF;
foreach (var type in module.GetTypes()) {
if (type is null)
continue;
types.Add(type);
uint val = (uint)index++;
if (type.GetType() == typeof(TypeDefMD))
val |= IS_TYPEDEFMD;
typeToIndex[type] = val;
}
var globalType = types[0];
types.Sort((a, b) => {
if (a == b)
return 0;
// Make sure the global <Module> type is always sorted first, even if it's
// a TypeDefUser
if (a == globalType)
return -1;
if (b == globalType)
return 1;
// Sort all TypeDefMDs before all TypeDefUsers
uint ai = typeToIndex[a];
uint bi = typeToIndex[b];
bool amd = (ai & IS_TYPEDEFMD) != 0;
bool bmd = (bi & IS_TYPEDEFMD) != 0;
if (amd == bmd) { // Both are TypeDefMDs or both are TypeDefUsers
// If TypeDefMDs, only compare rids since rids are preserved
if (amd)
return a.Rid.CompareTo(b.Rid);
// If TypeDefUsers, rids aren't preserved so compare by index
return (ai & INDEX_BITS).CompareTo(bi & INDEX_BITS);
}
if (amd)
return -1;
return 1;
});
// Some of the original types may have been removed. Create dummy types
// so TypeDef rids can be preserved.
var newTypes = new List<TypeDef>(types.Count);
uint prevRid = 1;
newTypes.Add(globalType);
for (int i = 1; i < types.Count; i++) {
var type = types[i];
// TypeDefUsers were sorted last so when we reach one, we can stop
if (type.GetType() != typeof(TypeDefMD)) {
while (i < types.Count)
newTypes.Add(types[i++]);
break;
}
uint currRid = type.Rid;
int extraTypes = (int)(currRid - prevRid - 1);
if (extraTypes != 0) { // always >= 0 since currRid > prevRid
// At least one type has been removed. Create dummy types.
for (int j = 0; j < extraTypes; j++)
newTypes.Add(new TypeDefUser("dummy", Guid.NewGuid().ToString("B"), module.CorLibTypes.Object.TypeDefOrRef));
}
newTypes.Add(type);
prevRid = currRid;
}
var newTypesArray = newTypes.ToArray();
InitializeTypeToRid(newTypesArray);
return newTypesArray;
}
void InitializeTypeToRid(TypeDef[] types) {
uint rid = 1;
foreach (var type in types) {
if (type is null)
continue;
if (typeToRid.ContainsKey(type))
continue;
typeToRid[type] = rid++;
}
}
/// <inheritdoc/>
protected override void AllocateTypeDefRids() {
foreach (var type in allTypeDefs) {
uint rid = tablesHeap.TypeDefTable.Create(new RawTypeDefRow());
if (typeToRid[type] != rid)
throw new ModuleWriterException("Got a different rid than expected");
}
}
/// <summary>
/// Reserves rows in <c>TypeRef</c>, <c>MemberRef</c>, <c>StandAloneSig</c>,
/// <c>TypeSpec</c> and <c>MethodSpec</c> where we will store the original rows
/// to make sure they get the same rid. Any user created rows will be stored at
/// the end of each table.
/// </summary>
void CreateEmptyTableRows() {
uint rows;
if (PreserveTypeRefRids) {
rows = mod.TablesStream.TypeRefTable.Rows;
for (uint i = 0; i < rows; i++)
tablesHeap.TypeRefTable.Create(new RawTypeRefRow());
}
if (PreserveMemberRefRids) {
rows = mod.TablesStream.MemberRefTable.Rows;
for (uint i = 0; i < rows; i++)
tablesHeap.MemberRefTable.Create(new RawMemberRefRow());
}
if (PreserveStandAloneSigRids) {
rows = mod.TablesStream.StandAloneSigTable.Rows;
for (uint i = 0; i < rows; i++)
tablesHeap.StandAloneSigTable.Create(new RawStandAloneSigRow());
}
if (PreserveTypeSpecRids) {
rows = mod.TablesStream.TypeSpecTable.Rows;
for (uint i = 0; i < rows; i++)
tablesHeap.TypeSpecTable.Create(new RawTypeSpecRow());
}
if (PreserveMethodSpecRids) {
rows = mod.TablesStream.MethodSpecTable.Rows;
for (uint i = 0; i < rows; i++)
tablesHeap.MethodSpecTable.Create(new RawMethodSpecRow());
}
}
/// <summary>
/// Adds any non-referenced rows that haven't been added yet but are present in
/// the original file. If there are any non-referenced rows, it's usually a sign
/// that an obfuscator has encrypted one or more methods or that it has added
/// some rows it uses to decrypt something.
/// </summary>
void InitializeUninitializedTableRows() {
InitializeTypeRefTableRows();
InitializeMemberRefTableRows();
InitializeStandAloneSigTableRows();
InitializeTypeSpecTableRows();
InitializeMethodSpecTableRows();
}
bool initdTypeRef = false;
void InitializeTypeRefTableRows() {
if (!PreserveTypeRefRids || initdTypeRef)
return;
initdTypeRef = true;
uint rows = mod.TablesStream.TypeRefTable.Rows;
for (uint rid = 1; rid <= rows; rid++)
AddTypeRef(mod.ResolveTypeRef(rid));
tablesHeap.TypeRefTable.ReAddRows();
}
bool initdMemberRef = false;
void InitializeMemberRefTableRows() {
if (!PreserveMemberRefRids || initdMemberRef)
return;
initdMemberRef = true;
uint rows = mod.TablesStream.MemberRefTable.Rows;
for (uint rid = 1; rid <= rows; rid++) {
if (tablesHeap.MemberRefTable[rid].Class != 0)
continue;
AddMemberRef(mod.ResolveMemberRef(rid), true);
}
tablesHeap.MemberRefTable.ReAddRows();
}
bool initdStandAloneSig = false;
void InitializeStandAloneSigTableRows() {
if (!PreserveStandAloneSigRids || initdStandAloneSig)
return;
initdStandAloneSig = true;
uint rows = mod.TablesStream.StandAloneSigTable.Rows;
for (uint rid = 1; rid <= rows; rid++) {
if (tablesHeap.StandAloneSigTable[rid].Signature != 0)
continue;
AddStandAloneSig(mod.ResolveStandAloneSig(rid), true);
}
tablesHeap.StandAloneSigTable.ReAddRows();
}
bool initdTypeSpec = false;
void InitializeTypeSpecTableRows() {
if (!PreserveTypeSpecRids || initdTypeSpec)
return;
initdTypeSpec = true;
uint rows = mod.TablesStream.TypeSpecTable.Rows;
for (uint rid = 1; rid <= rows; rid++) {
if (tablesHeap.TypeSpecTable[rid].Signature != 0)
continue;
AddTypeSpec(mod.ResolveTypeSpec(rid), true);
}
tablesHeap.TypeSpecTable.ReAddRows();
}
bool initdMethodSpec = false;
void InitializeMethodSpecTableRows() {
if (!PreserveMethodSpecRids || initdMethodSpec)
return;
initdMethodSpec = true;
uint rows = mod.TablesStream.MethodSpecTable.Rows;
for (uint rid = 1; rid <= rows; rid++) {
if (tablesHeap.MethodSpecTable[rid].Method != 0)
continue;
AddMethodSpec(mod.ResolveMethodSpec(rid), true);
}
tablesHeap.MethodSpecTable.ReAddRows();
}
/// <inheritdoc/>
protected override void AllocateMemberDefRids() {
FindMemberDefs();
const int numEvents = 5;
RaiseProgress(Writer.MetadataEvent.AllocateMemberDefRids, 0.0 / numEvents);
for (int i = 1; i <= fieldDefInfos.TableSize; i++) {
if ((uint)i != tablesHeap.FieldTable.Create(new RawFieldRow()))
throw new ModuleWriterException("Invalid field rid");
}
for (int i = 1; i <= methodDefInfos.TableSize; i++) {
if ((uint)i != tablesHeap.MethodTable.Create(new RawMethodRow()))
throw new ModuleWriterException("Invalid method rid");
}
for (int i = 1; i <= paramDefInfos.TableSize; i++) {
if ((uint)i != tablesHeap.ParamTable.Create(new RawParamRow()))
throw new ModuleWriterException("Invalid param rid");
}
for (int i = 1; i <= eventDefInfos.TableSize; i++) {
if ((uint)i != tablesHeap.EventTable.Create(new RawEventRow()))
throw new ModuleWriterException("Invalid event rid");
}
for (int i = 1; i <= propertyDefInfos.TableSize; i++) {
if ((uint)i != tablesHeap.PropertyTable.Create(new RawPropertyRow()))
throw new ModuleWriterException("Invalid property rid");
}
SortFields();
SortMethods();
SortParameters();
SortEvents();
SortProperties();
RaiseProgress(Writer.MetadataEvent.AllocateMemberDefRids, 1.0 / numEvents);
if (fieldDefInfos.NeedPtrTable) {
for (int i = 0; i < fieldDefInfos.Count; i++) {
var info = fieldDefInfos.GetSorted(i);
if ((uint)i + 1 != tablesHeap.FieldPtrTable.Add(new RawFieldPtrRow(info.Rid)))
throw new ModuleWriterException("Invalid field ptr rid");
}
ReUseDeletedFieldRows();
}
if (methodDefInfos.NeedPtrTable) {
for (int i = 0; i < methodDefInfos.Count; i++) {
var info = methodDefInfos.GetSorted(i);
if ((uint)i + 1 != tablesHeap.MethodPtrTable.Add(new RawMethodPtrRow(info.Rid)))
throw new ModuleWriterException("Invalid method ptr rid");
}
ReUseDeletedMethodRows();
}
if (paramDefInfos.NeedPtrTable) {
// NOTE: peverify does not support the ParamPtr table. It's a bug.
for (int i = 0; i < paramDefInfos.Count; i++) {
var info = paramDefInfos.GetSorted(i);
if ((uint)i + 1 != tablesHeap.ParamPtrTable.Add(new RawParamPtrRow(info.Rid)))
throw new ModuleWriterException("Invalid param ptr rid");
}
ReUseDeletedParamRows();
}
if (eventDefInfos.NeedPtrTable) {
for (int i = 0; i < eventDefInfos.Count; i++) {
var info = eventDefInfos.GetSorted(i);
if ((uint)i + 1 != tablesHeap.EventPtrTable.Add(new RawEventPtrRow(info.Rid)))
throw new ModuleWriterException("Invalid event ptr rid");
}
}
if (propertyDefInfos.NeedPtrTable) {
for (int i = 0; i < propertyDefInfos.Count; i++) {
var info = propertyDefInfos.GetSorted(i);
if ((uint)i + 1 != tablesHeap.PropertyPtrTable.Add(new RawPropertyPtrRow(info.Rid)))
throw new ModuleWriterException("Invalid property ptr rid");
}
}
RaiseProgress(Writer.MetadataEvent.AllocateMemberDefRids, 2.0 / numEvents);
InitializeMethodAndFieldList();
InitializeParamList();
InitializeEventMap();
InitializePropertyMap();
RaiseProgress(Writer.MetadataEvent.AllocateMemberDefRids, 3.0 / numEvents);
// We must re-use deleted event/property rows after we've initialized
// the event/prop map tables.
if (eventDefInfos.NeedPtrTable)
ReUseDeletedEventRows();
if (propertyDefInfos.NeedPtrTable)
ReUseDeletedPropertyRows();
RaiseProgress(Writer.MetadataEvent.AllocateMemberDefRids, 4.0 / numEvents);
InitializeTypeRefTableRows();
InitializeTypeSpecTableRows();
InitializeMemberRefTableRows();
InitializeMethodSpecTableRows();
}
/// <summary>
/// Re-uses all <c>Field</c> rows which aren't owned by any type due to the fields
/// having been deleted by the user. The reason we must do this is that the
/// <c>FieldPtr</c> and <c>Field</c> tables must be the same size.
/// </summary>
void ReUseDeletedFieldRows() {
if (tablesHeap.FieldPtrTable.IsEmpty)
return;
if (fieldDefInfos.TableSize == tablesHeap.FieldPtrTable.Rows)
return;
var hasOwner = new bool[fieldDefInfos.TableSize];
for (int i = 0; i < fieldDefInfos.Count; i++)
hasOwner[(int)fieldDefInfos.Get(i).Rid - 1] = true;
CreateDummyPtrTableType();
uint fieldSig = GetSignature(new FieldSig(module.CorLibTypes.Byte));
for (int i = 0; i < hasOwner.Length; i++) {
if (hasOwner[i])
continue;
uint frid = (uint)i + 1;
var frow = new RawFieldRow((ushort)(FieldAttributes.Public | FieldAttributes.Static), stringsHeap.Add($"f{frid:X6}"), fieldSig);
tablesHeap.FieldTable[frid] = frow;
tablesHeap.FieldPtrTable.Create(new RawFieldPtrRow(frid));
}
if (fieldDefInfos.TableSize != tablesHeap.FieldPtrTable.Rows)
throw new ModuleWriterException("Didn't create all dummy fields");
}
/// <summary>
/// Re-uses all <c>Method</c> rows which aren't owned by any type due to the methods
/// having been deleted by the user. The reason we must do this is that the
/// <c>MethodPtr</c> and <c>Method</c> tables must be the same size.
/// </summary>
void ReUseDeletedMethodRows() {
if (tablesHeap.MethodPtrTable.IsEmpty)
return;
if (methodDefInfos.TableSize == tablesHeap.MethodPtrTable.Rows)
return;
var hasOwner = new bool[methodDefInfos.TableSize];
for (int i = 0; i < methodDefInfos.Count; i++)
hasOwner[(int)methodDefInfos.Get(i).Rid - 1] = true;
CreateDummyPtrTableType();
uint methodSig = GetSignature(MethodSig.CreateInstance(module.CorLibTypes.Void));
for (int i = 0; i < hasOwner.Length; i++) {
if (hasOwner[i])
continue;
uint mrid = (uint)i + 1;
var mrow = new RawMethodRow(0, (ushort)(MethodImplAttributes.IL | MethodImplAttributes.Managed),
(ushort)(MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Abstract),
stringsHeap.Add($"m{mrid:X6}"), methodSig, (uint)paramDefInfos.Count);
tablesHeap.MethodTable[mrid] = mrow;
tablesHeap.MethodPtrTable.Create(new RawMethodPtrRow(mrid));
}
if (methodDefInfos.TableSize != tablesHeap.MethodPtrTable.Rows)
throw new ModuleWriterException("Didn't create all dummy methods");
}
/// <summary>
/// Re-uses all <c>Param</c> rows which aren't owned by any type due to the params
/// having been deleted by the user. The reason we must do this is that the
/// <c>ParamPtr</c> and <c>Param</c> tables must be the same size.
/// This method must be called after <see cref="ReUseDeletedMethodRows()"/> since
/// this method will create more methods at the end of the <c>Method</c> table.
/// </summary>
void ReUseDeletedParamRows() {
if (tablesHeap.ParamPtrTable.IsEmpty)
return;
if (paramDefInfos.TableSize == tablesHeap.ParamPtrTable.Rows)
return;
var hasOwner = new bool[paramDefInfos.TableSize];
for (int i = 0; i < paramDefInfos.Count; i++)
hasOwner[(int)paramDefInfos.Get(i).Rid - 1] = true;
CreateDummyPtrTableType();
// For each param, attach it to a new method. Another alternative would be to create
// one (or a few) methods with tons of parameters.
uint methodSig = GetSignature(MethodSig.CreateInstance(module.CorLibTypes.Void));
for (int i = 0; i < hasOwner.Length; i++) {
if (hasOwner[i])
continue;
uint prid = (uint)i + 1;
var prow = new RawParamRow(0, 0, stringsHeap.Add($"p{prid:X6}"));
tablesHeap.ParamTable[prid] = prow;
uint ptrRid = tablesHeap.ParamPtrTable.Create(new RawParamPtrRow(prid));
var mrow = new RawMethodRow(0,
(ushort)(MethodImplAttributes.IL | MethodImplAttributes.Managed),
(ushort)(MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Abstract),
stringsHeap.Add($"mp{prid:X6}"),
methodSig,
ptrRid);
uint mrid = tablesHeap.MethodTable.Create(mrow);
if (tablesHeap.MethodPtrTable.Rows > 0)
tablesHeap.MethodPtrTable.Create(new RawMethodPtrRow(mrid));
}
if (paramDefInfos.TableSize != tablesHeap.ParamPtrTable.Rows)
throw new ModuleWriterException("Didn't create all dummy params");
}
/// <summary>
/// Re-uses all <c>Event</c> rows which aren't owned by any type due to the events
/// having been deleted by the user. The reason we must do this is that the
/// <c>EventPtr</c> and <c>Event</c> tables must be the same size.
/// </summary>
void ReUseDeletedEventRows() {
if (tablesHeap.EventPtrTable.IsEmpty)
return;
if (eventDefInfos.TableSize == tablesHeap.EventPtrTable.Rows)
return;
var hasOwner = new bool[eventDefInfos.TableSize];
for (int i = 0; i < eventDefInfos.Count; i++)
hasOwner[(int)eventDefInfos.Get(i).Rid - 1] = true;
uint typeRid = CreateDummyPtrTableType();
tablesHeap.EventMapTable.Create(new RawEventMapRow(typeRid, (uint)tablesHeap.EventPtrTable.Rows + 1));
uint eventType = AddTypeDefOrRef(module.CorLibTypes.Object.TypeDefOrRef);
for (int i = 0; i < hasOwner.Length; i++) {
if (hasOwner[i])
continue;
uint erid = (uint)i + 1;
var frow = new RawEventRow(0, stringsHeap.Add($"E{erid:X6}"), eventType);
tablesHeap.EventTable[erid] = frow;
tablesHeap.EventPtrTable.Create(new RawEventPtrRow(erid));
}
if (eventDefInfos.TableSize != tablesHeap.EventPtrTable.Rows)
throw new ModuleWriterException("Didn't create all dummy events");
}
/// <summary>
/// Re-uses all <c>Property</c> rows which aren't owned by any type due to the properties
/// having been deleted by the user. The reason we must do this is that the
/// <c>PropertyPtr</c> and <c>Property</c> tables must be the same size.
/// </summary>
void ReUseDeletedPropertyRows() {
if (tablesHeap.PropertyPtrTable.IsEmpty)
return;
if (propertyDefInfos.TableSize == tablesHeap.PropertyPtrTable.Rows)
return;
var hasOwner = new bool[propertyDefInfos.TableSize];
for (int i = 0; i < propertyDefInfos.Count; i++)
hasOwner[(int)propertyDefInfos.Get(i).Rid - 1] = true;
uint typeRid = CreateDummyPtrTableType();
tablesHeap.PropertyMapTable.Create(new RawPropertyMapRow(typeRid, (uint)tablesHeap.PropertyPtrTable.Rows + 1));
uint propertySig = GetSignature(PropertySig.CreateStatic(module.CorLibTypes.Object));
for (int i = 0; i < hasOwner.Length; i++) {
if (hasOwner[i])
continue;
uint prid = (uint)i + 1;
var frow = new RawPropertyRow(0, stringsHeap.Add($"P{prid:X6}"), propertySig);
tablesHeap.PropertyTable[prid] = frow;
tablesHeap.PropertyPtrTable.Create(new RawPropertyPtrRow(prid));
}
if (propertyDefInfos.TableSize != tablesHeap.PropertyPtrTable.Rows)
throw new ModuleWriterException("Didn't create all dummy properties");
}
/// <summary>
/// Creates a dummy <c>TypeDef</c> at the end of the <c>TypeDef</c> table that will own
/// dummy methods and fields. These dummy methods and fields are only created if the size
/// of the ptr table is less than the size of the non-ptr table (eg. size MethodPtr table
/// is less than size Method table). The only reason the ptr table would be smaller than
/// the non-ptr table is when some field/method has been deleted and we must preserve
/// all method/field rids.
/// </summary>
uint CreateDummyPtrTableType() {
if (dummyPtrTableTypeRid != 0)
return dummyPtrTableTypeRid;
var flags = TypeAttributes.NotPublic | TypeAttributes.AutoLayout |
TypeAttributes.Class | TypeAttributes.Abstract | TypeAttributes.AnsiClass;
int numFields = fieldDefInfos.NeedPtrTable ? fieldDefInfos.Count : fieldDefInfos.TableSize;
int numMethods = methodDefInfos.NeedPtrTable ? methodDefInfos.Count : methodDefInfos.TableSize;
var row = new RawTypeDefRow((uint)flags,
stringsHeap.Add(Guid.NewGuid().ToString("B")),
stringsHeap.Add("dummy_ptr"),
AddTypeDefOrRef(module.CorLibTypes.Object.TypeDefOrRef),
(uint)numFields + 1,
(uint)numMethods + 1);
dummyPtrTableTypeRid = tablesHeap.TypeDefTable.Create(row);
if (dummyPtrTableTypeRid == 1)
throw new ModuleWriterException("Dummy ptr type is the first type");
return dummyPtrTableTypeRid;
}
uint dummyPtrTableTypeRid;
void FindMemberDefs() {
int count;
var added = new Dictionary<object, bool>();
int pos;
foreach (var type in allTypeDefs) {
if (type is null)
continue;
pos = 0;
var fields = type.Fields;
count = fields.Count;
for (int i = 0; i < count; i++) {
var field = fields[i];
if (field is null)
continue;
fieldDefInfos.Add(field, pos++);
}
pos = 0;
var methods = type.Methods;
count = methods.Count;
for (int i = 0; i < count; i++) {
var method = methods[i];
if (method is null)
continue;
methodDefInfos.Add(method, pos++);
}
pos = 0;
var events = type.Events;
count = events.Count;
for (int i = 0; i < count; i++) {
var evt = events[i];
if (evt is null || added.ContainsKey(evt))
continue;
added[evt] = true;
eventDefInfos.Add(evt, pos++);
}
pos = 0;
var properties = type.Properties;
count = properties.Count;
for (int i = 0; i < count; i++) {
var prop = properties[i];
if (prop is null || added.ContainsKey(prop))
continue;
added[prop] = true;
propertyDefInfos.Add(prop, pos++);
}
}
fieldDefInfos.SortDefs();
methodDefInfos.SortDefs();
eventDefInfos.SortDefs();
propertyDefInfos.SortDefs();
for (int i = 0; i < methodDefInfos.Count; i++) {
var method = methodDefInfos.Get(i).Def;
pos = 0;
foreach (var param in Sort(method.ParamDefs)) {
if (param is null)
continue;
paramDefInfos.Add(param, pos++);
}
}
paramDefInfos.SortDefs();
}
void SortFields() =>
fieldDefInfos.Sort((a, b) => {
var dta = a.Def.DeclaringType is null ? 0 : typeToRid[a.Def.DeclaringType];
var dtb = b.Def.DeclaringType is null ? 0 : typeToRid[b.Def.DeclaringType];
if (dta == 0 || dtb == 0)
return a.Rid.CompareTo(b.Rid);
if (dta != dtb)
return dta.CompareTo(dtb);
return fieldDefInfos.GetCollectionPosition(a.Def).CompareTo(fieldDefInfos.GetCollectionPosition(b.Def));
});
void SortMethods() =>
methodDefInfos.Sort((a, b) => {
var dta = a.Def.DeclaringType is null ? 0 : typeToRid[a.Def.DeclaringType];
var dtb = b.Def.DeclaringType is null ? 0 : typeToRid[b.Def.DeclaringType];
if (dta == 0 || dtb == 0)
return a.Rid.CompareTo(b.Rid);
if (dta != dtb)
return dta.CompareTo(dtb);
return methodDefInfos.GetCollectionPosition(a.Def).CompareTo(methodDefInfos.GetCollectionPosition(b.Def));
});
void SortParameters() =>
paramDefInfos.Sort((a, b) => {
var dma = a.Def.DeclaringMethod is null ? 0 : methodDefInfos.Rid(a.Def.DeclaringMethod);
var dmb = b.Def.DeclaringMethod is null ? 0 : methodDefInfos.Rid(b.Def.DeclaringMethod);
if (dma == 0 || dmb == 0)
return a.Rid.CompareTo(b.Rid);
if (dma != dmb)
return dma.CompareTo(dmb);
return paramDefInfos.GetCollectionPosition(a.Def).CompareTo(paramDefInfos.GetCollectionPosition(b.Def));
});
void SortEvents() =>
eventDefInfos.Sort((a, b) => {
var dta = a.Def.DeclaringType is null ? 0 : typeToRid[a.Def.DeclaringType];
var dtb = b.Def.DeclaringType is null ? 0 : typeToRid[b.Def.DeclaringType];
if (dta == 0 || dtb == 0)
return a.Rid.CompareTo(b.Rid);
if (dta != dtb)
return dta.CompareTo(dtb);
return eventDefInfos.GetCollectionPosition(a.Def).CompareTo(eventDefInfos.GetCollectionPosition(b.Def));
});
void SortProperties() =>
propertyDefInfos.Sort((a, b) => {
var dta = a.Def.DeclaringType is null ? 0 : typeToRid[a.Def.DeclaringType];
var dtb = b.Def.DeclaringType is null ? 0 : typeToRid[b.Def.DeclaringType];
if (dta == 0 || dtb == 0)
return a.Rid.CompareTo(b.Rid);
if (dta != dtb)
return dta.CompareTo(dtb);
return propertyDefInfos.GetCollectionPosition(a.Def).CompareTo(propertyDefInfos.GetCollectionPosition(b.Def));
});
void InitializeMethodAndFieldList() {
uint fieldList = 1, methodList = 1;
foreach (var type in allTypeDefs) {
uint index = typeToRid[type];
var typeRow = tablesHeap.TypeDefTable[index];
typeRow = new RawTypeDefRow(typeRow.Flags, typeRow.Name, typeRow.Namespace, typeRow.Extends, fieldList, methodList);
tablesHeap.TypeDefTable[index] = typeRow;
fieldList += (uint)type.Fields.Count;
methodList += (uint)type.Methods.Count;
}
}
void InitializeParamList() {
uint ridList = 1;
for (uint methodRid = 1; methodRid <= methodDefInfos.TableSize; methodRid++) {
var methodInfo = methodDefInfos.GetByRid(methodRid);
var row = tablesHeap.MethodTable[methodRid];
row = new RawMethodRow(row.RVA, row.ImplFlags, row.Flags, row.Name, row.Signature, ridList);
tablesHeap.MethodTable[methodRid] = row;
if (methodInfo is not null)
ridList += (uint)methodInfo.Def.ParamDefs.Count;
}
}
void InitializeEventMap() {
if (!tablesHeap.EventMapTable.IsEmpty)
throw new ModuleWriterException("EventMap table isn't empty");
TypeDef type = null;
for (int i = 0; i < eventDefInfos.Count; i++) {
var info = eventDefInfos.GetSorted(i);
if (type == info.Def.DeclaringType)
continue;
type = info.Def.DeclaringType;
var row = new RawEventMapRow(typeToRid[type], info.NewRid);
uint eventMapRid = tablesHeap.EventMapTable.Create(row);
eventMapInfos.Add(type, eventMapRid);
}
}
void InitializePropertyMap() {
if (!tablesHeap.PropertyMapTable.IsEmpty)
throw new ModuleWriterException("PropertyMap table isn't empty");
TypeDef type = null;
for (int i = 0; i < propertyDefInfos.Count; i++) {
var info = propertyDefInfos.GetSorted(i);
if (type == info.Def.DeclaringType)
continue;
type = info.Def.DeclaringType;
var row = new RawPropertyMapRow(typeToRid[type], info.NewRid);
uint propertyMapRid = tablesHeap.PropertyMapTable.Create(row);
propertyMapInfos.Add(type, propertyMapRid);
}
}
/// <inheritdoc/>
protected override uint AddTypeRef(TypeRef tr) {
if (tr is null) {
Error("TypeRef is null");
return 0;
}
if (typeRefInfos.TryGetRid(tr, out uint rid)) {
if (rid == 0)
Error("TypeRef 0x{0:X8} has an infinite ResolutionScope loop.", tr.MDToken.Raw);
return rid;
}
typeRefInfos.Add(tr, 0); // Prevent inf recursion
bool isOld = PreserveTypeRefRids && mod.ResolveTypeRef(tr.Rid) == tr;
var row = new RawTypeRefRow(AddResolutionScope(tr.ResolutionScope), stringsHeap.Add(tr.Name), stringsHeap.Add(tr.Namespace));
if (isOld) {
rid = tr.Rid;
tablesHeap.TypeRefTable[tr.Rid] = row;
}
else
rid = tablesHeap.TypeRefTable.Add(row);
typeRefInfos.SetRid(tr, rid);
AddCustomAttributes(Table.TypeRef, rid, tr);
AddCustomDebugInformationList(Table.TypeRef, rid, tr);
return rid;
}
/// <inheritdoc/>
protected override uint AddTypeSpec(TypeSpec ts) => AddTypeSpec(ts, false);
uint AddTypeSpec(TypeSpec ts, bool forceIsOld) {
if (ts is null) {
Error("TypeSpec is null");
return 0;
}
if (typeSpecInfos.TryGetRid(ts, out uint rid)) {
if (rid == 0)
Error("TypeSpec 0x{0:X8} has an infinite TypeSig loop.", ts.MDToken.Raw);
return rid;
}
typeSpecInfos.Add(ts, 0); // Prevent inf recursion
bool isOld = forceIsOld || (PreserveTypeSpecRids && mod.ResolveTypeSpec(ts.Rid) == ts);
var row = new RawTypeSpecRow(GetSignature(ts.TypeSig, ts.ExtraData));
if (isOld) {
rid = ts.Rid;
tablesHeap.TypeSpecTable[ts.Rid] = row;
}
else
rid = tablesHeap.TypeSpecTable.Add(row);
typeSpecInfos.SetRid(ts, rid);
AddCustomAttributes(Table.TypeSpec, rid, ts);
AddCustomDebugInformationList(Table.TypeSpec, rid, ts);
return rid;
}
/// <inheritdoc/>
protected override uint AddMemberRef(MemberRef mr) => AddMemberRef(mr, false);
uint AddMemberRef(MemberRef mr, bool forceIsOld) {
if (mr is null) {
Error("MemberRef is null");
return 0;
}
if (memberRefInfos.TryGetRid(mr, out uint rid))
return rid;
bool isOld = forceIsOld || (PreserveMemberRefRids && mod.ResolveMemberRef(mr.Rid) == mr);
var row = new RawMemberRefRow(AddMemberRefParent(mr.Class), stringsHeap.Add(mr.Name), GetSignature(mr.Signature));
if (isOld) {
rid = mr.Rid;
tablesHeap.MemberRefTable[mr.Rid] = row;
}
else
rid = tablesHeap.MemberRefTable.Add(row);
memberRefInfos.Add(mr, rid);
AddCustomAttributes(Table.MemberRef, rid, mr);
AddCustomDebugInformationList(Table.MemberRef, rid, mr);
return rid;
}
/// <inheritdoc/>
protected override uint AddStandAloneSig(StandAloneSig sas) => AddStandAloneSig(sas, false);
uint AddStandAloneSig(StandAloneSig sas, bool forceIsOld) {
if (sas is null) {
Error("StandAloneSig is null");
return 0;
}
if (standAloneSigInfos.TryGetRid(sas, out uint rid))
return rid;
bool isOld = forceIsOld || (PreserveStandAloneSigRids && mod.ResolveStandAloneSig(sas.Rid) == sas);
var row = new RawStandAloneSigRow(GetSignature(sas.Signature));
if (isOld) {
rid = sas.Rid;
tablesHeap.StandAloneSigTable[sas.Rid] = row;
}
else
rid = tablesHeap.StandAloneSigTable.Add(row);
standAloneSigInfos.Add(sas, rid);
AddCustomAttributes(Table.StandAloneSig, rid, sas);
AddCustomDebugInformationList(Table.StandAloneSig, rid, sas);
return rid;
}
/// <inheritdoc/>
public override MDToken GetToken(IList<TypeSig> locals, uint origToken) {
if (!PreserveStandAloneSigRids || !IsValidStandAloneSigToken(origToken))
return base.GetToken(locals, origToken);
uint rid = AddStandAloneSig(new LocalSig(locals, false), origToken);
if (rid == 0)
return base.GetToken(locals, origToken);
return new MDToken(Table.StandAloneSig, rid);
}
/// <inheritdoc/>
protected override uint AddStandAloneSig(MethodSig methodSig, uint origToken) {
if (!PreserveStandAloneSigRids || !IsValidStandAloneSigToken(origToken))
return base.AddStandAloneSig(methodSig, origToken);
uint rid = AddStandAloneSig(methodSig, origToken);
if (rid == 0)
return base.AddStandAloneSig(methodSig, origToken);
return rid;
}
/// <inheritdoc/>
protected override uint AddStandAloneSig(FieldSig fieldSig, uint origToken) {
if (!PreserveStandAloneSigRids || !IsValidStandAloneSigToken(origToken))
return base.AddStandAloneSig(fieldSig, origToken);
uint rid = AddStandAloneSig(fieldSig, origToken);
if (rid == 0)
return base.AddStandAloneSig(fieldSig, origToken);
return rid;
}
uint AddStandAloneSig(CallingConventionSig callConvSig, uint origToken) {
uint sig = GetSignature(callConvSig);
if (callConvTokenToSignature.TryGetValue(origToken, out uint otherSig)) {
if (sig == otherSig)
return MDToken.ToRID(origToken);
Warning("Could not preserve StandAloneSig token 0x{0:X8}", origToken);
return 0;
}
uint rid = MDToken.ToRID(origToken);
var sas = mod.ResolveStandAloneSig(rid);
if (standAloneSigInfos.Exists(sas)) {
Warning("StandAloneSig 0x{0:X8} already exists", origToken);
return 0;
}
// Make sure it uses the updated sig
var oldSig = sas.Signature;
try {
sas.Signature = callConvSig;
AddStandAloneSig(sas, true);
}
finally {
sas.Signature = oldSig;
}
callConvTokenToSignature.Add(origToken, sig);
return MDToken.ToRID(origToken);
}
bool IsValidStandAloneSigToken(uint token) {
if (MDToken.ToTable(token) != Table.StandAloneSig)
return false;
uint rid = MDToken.ToRID(token);
return mod.TablesStream.StandAloneSigTable.IsValidRID(rid);
}
/// <inheritdoc/>
protected override uint AddMethodSpec(MethodSpec ms) => AddMethodSpec(ms, false);
uint AddMethodSpec(MethodSpec ms, bool forceIsOld) {
if (ms is null) {
Error("MethodSpec is null");
return 0;
}
if (methodSpecInfos.TryGetRid(ms, out uint rid))
return rid;
bool isOld = forceIsOld || (PreserveMethodSpecRids && mod.ResolveMethodSpec(ms.Rid) == ms);
var row = new RawMethodSpecRow(AddMethodDefOrRef(ms.Method), GetSignature(ms.Instantiation));
if (isOld) {
rid = ms.Rid;
tablesHeap.MethodSpecTable[ms.Rid] = row;
}
else
rid = tablesHeap.MethodSpecTable.Add(row);
methodSpecInfos.Add(ms, rid);
AddCustomAttributes(Table.MethodSpec, rid, ms);
AddCustomDebugInformationList(Table.MethodSpec, rid, ms);
return rid;
}
/// <inheritdoc/>
protected override void BeforeSortingCustomAttributes() => InitializeUninitializedTableRows();
}
}