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