5 // Jb Evain (jbevain@gmail.com)
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
{
32 using System
.Collections
;
37 using Mono
.Cecil
.Binary
;
39 internal sealed class MetadataWriter
: BaseMetadataVisitor
{
41 AssemblyDefinition m_assembly
;
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
)
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 ()
152 public ImageWriter
GetImageWriter ()
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 ()
177 public uint AddString (string str
)
179 if (str
== null || str
.Length
== 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');
192 public uint AddBlob (byte [] data
)
194 if (data
== null || data
.Length
== 0)
197 object cached
= m_blobCache
[data
];
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
);
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 ());
219 public uint AddUserString (string str
)
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));
236 static bool RequiresSpecialHandling (byte [] chars
)
238 for (int i
= 0; i
< chars
.Length
; i
++) {
244 if (InRange (0x01, 0x08, c
) ||
245 InRange (0x0e, 0x1f, c
) ||
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
;
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 ();
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
;
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;
360 case TargetRuntime
.NET_1_0
:
361 m_root
.Header
.Version
= "v1.0.3705";
363 case TargetRuntime
.NET_1_1
:
364 m_root
.Header
.Version
= "v1.1.4322";
366 case TargetRuntime
.NET_2_0
:
367 m_root
.Header
.Version
= "v2.0.50727";
369 case TargetRuntime
.NET_4_0
:
370 m_root
.Header
.Version
= "v4.0.21006";
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
;
403 switch (header
.Name
) {
404 case MetadataStream
.Tables
:
405 container
= m_tWriter
;
406 size
+= 24; // header
408 case MetadataStream
.Strings
:
410 container
= m_stringWriter
;
412 case MetadataStream
.GUID
:
413 container
= m_guidWriter
;
415 case MetadataStream
.Blob
:
416 container
= m_blobWriter
;
418 case MetadataStream
.UserStrings
:
419 container
= m_usWriter
;
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
);
463 case TargetRuntime
.NET_1_0
:
464 case TargetRuntime
.NET_1_1
:
465 heap
.MajorVersion
= 1;
466 heap
.MinorVersion
= 0;
468 case TargetRuntime
.NET_2_0
:
469 case TargetRuntime
.NET_4_0
:
470 heap
.MajorVersion
= 2;
471 heap
.MinorVersion
= 0;
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
);
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
;
505 img
.CLIHeader
.Metadata
= new DataDirectory (
506 img
.TextSection
.VirtualAddress
+ m_mdStart
, m_imporTableStart
- m_mdStart
);
509 img
.CLIHeader
.Resources
= new DataDirectory (
510 img
.TextSection
.VirtualAddress
+ m_resStart
, m_resSize
);
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 ();
528 root
.GetImage ().Accept (m_imgWriter
);