500 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			500 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| //
 | |
| // Author:
 | |
| //   Jb Evain (jbevain@gmail.com)
 | |
| //
 | |
| // Copyright (c) 2008 - 2015 Jb Evain
 | |
| // Copyright (c) 2008 - 2011 Novell, Inc.
 | |
| //
 | |
| // Licensed under the MIT/X11 license.
 | |
| //
 | |
| 
 | |
| using MonoFN.Cecil.PE;
 | |
| using System;
 | |
| using System.Collections.Generic;
 | |
| using System.Text;
 | |
| using RVA = System.UInt32;
 | |
| 
 | |
| namespace MonoFN.Cecil.Metadata {
 | |
| 
 | |
| 	sealed class TableHeapBuffer : HeapBuffer {
 | |
| 
 | |
| 		readonly ModuleDefinition module;
 | |
| 		readonly MetadataBuilder metadata;
 | |
| 
 | |
| 		readonly internal TableInformation [] table_infos = new TableInformation [Mixin.TableCount];
 | |
| 		readonly internal MetadataTable [] tables = new MetadataTable [Mixin.TableCount];
 | |
| 
 | |
| 		bool large_string;
 | |
| 		bool large_blob;
 | |
| 		bool large_guid;
 | |
| 
 | |
| 		readonly int [] coded_index_sizes = new int [Mixin.CodedIndexCount];
 | |
| 		readonly Func<Table, int> counter;
 | |
| 
 | |
| 		internal uint [] string_offsets;
 | |
| 
 | |
| 		public override bool IsEmpty {
 | |
| 			get { return false; }
 | |
| 		}
 | |
| 
 | |
| 		public TableHeapBuffer (ModuleDefinition module, MetadataBuilder metadata)
 | |
| 			: base (24)
 | |
| 		{
 | |
| 			this.module = module;
 | |
| 			this.metadata = metadata;
 | |
| 			this.counter = GetTableLength;
 | |
| 		}
 | |
| 
 | |
| 		int GetTableLength (Table table)
 | |
| 		{
 | |
| 			return (int)table_infos [(int)table].Length;
 | |
| 		}
 | |
| 
 | |
| 		public TTable GetTable<TTable> (Table table) where TTable : MetadataTable, new()
 | |
| 		{
 | |
| 			var md_table = (TTable)tables [(int)table];
 | |
| 			if (md_table != null)
 | |
| 				return md_table;
 | |
| 
 | |
| 			md_table = new TTable ();
 | |
| 			tables [(int)table] = md_table;
 | |
| 			return md_table;
 | |
| 		}
 | |
| 
 | |
| 		public void WriteBySize (uint value, int size)
 | |
| 		{
 | |
| 			if (size == 4)
 | |
| 				WriteUInt32 (value);
 | |
| 			else
 | |
| 				WriteUInt16 ((ushort)value);
 | |
| 		}
 | |
| 
 | |
| 		public void WriteBySize (uint value, bool large)
 | |
| 		{
 | |
| 			if (large)
 | |
| 				WriteUInt32 (value);
 | |
| 			else
 | |
| 				WriteUInt16 ((ushort)value);
 | |
| 		}
 | |
| 
 | |
| 		public void WriteString (uint @string)
 | |
| 		{
 | |
| 			WriteBySize (string_offsets [@string], large_string);
 | |
| 		}
 | |
| 
 | |
| 		public void WriteBlob (uint blob)
 | |
| 		{
 | |
| 			WriteBySize (blob, large_blob);
 | |
| 		}
 | |
| 
 | |
| 		public void WriteGuid (uint guid)
 | |
| 		{
 | |
| 			WriteBySize (guid, large_guid);
 | |
| 		}
 | |
| 
 | |
| 		public void WriteRID (uint rid, Table table)
 | |
| 		{
 | |
| 			WriteBySize (rid, table_infos [(int)table].IsLarge);
 | |
| 		}
 | |
| 
 | |
| 		int GetCodedIndexSize (CodedIndex coded_index)
 | |
| 		{
 | |
| 			var index = (int)coded_index;
 | |
| 			var size = coded_index_sizes [index];
 | |
| 			if (size != 0)
 | |
| 				return size;
 | |
| 
 | |
| 			return coded_index_sizes [index] = coded_index.GetSize (counter);
 | |
| 		}
 | |
| 
 | |
| 		public void WriteCodedRID (uint rid, CodedIndex coded_index)
 | |
| 		{
 | |
| 			WriteBySize (rid, GetCodedIndexSize (coded_index));
 | |
| 		}
 | |
| 
 | |
| 		public void WriteTableHeap ()
 | |
| 		{
 | |
| 			WriteUInt32 (0);                    // Reserved
 | |
| 			WriteByte (GetTableHeapVersion ()); // MajorVersion
 | |
| 			WriteByte (0);                      // MinorVersion
 | |
| 			WriteByte (GetHeapSizes ());        // HeapSizes
 | |
| 			WriteByte (10);                     // Reserved2
 | |
| 			WriteUInt64 (GetValid ());          // Valid
 | |
| 			WriteUInt64 (0xc416003301fa00);     // Sorted
 | |
| 
 | |
| 			WriteRowCount ();
 | |
| 			WriteTables ();
 | |
| 		}
 | |
| 
 | |
| 		void WriteRowCount ()
 | |
| 		{
 | |
| 			for (int i = 0; i < tables.Length; i++) {
 | |
| 				var table = tables [i];
 | |
| 				if (table == null || table.Length == 0)
 | |
| 					continue;
 | |
| 
 | |
| 				WriteUInt32 ((uint)table.Length);
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		void WriteTables ()
 | |
| 		{
 | |
| 			for (int i = 0; i < tables.Length; i++) {
 | |
| 				var table = tables [i];
 | |
| 				if (table == null || table.Length == 0)
 | |
| 					continue;
 | |
| 
 | |
| 				table.Write (this);
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		ulong GetValid ()
 | |
| 		{
 | |
| 			ulong valid = 0;
 | |
| 
 | |
| 			for (int i = 0; i < tables.Length; i++) {
 | |
| 				var table = tables [i];
 | |
| 				if (table == null || table.Length == 0)
 | |
| 					continue;
 | |
| 
 | |
| 				table.Sort ();
 | |
| 				valid |= (1UL << i);
 | |
| 			}
 | |
| 
 | |
| 			return valid;
 | |
| 		}
 | |
| 
 | |
| 		public void ComputeTableInformations ()
 | |
| 		{
 | |
| 			if (metadata.metadata_builder != null)
 | |
| 				ComputeTableInformations (metadata.metadata_builder.table_heap);
 | |
| 
 | |
| 			ComputeTableInformations (metadata.table_heap);
 | |
| 		}
 | |
| 
 | |
| 		void ComputeTableInformations (TableHeapBuffer table_heap)
 | |
| 		{
 | |
| 			var tables = table_heap.tables;
 | |
| 			for (int i = 0; i < tables.Length; i++) {
 | |
| 				var table = tables [i];
 | |
| 				if (table != null && table.Length > 0)
 | |
| 					table_infos [i].Length = (uint)table.Length;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		byte GetHeapSizes ()
 | |
| 		{
 | |
| 			byte heap_sizes = 0;
 | |
| 
 | |
| 			if (metadata.string_heap.IsLarge) {
 | |
| 				large_string = true;
 | |
| 				heap_sizes |= 0x01;
 | |
| 			}
 | |
| 
 | |
| 			if (metadata.guid_heap.IsLarge) {
 | |
| 				large_guid = true;
 | |
| 				heap_sizes |= 0x02;
 | |
| 			}
 | |
| 
 | |
| 			if (metadata.blob_heap.IsLarge) {
 | |
| 				large_blob = true;
 | |
| 				heap_sizes |= 0x04;
 | |
| 			}
 | |
| 
 | |
| 			return heap_sizes;
 | |
| 		}
 | |
| 
 | |
| 		byte GetTableHeapVersion ()
 | |
| 		{
 | |
| 			switch (module.Runtime) {
 | |
| 			case TargetRuntime.Net_1_0:
 | |
| 			case TargetRuntime.Net_1_1:
 | |
| 				return 1;
 | |
| 			default:
 | |
| 				return 2;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		public void FixupData (RVA data_rva)
 | |
| 		{
 | |
| 			var table = GetTable<FieldRVATable> (Table.FieldRVA);
 | |
| 			if (table.length == 0)
 | |
| 				return;
 | |
| 
 | |
| 			var field_idx_size = GetTable<FieldTable> (Table.Field).IsLarge ? 4 : 2;
 | |
| 			var previous = this.position;
 | |
| 
 | |
| 			base.position = table.position;
 | |
| 			for (int i = 0; i < table.length; i++) {
 | |
| 				var rva = ReadUInt32 ();
 | |
| 				base.position -= 4;
 | |
| 				WriteUInt32 (rva + data_rva);
 | |
| 				base.position += field_idx_size;
 | |
| 			}
 | |
| 
 | |
| 			base.position = previous;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	sealed class ResourceBuffer : ByteBuffer {
 | |
| 
 | |
| 		public ResourceBuffer ()
 | |
| 			: base (0)
 | |
| 		{
 | |
| 		}
 | |
| 
 | |
| 		public uint AddResource (byte [] resource)
 | |
| 		{
 | |
| 			var offset = (uint)this.position;
 | |
| 			WriteInt32 (resource.Length);
 | |
| 			WriteBytes (resource);
 | |
| 			return offset;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	sealed class DataBuffer : ByteBuffer {
 | |
| 
 | |
| 		public DataBuffer ()
 | |
| 			: base (0)
 | |
| 		{
 | |
| 		}
 | |
| 
 | |
| 		public RVA AddData (byte [] data)
 | |
| 		{
 | |
| 			var rva = (RVA)position;
 | |
| 			WriteBytes (data);
 | |
| 			return rva;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	abstract class HeapBuffer : ByteBuffer {
 | |
| 
 | |
| 		public bool IsLarge {
 | |
| 			get { return base.length > 65535; }
 | |
| 		}
 | |
| 
 | |
| 		public abstract bool IsEmpty { get; }
 | |
| 
 | |
| 		protected HeapBuffer (int length)
 | |
| 			: base (length)
 | |
| 		{
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	sealed class GuidHeapBuffer : HeapBuffer {
 | |
| 
 | |
| 		readonly Dictionary<Guid, uint> guids = new Dictionary<Guid, uint> ();
 | |
| 
 | |
| 		public override bool IsEmpty {
 | |
| 			get { return length == 0; }
 | |
| 		}
 | |
| 
 | |
| 		public GuidHeapBuffer ()
 | |
| 			: base (16)
 | |
| 		{
 | |
| 		}
 | |
| 
 | |
| 		public uint GetGuidIndex (Guid guid)
 | |
| 		{
 | |
| 			uint index;
 | |
| 			if (guids.TryGetValue (guid, out index))
 | |
| 				return index;
 | |
| 
 | |
| 			index = (uint)guids.Count + 1;
 | |
| 			WriteGuid (guid);
 | |
| 			guids.Add (guid, index);
 | |
| 			return index;
 | |
| 		}
 | |
| 
 | |
| 		void WriteGuid (Guid guid)
 | |
| 		{
 | |
| 			WriteBytes (guid.ToByteArray ());
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	class StringHeapBuffer : HeapBuffer {
 | |
| 
 | |
| 		protected Dictionary<string, uint> strings = new Dictionary<string, uint> (StringComparer.Ordinal);
 | |
| 
 | |
| 		public sealed override bool IsEmpty {
 | |
| 			get { return length <= 1; }
 | |
| 		}
 | |
| 
 | |
| 		public StringHeapBuffer ()
 | |
| 			: base (1)
 | |
| 		{
 | |
| 			WriteByte (0);
 | |
| 		}
 | |
| 
 | |
| 		public virtual uint GetStringIndex (string @string)
 | |
| 		{
 | |
| 			uint index;
 | |
| 			if (strings.TryGetValue (@string, out index))
 | |
| 				return index;
 | |
| 
 | |
| 			index = (uint)strings.Count + 1;
 | |
| 			strings.Add (@string, index);
 | |
| 			return index;
 | |
| 		}
 | |
| 
 | |
| 		public uint [] WriteStrings ()
 | |
| 		{
 | |
| 			var sorted = SortStrings (strings);
 | |
| 			strings = null;
 | |
| 
 | |
| 			// Add 1 for empty string whose index and offset are both 0
 | |
| 			var string_offsets = new uint [sorted.Count + 1];
 | |
| 			string_offsets [0] = 0;
 | |
| 
 | |
| 			// Find strings that can be folded
 | |
| 			var previous = string.Empty;
 | |
| 			foreach (var entry in sorted) {
 | |
| 				var @string = entry.Key;
 | |
| 				var index = entry.Value;
 | |
| 				var position = base.position;
 | |
| 
 | |
| 				if (previous.EndsWith (@string, StringComparison.Ordinal) && !IsLowSurrogateChar (entry.Key [0])) {
 | |
| 					// Map over the tail of prev string. Watch for null-terminator of prev string.
 | |
| 					string_offsets [index] = (uint)(position - (Encoding.UTF8.GetByteCount (entry.Key) + 1));
 | |
| 				} else {
 | |
| 					string_offsets [index] = (uint)position;
 | |
| 					WriteString (@string);
 | |
| 				}
 | |
| 
 | |
| 				previous = entry.Key;
 | |
| 			}
 | |
| 
 | |
| 			return string_offsets;
 | |
| 		}
 | |
| 
 | |
| 		static List<KeyValuePair<string, uint>> SortStrings (Dictionary<string, uint> strings)
 | |
| 		{
 | |
| 			var sorted = new List<KeyValuePair<string, uint>> (strings);
 | |
| 			sorted.Sort (new SuffixSort ());
 | |
| 			return sorted;
 | |
| 		}
 | |
| 
 | |
| 		static bool IsLowSurrogateChar (int c)
 | |
| 		{
 | |
| 			return unchecked((uint)(c - 0xDC00)) <= 0xDFFF - 0xDC00;
 | |
| 		}
 | |
| 
 | |
| 		protected virtual void WriteString (string @string)
 | |
| 		{
 | |
| 			WriteBytes (Encoding.UTF8.GetBytes (@string));
 | |
| 			WriteByte (0);
 | |
| 		}
 | |
| 
 | |
| 		// Sorts strings such that a string is followed immediately by all strings
 | |
| 		// that are a suffix of it.  
 | |
| 		private class SuffixSort : IComparer<KeyValuePair<string, uint>> {
 | |
| 
 | |
| 			public int Compare (KeyValuePair<string, uint> xPair, KeyValuePair<string, uint> yPair)
 | |
| 			{
 | |
| 				var x = xPair.Key;
 | |
| 				var y = yPair.Key;
 | |
| 
 | |
| 				for (int i = x.Length - 1, j = y.Length - 1; i >= 0 & j >= 0; i--, j--) {
 | |
| 					if (x [i] < y [j]) {
 | |
| 						return -1;
 | |
| 					}
 | |
| 
 | |
| 					if (x [i] > y [j]) {
 | |
| 						return +1;
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				return y.Length.CompareTo (x.Length);
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	sealed class BlobHeapBuffer : HeapBuffer {
 | |
| 
 | |
| 		readonly Dictionary<ByteBuffer, uint> blobs = new Dictionary<ByteBuffer, uint> (new ByteBufferEqualityComparer ());
 | |
| 
 | |
| 		public override bool IsEmpty {
 | |
| 			get { return length <= 1; }
 | |
| 		}
 | |
| 
 | |
| 		public BlobHeapBuffer ()
 | |
| 			: base (1)
 | |
| 		{
 | |
| 			WriteByte (0);
 | |
| 		}
 | |
| 
 | |
| 		public uint GetBlobIndex (ByteBuffer blob)
 | |
| 		{
 | |
| 			uint index;
 | |
| 			if (blobs.TryGetValue (blob, out index))
 | |
| 				return index;
 | |
| 
 | |
| 			index = (uint)base.position;
 | |
| 			WriteBlob (blob);
 | |
| 			blobs.Add (blob, index);
 | |
| 			return index;
 | |
| 		}
 | |
| 
 | |
| 		void WriteBlob (ByteBuffer blob)
 | |
| 		{
 | |
| 			WriteCompressedUInt32 ((uint)blob.length);
 | |
| 			WriteBytes (blob);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	sealed class UserStringHeapBuffer : StringHeapBuffer {
 | |
| 
 | |
| 		public override uint GetStringIndex (string @string)
 | |
| 		{
 | |
| 			uint index;
 | |
| 			if (strings.TryGetValue (@string, out index))
 | |
| 				return index;
 | |
| 
 | |
| 			index = (uint)base.position;
 | |
| 			WriteString (@string);
 | |
| 			strings.Add (@string, index);
 | |
| 			return index;
 | |
| 		}
 | |
| 
 | |
| 		protected override void WriteString (string @string)
 | |
| 		{
 | |
| 			WriteCompressedUInt32 ((uint)@string.Length * 2 + 1);
 | |
| 
 | |
| 			byte special = 0;
 | |
| 
 | |
| 			for (int i = 0; i < @string.Length; i++) {
 | |
| 				var @char = @string [i];
 | |
| 				WriteUInt16 (@char);
 | |
| 
 | |
| 				if (special == 1)
 | |
| 					continue;
 | |
| 
 | |
| 				if (@char < 0x20 || @char > 0x7e) {
 | |
| 					if (@char > 0x7e
 | |
| 						|| (@char >= 0x01 && @char <= 0x08)
 | |
| 						|| (@char >= 0x0e && @char <= 0x1f)
 | |
| 						|| @char == 0x27
 | |
| 						|| @char == 0x2d) {
 | |
| 
 | |
| 						special = 1;
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			WriteByte (special);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	sealed class PdbHeapBuffer : HeapBuffer {
 | |
| 
 | |
| 		public override bool IsEmpty {
 | |
| 			get { return false; }
 | |
| 		}
 | |
| 
 | |
| 		public PdbHeapBuffer ()
 | |
| 			: base (0)
 | |
| 		{
 | |
| 		}
 | |
| 	}
 | |
| }
 |