1 // ****************************************************************
2 // Copyright 2007, Charlie Poole
3 // This is free software licensed under the NUnit license. You may
4 // obtain a copy of the license at http://nunit.org/?p=license&r=2.4
5 // ****************************************************************
7 using System
.Reflection
;
14 /// AssemblyReader knows how to find various things in an assembly header
16 public class AssemblyReader
: IDisposable
18 private string assemblyPath
;
19 private BinaryReader rdr
;
20 private FileStream fs
;
22 UInt16 dos_magic
= 0xffff;
23 uint pe_signature
= 0xffffffff;
24 UInt16 numDataSections
;
25 UInt16 optionalHeaderSize
;
27 private uint peHeader
= 0;
28 private uint fileHeader
= 0;
29 private uint optionalHeader
= 0;
30 private uint dataDirectory
= 0;
31 private uint dataSections
= 0;
33 private struct Section
35 public uint virtualAddress
;
36 public uint virtualSize
;
37 public uint fileOffset
;
40 private Section
[] sections
;
42 public AssemblyReader( string assemblyPath
)
44 this.assemblyPath
= assemblyPath
;
48 public AssemblyReader( Assembly assembly
)
50 this.assemblyPath
= TestFixtureBuilder
.GetAssemblyPath( assembly
);
54 private void CalcHeaderOffsets()
56 this.fs
= new FileStream( assemblyPath
, FileMode
.Open
, FileAccess
.Read
);
57 this.rdr
= new BinaryReader( fs
);
58 dos_magic
= rdr
.ReadUInt16();
59 if ( dos_magic
== 0x5a4d )
62 peHeader
= rdr
.ReadUInt32();
63 fileHeader
= peHeader
+ 4;
64 optionalHeader
= fileHeader
+ 20;
65 dataDirectory
= optionalHeader
+ 96;
66 // dotNetDirectoryEntry = dataDirectory + 14 * 8;
68 fs
.Position
= peHeader
;
69 pe_signature
= rdr
.ReadUInt32();
70 rdr
.ReadUInt16(); // machine
71 numDataSections
= rdr
.ReadUInt16();
73 optionalHeaderSize
= rdr
.ReadUInt16();
74 dataSections
= optionalHeader
+ optionalHeaderSize
;
76 sections
= new Section
[numDataSections
];
77 fs
.Position
= dataSections
;
78 for( int i
= 0; i
< numDataSections
; i
++ )
81 sections
[i
].virtualSize
= rdr
.ReadUInt32();
82 sections
[i
].virtualAddress
= rdr
.ReadUInt32();
83 uint rawDataSize
= rdr
.ReadUInt32();
84 sections
[i
].fileOffset
= rdr
.ReadUInt32();
85 if ( sections
[i
].virtualSize
== 0 )
86 sections
[i
].virtualSize
= rawDataSize
;
93 private uint DataDirectoryRva( int n
)
95 fs
.Position
= dataDirectory
+ n
* 8;
96 return rdr
.ReadUInt32();
99 private uint RvaToLfa( uint rva
)
101 for( int i
= 0; i
< numDataSections
; i
++ )
102 if ( rva
>= sections
[i
].virtualAddress
&& rva
< sections
[i
].virtualAddress
+ sections
[i
].virtualSize
)
103 return rva
- sections
[i
].virtualAddress
+ sections
[i
].fileOffset
;
108 public string AssemblyPath
110 get { return assemblyPath; }
113 public bool IsValidPeFile
115 get { return dos_magic == 0x5a4d && pe_signature == 0x00004550; }
118 public bool IsDotNetFile
120 get { return IsValidPeFile && DataDirectoryRva(14) != 0; }
123 public string ImageRuntimeVersion
127 string runtimeVersion
= string.Empty
;
129 uint rva
= DataDirectoryRva(14);
132 fs
.Position
= RvaToLfa( rva
) + 8;
133 uint metadata
= rdr
.ReadUInt32();
134 fs
.Position
= RvaToLfa( metadata
);
135 if ( rdr
.ReadUInt32() == 0x424a5342 )
137 // Copy string representing runtime version
139 StringBuilder sb
= new StringBuilder();
141 while ((c
= rdr
.ReadChar())!= '\0')
144 if (sb
[0] == 'v') // Last sanity check
145 runtimeVersion
= sb
.ToString();
147 // Could do fixups here for bad values in older files
148 // like 1.x86, 1.build, etc. But we are only using
149 // the major version anyway
153 return runtimeVersion
;
157 public void Dispose()