flush
[mcs.git] / class / Mono.Cecil / Mono.Cecil.Metadata / MetadataWriter.cs
blob93354565a9156f9d416c287850fd2642f801cd22
1 //
2 // MetadataWriter.cs
3 //
4 // Author:
5 // Jb Evain (jbevain@gmail.com)
6 //
7 // (C) 2005 Jb Evain
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 namespace Mono.Cecil.Metadata {
31 using System;
32 using System.Collections;
33 using System.IO;
34 using System.Text;
36 using Mono.Cecil;
37 using Mono.Cecil.Binary;
39 internal sealed class MetadataWriter : BaseMetadataVisitor {
41 AssemblyDefinition m_assembly;
42 MetadataRoot m_root;
43 TargetRuntime m_runtime;
44 ImageWriter m_imgWriter;
45 MetadataTableWriter m_tableWriter;
46 MemoryBinaryWriter m_binaryWriter;
48 IDictionary m_stringCache;
49 MemoryBinaryWriter m_stringWriter;
51 IDictionary m_guidCache;
52 MemoryBinaryWriter m_guidWriter;
54 IDictionary m_usCache;
55 MemoryBinaryWriter m_usWriter;
57 IDictionary m_blobCache;
58 MemoryBinaryWriter m_blobWriter;
60 MemoryBinaryWriter m_tWriter;
62 MemoryBinaryWriter m_cilWriter;
64 MemoryBinaryWriter m_fieldDataWriter;
65 MemoryBinaryWriter m_resWriter;
67 uint m_mdStart, m_mdSize;
68 uint m_resStart, m_resSize;
69 uint m_snsStart, m_snsSize;
70 uint m_debugHeaderStart;
71 uint m_imporTableStart;
73 uint m_entryPointToken;
75 RVA m_cursor = new RVA (0x2050);
77 public MemoryBinaryWriter CilWriter {
78 get { return m_cilWriter; }
81 public MemoryBinaryWriter StringWriter {
82 get { return m_stringWriter; }
85 public MemoryBinaryWriter GuidWriter {
86 get { return m_guidWriter; }
89 public MemoryBinaryWriter UserStringWriter {
90 get { return m_usWriter; }
93 public MemoryBinaryWriter BlobWriter {
94 get { return m_blobWriter; }
97 public uint DebugHeaderPosition {
98 get { return m_debugHeaderStart; }
101 public uint ImportTablePosition {
102 get { return m_imporTableStart; }
105 public uint EntryPointToken {
106 get { return m_entryPointToken; }
107 set { m_entryPointToken = value; }
110 public TargetRuntime TargetRuntime {
111 get { return m_runtime; }
114 public MetadataWriter (AssemblyDefinition asm, MetadataRoot root,
115 AssemblyKind kind, TargetRuntime rt, BinaryWriter writer)
117 m_assembly = asm;
118 m_root = root;
119 m_runtime = rt;
120 m_imgWriter = new ImageWriter (this, kind, writer);
121 m_binaryWriter = m_imgWriter.GetTextWriter ();
123 m_stringCache = new Hashtable ();
124 m_stringWriter = new MemoryBinaryWriter (Encoding.UTF8);
125 m_stringWriter.Write ((byte) 0);
127 m_guidCache = new Hashtable ();
128 m_guidWriter = new MemoryBinaryWriter ();
130 m_usCache = new Hashtable ();
131 m_usWriter = new MemoryBinaryWriter (Encoding.Unicode);
132 m_usWriter.Write ((byte) 0);
134 m_blobCache = new Hashtable (ByteArrayEqualityComparer.Instance, ByteArrayEqualityComparer.Instance);
135 m_blobWriter = new MemoryBinaryWriter ();
136 m_blobWriter.Write ((byte) 0);
138 m_tWriter = new MemoryBinaryWriter ();
139 m_tableWriter = new MetadataTableWriter (this, m_tWriter);
141 m_cilWriter = new MemoryBinaryWriter ();
143 m_fieldDataWriter = new MemoryBinaryWriter ();
144 m_resWriter = new MemoryBinaryWriter ();
147 public MetadataRoot GetMetadataRoot ()
149 return m_root;
152 public ImageWriter GetImageWriter ()
154 return m_imgWriter;
157 public MemoryBinaryWriter GetWriter ()
159 return m_binaryWriter;
162 public MetadataTableWriter GetTableVisitor ()
164 return m_tableWriter;
167 public void AddData (int length)
169 m_cursor += new RVA ((uint) length);
172 public RVA GetDataCursor ()
174 return m_cursor;
177 public uint AddString (string str)
179 if (str == null || str.Length == 0)
180 return 0;
182 if (m_stringCache.Contains (str))
183 return (uint) m_stringCache [str];
185 uint pointer = (uint) m_stringWriter.BaseStream.Position;
186 m_stringCache [str] = pointer;
187 m_stringWriter.Write (Encoding.UTF8.GetBytes (str));
188 m_stringWriter.Write ('\0');
189 return pointer;
192 public uint AddBlob (byte [] data)
194 if (data == null || data.Length == 0)
195 return 0;
197 object cached = m_blobCache [data];
198 if (cached != null)
199 return (uint) cached;
201 uint pointer = (uint) m_blobWriter.BaseStream.Position;
202 m_blobCache [data] = pointer;
203 Utilities.WriteCompressedInteger (m_blobWriter, data.Length);
204 m_blobWriter.Write (data);
205 return pointer;
208 public uint AddGuid (Guid g)
210 if (m_guidCache.Contains (g))
211 return (uint) m_guidCache [g];
213 uint pointer = (uint) m_guidWriter.BaseStream.Position;
214 m_guidCache [g] = pointer;
215 m_guidWriter.Write (g.ToByteArray ());
216 return pointer + 1;
219 public uint AddUserString (string str)
221 if (str == null)
222 return 0;
224 if (m_usCache.Contains (str))
225 return (uint) m_usCache [str];
227 uint pointer = (uint) m_usWriter.BaseStream.Position;
228 m_usCache [str] = pointer;
229 byte [] us = Encoding.Unicode.GetBytes (str);
230 Utilities.WriteCompressedInteger (m_usWriter, us.Length + 1);
231 m_usWriter.Write (us);
232 m_usWriter.Write ((byte) (RequiresSpecialHandling (us) ? 1 : 0));
233 return pointer;
236 static bool RequiresSpecialHandling (byte [] chars)
238 for (int i = 0; i < chars.Length; i++) {
239 byte c = chars [i];
240 if ((i % 2) == 1)
241 if (c != 0)
242 return true;
244 if (InRange (0x01, 0x08, c) ||
245 InRange (0x0e, 0x1f, c) ||
246 c == 0x27 ||
247 c == 0x2d ||
248 c == 0x7f) {
250 return true;
254 return false;
257 static bool InRange (int left, int right, int value)
259 return left <= value && value <= right;
262 void CreateStream (string name)
264 MetadataStream stream = new MetadataStream ();
265 stream.Header.Name = name;
266 stream.Heap = MetadataHeap.HeapFactory (stream);
267 m_root.Streams.Add (stream);
270 void SetHeapSize (MetadataHeap heap, MemoryBinaryWriter data, byte flag)
272 if (data.BaseStream.Length > 65536) {
273 m_root.Streams.TablesHeap.HeapSizes |= flag;
274 heap.IndexSize = 4;
275 } else
276 heap.IndexSize = 2;
279 public uint AddResource (byte [] data)
281 uint offset = (uint) m_resWriter.BaseStream.Position;
282 m_resWriter.Write (data.Length);
283 m_resWriter.Write (data);
284 m_resWriter.QuadAlign ();
285 return offset;
288 public void AddFieldInitData (byte [] data)
290 m_fieldDataWriter.Write (data);
291 m_fieldDataWriter.QuadAlign ();
294 uint GetStrongNameSignatureSize ()
296 if (m_assembly.Name.PublicKey != null) {
297 // in fx 2.0 the key may be from 384 to 16384 bits
298 // so we must calculate the signature size based on
299 // the size of the public key (minus the 32 byte header)
300 int size = m_assembly.Name.PublicKey.Length;
301 if (size > 32)
302 return (uint) (size - 32);
303 // note: size == 16 for the ECMA "key" which is replaced
304 // by the runtime with a 1024 bits key (128 bytes)
306 return 128; // default strongname signature size
309 public override void VisitMetadataRoot (MetadataRoot root)
311 WriteMemStream (m_cilWriter);
312 WriteMemStream (m_fieldDataWriter);
313 m_resStart = (uint) m_binaryWriter.BaseStream.Position;
314 WriteMemStream (m_resWriter);
315 m_resSize = (uint) (m_binaryWriter.BaseStream.Position - m_resStart);
317 // for now, we only reserve the place for the strong name signature
318 if ((m_assembly.Name.Flags & AssemblyFlags.PublicKey) > 0) {
319 m_snsStart = (uint) m_binaryWriter.BaseStream.Position;
320 m_snsSize = GetStrongNameSignatureSize ();
321 m_binaryWriter.Write (new byte [m_snsSize]);
322 m_binaryWriter.QuadAlign ();
325 // save place for debug header
326 if (m_imgWriter.GetImage ().DebugHeader != null) {
327 m_debugHeaderStart = (uint) m_binaryWriter.BaseStream.Position;
328 m_binaryWriter.Write (new byte [m_imgWriter.GetImage ().DebugHeader.GetSize ()]);
329 m_binaryWriter.QuadAlign ();
332 m_mdStart = (uint) m_binaryWriter.BaseStream.Position;
334 if (m_stringWriter.BaseStream.Length > 1) {
335 CreateStream (MetadataStream.Strings);
336 SetHeapSize (root.Streams.StringsHeap, m_stringWriter, 0x01);
337 m_stringWriter.QuadAlign ();
340 if (m_guidWriter.BaseStream.Length > 0) {
341 CreateStream (MetadataStream.GUID);
342 SetHeapSize (root.Streams.GuidHeap, m_guidWriter, 0x02);
345 if (m_blobWriter.BaseStream.Length > 1) {
346 CreateStream (MetadataStream.Blob);
347 SetHeapSize (root.Streams.BlobHeap, m_blobWriter, 0x04);
348 m_blobWriter.QuadAlign ();
351 if (m_usWriter.BaseStream.Length > 2) {
352 CreateStream (MetadataStream.UserStrings);
353 m_usWriter.QuadAlign ();
356 m_root.Header.MajorVersion = 1;
357 m_root.Header.MinorVersion = 1;
359 switch (m_runtime) {
360 case TargetRuntime.NET_1_0 :
361 m_root.Header.Version = "v1.0.3705";
362 break;
363 case TargetRuntime.NET_1_1 :
364 m_root.Header.Version = "v1.1.4322";
365 break;
366 case TargetRuntime.NET_2_0 :
367 m_root.Header.Version = "v2.0.50727";
368 break;
369 case TargetRuntime.NET_4_0 :
370 m_root.Header.Version = "v4.0.21006";
371 break;
374 m_root.Streams.TablesHeap.Tables.Accept (m_tableWriter);
376 if (m_tWriter.BaseStream.Length == 0)
377 m_root.Streams.Remove (m_root.Streams.TablesHeap.GetStream ());
380 public override void VisitMetadataRootHeader (MetadataRoot.MetadataRootHeader header)
382 m_binaryWriter.Write (header.Signature);
383 m_binaryWriter.Write (header.MajorVersion);
384 m_binaryWriter.Write (header.MinorVersion);
385 m_binaryWriter.Write (header.Reserved);
386 m_binaryWriter.Write (header.Version.Length + 3 & (~3));
387 m_binaryWriter.Write (Encoding.ASCII.GetBytes (header.Version));
388 m_binaryWriter.QuadAlign ();
389 m_binaryWriter.Write (header.Flags);
390 m_binaryWriter.Write ((ushort) m_root.Streams.Count);
393 public override void VisitMetadataStreamCollection (MetadataStreamCollection streams)
395 foreach (MetadataStream stream in streams) {
396 MetadataStream.MetadataStreamHeader header = stream.Header;
398 header.Offset = (uint) (m_binaryWriter.BaseStream.Position);
399 m_binaryWriter.Write (header.Offset);
400 MemoryBinaryWriter container;
401 string name = header.Name;
402 uint size = 0;
403 switch (header.Name) {
404 case MetadataStream.Tables :
405 container = m_tWriter;
406 size += 24; // header
407 break;
408 case MetadataStream.Strings :
409 name += "\0\0\0\0";
410 container = m_stringWriter;
411 break;
412 case MetadataStream.GUID :
413 container = m_guidWriter;
414 break;
415 case MetadataStream.Blob :
416 container = m_blobWriter;
417 break;
418 case MetadataStream.UserStrings :
419 container = m_usWriter;
420 break;
421 default :
422 throw new MetadataFormatException ("Unknown stream kind");
425 size += (uint) (container.BaseStream.Length + 3 & (~3));
426 m_binaryWriter.Write (size);
427 m_binaryWriter.Write (Encoding.ASCII.GetBytes (name));
428 m_binaryWriter.QuadAlign ();
432 void WriteMemStream (MemoryBinaryWriter writer)
434 m_binaryWriter.Write (writer);
435 m_binaryWriter.QuadAlign ();
438 void PatchStreamHeaderOffset (MetadataHeap heap)
440 long pos = m_binaryWriter.BaseStream.Position;
441 m_binaryWriter.BaseStream.Position = heap.GetStream ().Header.Offset;
442 m_binaryWriter.Write ((uint) (pos - m_mdStart));
443 m_binaryWriter.BaseStream.Position = pos;
446 public override void VisitGuidHeap (GuidHeap heap)
448 PatchStreamHeaderOffset (heap);
449 WriteMemStream (m_guidWriter);
452 public override void VisitStringsHeap (StringsHeap heap)
454 PatchStreamHeaderOffset (heap);
455 WriteMemStream (m_stringWriter);
458 public override void VisitTablesHeap (TablesHeap heap)
460 PatchStreamHeaderOffset (heap);
461 m_binaryWriter.Write (heap.Reserved);
462 switch (m_runtime) {
463 case TargetRuntime.NET_1_0 :
464 case TargetRuntime.NET_1_1 :
465 heap.MajorVersion = 1;
466 heap.MinorVersion = 0;
467 break;
468 case TargetRuntime.NET_2_0 :
469 case TargetRuntime.NET_4_0 :
470 heap.MajorVersion = 2;
471 heap.MinorVersion = 0;
472 break;
474 m_binaryWriter.Write (heap.MajorVersion);
475 m_binaryWriter.Write (heap.MinorVersion);
476 m_binaryWriter.Write (heap.HeapSizes);
477 m_binaryWriter.Write (heap.Reserved2);
478 m_binaryWriter.Write (heap.Valid);
479 m_binaryWriter.Write (heap.Sorted);
480 WriteMemStream (m_tWriter);
483 public override void VisitBlobHeap (BlobHeap heap)
485 PatchStreamHeaderOffset (heap);
486 WriteMemStream (m_blobWriter);
489 public override void VisitUserStringsHeap (UserStringsHeap heap)
491 PatchStreamHeaderOffset (heap);
492 WriteMemStream (m_usWriter);
495 void PatchHeader ()
497 Image img = m_imgWriter.GetImage ();
499 img.CLIHeader.EntryPointToken = m_entryPointToken;
501 if ((m_assembly.Name.Flags & AssemblyFlags.PublicKey) == 0)
502 img.CLIHeader.Flags &= ~RuntimeImage.StrongNameSigned;
504 if (m_mdSize > 0)
505 img.CLIHeader.Metadata = new DataDirectory (
506 img.TextSection.VirtualAddress + m_mdStart, m_imporTableStart - m_mdStart);
508 if (m_resSize > 0)
509 img.CLIHeader.Resources = new DataDirectory (
510 img.TextSection.VirtualAddress + m_resStart, m_resSize);
512 if (m_snsStart > 0)
513 img.CLIHeader.StrongNameSignature = new DataDirectory (
514 img.TextSection.VirtualAddress + m_snsStart, m_snsSize);
516 if (m_debugHeaderStart > 0)
517 img.PEOptionalHeader.DataDirectories.Debug = new DataDirectory (
518 img.TextSection.VirtualAddress + m_debugHeaderStart, 0x1c);
521 public override void TerminateMetadataRoot (MetadataRoot root)
523 m_mdSize = (uint) (m_binaryWriter.BaseStream.Position - m_mdStart);
524 m_imporTableStart = (uint) m_binaryWriter.BaseStream.Position;
525 m_binaryWriter.Write (new byte [0x60]); // imports
526 m_imgWriter.Initialize ();
527 PatchHeader ();
528 root.GetImage ().Accept (m_imgWriter);