xref: /reactos/dll/win32/mscoree/assembly.c (revision 84ccccab)
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
287 HRESULT assembly_get_runtime_version(ASSEMBLY *assembly, LPSTR *version)
288 {
289     *version = assembly->metadatahdr->Version;
290 
291     return S_OK;
292 }
293 
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