2010-04-02 Jb Evain <jbevain@novell.com>
[mcs.git] / class / PEAPI / PEAPI.cs
blobb389b3cb8bfe543a4ab9e9dbe316025d3487e888
1 using System;
2 using System.IO;
3 using System.Collections;
4 using System.Text;
6 namespace PEAPI {
8 /**************************************************************************/
9 /// <summary>
10 /// Image for a PEFile
11 /// File Structure
12 /// DOS Header (128 bytes)
13 /// PE Signature ("PE\0\0")
14 /// PEFileHeader (20 bytes)
15 /// PEOptionalHeader (224 bytes)
16 /// SectionHeaders (40 bytes * NumSections)
17 ///
18 /// Sections .text (always present - contains metadata)
19 /// .sdata (contains any initialised data in the file - may not be present)
20 /// (for ilams /debug this contains the Debug table)
21 /// .reloc (always present - in pure CIL only has one fixup)
22 /// others??? c# produces .rsrc section containing a Resource Table
23 ///
24 /// .text layout
25 /// IAT (single entry 8 bytes for pure CIL)
26 /// CLIHeader (72 bytes)
27 /// CIL instructions for all methods (variable size)
28 /// MetaData
29 /// Root (20 bytes + UTF-8 Version String + quad align padding)
30 /// StreamHeaders (8 bytes + null terminated name string + quad align padding)
31 /// Streams
32 /// #~ (always present - holds metadata tables)
33 /// #Strings (always present - holds identifier strings)
34 /// #US (Userstring heap)
35 /// #Blob (signature blobs)
36 /// #GUID (guids for assemblies or Modules)
37 /// ImportTable (40 bytes)
38 /// ImportLookupTable(8 bytes) (same as IAT for standard CIL files)
39 /// Hint/Name Tables with entry "_CorExeMain" for .exe file and "_CorDllMain" for .dll (14 bytes)
40 /// ASCII string "mscoree.dll" referenced in ImportTable (+ padding = 16 bytes)
41 /// Entry Point (0xFF25 followed by 4 bytes 0x400000 + RVA of .text)
42 ///
43 /// #~ stream structure
44 /// Header (24 bytes)
45 /// Rows (4 bytes * numTables)
46 /// Tables
47 /// </summary>
48 internal class FileImage : BinaryWriter {
50 internal readonly static uint[] iByteMask = {0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000};
51 internal readonly static ulong[] lByteMask = {0x00000000000000FF, 0x000000000000FF00,
52 0x0000000000FF0000, 0x00000000FF000000,
53 0x000000FF00000000, 0x0000FF0000000000,
54 0x00FF000000000000, 0xFF00000000000000 };
55 internal readonly static uint nibble0Mask = 0x0000000F;
56 internal readonly static uint nibble1Mask = 0x000000F0;
58 private static readonly byte[] DOSHeader = { 0x4d,0x5a,0x90,0x00,0x03,0x00,0x00,0x00,
59 0x04,0x00,0x00,0x00,0xff,0xff,0x00,0x00,
60 0xb8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
61 0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
62 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
63 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
64 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
65 0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,
66 0x0e,0x1f,0xba,0x0e,0x00,0xb4,0x09,0xcd,
67 0x21,0xb8,0x01,0x4c,0xcd,0x21,0x54,0x68,
68 0x69,0x73,0x20,0x70,0x72,0x6f,0x67,0x72,
69 0x61,0x6d,0x20,0x63,0x61,0x6e,0x6e,0x6f,
70 0x74,0x20,0x62,0x65,0x20,0x72,0x75,0x6e,
71 0x20,0x69,0x6e,0x20,0x44,0x4f,0x53,0x20,
72 0x6d,0x6f,0x64,0x65,0x2e,0x0d,0x0d,0x0a,
73 0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
74 0x50,0x45,0x00,0x00};
75 private static byte[] PEHeader = { 0x4c, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
76 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
77 0xE0, 0x00, 0x0E, 0x01, // PE Header Standard Fields
78 0x0B, 0x01, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
79 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
80 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
83 private static readonly uint minFileAlign = 0x200;
84 private static readonly uint maxFileAlign = 0x1000;
85 private static readonly uint fileHeaderSize = 0x178;
86 private static readonly uint sectionHeaderSize = 40;
87 private static readonly uint SectionAlignment = 0x2000;
88 private static readonly uint ImageBase = 0x400000;
89 private static readonly uint ImportTableSize = 40;
90 private static readonly uint IATSize = 8;
91 private static readonly uint CLIHeaderSize = 72;
92 private uint runtimeFlags = 0x01; // COMIMAGE_FLAGS_ILONLY
93 // 32BITREQUIRED 0x02, STRONGNAMESIGNED 0x08, TRACKDEBUGDATA 0x10000
94 private static readonly uint StrongNameSignatureSize = 128;
95 private bool reserveStrongNameSignatureSpace = false;
97 private static readonly uint relocFlags = 0x42000040;
98 private static readonly ushort exeCharacteristics = 0x010E;
99 private static readonly ushort dllCharacteristics = 0x210E;
100 // section names are all 8 bytes
101 private static readonly string textName = ".text\0\0\0";
102 private static readonly string sdataName = ".sdata\0\0";
103 private static readonly string relocName = ".reloc\0\0";
104 private static readonly string rsrcName = ".rsrc\0\0\0";
105 private static readonly string exeHintNameTable = "\0\0_CorExeMain\0";
106 private static readonly string dllHintNameTable = "\0\0_CorDllMain\0";
107 private static readonly string runtimeEngineName = "mscoree.dll\0\0";
109 private Section text, sdata, rsrc;
110 ArrayList data;
111 BinaryWriter reloc = new BinaryWriter(new MemoryStream());
112 uint dateStamp = 0;
113 DateTime origin = new DateTime(1970,1,1);
114 uint numSections = 2; // always have .text and .reloc sections
115 internal SubSystem subSys = SubSystem.Windows_CUI; // default is Windows Console mode
116 internal long stackReserve = 0x100000; // default is 1Mb
117 internal uint fileAlign = minFileAlign;
118 uint entryPointOffset, entryPointPadding, imageSize, headerSize, headerPadding, entryPointToken = 0;
119 uint relocOffset, relocRVA, relocSize, relocPadding, relocTide, hintNameTableOffset;
120 uint metaDataOffset, runtimeEngineOffset, initDataSize = 0, importTablePadding;
121 uint resourcesSize, resourcesOffset;
122 uint strongNameSigOffset;
123 uint importTableOffset, importLookupTableOffset, totalImportTableSize;
124 MetaData metaData;
125 char[] runtimeEngine = runtimeEngineName.ToCharArray(), hintNameTable;
126 bool doDLL, largeStrings, largeGUID, largeUS, largeBlob;
127 ushort characteristics;
129 internal FileImage(bool makeDLL, string fileName) : base(new FileStream(fileName,FileMode.Create))
131 InitFileImage(makeDLL);
132 TimeSpan tmp = System.IO.File.GetCreationTime(fileName).Subtract(origin);
133 dateStamp = Convert.ToUInt32(tmp.TotalSeconds);
136 internal FileImage(bool makeDLL, Stream str) : base(str)
138 InitFileImage(makeDLL);
139 TimeSpan tmp = DateTime.Now.Subtract(origin);
140 dateStamp = Convert.ToUInt32(tmp.TotalSeconds);
143 private void InitFileImage(bool makeDLL)
145 doDLL = makeDLL;
146 if (doDLL) {
147 hintNameTable = dllHintNameTable.ToCharArray();
148 characteristics = dllCharacteristics;
149 } else {
150 hintNameTable = exeHintNameTable.ToCharArray();
151 characteristics = exeCharacteristics;
153 text = new Section(textName,0x60000020); // IMAGE_SCN_CNT CODE, EXECUTE, READ
154 // rsrc = new Section(rsrcName,0x40000040); // IMAGE_SCN_CNT INITIALIZED_DATA, READ
155 metaData = new MetaData(this);
158 internal MetaData GetMetaData()
160 return metaData;
163 private uint GetNextSectStart(uint rva, uint tide)
165 uint c = tide / SectionAlignment;
166 if ((tide % SectionAlignment) != 0)
167 c++;
168 return rva + (c * SectionAlignment);
171 private void BuildTextSection()
173 // .text layout
174 // IAT (single entry 8 bytes for pure CIL)
175 // CLIHeader (72 bytes)
176 // CIL instructions for all methods (variable size)
177 // MetaData
178 // ImportTable (40 bytes)
179 // ImportLookupTable(8 bytes) (same as IAT for standard CIL files)
180 // Hint/Name Tables with entry "_CorExeMain" for .exe file and "_CorDllMain" for .dll (14 bytes)
181 // ASCII string "mscoree.dll" referenced in ImportTable (+ padding = 16 bytes)
182 // Entry Point (0xFF25 followed by 4 bytes 0x400000 + RVA of .text)
183 metaData.BuildMetaData(IATSize + CLIHeaderSize);
184 metaDataOffset = IATSize + CLIHeaderSize;
185 // Console.WriteLine("Code starts at " + metaDataOffset);
186 metaDataOffset += metaData.CodeSize();
187 // resourcesStart =
188 resourcesOffset = metaDataOffset + metaData.Size ();
189 resourcesSize = metaData.GetResourcesSize ();
190 if (reserveStrongNameSignatureSpace) {
191 strongNameSigOffset = resourcesOffset + resourcesSize;
192 // fixUps = RVA for vtable
193 importTableOffset = strongNameSigOffset + StrongNameSignatureSize;
194 } else {
195 strongNameSigOffset = 0;
196 // fixUps = RVA for vtable
197 importTableOffset = resourcesOffset + resourcesSize;
199 importTablePadding = NumToAlign(importTableOffset,16);
200 importTableOffset += importTablePadding;
201 importLookupTableOffset = importTableOffset + ImportTableSize;
202 hintNameTableOffset = importLookupTableOffset + IATSize;
203 runtimeEngineOffset = hintNameTableOffset + (uint)hintNameTable.Length;
204 entryPointOffset = runtimeEngineOffset + (uint)runtimeEngine.Length;
205 totalImportTableSize = entryPointOffset - importTableOffset;
206 // Console.WriteLine("total import table size = " + totalImportTableSize);
207 // Console.WriteLine("entrypoint offset = " + entryPointOffset);
208 entryPointPadding = NumToAlign(entryPointOffset,4) + 2;
209 entryPointOffset += entryPointPadding;
210 text.AddReloc(entryPointOffset+2);
211 text.IncTide(entryPointOffset + 6);
212 //if (text.Tide() < fileAlign) fileAlign = minFileAlign;
213 text.SetSize(NumToAlign(text.Tide(),fileAlign));
214 // Console.WriteLine("text size = " + text.Size() + " text tide = " + text.Tide() + " text padding = " + text.Padding());
215 // Console.WriteLine("metaDataOffset = " + Hex.Int(metaDataOffset));
216 // Console.WriteLine("importTableOffset = " + Hex.Int(importTableOffset));
217 // Console.WriteLine("importLookupTableOffset = " + Hex.Int(importLookupTableOffset));
218 // Console.WriteLine("hintNameTableOffset = " + Hex.Int(hintNameTableOffset));
219 // Console.WriteLine("runtimeEngineOffset = " + Hex.Int(runtimeEngineOffset));
220 // Console.WriteLine("entryPointOffset = " + Hex.Int(entryPointOffset));
221 // Console.WriteLine("entryPointPadding = " + Hex.Int(entryPointPadding));
225 internal void BuildRelocSection()
227 text.DoRelocs(reloc);
228 if (sdata != null) sdata.DoRelocs(reloc);
229 if (rsrc != null) rsrc.DoRelocs(reloc);
230 relocTide = (uint)reloc.Seek(0,SeekOrigin.Current);
231 relocPadding = NumToAlign(relocTide,fileAlign);
232 relocSize = relocTide + relocPadding;
233 imageSize = relocRVA + SectionAlignment;
234 initDataSize += relocSize;
237 private void CalcOffsets()
239 if (sdata != null)
240 numSections++;
241 if (rsrc != null)
242 numSections++;
243 headerSize = fileHeaderSize + (numSections * sectionHeaderSize);
244 headerPadding = NumToAlign(headerSize,fileAlign);
245 headerSize += headerPadding;
246 uint offset = headerSize;
247 uint rva = SectionAlignment;
248 text.SetOffset(offset);
249 text.SetRVA(rva);
250 offset += text.Size();
251 rva = GetNextSectStart(rva,text.Tide());
252 // Console.WriteLine("headerSize = " + headerSize);
253 // Console.WriteLine("headerPadding = " + headerPadding);
254 // Console.WriteLine("textOffset = " + Hex.Int(text.Offset()));
255 if (sdata != null) {
256 sdata.SetSize(NumToAlign(sdata.Tide(),fileAlign));
257 sdata.SetOffset(offset);
258 sdata.SetRVA(rva);
259 offset += sdata.Size();
260 rva = GetNextSectStart(rva,sdata.Tide());
261 initDataSize += sdata.Size();
263 if (rsrc != null) {
264 rsrc.SetSize(NumToAlign(rsrc.Tide(),fileAlign));
265 rsrc.SetOffset(offset);
266 rsrc.SetRVA(rva);
267 offset += rsrc.Size();
268 rva = GetNextSectStart(rva,rsrc.Tide());
269 initDataSize += rsrc.Size();
271 relocOffset = offset;
272 relocRVA = rva;
275 internal void MakeFile()
277 if (doDLL) hintNameTable = dllHintNameTable.ToCharArray();
278 else hintNameTable = exeHintNameTable.ToCharArray();
279 BuildTextSection();
280 CalcOffsets();
281 BuildRelocSection();
282 // now write it out
283 WriteHeader();
284 WriteSections();
285 Flush();
286 Close();
289 private void WriteHeader()
291 Write(DOSHeader);
292 // Console.WriteLine("Writing PEHeader at offset " + Seek(0,SeekOrigin.Current));
293 WritePEHeader();
294 // Console.WriteLine("Writing text section header at offset " + Hex.Long(Seek(0,SeekOrigin.Current)));
295 text.WriteHeader(this,relocRVA);
296 if (sdata != null) sdata.WriteHeader(this,relocRVA);
297 if (rsrc != null) rsrc.WriteHeader(this,relocRVA);
298 // Console.WriteLine("Writing reloc section header at offset " + Seek(0,SeekOrigin.Current));
299 WriteRelocSectionHeader();
300 // Console.WriteLine("Writing padding at offset " + Seek(0,SeekOrigin.Current));
301 WriteZeros(headerPadding);
304 private void WriteSections()
306 // Console.WriteLine("Writing text section at offset " + Seek(0,SeekOrigin.Current));
307 WriteTextSection();
308 if (sdata != null) WriteSDataSection();
309 if (rsrc != null) WriteRsrcSection();
310 WriteRelocSection();
313 private void WriteIAT()
315 Write(text.RVA() + hintNameTableOffset);
316 Write(0);
319 private void WriteImportTables()
321 // Import Table
322 WriteZeros(importTablePadding);
323 // Console.WriteLine("Writing import tables at offset " + Hex.Long(Seek(0,SeekOrigin.Current)));
324 Write(importLookupTableOffset + text.RVA());
325 WriteZeros(8);
326 Write(runtimeEngineOffset + text.RVA());
327 Write(text.RVA()); // IAT is at the beginning of the text section
328 WriteZeros(20);
329 // Import Lookup Table
330 WriteIAT(); // lookup table and IAT are the same
331 // Hint/Name Table
332 // Console.WriteLine("Writing hintname table at " + Hex.Long(Seek(0,SeekOrigin.Current)));
333 Write(hintNameTable);
334 Write(runtimeEngineName.ToCharArray());
337 private void WriteTextSection()
339 WriteIAT();
340 WriteCLIHeader();
341 // Console.WriteLine("Writing code at " + Hex.Long(Seek(0,SeekOrigin.Current)));
342 metaData.WriteByteCodes(this);
343 // Console.WriteLine("Finished writing code at " + Hex.Long(Seek(0,SeekOrigin.Current)));
344 largeStrings = metaData.LargeStringsIndex();
345 largeGUID = metaData.LargeGUIDIndex();
346 largeUS = metaData.LargeUSIndex();
347 largeBlob = metaData.LargeBlobIndex();
348 metaData.WriteMetaData(this);
349 metaData.WriteResources (this);
350 if (reserveStrongNameSignatureSpace) {
351 WriteZeros(StrongNameSignatureSize);
353 WriteImportTables();
354 WriteZeros(entryPointPadding);
355 Write((ushort)0x25FF);
356 Write(ImageBase + text.RVA());
357 WriteZeros(text.Padding());
360 private void WriteCLIHeader()
362 Write(CLIHeaderSize); // Cb
363 Write((short)2); // Major runtime version
364 Write((short)0); // Minor runtime version
365 Write(text.RVA() + metaDataOffset);
366 Write(metaData.Size());
367 Write(runtimeFlags);
368 Write(entryPointToken);
369 if (resourcesSize > 0) {
370 Write (text.RVA () + resourcesOffset);
371 Write (resourcesSize);
372 } else {
373 WriteZeros (8);
375 // Strong Name Signature (RVA, size)
376 if (reserveStrongNameSignatureSpace) {
377 Write(text.RVA() + strongNameSigOffset);
378 Write(StrongNameSignatureSize);
379 } else {
380 WriteZeros(8);
382 WriteZeros(8); // CodeManagerTable
383 WriteZeros(8); // VTableFixups NYI
384 WriteZeros(16); // ExportAddressTableJumps, ManagedNativeHeader
387 private void WriteSDataSection()
389 long size = sdata.Size ();
390 long start = BaseStream.Position;
391 for (int i=0; i < data.Count; i++) {
392 ((DataConstant)data[i]).Write(this);
394 while (BaseStream.Position < (start + size))
395 Write ((byte) 0);
398 private void WriteRsrcSection()
402 private void WriteRelocSection()
404 // Console.WriteLine("Writing reloc section at " + Seek(0,SeekOrigin.Current) + " = " + relocOffset);
405 MemoryStream str = (MemoryStream)reloc.BaseStream;
406 Write(str.ToArray());
407 WriteZeros(NumToAlign((uint)str.Position,fileAlign));
410 internal void SetEntryPoint(uint entryPoint)
412 entryPointToken = entryPoint;
415 internal void AddInitData(DataConstant cVal)
417 if (sdata == null) {
418 sdata = new Section(sdataName,0xC0000040); // IMAGE_SCN_CNT INITIALIZED_DATA, READ, WRITE
419 data = new ArrayList();
421 data.Add(cVal);
422 cVal.DataOffset = sdata.Tide();
423 sdata.IncTide(cVal.GetSize());
426 internal void WriteZeros(uint numZeros)
428 for (int i=0; i < numZeros; i++) {
429 Write((byte)0);
433 internal void WritePEHeader()
435 Write((ushort)0x014C); // Machine - always 0x14C for Managed PE Files (allow others??)
436 Write((ushort)numSections);
437 Write(dateStamp);
438 WriteZeros(8); // Pointer to Symbol Table and Number of Symbols (always zero for ECMA CLI files)
439 Write((ushort)0x00E0); // Size of Optional Header
440 Write(characteristics);
441 // PE Optional Header
442 Write((ushort)0x010B); // Magic
443 Write((byte)0x6); // LMajor pure-IL = 6 C++ = 7
444 Write((byte)0x0); // LMinor
445 Write(text.Size());
446 Write(initDataSize);
447 Write(0); // Check other sections here!!
448 Write(text.RVA() + entryPointOffset);
449 Write(text.RVA());
450 uint dataBase = 0;
451 if (sdata != null) dataBase = sdata.RVA();
452 else if (rsrc != null) dataBase = rsrc.RVA();
453 else dataBase = relocRVA;
454 Write(dataBase);
455 Write(ImageBase);
456 Write(SectionAlignment);
457 Write(fileAlign);
458 Write((ushort)0x04); // OS Major
459 WriteZeros(6); // OS Minor, User Major, User Minor
460 Write((ushort)0x04); // SubSys Major
461 WriteZeros(6); // SybSys Minor, Reserved
462 Write(imageSize);
463 Write(headerSize);
464 Write((int)0); // File Checksum
465 Write((ushort)subSys);
466 Write((short)0); // DLL Flags
467 Write((uint)stackReserve); // Stack Reserve Size
468 Write((uint)0x1000); // Stack Commit Size
469 Write((uint)0x100000); // Heap Reserve Size
470 Write((uint)0x1000); // Heap Commit Size
471 Write(0); // Loader Flags
472 Write(0x10); // Number of Data Directories
473 WriteZeros(8); // Export Table
474 Write(importTableOffset + text.RVA());
475 Write(totalImportTableSize);
476 WriteZeros(24); // Resource, Exception and Certificate Tables
477 Write(relocRVA);
478 Write(relocTide);
479 WriteZeros(48); // Debug, Copyright, Global Ptr, TLS, Load Config and Bound Import Tables
480 Write(text.RVA()); // IATRVA - IAT is at start of .text Section
481 Write(IATSize);
482 WriteZeros(8); // Delay Import Descriptor
483 Write(text.RVA()+IATSize); // CLIHeader immediately follows IAT
484 Write(CLIHeaderSize);
485 WriteZeros(8); // Reserved
488 internal void WriteRelocSectionHeader()
490 Write(relocName.ToCharArray());
491 Write(relocTide);
492 Write(relocRVA);
493 Write(relocSize);
494 Write(relocOffset);
495 WriteZeros(12);
496 Write(relocFlags);
499 private void Align (MemoryStream str, int val)
501 if ((str.Position % val) != 0) {
502 for (int i=val - (int)(str.Position % val); i > 0; i--) {
503 str.WriteByte(0);
508 private uint Align(uint val, uint alignVal)
510 if ((val % alignVal) != 0) {
511 val += alignVal - (val % alignVal);
513 return val;
516 private uint NumToAlign(uint val, uint alignVal)
518 if ((val % alignVal) == 0) return 0;
519 return alignVal - (val % alignVal);
522 internal void StringsIndex(uint ix)
524 if (largeStrings) Write(ix);
525 else Write((ushort)ix);
528 internal void GUIDIndex(uint ix)
530 if (largeGUID) Write(ix);
531 else Write((ushort)ix);
534 internal void USIndex(uint ix)
536 if (largeUS) Write(ix);
537 else Write((ushort)ix);
540 internal void BlobIndex(uint ix)
542 if (largeBlob) Write(ix);
543 else Write((ushort)ix);
546 internal void WriteIndex(MDTable tabIx,uint ix)
548 if (metaData.LargeIx(tabIx)) Write(ix);
549 else Write((ushort)ix);
552 internal void WriteCodedIndex(CIx code, MetaDataElement elem)
554 metaData.WriteCodedIndex(code,elem,this);
557 internal void WriteCodeRVA(uint offs)
559 Write(text.RVA() + offs);
562 internal void WriteDataRVA(uint offs)
564 Write(sdata.RVA() + offs);
567 internal void Write3Bytes(uint val)
569 byte b3 = (byte)((val & FileImage.iByteMask[2]) >> 16);
570 byte b2 = (byte)((val & FileImage.iByteMask[1]) >> 8);;
571 byte b1 = (byte)(val & FileImage.iByteMask[0]);
572 Write(b1);
573 Write(b2);
574 Write(b3);
577 internal bool ReserveStrongNameSignatureSpace {
578 get { return reserveStrongNameSignatureSpace; }
579 set { reserveStrongNameSignatureSpace = value; }
584 /**************************************************************************/
585 /// <summary>
586 /// Base class for the PEFile (starting point)
587 /// </summary>
588 public class PEFile {
590 private static readonly string mscorlibName = "mscorlib";
591 private Module thisMod;
592 private ClassDef moduleClass;
593 private ArrayList classRefList = new ArrayList();
594 private ArrayList classDefList = new ArrayList();
595 private ArrayList resources = new ArrayList ();
596 private Assembly thisAssembly;
597 private static bool isMSCorlib;
598 private int corFlags = 1;
599 FileImage fileImage;
600 MetaData metaData;
602 /// <summary>
603 /// Create a new PEFile. Each PEFile is a module.
604 /// </summary>
605 /// <param name="name">module name, also used for the file name</param>
606 /// <param name="isDLL">create a .dll or .exe file</param>
607 /// <param name="hasAssembly">this file is an assembly and
608 /// will contain the assembly manifest. The assembly name is the
609 /// same as the module name</param>
610 public PEFile(string name, bool isDLL, bool hasAssembly)
611 : this (name, null, isDLL, hasAssembly, null, null)
613 // Console.WriteLine(Hex.Byte(0x12));
614 // Console.WriteLine(Hex.Short(0x1234));
615 // Console.WriteLine(Hex.Int(0x12345678));
618 /// <summary>
619 /// Create a new PEFile. Each PEFile is a module.
620 /// </summary>
621 /// <param name="name">module name, also used for the file name</param>
622 /// <param name="isDLL">create a .dll or .exe file</param>
623 /// <param name="hasAssembly">this file is an assembly and
624 /// will contain the assembly manifest. The assembly name is the
625 /// same as the module name</param>
626 /// <param name="outputDir">write the PEFile to this directory. If this
627 /// string is null then the output will be to the current directory</param>
628 public PEFile(string name, bool isDLL, bool hasAssembly, string outputDir)
629 : this (name, null, isDLL, hasAssembly, outputDir, null)
631 // Console.WriteLine(Hex.Byte(0x12));
632 // Console.WriteLine(Hex.Short(0x1234));
633 // Console.WriteLine(Hex.Int(0x12345678));
636 /// <summary>
637 /// Create a new PEFile
638 /// </summary>
639 /// <param name="name">module name</param>
640 /// <param name="isDLL">create a .dll or .exe</param>
641 /// <param name="hasAssembly">this PEfile is an assembly and
642 /// will contain the assemly manifest. The assembly name is the
643 /// same as the module name</param>
644 /// <param name="outStream">write the PEFile to this stream instead
645 /// of to a new file</param>
646 public PEFile(string name, bool isDLL, bool hasAssembly, Stream outStream)
647 : this (name, null, isDLL, hasAssembly, null, outStream)
651 public PEFile(string name, string module_name, bool isDLL, bool hasAssembly, Stream outStream)
652 : this (name, module_name, isDLL, hasAssembly, null, outStream)
656 public PEFile(string name, string module_name, bool isDLL, bool hasAssembly, string outputDir, Stream outStream)
658 SetName (name);
659 string fname = module_name == null ? MakeFileName (outputDir, name, isDLL) : module_name;
660 if (outStream == null)
661 fileImage = new FileImage (isDLL, fname);
662 else
663 fileImage = new FileImage (isDLL, outStream);
665 InitPEFile (name, fname, hasAssembly);
668 private void SetName (string name)
670 if (name == "mscorlib")
671 isMSCorlib = true;
674 private void InitPEFile(string name, string fName, bool hasAssembly)
676 metaData = fileImage.GetMetaData();
677 thisMod = new Module(fName,metaData);
678 if (hasAssembly) {
679 thisAssembly = new Assembly(name,metaData);
680 metaData.AddToTable(MDTable.Assembly,thisAssembly);
682 moduleClass = AddClass(TypeAttr.Private,"","<Module>");
683 moduleClass.SpecialNoSuper();
684 metaData.AddToTable(MDTable.Module,thisMod);
687 internal static bool IsMSCorlib {
688 get { return isMSCorlib; }
691 public ClassDef ModuleClass {
692 get { return moduleClass; }
695 /// <summary>
696 /// Set the subsystem (.subsystem) (Default is Windows Console mode)
697 /// </summary>
698 /// <param name="subS">subsystem value</param>
699 public void SetSubSystem(SubSystem subS)
701 fileImage.subSys = subS;
704 /// <summary>
705 /// Set the flags (.corflags)
706 /// </summary>
707 /// <param name="flags">the flags value</param>
708 public void SetCorFlags(int flags)
710 corFlags = flags;
713 public void SetStackReserve (long stackReserve)
715 fileImage.stackReserve = stackReserve;
718 private string MakeFileName(string dirName, string name, bool isDLL)
720 string result = "";
721 if ((dirName != null) && (dirName.CompareTo("") != 0)) {
722 result = dirName;
723 if (!dirName.EndsWith("\\")) result += "\\";
725 result += name;
727 // if (isDLL) result += ".dll"; else result += ".exe";
729 return result;
732 /// <summary>
733 /// Add an external assembly to this PEFile (.assembly extern)
734 /// </summary>
735 /// <param name="assemName">the external assembly name</param>
736 /// <returns>a descriptor for this external assembly</returns>
737 public AssemblyRef AddExternAssembly(string assemName)
739 if (assemName.CompareTo(mscorlibName) == 0) return metaData.mscorlib;
740 AssemblyRef anAssem = new AssemblyRef(metaData,assemName);
741 metaData.AddToTable(MDTable.AssemblyRef,anAssem);
742 // Console.WriteLine("Adding assembly " + assemName);
743 return anAssem;
746 /// <summary>
747 /// Add an external module to this PEFile (.module extern)
748 /// </summary>
749 /// <param name="name">the external module name</param>
750 /// <returns>a descriptor for this external module</returns>
751 public ModuleRef AddExternModule(string name)
753 ModuleRef modRef = new ModuleRef(metaData,name);
754 metaData.AddToTable(MDTable.ModuleRef,modRef);
755 return modRef;
758 /// <summary>
759 /// Add a "global" method to this module
760 /// </summary>
761 /// <param name="name">method name</param>
762 /// <param name="retType">return type</param>
763 /// <param name="pars">method parameters</param>
764 /// <returns>a descriptor for this new "global" method</returns>
765 public MethodDef AddMethod (string name, Param ret_param, Param [] pars)
767 return moduleClass.AddMethod (name, ret_param, pars);
770 public MethodDef AddMethod(string name, Type retType, Param[] pars)
772 return AddMethod (name, new Param (ParamAttr.Default, "", retType), pars);
775 /// <summary>
776 /// Add a "global" method to this module
777 /// </summary>
778 /// <param name="mAtts">method attributes</param>
779 /// <param name="iAtts">method implementation attributes</param>
780 /// <param name="name">method name</param>
781 /// <param name="retType">return type</param>
782 /// <param name="pars">method parameters</param>
783 /// <returns>a descriptor for this new "global" method</returns>
784 public MethodDef AddMethod (MethAttr mAtts, ImplAttr iAtts, string name, Param ret_param, Param [] pars)
786 return moduleClass.AddMethod (mAtts, iAtts, name, ret_param, pars);
789 public MethodDef AddMethod(MethAttr mAtts, ImplAttr iAtts, string name, Type retType, Param[] pars)
791 return AddMethod (mAtts, iAtts, name, new Param (ParamAttr.Default, "", retType), pars);
794 public MethodRef AddMethodToTypeSpec (Type item, string name, Type retType, Type[] pars)
796 return AddMethodToTypeSpec (item, name, retType, pars, 0);
799 public MethodRef AddMethodToTypeSpec (Type item, string name, Type retType, Type[] pars, int gen_param_count)
801 MethodRef meth = new MethodRef (item.GetTypeSpec (metaData), name, retType, pars, false, null, gen_param_count);
802 metaData.AddToTable (MDTable.MemberRef,meth);
803 return meth;
806 public MethodRef AddVarArgMethodToTypeSpec (Type item, string name, Type retType,
807 Type[] pars, Type[] optPars) {
808 MethodRef meth = new MethodRef(item.GetTypeSpec (metaData), name,retType,pars,true,optPars, 0);
809 metaData.AddToTable(MDTable.MemberRef,meth);
810 return meth;
813 public FieldRef AddFieldToTypeSpec (Type item, string name, Type fType)
815 FieldRef field = new FieldRef (item.GetTypeSpec (metaData), name,fType);
816 metaData.AddToTable (MDTable.MemberRef,field);
817 return field;
820 public Method AddMethodSpec (Method m, GenericMethodSig g_sig)
822 MethodSpec ms = new MethodSpec (m, g_sig);
823 metaData.AddToTable (MDTable.MethodSpec, ms);
824 return ms;
827 /// <summary>
828 /// Add a "global" field to this module
829 /// </summary>
830 /// <param name="name">field name</param>
831 /// <param name="fType">field type</param>
832 /// <returns>a descriptor for this new "global" field</returns>
833 public FieldDef AddField(string name, Type fType)
835 return moduleClass.AddField(name,fType);
838 /// <summary>
839 /// Add a "global" field to this module
840 /// </summary>
841 /// <param name="attrSet">attributes of this field</param>
842 /// <param name="name">field name</param>
843 /// <param name="fType">field type</param>
844 /// <returns>a descriptor for this new "global" field</returns>
845 public FieldDef AddField(FieldAttr attrSet, string name, Type fType)
847 return moduleClass.AddField(attrSet,name,fType);
850 /// <summary>
851 /// Add a class to this module
852 /// </summary>
853 /// <param name="attrSet">attributes of this class</param>
854 /// <param name="nsName">name space name</param>
855 /// <param name="name">class name</param>
856 /// <returns>a descriptor for this new class</returns>
857 public ClassDef AddClass(TypeAttr attrSet, string nsName, string name)
859 return AddClass (attrSet, nsName, name, null);
862 /// <summary>
863 /// Add a class which extends System.ValueType to this module
864 /// </summary>
865 /// <param name="attrSet">attributes of this class</param>
866 /// <param name="nsName">name space name</param>
867 /// <param name="name">class name</param>
868 /// <returns>a descriptor for this new class</returns>
869 public ClassDef AddValueClass(TypeAttr attrSet, string nsName, string name, ValueClass vClass)
871 ClassDef aClass = new ClassDef(attrSet,nsName,name,metaData);
872 if (!ClassDef.IsValueType (nsName, name) && !ClassDef.IsEnum (nsName, name)) {
873 aClass.MakeValueClass(vClass);
874 } else {
875 if (ClassDef.IsEnum (nsName, name))
876 aClass.SetSuper (metaData.mscorlib.ValueType ());
877 else
878 aClass.SetSuper (metaData.mscorlib.GetSpecialSystemClass (PrimitiveType.Object));
880 metaData.mscorlib.SetSpecialSystemClass (nsName, name, aClass);
882 aClass.SetTypeIndex (PrimitiveType.ValueType.GetTypeIndex ());
883 metaData.AddToTable(MDTable.TypeDef,aClass);
884 return aClass;
887 /// <summary>
888 /// Add a class to this module
889 /// </summary>
890 /// <param name="attrSet">attributes of this class</param>
891 /// <param name="nsName">name space name</param>
892 /// <param name="name">class name</param>
893 /// <param name="superType">super type of this class (extends)</param>
894 /// <returns>a descriptor for this new class</returns>
895 public ClassDef AddClass(TypeAttr attrSet, string nsName, string name, Class superType)
897 ClassDef aClass = new ClassDef(attrSet,nsName,name,metaData);
898 if (superType != null)
899 aClass.SetSuper(superType);
900 if (PEFile.IsMSCorlib)
901 metaData.mscorlib.SetSpecialSystemClass (nsName, name, aClass);
902 metaData.AddToTable(MDTable.TypeDef,aClass);
903 return aClass;
906 public void AddGenericClass (GenericTypeInst gti)
908 metaData.AddToTable (MDTable.TypeSpec, gti);
911 public void AddGenericParam (GenParam param)
913 metaData.AddToTable (MDTable.TypeSpec, param);
916 public FileRef AddFile(string fName, byte[] hashBytes, bool hasMetaData, bool entryPoint)
918 FileRef file = new FileRef(fName,hashBytes,hasMetaData,entryPoint,metaData);
919 metaData.AddToTable(MDTable.File,file);
920 return file;
923 /// <summary>
924 /// Add a manifest resource to this PEFile NOT YET IMPLEMENTED
925 /// </summary>
926 /// <param name="mr"></param>
927 public void AddManifestResource(ManifestResource mr)
929 metaData.AddToTable(MDTable.ManifestResource,mr);
930 resources.Add (mr);
931 //mr.FixName(metaData);
934 public void AddCustomAttribute (Method meth, byte [] data, MetaDataElement element)
936 metaData.AddCustomAttribute (new CustomAttribute (element, meth, data));
937 element.HasCustomAttr = true;
940 public void AddDeclSecurity (SecurityAction sec_action, byte [] data, MetaDataElement element)
942 metaData.AddDeclSecurity (new DeclSecurity (element, (ushort) sec_action, data));
945 public void AddDeclSecurity (SecurityAction sec_action, PEAPI.PermissionSet ps, MetaDataElement element)
947 metaData.AddDeclSecurity (new DeclSecurity_20 (element, (ushort) sec_action, ps));
950 /// <summary>
951 /// Add a managed resource from another assembly.
952 /// </summary>
953 /// <param name="resName">The name of the resource</param>
954 /// <param name="assem">The assembly where the resource is</param>
955 /// <param name="isPublic">Access for the resource</param>
956 public void AddExternalManagedResource (string resName, AssemblyRef assem, uint flags)
958 resources.Add (new ManifestResource (resName, flags, assem));
961 /// <summary>
962 /// Add a managed resource from another assembly.
963 /// </summary>
964 /// <param name="mr"></param>
965 /// <param name="isPublic"></param>
966 public void AddExternalManagedResource (ManifestResource mr)
968 resources.Add (new ManifestResource (mr));
970 /// <summary>
971 /// Find a resource
972 /// </summary>
973 /// <param name="name">The name of the resource</param>
974 /// <returns>The resource with the name "name" or null </returns>
975 public ManifestResource GetResource (string name)
977 for (int i = 0; i < resources.Count; i ++) {
978 if (((ManifestResource) resources [i]).Name == name)
979 return (ManifestResource) resources [i];
981 return null;
984 public ManifestResource [] GetResources()
986 return (ManifestResource []) resources.ToArray (typeof (ManifestResource));
989 /// <summary>
990 /// Write out the PEFile (the "bake" function)
991 /// </summary>
992 public void WritePEFile() { /* the "bake" function */
993 if (thisAssembly != null)
994 fileImage.ReserveStrongNameSignatureSpace = thisAssembly.HasPublicKey;
995 fileImage.MakeFile();
998 /// <summary>
999 /// Get the descriptor of this module
1000 /// </summary>
1001 /// <returns>the descriptor for this module</returns>
1002 public Module GetThisModule()
1004 return thisMod;
1007 /// <summary>
1008 /// Get the descriptor for this assembly. The PEFile must have been
1009 /// created with hasAssembly = true
1010 /// </summary>
1011 /// <returns>the descriptor for this assembly</returns>
1012 public Assembly GetThisAssembly()
1014 return thisAssembly;
1019 /**************************************************************************/
1020 /// <summary>
1021 /// Descriptor for a Section in a PEFile eg .text, .sdata
1022 /// </summary>
1023 internal class Section {
1024 private static readonly uint relocPageSize = 4096; // 4K pages for fixups
1026 char[] name;
1027 uint offset = 0, tide = 0, size = 0, rva = 0, relocTide = 0;
1028 //uint relocOff = 0;
1029 uint flags = 0, padding = 0;
1030 uint[] relocs;
1032 internal Section(string sName, uint sFlags)
1034 name = sName.ToCharArray();
1035 flags = sFlags;
1038 internal uint Tide() { return tide; }
1040 internal void IncTide(uint incVal) { tide += incVal; }
1042 internal uint Padding() { return padding; }
1044 internal uint Size() { return size; }
1046 internal void SetSize(uint pad)
1048 padding = pad;
1049 size = tide + padding;
1052 internal uint RVA() { return rva; }
1054 internal void SetRVA(uint rva) { this.rva = rva; }
1056 internal uint Offset() { return offset; }
1058 internal void SetOffset(uint offs) { offset = offs; }
1060 internal void DoBlock(BinaryWriter reloc, uint page, int start, int end)
1062 //Console.WriteLine("rva = " + rva + " page = " + page);
1063 reloc.Write(rva + page);
1064 reloc.Write((uint)(((end-start+1)*2) + 8));
1065 for (int j=start; j < end; j++) {
1066 //Console.WriteLine("reloc offset = " + relocs[j]);
1067 reloc.Write((ushort)((0x3 << 12) | (relocs[j] - page)));
1069 reloc.Write((ushort)0);
1072 internal void DoRelocs(BinaryWriter reloc)
1074 if (relocTide > 0) {
1075 //relocOff = (uint)reloc.Seek(0,SeekOrigin.Current);
1076 uint block = (relocs[0]/relocPageSize + 1) * relocPageSize;
1077 int start = 0;
1078 for (int i=1; i < relocTide; i++) {
1079 if (relocs[i] >= block) {
1080 DoBlock(reloc,block-relocPageSize,start,i);
1081 start = i;
1082 block = (relocs[i]/relocPageSize + 1) * relocPageSize;
1085 DoBlock(reloc,block-relocPageSize,start,(int)relocTide);
1089 internal void AddReloc(uint offs)
1091 int pos = 0;
1092 if (relocs == null) {
1093 relocs = new uint[5];
1094 } else {
1095 if (relocTide >= relocs.Length) {
1096 uint[] tmp = relocs;
1097 relocs = new uint[tmp.Length + 5];
1098 for (int i=0; i < relocTide; i++) {
1099 relocs[i] = tmp[i];
1102 while ((pos < relocTide) && (relocs[pos] < offs)) pos++;
1103 for (int i=pos; i < relocTide; i++) {
1104 relocs[i+1] = relocs[i];
1107 relocs[pos] = offs;
1108 relocTide++;
1111 internal void WriteHeader(BinaryWriter output, uint relocRVA)
1113 output.Write(name);
1114 output.Write(tide);
1115 output.Write(rva);
1116 output.Write(size);
1117 output.Write(offset);
1118 output.Write(0);
1119 //output.Write(relocRVA + relocOff);
1120 output.Write(0);
1121 output.Write(0);
1122 //output.Write((ushort)relocTide);
1123 //output.Write((ushort)0);
1124 output.Write(flags);
1129 public class Hex {
1130 readonly static char[] hexDigit = {'0','1','2','3','4','5','6','7',
1131 '8','9','A','B','C','D','E','F'};
1132 readonly static uint[] iByteMask = {0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000};
1133 readonly static ulong[] lByteMask = {0x00000000000000FF, 0x000000000000FF00,
1134 0x0000000000FF0000, 0x00000000FF000000,
1135 0x000000FF00000000, 0x0000FF0000000000,
1136 0x00FF000000000000, 0xFF00000000000000 };
1137 readonly static uint nibble0Mask = 0x0000000F;
1138 readonly static uint nibble1Mask = 0x000000F0;
1140 public static String Byte(int b)
1142 char[] str = new char[2];
1143 uint num = (uint)b;
1144 uint b1 = num & nibble0Mask;
1145 uint b2 = (num & nibble1Mask) >> 4;
1146 str[0] = hexDigit[b2];
1147 str[1] = hexDigit[b1];
1148 return new String(str);
1151 public static String Short(int b)
1153 char[] str = new char[4];
1154 uint num1 = (uint)b & iByteMask[0];
1155 uint num2 = ((uint)b & iByteMask[1]) >> 8;
1156 uint b1 = num1 & nibble0Mask;
1157 uint b2 = (num1 & nibble1Mask) >> 4;
1158 uint b3 = num2 & nibble0Mask;
1159 uint b4 = (num2 & nibble1Mask) >> 4;
1160 str[0] = hexDigit[b4];
1161 str[1] = hexDigit[b3];
1162 str[2] = hexDigit[b2];
1163 str[3] = hexDigit[b1];
1164 return new String(str);
1167 public static String Int(int val)
1169 char[] str = new char[8];
1170 uint num = (uint)val;
1171 int strIx = 7;
1172 for (int i=0; i < iByteMask.Length; i++) {
1173 uint b = num & iByteMask[i];
1174 b >>= (i*8);
1175 uint b1 = b & nibble0Mask;
1176 uint b2 = (b & nibble1Mask) >> 4;
1177 str[strIx--] = hexDigit[b1];
1178 str[strIx--] = hexDigit[b2];
1180 return new String(str);
1183 public static String Int(uint num)
1185 char[] str = new char[8];
1186 int strIx = 7;
1187 for (int i=0; i < iByteMask.Length; i++) {
1188 uint b = num & iByteMask[i];
1189 b >>= (i*8);
1190 uint b1 = b & nibble0Mask;
1191 uint b2 = (b & nibble1Mask) >> 4;
1192 str[strIx--] = hexDigit[b1];
1193 str[strIx--] = hexDigit[b2];
1195 return new String(str);
1198 public static String Long(long lnum)
1200 ulong num = (ulong)lnum;
1201 char[] str = new char[16];
1202 int strIx = 15;
1203 for (int i=0; i < lByteMask.Length; i++) {
1204 ulong b = num & lByteMask[i];
1205 b >>= (i*8);
1206 ulong b1 = b & nibble0Mask;
1207 ulong b2 = (b & nibble1Mask) >> 4;
1208 str[strIx--] = hexDigit[b1];
1209 str[strIx--] = hexDigit[b2];
1211 return new String(str);
1215 /// <summary>
1216 /// Error for invalid PE file
1217 /// </summary>
1218 public class PEFileException : System.Exception {
1219 public PEFileException(string msg) : base(msg) { }
1222 public class NotYetImplementedException : System.Exception {
1223 public NotYetImplementedException(string msg) : base(msg + " Not Yet Implemented") { }
1226 public class TypeSignatureException : System.Exception {
1227 public TypeSignatureException(string msg) : base(msg) { }