2003-04-24 Miguel de Icaza <miguel@ximian.com>
[mono.git] / mcs / class / Mono.PEToolkit / Image.cs
1 /*\r
2  * Copyright (c) 2002 Sergey Chaban <serge@wildwestsoftware.com>\r
3  */\r
4 \r
5 using System;\r
6 using System.IO;\r
7 using System.Collections;\r
8 using System.Runtime.InteropServices;\r
9 \r
10 using Mono.PEToolkit.Metadata;\r
11 \r
12 namespace Mono.PEToolkit {\r
13 \r
14         public class Image : IDisposable {\r
15 \r
16                 internal DOSHeader dosHdr;\r
17                 internal COFFHeader coffHdr;\r
18                 internal PEHeader peHdr;\r
19 \r
20                 internal CorHeader corHdr;\r
21 \r
22                 internal Hashtable sections;\r
23                 // File position right after PEHeader (NT Optional Header).\r
24                 protected long sectionsPos;\r
25 \r
26                 private MetaDataRoot mdRoot;\r
27 \r
28                 private string name;\r
29                 private bool open;\r
30                 internal BinaryReader reader;\r
31 \r
32                 public Image(string name)\r
33                 {\r
34                         this.name = name;\r
35                         open = false;\r
36                         reader = null;\r
37 \r
38                         mdRoot = null;\r
39 \r
40                         dosHdr = new DOSHeader();\r
41                         coffHdr = new COFFHeader();\r
42                         peHdr = new PEHeader();\r
43                         corHdr = new CorHeader();\r
44 \r
45                         sections = new Hashtable();\r
46                         sectionsPos = -1;\r
47                 }\r
48 \r
49                 ~Image()\r
50                 {\r
51                         Close();\r
52                 }\r
53 \r
54 \r
55                 public Hashtable Sections {\r
56                         get {\r
57                                 return sections;\r
58                         }\r
59                 }\r
60 \r
61                 public void Open()\r
62                 {\r
63                         lock (this) if (!open) {\r
64                                 FileInfo pe = new FileInfo(name);\r
65                                 if (!pe.Exists) {\r
66                                         throw new Exception("Invalid file path.");\r
67                                 }\r
68 \r
69                                 reader = new BinaryReader(new BufferedStream(pe.OpenRead()));\r
70                                 if (!reader.BaseStream.CanSeek) {\r
71                                         throw new Exception("Can't seek.");\r
72                                 }\r
73 \r
74                                 open = true;\r
75                         }\r
76                 }\r
77 \r
78                 public void Close()\r
79                 {\r
80                         lock (this) if (open) {\r
81                                 reader.Close();\r
82                                 open = false;\r
83                         }\r
84                 }\r
85 \r
86                 // IDisposable\r
87                 public void Dispose()\r
88                 {\r
89                         Close();\r
90                 }\r
91 \r
92 \r
93                 public bool IsCLI {\r
94                         get {\r
95                                 return peHdr.IsCLIImage;\r
96                         }\r
97                 }\r
98 \r
99                 public MetaDataRoot MetadataRoot {\r
100                         get {\r
101                                 return mdRoot;\r
102                         }\r
103                 }\r
104 \r
105                 /// <summary>\r
106                 /// </summary>\r
107                 public void ReadHeaders()\r
108                 {\r
109                         if (!open) {\r
110                                 throw new Exception("You must open image before trying to read it.");\r
111                         }\r
112 \r
113                         dosHdr.Read(reader);\r
114                         reader.BaseStream.Position = dosHdr.Lfanew;\r
115                         ExeSignature peSig = (ExeSignature) reader.ReadUInt16();\r
116                         if (peSig != ExeSignature.NT) {\r
117                                 throw new Exception ("Invalid image format: cannot find PE signature.");\r
118                         }\r
119                         peSig = (ExeSignature) reader.ReadUInt16();\r
120                         if (peSig != ExeSignature.NT2) {\r
121                                 throw new Exception ("Invalid image format: cannot find PE signature.");\r
122                         }\r
123 \r
124                         coffHdr.Read(reader);\r
125                         peHdr.Read(reader);\r
126                 \r
127                         sectionsPos = reader.BaseStream.Position;\r
128                         ReadSections();\r
129                         \r
130                         if (this.IsCLI) {\r
131                                 \r
132                                 reader.BaseStream.Position = RVAToVA(peHdr.CLIHdrDir.virtAddr);\r
133                                 corHdr.Read (reader);\r
134 \r
135                                 mdRoot = new MetaDataRoot(this);\r
136                                 reader.BaseStream.Position = RVAToVA(corHdr.MetaData.virtAddr);\r
137                                 mdRoot.Read(reader);\r
138                         }\r
139                         \r
140                 }\r
141 \r
142                 public void WriteHeaders (BinaryWriter writer)\r
143                 {\r
144                         dosHdr.Write (writer);\r
145                         writer.BaseStream.Position = dosHdr.Lfanew;\r
146                         writer.Write ((ushort)ExeSignature.NT);\r
147                         writer.Write ((ushort)ExeSignature.NT2);\r
148                         \r
149                         coffHdr.Write (writer);\r
150                         peHdr.Write (writer);\r
151                 \r
152                         WriteSections (writer);\r
153                         \r
154                         if (this.IsCLI) {\r
155                                 \r
156                                 writer.BaseStream.Position = RVAToVA (peHdr.CLIHdrDir.virtAddr);\r
157                                 corHdr.Write (writer);\r
158 \r
159                                 long pos = RVAToVA (corHdr.MetaData.virtAddr);\r
160                                 writer.BaseStream.Position = pos;\r
161                                 mdRoot.Write (writer);\r
162                                 \r
163                         }\r
164                         \r
165                 }\r
166 \r
167                 /// <summary>\r
168                 /// </summary>\r
169                 protected void ReadSections()\r
170                 {\r
171                         if (sectionsPos < 0) {\r
172                                 throw new Exception("Read headers first.");\r
173                         }\r
174                         reader.BaseStream.Position = sectionsPos;\r
175 \r
176                         int n = coffHdr.NumberOfSections;\r
177                         for (int i = n; --i >=0;) {\r
178                                 Section sect = new Section();\r
179                                 sect.Read(reader);\r
180                                 sections [sect.Name] = sect;\r
181                         }\r
182                 }\r
183 \r
184                 protected void WriteSections (BinaryWriter writer)\r
185                 {\r
186                         foreach (Section section in sections.Values) {\r
187                                 section.Write (writer);\r
188                         }\r
189                 }\r
190 \r
191 \r
192                 /// <summary>\r
193                 /// </summary>\r
194                 /// <param name="writer"></param>\r
195                 public void Dump(TextWriter writer)\r
196                 {\r
197                         writer.WriteLine (\r
198                                 "COFF Header:" + Environment.NewLine +\r
199                                 coffHdr.ToString() + Environment.NewLine +\r
200                                 "PE Header:" + Environment.NewLine +\r
201                                 peHdr.ToString() + Environment.NewLine +\r
202                                 "Core Header:" + Environment.NewLine +\r
203                                 corHdr.ToString()\r
204                         );\r
205                 }\r
206 \r
207 \r
208                 /// <summary>\r
209                 /// </summary>\r
210                 /// <returns></returns>\r
211                 public override string ToString()\r
212                 {\r
213                         StringWriter sw = new StringWriter();\r
214                         Dump(sw);\r
215                         return sw.ToString();\r
216                 }\r
217 \r
218 \r
219                 /// <summary>\r
220                 ///  Returns name of the section for the given RVA.\r
221                 /// </summary>\r
222                 /// <param name="rva"></param>\r
223                 /// <returns></returns>\r
224                 public string RVAToSectionName(RVA rva)\r
225                 {\r
226                         string res = null;\r
227                         foreach (Section s in Sections.Values) {\r
228                                 RVA sva = s.VirtualAddress;\r
229                                 if (rva >= sva && rva < sva + s.SizeOfRawData) {\r
230                                         res = s.Name;\r
231                                         break;\r
232                                 }\r
233                         }\r
234                         return res;\r
235                 }\r
236 \r
237                 public long RVAToVA(RVA rva)\r
238                 {\r
239                         string sectName = RVAToSectionName(rva);\r
240                         long res = 0;\r
241                         if (sectName != null) {\r
242                                 Section s = (Section) Sections [sectName];\r
243                                 res = rva + (s.PointerToRawData - s.VirtualAddress);\r
244                         }\r
245                         return res;\r
246                 }\r
247 \r
248                 public MetaDataRoot MetaDataRoot {\r
249                         get {\r
250                                 return mdRoot;\r
251                         }\r
252                 }\r
253 \r
254                 public void DumpStreamHeader(TextWriter writer, string name)\r
255                 {\r
256                         if (mdRoot == null || name == null || name == String.Empty || writer == null) return;\r
257                         writer.Write(name + " header: ");\r
258                         MDStream s = MetaDataRoot.Streams[name] as MDStream;\r
259                         if (s != null) {\r
260                                 writer.WriteLine();\r
261                                 writer.WriteLine(s);\r
262                         } else {\r
263                                 writer.WriteLine("not present.");\r
264                                 writer.WriteLine();\r
265                         }\r
266                 }\r
267 \r
268                 public void DumpStreamHeader(string name)\r
269                 {\r
270                         DumpStreamHeader(Console.Out, name);\r
271                 }\r
272 \r
273         }\r
274 \r
275 }\r
276 \r