1 //
2 // Author:
3 //   Jb Evain (jbevain@gmail.com)
4 //
5 // Copyright (c) 2008 - 2015 Jb Evain
6 // Copyright (c) 2008 - 2011 Novell, Inc.
7 //
8 // Licensed under the MIT/X11 license.
9 //
10 
11 using System;
12 using System.Collections.Generic;
13 using System.Text;
14 
15 using Mono.Cecil.PE;
16 
17 using RVA = System.UInt32;
18 
19 #if !READ_ONLY
20 
21 namespace Mono.Cecil.Metadata {
22 
23 	sealed class TableHeapBuffer : HeapBuffer {
24 
25 		readonly ModuleDefinition module;
26 		readonly MetadataBuilder metadata;
27 
28 		readonly internal TableInformation [] table_infos = new TableInformation [Mixin.TableCount];
29 		readonly internal MetadataTable [] tables = new MetadataTable [Mixin.TableCount];
30 
31 		bool large_string;
32 		bool large_blob;
33 		bool large_guid;
34 
35 		readonly int [] coded_index_sizes = new int [Mixin.CodedIndexCount];
36 		readonly Func<Table, int> counter;
37 
38 		internal uint [] string_offsets;
39 
40 		public override bool IsEmpty {
41 			get { return false; }
42 		}
43 
TableHeapBuffer(ModuleDefinition module, MetadataBuilder metadata)44 		public TableHeapBuffer (ModuleDefinition module, MetadataBuilder metadata)
45 			: base (24)
46 		{
47 			this.module = module;
48 			this.metadata = metadata;
49 			this.counter = GetTableLength;
50 		}
51 
GetTableLength(Table table)52 		int GetTableLength (Table table)
53 		{
54 			return (int) table_infos [(int) table].Length;
55 		}
56 
57 		public TTable GetTable<TTable> (Table table) where TTable : MetadataTable, new ()
58 		{
59 			var md_table = (TTable) tables [(int) table];
60 			if (md_table != null)
61 				return md_table;
62 
63 			md_table = new TTable ();
64 			tables [(int) table] = md_table;
65 			return md_table;
66 		}
67 
WriteBySize(uint value, int size)68 		public void WriteBySize (uint value, int size)
69 		{
70 			if (size == 4)
71 				WriteUInt32 (value);
72 			else
73 				WriteUInt16 ((ushort) value);
74 		}
75 
WriteBySize(uint value, bool large)76 		public void WriteBySize (uint value, bool large)
77 		{
78 			if (large)
79 				WriteUInt32 (value);
80 			else
81 				WriteUInt16 ((ushort) value);
82 		}
83 
WriteString(uint @string)84 		public void WriteString (uint @string)
85 		{
86 			WriteBySize (string_offsets [@string], large_string);
87 		}
88 
WriteBlob(uint blob)89 		public void WriteBlob (uint blob)
90 		{
91 			WriteBySize (blob, large_blob);
92 		}
93 
WriteGuid(uint guid)94 		public void WriteGuid (uint guid)
95 		{
96 			WriteBySize (guid, large_guid);
97 		}
98 
WriteRID(uint rid, Table table)99 		public void WriteRID (uint rid, Table table)
100 		{
101 			WriteBySize (rid, table_infos [(int) table].IsLarge);
102 		}
103 
GetCodedIndexSize(CodedIndex coded_index)104 		int GetCodedIndexSize (CodedIndex coded_index)
105 		{
106 			var index = (int) coded_index;
107 			var size = coded_index_sizes [index];
108 			if (size != 0)
109 				return size;
110 
111 			return coded_index_sizes [index] = coded_index.GetSize (counter);
112 		}
113 
WriteCodedRID(uint rid, CodedIndex coded_index)114 		public void WriteCodedRID (uint rid, CodedIndex coded_index)
115 		{
116 			WriteBySize (rid, GetCodedIndexSize (coded_index));
117 		}
118 
WriteTableHeap()119 		public void WriteTableHeap ()
120 		{
121 			WriteUInt32 (0);					// Reserved
122 			WriteByte (GetTableHeapVersion ());	// MajorVersion
123 			WriteByte (0);						// MinorVersion
124 			WriteByte (GetHeapSizes ());		// HeapSizes
125 			WriteByte (10);						// Reserved2
126 			WriteUInt64 (GetValid ());			// Valid
127 			WriteUInt64 (0xc416003301fa00);		// Sorted
128 
129 			WriteRowCount ();
130 			WriteTables ();
131 		}
132 
WriteRowCount()133 		void WriteRowCount ()
134 		{
135 			for (int i = 0; i < tables.Length; i++) {
136 				var table = tables [i];
137 				if (table == null || table.Length == 0)
138 					continue;
139 
140 				WriteUInt32 ((uint) table.Length);
141 			}
142 		}
143 
WriteTables()144 		void WriteTables ()
145 		{
146 			for (int i = 0; i < tables.Length; i++) {
147 				var table = tables [i];
148 				if (table == null || table.Length == 0)
149 					continue;
150 
151 				table.Write (this);
152 			}
153 		}
154 
GetValid()155 		ulong GetValid ()
156 		{
157 			ulong valid = 0;
158 
159 			for (int i = 0; i < tables.Length; i++) {
160 				var table = tables [i];
161 				if (table == null || table.Length == 0)
162 					continue;
163 
164 				table.Sort ();
165 				valid |= (1UL << i);
166 			}
167 
168 			return valid;
169 		}
170 
ComputeTableInformations()171 		public void ComputeTableInformations ()
172 		{
173 			if (metadata.metadata_builder != null)
174 				ComputeTableInformations (metadata.metadata_builder.table_heap);
175 
176 			ComputeTableInformations (metadata.table_heap);
177 		}
178 
ComputeTableInformations(TableHeapBuffer table_heap)179 		void ComputeTableInformations (TableHeapBuffer table_heap)
180 		{
181 			var tables = table_heap.tables;
182 			for (int i = 0; i < tables.Length; i++) {
183 				var table = tables [i];
184 				if (table != null && table.Length > 0)
185 					table_infos [i].Length = (uint) table.Length;
186 			}
187 		}
188 
GetHeapSizes()189 		byte GetHeapSizes ()
190 		{
191 			byte heap_sizes = 0;
192 
193 			if (metadata.string_heap.IsLarge) {
194 				large_string = true;
195 				heap_sizes |= 0x01;
196 			}
197 
198 			if (metadata.guid_heap.IsLarge) {
199 				large_guid = true;
200 				heap_sizes |= 0x02;
201 			}
202 
203 			if (metadata.blob_heap.IsLarge) {
204 				large_blob = true;
205 				heap_sizes |= 0x04;
206 			}
207 
208 			return heap_sizes;
209 		}
210 
GetTableHeapVersion()211 		byte GetTableHeapVersion ()
212 		{
213 			switch (module.Runtime) {
214 			case TargetRuntime.Net_1_0:
215 			case TargetRuntime.Net_1_1:
216 				return 1;
217 			default:
218 				return 2;
219 			}
220 		}
221 
FixupData(RVA data_rva)222 		public void FixupData (RVA data_rva)
223 		{
224 			var table = GetTable<FieldRVATable> (Table.FieldRVA);
225 			if (table.length == 0)
226 				return;
227 
228 			var field_idx_size = GetTable<FieldTable> (Table.Field).IsLarge ? 4 : 2;
229 			var previous = this.position;
230 
231 			base.position = table.position;
232 			for (int i = 0; i < table.length; i++) {
233 				var rva = ReadUInt32 ();
234 				base.position -= 4;
235 				WriteUInt32 (rva + data_rva);
236 				base.position += field_idx_size;
237 			}
238 
239 			base.position = previous;
240 		}
241 	}
242 
243 	sealed class ResourceBuffer : ByteBuffer {
244 
ResourceBuffer()245 		public ResourceBuffer ()
246 			: base (0)
247 		{
248 		}
249 
AddResource(byte [] resource)250 		public uint AddResource (byte [] resource)
251 		{
252 			var offset = (uint) this.position;
253 			WriteInt32 (resource.Length);
254 			WriteBytes (resource);
255 			return offset;
256 		}
257 	}
258 
259 	sealed class DataBuffer : ByteBuffer {
260 
DataBuffer()261 		public DataBuffer ()
262 			: base (0)
263 		{
264 		}
265 
AddData(byte [] data)266 		public RVA AddData (byte [] data)
267 		{
268 			var rva = (RVA) position;
269 			WriteBytes (data);
270 			return rva;
271 		}
272 	}
273 
274 	abstract class HeapBuffer : ByteBuffer {
275 
276 		public bool IsLarge {
277 			get { return base.length > 65535; }
278 		}
279 
280 		public abstract bool IsEmpty { get; }
281 
HeapBuffer(int length)282 		protected HeapBuffer (int length)
283 			: base (length)
284 		{
285 		}
286 	}
287 
288 	sealed class GuidHeapBuffer : HeapBuffer {
289 
290 		readonly Dictionary<Guid, uint> guids = new Dictionary<Guid, uint> ();
291 
292 		public override bool IsEmpty {
293 			get { return length == 0; }
294 		}
295 
GuidHeapBuffer()296 		public GuidHeapBuffer ()
297 			: base (16)
298 		{
299 		}
300 
GetGuidIndex(Guid guid)301 		public uint GetGuidIndex (Guid guid)
302 		{
303 			uint index;
304 			if (guids.TryGetValue (guid, out index))
305 				return index;
306 
307 			index = (uint) guids.Count + 1;
308 			WriteGuid (guid);
309 			guids.Add (guid, index);
310 			return index;
311 		}
312 
WriteGuid(Guid guid)313 		void WriteGuid (Guid guid)
314 		{
315 			WriteBytes (guid.ToByteArray ());
316 		}
317 	}
318 
319 	class StringHeapBuffer : HeapBuffer {
320 
321 		protected Dictionary<string, uint> strings = new Dictionary<string, uint> (StringComparer.Ordinal);
322 
323 		public sealed override bool IsEmpty {
324 			get { return length <= 1; }
325 		}
326 
StringHeapBuffer()327 		public StringHeapBuffer ()
328 			: base (1)
329 		{
330 			WriteByte (0);
331 		}
332 
GetStringIndex(string @string)333 		public virtual uint GetStringIndex (string @string)
334 		{
335 			uint index;
336 			if (strings.TryGetValue (@string, out index))
337 				return index;
338 
339 			index = (uint) strings.Count + 1;
340 			strings.Add (@string, index);
341 			return index;
342 		}
343 
WriteStrings()344 		public uint [] WriteStrings ()
345 		{
346 			var sorted = SortStrings (strings);
347 			strings = null;
348 
349 			// Add 1 for empty string whose index and offset are both 0
350 			var string_offsets = new uint [sorted.Count + 1];
351 			string_offsets [0] = 0;
352 
353 			// Find strings that can be folded
354 			var previous = string.Empty;
355 			foreach (var entry in sorted) {
356 				var @string = entry.Key;
357 				var index = entry.Value;
358 				var position = base.position;
359 
360 				if (previous.EndsWith (@string, StringComparison.Ordinal) && !IsLowSurrogateChar (entry.Key [0])) {
361 					// Map over the tail of prev string. Watch for null-terminator of prev string.
362 					string_offsets [index] = (uint) (position - (Encoding.UTF8.GetByteCount (entry.Key) + 1));
363 				} else {
364 					string_offsets [index] = (uint) position;
365 					WriteString (@string);
366 				}
367 
368 				previous = entry.Key;
369 			}
370 
371 			return string_offsets;
372 		}
373 
SortStrings(Dictionary<string, uint> strings)374 		static List<KeyValuePair<string, uint>> SortStrings (Dictionary<string, uint> strings)
375 		{
376 			var sorted = new List<KeyValuePair<string, uint>> (strings);
377 			sorted.Sort (new SuffixSort ());
378 			return sorted;
379 		}
380 
IsLowSurrogateChar(int c)381 		static bool IsLowSurrogateChar (int c)
382 		{
383 			return unchecked((uint)(c - 0xDC00)) <= 0xDFFF - 0xDC00;
384 		}
385 
WriteString(string @string)386 		protected virtual void WriteString (string @string)
387 		{
388 			WriteBytes (Encoding.UTF8.GetBytes (@string));
389 			WriteByte (0);
390 		}
391 
392 		// Sorts strings such that a string is followed immediately by all strings
393 		// that are a suffix of it.
394 		private class SuffixSort : IComparer<KeyValuePair<string, uint>> {
395 
Compare(KeyValuePair<string, uint> xPair, KeyValuePair<string, uint> yPair)396 			public int Compare(KeyValuePair<string, uint> xPair, KeyValuePair<string, uint> yPair)
397 			{
398 				var x = xPair.Key;
399 				var y = yPair.Key;
400 
401 				for (int i = x.Length - 1, j = y.Length - 1; i >= 0 & j >= 0; i--, j--) {
402 					if (x [i] < y [j]) {
403 						return -1;
404 					}
405 
406 					if (x [i] > y [j]) {
407 						return +1;
408 					}
409 				}
410 
411 				return y.Length.CompareTo (x.Length);
412 			}
413 		}
414 	}
415 
416 	sealed class BlobHeapBuffer : HeapBuffer {
417 
418 		readonly Dictionary<ByteBuffer, uint> blobs = new Dictionary<ByteBuffer, uint> (new ByteBufferEqualityComparer ());
419 
420 		public override bool IsEmpty {
421 			get { return length <= 1; }
422 		}
423 
BlobHeapBuffer()424 		public BlobHeapBuffer ()
425 			: base (1)
426 		{
427 			WriteByte (0);
428 		}
429 
GetBlobIndex(ByteBuffer blob)430 		public uint GetBlobIndex (ByteBuffer blob)
431 		{
432 			uint index;
433 			if (blobs.TryGetValue (blob, out index))
434 				return index;
435 
436 			index = (uint) base.position;
437 			WriteBlob (blob);
438 			blobs.Add (blob, index);
439 			return index;
440 		}
441 
WriteBlob(ByteBuffer blob)442 		void WriteBlob (ByteBuffer blob)
443 		{
444 			WriteCompressedUInt32 ((uint) blob.length);
445 			WriteBytes (blob);
446 		}
447 	}
448 
449 	sealed class UserStringHeapBuffer : StringHeapBuffer {
450 
GetStringIndex(string @string)451 		public override uint GetStringIndex (string @string)
452 		{
453 			uint index;
454 			if (strings.TryGetValue (@string, out index))
455 				return index;
456 
457 			index = (uint) base.position;
458 			WriteString (@string);
459 			strings.Add (@string, index);
460 			return index;
461 		}
462 
WriteString(string @string)463 		protected override void WriteString (string @string)
464 		{
465 			WriteCompressedUInt32 ((uint) @string.Length * 2 + 1);
466 
467 			byte special = 0;
468 
469 			for (int i = 0; i < @string.Length; i++) {
470 				var @char = @string [i];
471 				WriteUInt16 (@char);
472 
473 				if (special == 1)
474 					continue;
475 
476 				if (@char < 0x20 || @char > 0x7e) {
477 					if (@char > 0x7e
478 						|| (@char >= 0x01 && @char <= 0x08)
479 						|| (@char >= 0x0e && @char <= 0x1f)
480 						|| @char == 0x27
481 						|| @char == 0x2d) {
482 
483 						special = 1;
484 					}
485 				}
486 			}
487 
488 			WriteByte (special);
489 		}
490 	}
491 
492 	sealed class PdbHeapBuffer : HeapBuffer {
493 
494 		public override bool IsEmpty {
495 			get { return false; }
496 		}
497 
PdbHeapBuffer()498 		public PdbHeapBuffer ()
499 			: base (0)
500 		{
501 		}
502 	}
503 }
504 
505 #endif
506