e88465d6a898bd65d5d625e1f68fc416573db2ae
[mono.git] / mcs / tools / cil-strip / Mono.Cecil.Binary / ImageWriter.cs
1 //
2 // ImageWriter.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:
16 //
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 //
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.
27 //
28
29 namespace Mono.Cecil.Binary {
30
31         using System.IO;
32         using System.Text;
33
34         using Mono.Cecil.Metadata;
35
36         sealed class ImageWriter : BaseImageVisitor {
37
38                 Image m_img;
39                 AssemblyKind m_kind;
40                 MetadataWriter m_mdWriter;
41                 BinaryWriter m_binaryWriter;
42
43                 Section m_textSect;
44                 MemoryBinaryWriter m_textWriter;
45                 Section m_relocSect;
46                 MemoryBinaryWriter m_relocWriter;
47                 Section m_rsrcSect;
48                 MemoryBinaryWriter m_rsrcWriter;
49
50                 public ImageWriter (MetadataWriter writer, AssemblyKind kind, BinaryWriter bw)
51                 {
52                         m_mdWriter= writer;
53                         m_img = writer.GetMetadataRoot ().GetImage ();
54                         m_kind = kind;
55                         m_binaryWriter = bw;
56
57                         m_textWriter = new MemoryBinaryWriter ();
58                         m_textWriter.BaseStream.Position = 80;
59                         m_relocWriter = new MemoryBinaryWriter ();
60                 }
61
62                 public Image GetImage ()
63                 {
64                         return m_img;
65                 }
66
67                 public MemoryBinaryWriter GetTextWriter ()
68                 {
69                         return m_textWriter;
70                 }
71
72                 public uint GetAligned (uint integer, uint alignWith)
73                 {
74                         return (integer + alignWith - 1) & ~(alignWith - 1);
75                 }
76
77                 public void Initialize ()
78                 {
79                         Image img = m_img;
80                         ResourceWriter resWriter = null;
81
82                         uint sectAlign = img.PEOptionalHeader.NTSpecificFields.SectionAlignment;
83                         uint fileAlign = img.PEOptionalHeader.NTSpecificFields.FileAlignment;
84
85                         m_textSect = img.TextSection;
86                         foreach (Section s in img.Sections) {
87                                 if (s.Name == Section.Relocs)
88                                         m_relocSect = s;
89                                 else if (s.Name == Section.Resources) {
90                                         m_rsrcSect = s;
91                                         m_rsrcWriter = new MemoryBinaryWriter ();
92
93                                         resWriter = new ResourceWriter (img, m_rsrcSect, m_rsrcWriter);
94                                         resWriter.Write ();
95                                 }
96                         }
97
98                         // size computations, fields setting, etc.
99                         uint nbSects = (uint) img.Sections.Count;
100                         img.PEFileHeader.NumberOfSections = (ushort) nbSects;
101
102                         // build the reloc section data
103                         uint relocSize = 12;
104                         m_relocWriter.Write ((uint) 0);
105                         m_relocWriter.Write (relocSize);
106                         m_relocWriter.Write ((ushort) 0);
107                         m_relocWriter.Write ((ushort) 0);
108
109                         m_textSect.VirtualSize = (uint) m_textWriter.BaseStream.Length;
110                         m_relocSect.VirtualSize = (uint) m_relocWriter.BaseStream.Length;
111                         if (m_rsrcSect != null)
112                                 m_rsrcSect.VirtualSize = (uint) m_rsrcWriter.BaseStream.Length;
113
114                         // start counting before sections headers
115                         // section start + section header sixe * number of sections
116                         uint headersEnd = 0x178 + 0x28 * nbSects;
117                         uint fileOffset = headersEnd;
118                         uint sectOffset = sectAlign;
119                         uint imageSize = 0;
120
121                         foreach (Section sect in img.Sections) {
122                                 fileOffset = GetAligned (fileOffset, fileAlign);
123                                 sectOffset = GetAligned (sectOffset, sectAlign);
124
125                                 sect.PointerToRawData = new RVA (fileOffset);
126                                 sect.VirtualAddress = new RVA (sectOffset);
127                                 sect.SizeOfRawData = GetAligned (sect.VirtualSize, fileAlign);
128
129                                 fileOffset += sect.SizeOfRawData;
130                                 sectOffset += sect.SizeOfRawData;
131                                 imageSize += GetAligned (sect.SizeOfRawData, sectAlign);
132                         }
133
134                         if (m_textSect.VirtualAddress.Value != 0x2000)
135                                 throw new ImageFormatException ("Wrong RVA for .text section");
136
137                         if (resWriter != null)
138                                 resWriter.Patch ();
139
140                         img.PEOptionalHeader.StandardFields.CodeSize = GetAligned (
141                                 m_textSect.SizeOfRawData, fileAlign);
142                         img.PEOptionalHeader.StandardFields.InitializedDataSize = m_textSect.SizeOfRawData;
143                         if (m_rsrcSect != null)
144                                 img.PEOptionalHeader.StandardFields.InitializedDataSize += m_rsrcSect.SizeOfRawData;
145                         img.PEOptionalHeader.StandardFields.BaseOfCode = m_textSect.VirtualAddress;
146                         img.PEOptionalHeader.StandardFields.BaseOfData = m_relocSect.VirtualAddress;
147
148                         imageSize += headersEnd;
149                         img.PEOptionalHeader.NTSpecificFields.ImageSize = GetAligned (imageSize, sectAlign);
150
151                         img.PEOptionalHeader.DataDirectories.BaseRelocationTable = new DataDirectory (
152                                 m_relocSect.VirtualAddress, m_relocSect.VirtualSize);
153                         if (m_rsrcSect != null)
154                                 img.PEOptionalHeader.DataDirectories.ResourceTable = new DataDirectory (
155                                         m_rsrcSect.VirtualAddress, (uint) m_rsrcWriter.BaseStream.Length);
156
157                         if (m_kind == AssemblyKind.Dll) {
158                                 img.PEFileHeader.Characteristics = ImageCharacteristics.CILOnlyDll;
159                                 img.HintNameTable.RuntimeMain = HintNameTable.RuntimeMainDll;
160                                 img.PEOptionalHeader.NTSpecificFields.DLLFlags = 0x400;
161                         } else {
162                                 img.PEFileHeader.Characteristics = ImageCharacteristics.CILOnlyExe;
163                                 img.HintNameTable.RuntimeMain = HintNameTable.RuntimeMainExe;
164                         }
165
166                         switch (m_kind) {
167                         case AssemblyKind.Dll :
168                         case AssemblyKind.Console :
169                                 img.PEOptionalHeader.NTSpecificFields.SubSystem = SubSystem.WindowsCui;
170                                 break;
171                         case AssemblyKind.Windows :
172                                 img.PEOptionalHeader.NTSpecificFields.SubSystem = SubSystem.WindowsGui;
173                                 break;
174                         }
175
176                         RVA importTable = new RVA (img.TextSection.VirtualAddress + m_mdWriter.ImportTablePosition);
177
178                         img.PEOptionalHeader.DataDirectories.ImportTable = new DataDirectory (importTable, 0x57);
179
180                         img.ImportTable.ImportLookupTable = new RVA ((uint) importTable + 0x28);
181
182                         img.ImportLookupTable.HintNameRVA = img.ImportAddressTable.HintNameTableRVA =
183                                 new RVA ((uint) img.ImportTable.ImportLookupTable + 0x14);
184                         img.ImportTable.Name = new RVA ((uint) img.ImportLookupTable.HintNameRVA + 0xe);
185                 }
186
187                 public override void VisitDOSHeader (DOSHeader header)
188                 {
189                         m_binaryWriter.Write (header.Start);
190                         m_binaryWriter.Write (header.Lfanew);
191                         m_binaryWriter.Write (header.End);
192
193                         m_binaryWriter.Write ((ushort) 0x4550);
194                         m_binaryWriter.Write ((ushort) 0);
195                 }
196
197                 public override void VisitPEFileHeader (PEFileHeader header)
198                 {
199                         m_binaryWriter.Write (header.Machine);
200                         m_binaryWriter.Write (header.NumberOfSections);
201                         m_binaryWriter.Write (header.TimeDateStamp);
202                         m_binaryWriter.Write (header.PointerToSymbolTable);
203                         m_binaryWriter.Write (header.NumberOfSymbols);
204                         m_binaryWriter.Write (header.OptionalHeaderSize);
205                         m_binaryWriter.Write ((ushort) header.Characteristics);
206                 }
207
208                 public override void VisitNTSpecificFieldsHeader (PEOptionalHeader.NTSpecificFieldsHeader header)
209                 {
210                         WriteIntOrLong (header.ImageBase);
211                         m_binaryWriter.Write (header.SectionAlignment);
212                         m_binaryWriter.Write (header.FileAlignment);
213                         m_binaryWriter.Write (header.OSMajor);
214                         m_binaryWriter.Write (header.OSMinor);
215                         m_binaryWriter.Write (header.UserMajor);
216                         m_binaryWriter.Write (header.UserMinor);
217                         m_binaryWriter.Write (header.SubSysMajor);
218                         m_binaryWriter.Write (header.SubSysMinor);
219                         m_binaryWriter.Write (header.Reserved);
220                         m_binaryWriter.Write (header.ImageSize);
221                         m_binaryWriter.Write (header.HeaderSize);
222                         m_binaryWriter.Write (header.FileChecksum);
223                         m_binaryWriter.Write ((ushort) header.SubSystem);
224                         m_binaryWriter.Write (header.DLLFlags);
225                         WriteIntOrLong (header.StackReserveSize);
226                         WriteIntOrLong (header.StackCommitSize);
227                         WriteIntOrLong (header.HeapReserveSize);
228                         WriteIntOrLong (header.HeapCommitSize);
229                         m_binaryWriter.Write (header.LoaderFlags);
230                         m_binaryWriter.Write (header.NumberOfDataDir);
231                 }
232
233                 public override void VisitStandardFieldsHeader (PEOptionalHeader.StandardFieldsHeader header)
234                 {
235                         m_binaryWriter.Write (header.Magic);
236                         m_binaryWriter.Write (header.LMajor);
237                         m_binaryWriter.Write (header.LMinor);
238                         m_binaryWriter.Write (header.CodeSize);
239                         m_binaryWriter.Write (header.InitializedDataSize);
240                         m_binaryWriter.Write (header.UninitializedDataSize);
241                         m_binaryWriter.Write (header.EntryPointRVA.Value);
242                         m_binaryWriter.Write (header.BaseOfCode.Value);
243                         if (!header.IsPE64)
244                                 m_binaryWriter.Write (header.BaseOfData.Value);
245                 }
246
247                 void WriteIntOrLong (ulong value)
248                 {
249                         if (m_img.PEOptionalHeader.StandardFields.IsPE64)
250                                 m_binaryWriter.Write (value);
251                         else
252                                 m_binaryWriter.Write ((uint) value);
253                 }
254
255                 public override void VisitDataDirectoriesHeader (PEOptionalHeader.DataDirectoriesHeader header)
256                 {
257                         m_binaryWriter.Write (header.ExportTable.VirtualAddress);
258                         m_binaryWriter.Write (header.ExportTable.Size);
259                         m_binaryWriter.Write (header.ImportTable.VirtualAddress);
260                         m_binaryWriter.Write (header.ImportTable.Size);
261                         m_binaryWriter.Write (header.ResourceTable.VirtualAddress);
262                         m_binaryWriter.Write (header.ResourceTable.Size);
263                         m_binaryWriter.Write (header.ExceptionTable.VirtualAddress);
264                         m_binaryWriter.Write (header.ExceptionTable.Size);
265                         m_binaryWriter.Write (header.CertificateTable.VirtualAddress);
266                         m_binaryWriter.Write (header.CertificateTable.Size);
267                         m_binaryWriter.Write (header.BaseRelocationTable.VirtualAddress);
268                         m_binaryWriter.Write (header.BaseRelocationTable.Size);
269                         m_binaryWriter.Write (header.Debug.VirtualAddress);
270                         m_binaryWriter.Write (header.Debug.Size);
271                         m_binaryWriter.Write (header.Copyright.VirtualAddress);
272                         m_binaryWriter.Write (header.Copyright.Size);
273                         m_binaryWriter.Write (header.GlobalPtr.VirtualAddress);
274                         m_binaryWriter.Write (header.GlobalPtr.Size);
275                         m_binaryWriter.Write (header.TLSTable.VirtualAddress);
276                         m_binaryWriter.Write (header.TLSTable.Size);
277                         m_binaryWriter.Write (header.LoadConfigTable.VirtualAddress);
278                         m_binaryWriter.Write (header.LoadConfigTable.Size);
279                         m_binaryWriter.Write (header.BoundImport.VirtualAddress);
280                         m_binaryWriter.Write (header.BoundImport.Size);
281                         m_binaryWriter.Write (header.IAT.VirtualAddress);
282                         m_binaryWriter.Write (header.IAT.Size);
283                         m_binaryWriter.Write (header.DelayImportDescriptor.VirtualAddress);
284                         m_binaryWriter.Write (header.DelayImportDescriptor.Size);
285                         m_binaryWriter.Write (header.CLIHeader.VirtualAddress);
286                         m_binaryWriter.Write (header.CLIHeader.Size);
287                         m_binaryWriter.Write (header.Reserved.VirtualAddress);
288                         m_binaryWriter.Write (header.Reserved.Size);
289                 }
290
291                 public override void VisitSection (Section sect)
292                 {
293                         m_binaryWriter.Write (Encoding.ASCII.GetBytes (sect.Name));
294                         int more = 8 - sect.Name.Length;
295                         for (int i = 0; i < more; i++)
296                                 m_binaryWriter.Write ((byte) 0);
297
298                         m_binaryWriter.Write (sect.VirtualSize);
299                         m_binaryWriter.Write (sect.VirtualAddress.Value);
300                         m_binaryWriter.Write (sect.SizeOfRawData);
301                         m_binaryWriter.Write (sect.PointerToRawData.Value);
302                         m_binaryWriter.Write (sect.PointerToRelocations.Value);
303                         m_binaryWriter.Write (sect.PointerToLineNumbers.Value);
304                         m_binaryWriter.Write (sect.NumberOfRelocations);
305                         m_binaryWriter.Write (sect.NumberOfLineNumbers);
306                         m_binaryWriter.Write ((uint) sect.Characteristics);
307                 }
308
309                 public override void VisitImportAddressTable (ImportAddressTable iat)
310                 {
311                         m_textWriter.BaseStream.Position = 0;
312                         m_textWriter.Write (iat.HintNameTableRVA.Value);
313                         m_textWriter.Write (new byte [4]);
314                 }
315
316                 public override void VisitCLIHeader (CLIHeader header)
317                 {
318                         m_textWriter.Write (header.Cb);
319
320                         if (m_mdWriter.TargetRuntime >= TargetRuntime.NET_2_0) {
321                                 m_textWriter.Write ((ushort) 2);
322                                 m_textWriter.Write ((ushort) 5);
323                         } else {
324                                 m_textWriter.Write ((ushort) 2);
325                                 m_textWriter.Write ((ushort) 0);
326                         }
327
328                         m_textWriter.Write (header.Metadata.VirtualAddress);
329                         m_textWriter.Write (header.Metadata.Size);
330                         m_textWriter.Write ((uint) header.Flags);
331                         m_textWriter.Write (header.EntryPointToken);
332                         m_textWriter.Write (header.Resources.VirtualAddress);
333                         m_textWriter.Write (header.Resources.Size);
334                         m_textWriter.Write (header.StrongNameSignature.VirtualAddress);
335                         m_textWriter.Write (header.StrongNameSignature.Size);
336                         m_textWriter.Write (header.CodeManagerTable.VirtualAddress);
337                         m_textWriter.Write (header.CodeManagerTable.Size);
338                         m_textWriter.Write (header.VTableFixups.VirtualAddress);
339                         m_textWriter.Write (header.VTableFixups.Size);
340                         m_textWriter.Write (header.ExportAddressTableJumps.VirtualAddress);
341                         m_textWriter.Write (header.ExportAddressTableJumps.Size);
342                         m_textWriter.Write (header.ManagedNativeHeader.VirtualAddress);
343                         m_textWriter.Write (header.ManagedNativeHeader.Size);
344                 }
345
346                 public override void VisitDebugHeader (DebugHeader header)
347                 {
348                         m_textWriter.BaseStream.Position = m_mdWriter.DebugHeaderPosition;
349                         uint sizeUntilData = 0x1c;
350                         header.AddressOfRawData = m_img.TextSection.VirtualAddress + m_mdWriter.DebugHeaderPosition + sizeUntilData;
351                         header.PointerToRawData = 0x200 + m_mdWriter.DebugHeaderPosition + sizeUntilData;
352                         header.SizeOfData = 0x18 + (uint) header.FileName.Length + 1;
353
354                         m_textWriter.Write (header.Characteristics);
355                         m_textWriter.Write (header.TimeDateStamp);
356                         m_textWriter.Write (header.MajorVersion);
357                         m_textWriter.Write (header.MinorVersion);
358                         m_textWriter.Write ((uint) header.Type);
359                         m_textWriter.Write (header.SizeOfData);
360                         m_textWriter.Write (header.AddressOfRawData.Value);
361                         m_textWriter.Write (header.PointerToRawData);
362
363                         m_textWriter.Write (header.Magic);
364                         m_textWriter.Write (header.Signature.ToByteArray ());
365                         m_textWriter.Write (header.Age);
366                         m_textWriter.Write (Encoding.ASCII.GetBytes (header.FileName));
367                         m_textWriter.Write ((byte) 0);
368                 }
369
370                 public override void VisitImportTable (ImportTable it)
371                 {
372                         m_textWriter.BaseStream.Position = m_mdWriter.ImportTablePosition;
373                         m_textWriter.Write (it.ImportLookupTable.Value);
374                         m_textWriter.Write (it.DateTimeStamp);
375                         m_textWriter.Write (it.ForwardChain);
376                         m_textWriter.Write (it.Name.Value);
377                         m_textWriter.Write (it.ImportAddressTable.Value);
378                         m_textWriter.Write (new byte [20]);
379                 }
380
381                 public override void VisitImportLookupTable (ImportLookupTable ilt)
382                 {
383                         m_textWriter.Write (ilt.HintNameRVA.Value);
384                         m_textWriter.Write (new byte [16]);
385                 }
386
387                 public override void VisitHintNameTable (HintNameTable hnt)
388                 {
389                         m_textWriter.Write (hnt.Hint);
390                         m_textWriter.Write (Encoding.ASCII.GetBytes (hnt.RuntimeMain));
391                         m_textWriter.Write ('\0');
392                         m_textWriter.Write (Encoding.ASCII.GetBytes (hnt.RuntimeLibrary));
393                         m_textWriter.Write ('\0');
394                         m_textWriter.Write (new byte [4]);
395
396                         // patch header with ep rva
397                         RVA ep = m_img.TextSection.VirtualAddress +
398                                 (uint) m_textWriter.BaseStream.Position;
399                         long pos = m_binaryWriter.BaseStream.Position;
400                         m_binaryWriter.BaseStream.Position = 0xa8;
401                         m_binaryWriter.Write (ep.Value);
402                         m_binaryWriter.BaseStream.Position = pos;
403
404                         // patch reloc Sect with ep
405                         uint reloc = (ep.Value + 2) % 0x1000;
406                         uint rva = (ep.Value + 2) - reloc;
407
408                         m_relocWriter.BaseStream.Position = 0;
409                         m_relocWriter.Write (rva);
410                         m_relocWriter.BaseStream.Position = 8;
411                         m_relocWriter.Write ((ushort) ((3 << 12) | reloc));
412
413                         m_textWriter.Write (hnt.EntryPoint);
414                         m_textWriter.Write (hnt.RVA);
415                 }
416
417                 public override void TerminateImage (Image img)
418                 {
419                         m_binaryWriter.BaseStream.Position = 0x200;
420
421                         WriteSection (m_textSect, m_textWriter);
422                         WriteSection (m_relocSect, m_relocWriter);
423                         if (m_rsrcSect != null)
424                                 WriteSection (m_rsrcSect, m_rsrcWriter);
425                 }
426
427                 void WriteSection (Section sect, MemoryBinaryWriter sectWriter)
428                 {
429                         sectWriter.MemoryStream.WriteTo (m_binaryWriter.BaseStream);
430                         m_binaryWriter.Write (new byte [
431                                                 sect.SizeOfRawData - sectWriter.BaseStream.Length]);
432                 }
433         }
434 }