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 uint DebugHeaderPosition {
82
get { return m_debugHeaderStart; }
85
public uint ImportTablePosition {
86
get { return m_imporTableStart; }
89
public uint EntryPointToken {
90
get { return m_entryPointToken; }
91
set { m_entryPointToken = value; }
94
public MetadataWriter (AssemblyDefinition asm, MetadataRoot root,
95
AssemblyKind kind, TargetRuntime rt, BinaryWriter writer)
100
m_imgWriter = new ImageWriter (this, kind, writer);
101
m_binaryWriter = m_imgWriter.GetTextWriter ();
103
m_stringCache = new Hashtable ();
104
m_stringWriter = new MemoryBinaryWriter (Encoding.UTF8);
105
m_stringWriter.Write ((byte) 0);
107
m_guidCache = new Hashtable ();
108
m_guidWriter = new MemoryBinaryWriter ();
110
m_usCache = new Hashtable ();
111
m_usWriter = new MemoryBinaryWriter (Encoding.Unicode);
112
m_usWriter.Write ((byte) 0);
114
m_blobCache = new Hashtable ();
115
m_blobWriter = new MemoryBinaryWriter ();
116
m_blobWriter.Write ((byte) 0);
118
m_tWriter = new MemoryBinaryWriter ();
119
m_tableWriter = new MetadataTableWriter (this, m_tWriter);
121
m_cilWriter = new MemoryBinaryWriter ();
123
m_fieldDataWriter = new MemoryBinaryWriter ();
124
m_resWriter = new MemoryBinaryWriter ();
127
public MetadataRoot GetMetadataRoot ()
132
public ImageWriter GetImageWriter ()
137
public MemoryBinaryWriter GetWriter ()
139
return m_binaryWriter;
142
public MetadataTableWriter GetTableVisitor ()
144
return m_tableWriter;
147
public void AddData (int length)
149
m_cursor += new RVA ((uint) length);
152
public RVA GetDataCursor ()
157
public uint AddString (string str)
159
if (str == null || str.Length == 0)
162
if (m_stringCache.Contains (str))
163
return (uint) m_stringCache [str];
165
uint pointer = (uint) m_stringWriter.BaseStream.Position;
166
m_stringCache [str] = pointer;
167
m_stringWriter.Write (Encoding.UTF8.GetBytes (str));
168
m_stringWriter.Write ('\0');
172
public uint AddBlob (byte [] data)
174
if (data == null || data.Length == 0)
177
// using CompactFramework compatible version of
178
// Convert.ToBase64String
179
string key = Convert.ToBase64String (data, 0, data.Length);
180
if (m_blobCache.Contains (key))
181
return (uint) m_blobCache [key];
183
uint pointer = (uint) m_blobWriter.BaseStream.Position;
184
m_blobCache [key] = pointer;
185
Utilities.WriteCompressedInteger (m_blobWriter, data.Length);
186
m_blobWriter.Write (data);
190
public uint AddGuid (Guid g)
192
if (m_guidCache.Contains (g))
193
return (uint) m_guidCache [g];
195
uint pointer = (uint) m_guidWriter.BaseStream.Position;
196
m_guidCache [g] = pointer;
197
m_guidWriter.Write (g.ToByteArray ());
201
public uint AddUserString (string str)
206
if (m_usCache.Contains (str))
207
return (uint) m_usCache [str];
209
uint pointer = (uint) m_usWriter.BaseStream.Position;
210
m_usCache [str] = pointer;
211
byte [] us = Encoding.Unicode.GetBytes (str);
212
Utilities.WriteCompressedInteger (m_usWriter, us.Length + 1);
213
m_usWriter.Write (us);
214
m_usWriter.Write ((byte) (RequiresSpecialHandling (us) ? 1 : 0));
218
static bool RequiresSpecialHandling (byte [] chars)
220
for (int i = 0; i < chars.Length; i++) {
226
if (InRange (0x01, 0x08, c) ||
227
InRange (0x0e, 0x1f, c) ||
239
static bool InRange (int left, int right, int value)
241
return left <= value && value <= right;
244
void CreateStream (string name)
246
MetadataStream stream = new MetadataStream ();
247
stream.Header.Name = name;
248
stream.Heap = MetadataHeap.HeapFactory (stream);
249
m_root.Streams.Add (stream);
252
void SetHeapSize (MetadataHeap heap, MemoryBinaryWriter data, byte flag)
254
if (data.BaseStream.Length > 65536) {
255
m_root.Streams.TablesHeap.HeapSizes |= flag;
261
public uint AddResource (byte [] data)
263
uint offset = (uint) m_resWriter.BaseStream.Position;
264
m_resWriter.Write (data.Length);
265
m_resWriter.Write (data);
266
m_resWriter.QuadAlign ();
270
public void AddFieldInitData (byte [] data)
272
m_fieldDataWriter.Write (data);
273
m_fieldDataWriter.QuadAlign ();
276
uint GetStrongNameSignatureSize ()
278
if (m_assembly.Name.PublicKey != null) {
279
// in fx 2.0 the key may be from 384 to 16384 bits
280
// so we must calculate the signature size based on
281
// the size of the public key (minus the 32 byte header)
282
int size = m_assembly.Name.PublicKey.Length;
284
return (uint) (size - 32);
285
// note: size == 16 for the ECMA "key" which is replaced
286
// by the runtime with a 1024 bits key (128 bytes)
288
return 128; // default strongname signature size
291
public override void VisitMetadataRoot (MetadataRoot root)
293
WriteMemStream (m_cilWriter);
294
WriteMemStream (m_fieldDataWriter);
295
m_resStart = (uint) m_binaryWriter.BaseStream.Position;
296
WriteMemStream (m_resWriter);
297
m_resSize = (uint) (m_binaryWriter.BaseStream.Position - m_resStart);
299
// for now, we only reserve the place for the strong name signature
300
if ((m_assembly.Name.Flags & AssemblyFlags.PublicKey) > 0) {
301
m_snsStart = (uint) m_binaryWriter.BaseStream.Position;
302
m_snsSize = GetStrongNameSignatureSize ();
303
m_binaryWriter.Write (new byte [m_snsSize]);
304
m_binaryWriter.QuadAlign ();
307
// save place for debug header
308
if (m_imgWriter.GetImage ().DebugHeader != null) {
309
m_debugHeaderStart = (uint) m_binaryWriter.BaseStream.Position;
310
m_binaryWriter.Write (new byte [m_imgWriter.GetImage ().DebugHeader.GetSize ()]);
311
m_binaryWriter.QuadAlign ();
314
m_mdStart = (uint) m_binaryWriter.BaseStream.Position;
316
if (m_stringWriter.BaseStream.Length > 1) {
317
CreateStream (MetadataStream.Strings);
318
SetHeapSize (root.Streams.StringsHeap, m_stringWriter, 0x01);
319
m_stringWriter.QuadAlign ();
322
if (m_guidWriter.BaseStream.Length > 0) {
323
CreateStream (MetadataStream.GUID);
324
SetHeapSize (root.Streams.GuidHeap, m_guidWriter, 0x02);
327
if (m_blobWriter.BaseStream.Length > 1) {
328
CreateStream (MetadataStream.Blob);
329
SetHeapSize (root.Streams.BlobHeap, m_blobWriter, 0x04);
330
m_blobWriter.QuadAlign ();
333
if (m_usWriter.BaseStream.Length > 2) {
334
CreateStream (MetadataStream.UserStrings);
335
m_usWriter.QuadAlign ();
338
m_root.Header.MajorVersion = 1;
339
m_root.Header.MinorVersion = 1;
342
case TargetRuntime.NET_1_0 :
343
m_root.Header.Version = "v1.0.3705";
345
case TargetRuntime.NET_1_1 :
346
m_root.Header.Version = "v1.1.4322";
348
case TargetRuntime.NET_2_0 :
349
m_root.Header.Version = "v2.0.50727";
353
m_root.Streams.TablesHeap.Tables.Accept (m_tableWriter);
355
if (m_tWriter.BaseStream.Length == 0)
356
m_root.Streams.Remove (m_root.Streams.TablesHeap.GetStream ());
359
public override void VisitMetadataRootHeader (MetadataRoot.MetadataRootHeader header)
361
m_binaryWriter.Write (header.Signature);
362
m_binaryWriter.Write (header.MajorVersion);
363
m_binaryWriter.Write (header.MinorVersion);
364
m_binaryWriter.Write (header.Reserved);
365
m_binaryWriter.Write (header.Version.Length + 3 & (~3));
366
m_binaryWriter.Write (Encoding.ASCII.GetBytes (header.Version));
367
m_binaryWriter.QuadAlign ();
368
m_binaryWriter.Write (header.Flags);
369
m_binaryWriter.Write ((ushort) m_root.Streams.Count);
372
public override void VisitMetadataStreamCollection (MetadataStreamCollection streams)
374
foreach (MetadataStream stream in streams) {
375
MetadataStream.MetadataStreamHeader header = stream.Header;
377
header.Offset = (uint) (m_binaryWriter.BaseStream.Position);
378
m_binaryWriter.Write (header.Offset);
379
MemoryBinaryWriter container;
380
string name = header.Name;
382
switch (header.Name) {
383
case MetadataStream.Tables :
384
container = m_tWriter;
385
size += 24; // header
387
case MetadataStream.Strings :
389
container = m_stringWriter;
391
case MetadataStream.GUID :
392
container = m_guidWriter;
394
case MetadataStream.Blob :
395
container = m_blobWriter;
397
case MetadataStream.UserStrings :
398
container = m_usWriter;
401
throw new MetadataFormatException ("Unknown stream kind");
404
size += (uint) (container.BaseStream.Length + 3 & (~3));
405
m_binaryWriter.Write (size);
406
m_binaryWriter.Write (Encoding.ASCII.GetBytes (name));
407
m_binaryWriter.QuadAlign ();
411
void WriteMemStream (MemoryBinaryWriter writer)
413
m_binaryWriter.Write (writer);
414
m_binaryWriter.QuadAlign ();
417
void PatchStreamHeaderOffset (MetadataHeap heap)
419
long pos = m_binaryWriter.BaseStream.Position;
420
m_binaryWriter.BaseStream.Position = heap.GetStream ().Header.Offset;
421
m_binaryWriter.Write ((uint) (pos - m_mdStart));
422
m_binaryWriter.BaseStream.Position = pos;
425
public override void VisitGuidHeap (GuidHeap heap)
427
PatchStreamHeaderOffset (heap);
428
WriteMemStream (m_guidWriter);
431
public override void VisitStringsHeap (StringsHeap heap)
433
PatchStreamHeaderOffset (heap);
434
WriteMemStream (m_stringWriter);
437
public override void VisitTablesHeap (TablesHeap heap)
439
PatchStreamHeaderOffset (heap);
440
m_binaryWriter.Write (heap.Reserved);
442
case TargetRuntime.NET_1_0 :
443
case TargetRuntime.NET_1_1 :
444
heap.MajorVersion = 1;
445
heap.MinorVersion = 0;
447
case TargetRuntime.NET_2_0 :
448
heap.MajorVersion = 2;
449
heap.MinorVersion = 0;
452
m_binaryWriter.Write (heap.MajorVersion);
453
m_binaryWriter.Write (heap.MinorVersion);
454
m_binaryWriter.Write (heap.HeapSizes);
455
m_binaryWriter.Write (heap.Reserved2);
456
m_binaryWriter.Write (heap.Valid);
457
m_binaryWriter.Write (heap.Sorted);
458
WriteMemStream (m_tWriter);
461
public override void VisitBlobHeap (BlobHeap heap)
463
PatchStreamHeaderOffset (heap);
464
WriteMemStream (m_blobWriter);
467
public override void VisitUserStringsHeap (UserStringsHeap heap)
469
PatchStreamHeaderOffset (heap);
470
WriteMemStream (m_usWriter);
475
Image img = m_imgWriter.GetImage ();
477
img.CLIHeader.EntryPointToken = m_entryPointToken;
480
img.CLIHeader.Metadata = new DataDirectory (
481
img.TextSection.VirtualAddress + m_mdStart, m_imporTableStart - m_mdStart);
484
img.CLIHeader.Resources = new DataDirectory (
485
img.TextSection.VirtualAddress + m_resStart, m_resSize);
488
img.CLIHeader.StrongNameSignature = new DataDirectory (
489
img.TextSection.VirtualAddress + m_snsStart, m_snsSize);
491
if (m_debugHeaderStart > 0)
492
img.PEOptionalHeader.DataDirectories.Debug = new DataDirectory (
493
img.TextSection.VirtualAddress + m_debugHeaderStart, 0x1c);
496
public override void TerminateMetadataRoot (MetadataRoot root)
498
m_mdSize = (uint) (m_binaryWriter.BaseStream.Position - m_mdStart);
499
m_imporTableStart = (uint) m_binaryWriter.BaseStream.Position;
500
m_binaryWriter.Write (new byte [0x60]); // imports
501
m_imgWriter.Initialize ();
503
root.GetImage ().Accept (m_imgWriter);