1 /*
2 * assembly parser
3 *
4 * Copyright 2008 James Hawkins
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 */
20
21 #include "mscoree_private.h"
22
23 #include <winver.h>
24 #include <dbghelp.h>
25
26 typedef struct
27 {
28 ULONG Signature;
29 USHORT MajorVersion;
30 USHORT MinorVersion;
31 ULONG Reserved;
32 ULONG VersionLength;
33 LPSTR Version;
34 BYTE Flags;
35 WORD Streams;
36 } METADATAHDR;
37
38 typedef struct
39 {
40 DWORD Offset;
41 DWORD Size;
42 } METADATASTREAMHDR;
43
44 typedef struct tagCLRTABLE
45 {
46 INT rows;
47 DWORD offset;
48 } CLRTABLE;
49
50 struct tagASSEMBLY
51 {
52 int is_mapped_file;
53
54 /* mapped files */
55 LPWSTR path;
56 HANDLE hfile;
57 HANDLE hmap;
58
59 BYTE *data;
60
61 IMAGE_NT_HEADERS *nthdr;
62 IMAGE_COR20_HEADER *corhdr;
63
64 METADATAHDR *metadatahdr;
65 };
66
strdupW(LPCWSTR src)67 static inline LPWSTR strdupW(LPCWSTR src)
68 {
69 LPWSTR dest;
70
71 if (!src)
72 return NULL;
73
74 dest = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(src) + 1) * sizeof(WCHAR));
75 if (dest)
76 lstrcpyW(dest, src);
77
78 return dest;
79 }
80
assembly_rva_to_va(ASSEMBLY * assembly,ULONG rva)81 static void* assembly_rva_to_va(ASSEMBLY *assembly, ULONG rva)
82 {
83 if (assembly->is_mapped_file)
84 return ImageRvaToVa(assembly->nthdr, assembly->data, rva, NULL);
85 else
86 return assembly->data + rva;
87 }
88
assembly_datadir_get_data(ASSEMBLY * assembly,IMAGE_DATA_DIRECTORY * datadir,void ** data)89 static ULONG assembly_datadir_get_data(ASSEMBLY *assembly,
90 IMAGE_DATA_DIRECTORY *datadir, void **data)
91 {
92 if (!datadir->VirtualAddress || !datadir->Size)
93 {
94 *data = NULL;
95 return 0;
96 }
97 else
98 {
99 *data = assembly_rva_to_va(assembly, datadir->VirtualAddress);
100 return datadir->Size;
101 }
102 }
103
parse_metadata_header(ASSEMBLY * assembly,DWORD * hdrsz)104 static HRESULT parse_metadata_header(ASSEMBLY *assembly, DWORD *hdrsz)
105 {
106 METADATAHDR *metadatahdr;
107 BYTE *ptr, *dest;
108 DWORD size, ofs;
109 ULONG rva;
110
111 rva = assembly->corhdr->MetaData.VirtualAddress;
112 ptr = assembly_rva_to_va(assembly, rva);
113 if (!ptr)
114 return E_FAIL;
115
116 metadatahdr = (METADATAHDR *)ptr;
117
118 assembly->metadatahdr = HeapAlloc(GetProcessHeap(), 0, sizeof(METADATAHDR));
119 if (!assembly->metadatahdr)
120 return E_OUTOFMEMORY;
121
122 size = FIELD_OFFSET(METADATAHDR, Version);
123 memcpy(assembly->metadatahdr, metadatahdr, size);
124
125 assembly->metadatahdr->Version = (LPSTR)&metadatahdr->Version;
126
127 ofs = FIELD_OFFSET(METADATAHDR, Flags);
128 ptr += FIELD_OFFSET(METADATAHDR, Version) + metadatahdr->VersionLength + 1;
129 dest = (BYTE *)assembly->metadatahdr + ofs;
130 memcpy(dest, ptr, sizeof(METADATAHDR) - ofs);
131
132 *hdrsz = sizeof(METADATAHDR) - sizeof(LPSTR) + metadatahdr->VersionLength + 1;
133
134 return S_OK;
135 }
136
parse_clr_metadata(ASSEMBLY * assembly)137 static HRESULT parse_clr_metadata(ASSEMBLY *assembly)
138 {
139 HRESULT hr;
140 DWORD hdrsz;
141
142 hr = parse_metadata_header(assembly, &hdrsz);
143 if (FAILED(hr))
144 return hr;
145
146 return S_OK;
147 }
148
parse_pe_header(ASSEMBLY * assembly)149 static HRESULT parse_pe_header(ASSEMBLY *assembly)
150 {
151 IMAGE_DATA_DIRECTORY *datadirs;
152
153 assembly->nthdr = ImageNtHeader(assembly->data);
154 if (!assembly->nthdr)
155 return E_FAIL;
156
157 if (assembly->nthdr->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC)
158 {
159 IMAGE_OPTIONAL_HEADER64 *opthdr =
160 (IMAGE_OPTIONAL_HEADER64 *)&assembly->nthdr->OptionalHeader;
161 datadirs = opthdr->DataDirectory;
162 }
163 else
164 {
165 IMAGE_OPTIONAL_HEADER32 *opthdr =
166 (IMAGE_OPTIONAL_HEADER32 *)&assembly->nthdr->OptionalHeader;
167 datadirs = opthdr->DataDirectory;
168 }
169
170 if (!datadirs)
171 return E_FAIL;
172
173 if (!assembly_datadir_get_data(assembly, &datadirs[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR], (void**)&assembly->corhdr))
174 return E_FAIL;
175
176 return S_OK;
177 }
178
parse_headers(ASSEMBLY * assembly)179 static HRESULT parse_headers(ASSEMBLY *assembly)
180 {
181 HRESULT hr;
182
183 hr = parse_pe_header(assembly);
184
185 if (SUCCEEDED(hr))
186 hr = parse_clr_metadata(assembly);
187
188 return hr;
189 }
190
assembly_create(ASSEMBLY ** out,LPCWSTR file)191 HRESULT assembly_create(ASSEMBLY **out, LPCWSTR file)
192 {
193 ASSEMBLY *assembly;
194 HRESULT hr;
195
196 *out = NULL;
197
198 assembly = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ASSEMBLY));
199 if (!assembly)
200 return E_OUTOFMEMORY;
201
202 assembly->is_mapped_file = 1;
203
204 assembly->path = strdupW(file);
205 if (!assembly->path)
206 {
207 hr = E_OUTOFMEMORY;
208 goto failed;
209 }
210
211 assembly->hfile = CreateFileW(file, GENERIC_READ, FILE_SHARE_READ,
212 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
213 if (assembly->hfile == INVALID_HANDLE_VALUE)
214 {
215 hr = HRESULT_FROM_WIN32(GetLastError());
216 goto failed;
217 }
218
219 assembly->hmap = CreateFileMappingW(assembly->hfile, NULL, PAGE_READONLY,
220 0, 0, NULL);
221 if (!assembly->hmap)
222 {
223 hr = HRESULT_FROM_WIN32(GetLastError());
224 goto failed;
225 }
226
227 assembly->data = MapViewOfFile(assembly->hmap, FILE_MAP_READ, 0, 0, 0);
228 if (!assembly->data)
229 {
230 hr = HRESULT_FROM_WIN32(GetLastError());
231 goto failed;
232 }
233
234 hr = parse_headers(assembly);
235 if (FAILED(hr)) goto failed;
236
237 *out = assembly;
238 return S_OK;
239
240 failed:
241 assembly_release(assembly);
242 return hr;
243 }
244
assembly_from_hmodule(ASSEMBLY ** out,HMODULE hmodule)245 HRESULT assembly_from_hmodule(ASSEMBLY **out, HMODULE hmodule)
246 {
247 ASSEMBLY *assembly;
248 HRESULT hr;
249
250 *out = NULL;
251
252 assembly = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ASSEMBLY));
253 if (!assembly)
254 return E_OUTOFMEMORY;
255
256 assembly->is_mapped_file = 0;
257
258 assembly->data = (BYTE*)hmodule;
259
260 hr = parse_headers(assembly);
261 if (SUCCEEDED(hr))
262 *out = assembly;
263 else
264 assembly_release(assembly);
265
266 return hr;
267 }
268
assembly_release(ASSEMBLY * assembly)269 HRESULT assembly_release(ASSEMBLY *assembly)
270 {
271 if (!assembly)
272 return S_OK;
273
274 if (assembly->is_mapped_file)
275 {
276 UnmapViewOfFile(assembly->data);
277 CloseHandle(assembly->hmap);
278 CloseHandle(assembly->hfile);
279 }
280 HeapFree(GetProcessHeap(), 0, assembly->metadatahdr);
281 HeapFree(GetProcessHeap(), 0, assembly->path);
282 HeapFree(GetProcessHeap(), 0, assembly);
283
284 return S_OK;
285 }
286
assembly_get_runtime_version(ASSEMBLY * assembly,LPSTR * version)287 HRESULT assembly_get_runtime_version(ASSEMBLY *assembly, LPSTR *version)
288 {
289 *version = assembly->metadatahdr->Version;
290
291 return S_OK;
292 }
293
assembly_get_vtable_fixups(ASSEMBLY * assembly,VTableFixup ** fixups,DWORD * count)294 HRESULT assembly_get_vtable_fixups(ASSEMBLY *assembly, VTableFixup **fixups, DWORD *count)
295 {
296 ULONG size;
297
298 size = assembly_datadir_get_data(assembly, &assembly->corhdr->VTableFixups, (void**)fixups);
299 *count = size / sizeof(VTableFixup);
300
301 return S_OK;
302 }
303