* Sync up to trunk HEAD (r62285). Branch guys deserve the significant speedups too ;)
[reactos.git] / dll / win32 / fusion / assembly.c
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 "fusionpriv.h"
22
23 #include <wincrypt.h>
24 #include <dbghelp.h>
25 #include <corhdr.h>
26
27 #define TableFromToken(tk) (TypeFromToken(tk) >> 24)
28 #define TokenFromTable(idx) (idx << 24)
29
30 #define MAX_CLR_TABLES 64
31
32 #define MD_STRINGS_BIT 0x1
33 #define MD_GUIDS_BIT 0x2
34 #define MD_BLOBS_BIT 0x4
35
36 typedef struct tagCLRTABLE
37 {
38 INT rows;
39 DWORD offset;
40 } CLRTABLE;
41
42 struct tagASSEMBLY
43 {
44 LPWSTR path;
45
46 HANDLE hfile;
47 HANDLE hmap;
48 BYTE *data;
49
50 IMAGE_NT_HEADERS *nthdr;
51 IMAGE_COR20_HEADER *corhdr;
52
53 METADATAHDR *metadatahdr;
54
55 METADATATABLESHDR *tableshdr;
56 DWORD numtables;
57 DWORD *numrows;
58 CLRTABLE tables[MAX_CLR_TABLES];
59
60 DWORD stringsz;
61 DWORD guidsz;
62 DWORD blobsz;
63
64 BYTE *strings;
65 BYTE *blobs;
66 };
67
68 static DWORD rva_to_offset(IMAGE_NT_HEADERS *nthdrs, DWORD rva)
69 {
70 DWORD offset = rva, limit;
71 IMAGE_SECTION_HEADER *img;
72 WORD i;
73
74 img = IMAGE_FIRST_SECTION(nthdrs);
75
76 if (rva < img->PointerToRawData)
77 return rva;
78
79 for (i = 0; i < nthdrs->FileHeader.NumberOfSections; i++)
80 {
81 if (img[i].SizeOfRawData)
82 limit = img[i].SizeOfRawData;
83 else
84 limit = img[i].Misc.VirtualSize;
85
86 if (rva >= img[i].VirtualAddress &&
87 rva < (img[i].VirtualAddress + limit))
88 {
89 if (img[i].PointerToRawData != 0)
90 {
91 offset -= img[i].VirtualAddress;
92 offset += img[i].PointerToRawData;
93 }
94
95 return offset;
96 }
97 }
98
99 return 0;
100 }
101
102 static BYTE *GetData(BYTE *pData, ULONG *pLength)
103 {
104 if ((*pData & 0x80) == 0x00)
105 {
106 *pLength = (*pData & 0x7f);
107 return pData + 1;
108 }
109
110 if ((*pData & 0xC0) == 0x80)
111 {
112 *pLength = ((*pData & 0x3f) << 8 | *(pData + 1));
113 return pData + 2;
114 }
115
116 if ((*pData & 0xE0) == 0xC0)
117 {
118 *pLength = ((*pData & 0x1f) << 24 | *(pData + 1) << 16 |
119 *(pData + 2) << 8 | *(pData + 3));
120 return pData + 4;
121 }
122
123 *pLength = (ULONG)-1;
124 return 0;
125 }
126
127 static VOID *assembly_data_offset(ASSEMBLY *assembly, ULONG offset)
128 {
129 return &assembly->data[offset];
130 }
131
132 #define MAX_TABLES_WORD 0xFFFF
133 #define MAX_TABLES_1BIT_ENCODE 32767
134 #define MAX_TABLES_2BIT_ENCODE 16383
135 #define MAX_TABLES_3BIT_ENCODE 8191
136 #define MAX_TABLES_5BIT_ENCODE 2047
137
138 static inline ULONG get_table_size(const ASSEMBLY *assembly, DWORD index)
139 {
140 DWORD size;
141 INT tables;
142
143 switch (TokenFromTable(index))
144 {
145 case mdtModule:
146 {
147 size = sizeof(MODULETABLE) + (assembly->stringsz - sizeof(WORD)) +
148 2 * (assembly->guidsz - sizeof(WORD));
149 break;
150 }
151 case mdtTypeRef:
152 {
153 size = sizeof(TYPEREFTABLE) + 2 * (assembly->stringsz - sizeof(WORD));
154
155 /* ResolutionScope:ResolutionScope */
156 tables = max(assembly->tables[TableFromToken(mdtModule)].rows,
157 assembly->tables[TableFromToken(mdtModuleRef)].rows);
158 tables = max(tables, assembly->tables[TableFromToken(mdtAssemblyRef)].rows);
159 tables = max(tables, assembly->tables[TableFromToken(mdtTypeRef)].rows);
160 size += (tables > MAX_TABLES_2BIT_ENCODE) ? sizeof(WORD) : 0;
161 break;
162 }
163 case mdtTypeDef:
164 {
165 size = sizeof(TYPEDEFTABLE) + 2 * (assembly->stringsz - sizeof(WORD));
166
167 /* Extends:TypeDefOrRef */
168 tables = max(assembly->tables[TableFromToken(mdtTypeDef)].rows,
169 assembly->tables[TableFromToken(mdtTypeRef)].rows);
170 tables = max(tables, assembly->tables[TableFromToken(mdtTypeSpec)].rows);
171 size += (tables > MAX_TABLES_2BIT_ENCODE) ? sizeof(WORD) : 0;
172
173 size += (assembly->tables[TableFromToken(mdtFieldDef)].rows >
174 MAX_TABLES_WORD) ? sizeof(WORD) : 0;
175 size += (assembly->tables[TableFromToken(mdtMethodDef)].rows >
176 MAX_TABLES_WORD) ? sizeof(WORD) : 0;
177 break;
178 }
179 case mdtFieldDef:
180 {
181 size = sizeof(FIELDTABLE) + (assembly->stringsz - sizeof(WORD)) +
182 (assembly->blobsz - sizeof(WORD));
183 break;
184 }
185 case mdtMethodDef:
186 {
187 size = sizeof(METHODDEFTABLE) + (assembly->stringsz - sizeof(WORD)) +
188 (assembly->blobsz - sizeof(WORD));
189
190 size += (assembly->tables[TableFromToken(mdtParamDef)].rows >
191 MAX_TABLES_WORD) ? sizeof(WORD) : 0;
192 break;
193 }
194 case mdtParamDef:
195 {
196 size = sizeof(PARAMTABLE) + (assembly->stringsz - sizeof(WORD));
197 break;
198 }
199 case mdtInterfaceImpl:
200 {
201 size = sizeof(INTERFACEIMPLTABLE);
202
203 /* Interface:TypeDefOrRef */
204 tables = max(assembly->tables[TableFromToken(mdtTypeDef)].rows,
205 assembly->tables[TableFromToken(mdtTypeRef)].rows);
206 tables = max(tables, assembly->tables[TableFromToken(mdtTypeSpec)].rows);
207 size += (tables > MAX_TABLES_2BIT_ENCODE) ? sizeof(WORD) : 0;
208 break;
209 }
210 case mdtMemberRef:
211 {
212 size = sizeof(MEMBERREFTABLE) + (assembly->stringsz - sizeof(WORD)) +
213 (assembly->blobsz - sizeof(WORD));
214
215 /* Class:MemberRefParent */
216 tables = max(assembly->tables[TableFromToken(mdtTypeRef)].rows,
217 assembly->tables[TableFromToken(mdtModuleRef)].rows);
218 tables = max(tables, assembly->tables[TableFromToken(mdtMethodDef)].rows);
219 tables = max(tables, assembly->tables[TableFromToken(mdtTypeSpec)].rows);
220 tables = max(tables, assembly->tables[TableFromToken(mdtTypeDef)].rows);
221 size += (tables > MAX_TABLES_3BIT_ENCODE) ? sizeof(WORD) : 0;
222 break;
223 }
224 case 0x0B000000: /* FIXME */
225 {
226 size = sizeof(CONSTANTTABLE) + (assembly->blobsz - sizeof(WORD));
227
228 /* Parent:HasConstant */
229 tables = max(assembly->tables[TableFromToken(mdtParamDef)].rows,
230 assembly->tables[TableFromToken(mdtFieldDef)].rows);
231 tables = max(tables, assembly->tables[TableFromToken(mdtProperty)].rows);
232 size += (tables > MAX_TABLES_2BIT_ENCODE) ? sizeof(WORD) : 0;
233 break;
234 }
235 case mdtCustomAttribute:
236 {
237 size = sizeof(CUSTOMATTRIBUTETABLE) + (assembly->blobsz - sizeof(WORD));
238
239 /* Parent:HasCustomAttribute */
240 tables = max(assembly->tables[TableFromToken(mdtMethodDef)].rows,
241 assembly->tables[TableFromToken(mdtFieldDef)].rows);
242 tables = max(tables, assembly->tables[TableFromToken(mdtTypeRef)].rows);
243 tables = max(tables, assembly->tables[TableFromToken(mdtTypeDef)].rows);
244 tables = max(tables, assembly->tables[TableFromToken(mdtParamDef)].rows);
245 tables = max(tables, assembly->tables[TableFromToken(mdtInterfaceImpl)].rows);
246 tables = max(tables, assembly->tables[TableFromToken(mdtMemberRef)].rows);
247 tables = max(tables, assembly->tables[TableFromToken(mdtPermission)].rows);
248 tables = max(tables, assembly->tables[TableFromToken(mdtProperty)].rows);
249 tables = max(tables, assembly->tables[TableFromToken(mdtEvent)].rows);
250 tables = max(tables, assembly->tables[TableFromToken(mdtSignature)].rows);
251 tables = max(tables, assembly->tables[TableFromToken(mdtModuleRef)].rows);
252 tables = max(tables, assembly->tables[TableFromToken(mdtTypeSpec)].rows);
253 tables = max(tables, assembly->tables[TableFromToken(mdtAssembly)].rows);
254 tables = max(tables, assembly->tables[TableFromToken(mdtFile)].rows);
255 tables = max(tables, assembly->tables[TableFromToken(mdtExportedType)].rows);
256 tables = max(tables, assembly->tables[TableFromToken(mdtManifestResource)].rows);
257 size += (tables > MAX_TABLES_5BIT_ENCODE) ? sizeof(WORD) : 0;
258
259 /* Type:CustomAttributeType */
260 tables = max(assembly->tables[TableFromToken(mdtMethodDef)].rows,
261 assembly->tables[TableFromToken(mdtMemberRef)].rows);
262 size += (tables > MAX_TABLES_3BIT_ENCODE) ? sizeof(WORD) : 0;
263 break;
264 }
265 case 0x0D000000: /* FIXME */
266 {
267 size = sizeof(FIELDMARSHALTABLE) + (assembly->blobsz - sizeof(WORD));
268
269 /* Parent:HasFieldMarshal */
270 tables = max(assembly->tables[TableFromToken(mdtFieldDef)].rows,
271 assembly->tables[TableFromToken(mdtParamDef)].rows);
272 size += (tables > MAX_TABLES_1BIT_ENCODE) ? sizeof(WORD) : 0;
273 break;
274 }
275 case mdtPermission:
276 {
277 size = sizeof(DECLSECURITYTABLE) + (assembly->blobsz - sizeof(WORD));
278
279 /* Parent:HasDeclSecurity */
280 tables = max(assembly->tables[TableFromToken(mdtTypeDef)].rows,
281 assembly->tables[TableFromToken(mdtMethodDef)].rows);
282 tables = max(tables, assembly->tables[TableFromToken(mdtAssembly)].rows);
283 size += (tables > MAX_TABLES_2BIT_ENCODE) ? sizeof(WORD) : 0;
284 break;
285 }
286 case 0x0F000000: /* FIXME */
287 {
288 size = sizeof(CLASSLAYOUTTABLE);
289 size += (assembly->tables[TableFromToken(mdtTypeDef)].rows >
290 MAX_TABLES_WORD) ? sizeof(WORD) : 0;
291 break;
292 }
293 case 0x10000000: /* FIXME */
294 {
295 size = sizeof(FIELDLAYOUTTABLE);
296 size += (assembly->tables[TableFromToken(mdtFieldDef)].rows >
297 MAX_TABLES_WORD) ? sizeof(WORD) : 0;
298 break;
299 }
300 case mdtSignature:
301 {
302 size = sizeof(STANDALONESIGTABLE) + (assembly->blobsz - sizeof(WORD));
303 break;
304 }
305 case 0x12000000: /* FIXME */
306 {
307 size = sizeof(EVENTMAPTABLE);
308 size += (assembly->tables[TableFromToken(mdtTypeDef)].rows >
309 MAX_TABLES_WORD) ? sizeof(WORD) : 0;
310 size += (assembly->tables[TableFromToken(mdtEvent)].rows >
311 MAX_TABLES_WORD) ? sizeof(WORD) : 0;
312 break;
313 }
314 case mdtEvent:
315 {
316 size = sizeof(EVENTTABLE) + (assembly->stringsz - sizeof(WORD));
317
318 /* EventType:TypeDefOrRef */
319 tables = max(assembly->tables[TableFromToken(mdtTypeDef)].rows,
320 assembly->tables[TableFromToken(mdtTypeRef)].rows);
321 tables = max(tables, assembly->tables[TableFromToken(mdtTypeSpec)].rows);
322 size += (tables > MAX_TABLES_2BIT_ENCODE) ? sizeof(WORD) : 0;
323 break;
324 }
325 case 0x15000000:/* FIXME */
326 {
327 size = sizeof(PROPERTYMAPTABLE);
328 size += (assembly->tables[TableFromToken(mdtTypeDef)].rows >
329 MAX_TABLES_WORD) ? sizeof(WORD) : 0;
330 size += (assembly->tables[TableFromToken(mdtProperty)].rows >
331 MAX_TABLES_WORD) ? sizeof(WORD) : 0;
332 break;
333 }
334 case mdtProperty:
335 {
336 size = sizeof(PROPERTYTABLE) + (assembly->stringsz - sizeof(WORD)) +
337 (assembly->blobsz - sizeof(WORD));
338 break;
339 }
340 case 0x18000000: /* FIXME */
341 {
342 size = sizeof(METHODSEMANTICSTABLE);
343
344 /* Association:HasSemantics */
345 tables = max(assembly->tables[TableFromToken(mdtEvent)].rows,
346 assembly->tables[TableFromToken(mdtProperty)].rows);
347 size += (tables > MAX_TABLES_1BIT_ENCODE) ? sizeof(WORD) : 0;
348
349 size += (assembly->tables[TableFromToken(mdtMethodDef)].rows >
350 MAX_TABLES_WORD) ? sizeof(WORD) : 0;
351 break;
352 }
353 case 0x19000000: /* FIXME */
354 {
355 size = sizeof(METHODIMPLTABLE);
356
357 /* MethodBody:MethodDefOrRef, MethodDeclaration:MethodDefOrRef */
358 tables = max(assembly->tables[TableFromToken(mdtMethodDef)].rows,
359 assembly->tables[TableFromToken(mdtMemberRef)].rows);
360 size += (tables > MAX_TABLES_1BIT_ENCODE) ? 2 * sizeof(WORD) : 0;
361
362 size += (assembly->tables[TableFromToken(mdtTypeDef)].rows >
363 MAX_TABLES_WORD) ? sizeof(WORD) : 0;
364 break;
365 }
366 case mdtModuleRef:
367 {
368 size = sizeof(MODULEREFTABLE) + (assembly->stringsz - sizeof(WORD));
369 break;
370 }
371 case mdtTypeSpec:
372 {
373 size = sizeof(TYPESPECTABLE) + (assembly->blobsz - sizeof(WORD));
374 break;
375 }
376 case 0x1C000000: /* FIXME */
377 {
378 size = sizeof(IMPLMAPTABLE) + (assembly->stringsz - sizeof(WORD));
379
380 /* MemberForwarded:MemberForwarded */
381 tables = max(assembly->tables[TableFromToken(mdtFieldDef)].rows,
382 assembly->tables[TableFromToken(mdtMethodDef)].rows);
383 size += (tables > MAX_TABLES_1BIT_ENCODE) ? sizeof(WORD) : 0;
384
385 size += (assembly->tables[TableFromToken(mdtModuleRef)].rows >
386 MAX_TABLES_WORD) ? sizeof(WORD) : 0;
387 break;
388 }
389 case 0x1D000000: /* FIXME */
390 {
391 size = sizeof(FIELDRVATABLE);
392 size += (assembly->tables[TableFromToken(mdtFieldDef)].rows >
393 MAX_TABLES_WORD) ? sizeof(WORD) : 0;
394 break;
395 }
396 case mdtAssembly:
397 {
398 size = sizeof(ASSEMBLYTABLE) + 2 * (assembly->stringsz - sizeof(WORD)) +
399 (assembly->blobsz - sizeof(WORD));
400 break;
401 }
402 case 0x20000001: /* FIXME */
403 {
404 size = sizeof(ASSEMBLYPROCESSORTABLE);
405 break;
406 }
407 case 0x22000000: /* FIXME */
408 {
409 size = sizeof(ASSEMBLYOSTABLE);
410 break;
411 }
412 case mdtAssemblyRef:
413 {
414 size = sizeof(ASSEMBLYREFTABLE) + 2 * (assembly->stringsz - sizeof(WORD)) +
415 2 * (assembly->blobsz - sizeof(WORD));
416 break;
417 }
418 case 0x24000000: /* FIXME */
419 {
420 size = sizeof(ASSEMBLYREFPROCESSORTABLE);
421 size += (assembly->tables[TableFromToken(mdtAssemblyRef)].rows >
422 MAX_TABLES_WORD) ? sizeof(WORD) : 0;
423 break;
424 }
425 case 0x25000000: /* FIXME */
426 {
427 size = sizeof(ASSEMBLYREFOSTABLE);
428 size += (assembly->tables[TableFromToken(mdtAssemblyRef)].rows >
429 MAX_TABLES_WORD) ? sizeof(WORD) : 0;
430 break;
431 }
432 case mdtFile:
433 {
434 size = sizeof(FILETABLE) + (assembly->stringsz - sizeof(WORD)) +
435 (assembly->blobsz - sizeof(WORD));
436 break;
437 }
438 case mdtExportedType:
439 {
440 size = sizeof(EXPORTEDTYPETABLE) + 2 * (assembly->stringsz - sizeof(WORD));
441
442 /* Implementation:Implementation */
443 tables = max(assembly->tables[TableFromToken(mdtFile)].rows,
444 assembly->tables[TableFromToken(mdtMethodDef)].rows);
445 size += (tables > MAX_TABLES_2BIT_ENCODE) ? sizeof(WORD) : 0;
446 break;
447 }
448 case mdtManifestResource:
449 {
450 size = sizeof(MANIFESTRESTABLE) + (assembly->stringsz - sizeof(WORD));
451
452 /* Implementation:Implementation */
453 tables = max(assembly->tables[TableFromToken(mdtFile)].rows,
454 assembly->tables[TableFromToken(mdtAssemblyRef)].rows);
455 size += (tables > MAX_TABLES_2BIT_ENCODE) ? sizeof(WORD) : 0;
456 break;
457 }
458 case 0x29000000: /* FIXME */
459 {
460 size = sizeof(NESTEDCLASSTABLE);
461 size += (assembly->tables[TableFromToken(mdtTypeDef)].rows >
462 MAX_TABLES_WORD) ? 2 * sizeof(WORD) : 0;
463 break;
464 }
465 default:
466 return 0;
467 }
468
469 return size;
470 }
471
472 static HRESULT parse_clr_tables(ASSEMBLY *assembly, ULONG offset)
473 {
474 DWORD i, count;
475 ULONG currofs;
476 ULONGLONG mask;
477
478 currofs = offset;
479 assembly->tableshdr = assembly_data_offset(assembly, currofs);
480 if (!assembly->tableshdr)
481 return E_FAIL;
482
483 assembly->stringsz = (assembly->tableshdr->HeapOffsetSizes & MD_STRINGS_BIT) ?
484 sizeof(DWORD) : sizeof(WORD);
485 assembly->guidsz = (assembly->tableshdr->HeapOffsetSizes & MD_GUIDS_BIT) ?
486 sizeof(DWORD) : sizeof(WORD);
487 assembly->blobsz = (assembly->tableshdr->HeapOffsetSizes & MD_BLOBS_BIT) ?
488 sizeof(DWORD) : sizeof(WORD);
489
490 currofs += sizeof(METADATATABLESHDR);
491 assembly->numrows = assembly_data_offset(assembly, currofs);
492 if (!assembly->numrows)
493 return E_FAIL;
494
495 memset(assembly->tables, -1, MAX_CLR_TABLES * sizeof(CLRTABLE));
496
497 for (i = count = 0, mask = 1; i < MAX_CLR_TABLES; i++, mask <<= 1)
498 {
499 if (assembly->tableshdr->MaskValid.QuadPart & mask)
500 assembly->tables[i].rows = assembly->numrows[count++];
501 }
502 assembly->numtables = count;
503 currofs += assembly->numtables * sizeof(DWORD);
504
505 for (i = 0, mask = 1; i < MAX_CLR_TABLES; i++, mask <<= 1)
506 {
507 if (assembly->tableshdr->MaskValid.QuadPart & mask)
508 {
509 assembly->tables[i].offset = currofs;
510 currofs += get_table_size(assembly, i) * assembly->tables[i].rows;
511 }
512 }
513
514 return S_OK;
515 }
516
517 static HRESULT parse_metadata_header(ASSEMBLY *assembly, DWORD *hdrsz)
518 {
519 METADATAHDR *metadatahdr;
520 BYTE *ptr, *dest;
521 DWORD size, ofs;
522 ULONG rva;
523
524 rva = assembly->corhdr->MetaData.VirtualAddress;
525 ptr = ImageRvaToVa(assembly->nthdr, assembly->data, rva, NULL);
526 if (!ptr)
527 return E_FAIL;
528
529 metadatahdr = (METADATAHDR *)ptr;
530
531 assembly->metadatahdr = HeapAlloc(GetProcessHeap(), 0, sizeof(METADATAHDR));
532 if (!assembly->metadatahdr)
533 return E_OUTOFMEMORY;
534
535 size = FIELD_OFFSET(METADATAHDR, Version);
536 memcpy(assembly->metadatahdr, metadatahdr, size);
537
538 assembly->metadatahdr->Version = (LPSTR)&metadatahdr->Version;
539
540 ofs = FIELD_OFFSET(METADATAHDR, Flags);
541 ptr += FIELD_OFFSET(METADATAHDR, Version) + metadatahdr->VersionLength + 1;
542 dest = (BYTE *)assembly->metadatahdr + ofs;
543 memcpy(dest, ptr, sizeof(METADATAHDR) - ofs);
544
545 *hdrsz = sizeof(METADATAHDR) - sizeof(LPSTR) + metadatahdr->VersionLength + 1;
546
547 return S_OK;
548 }
549
550 static HRESULT parse_clr_metadata(ASSEMBLY *assembly)
551 {
552 METADATASTREAMHDR *streamhdr;
553 ULONG rva, i, ofs;
554 LPSTR stream;
555 HRESULT hr;
556 DWORD hdrsz;
557 BYTE *ptr;
558
559 hr = parse_metadata_header(assembly, &hdrsz);
560 if (FAILED(hr))
561 return hr;
562
563 rva = assembly->corhdr->MetaData.VirtualAddress;
564 ptr = ImageRvaToVa(assembly->nthdr, assembly->data, rva + hdrsz, NULL);
565 if (!ptr)
566 return E_FAIL;
567
568 for (i = 0; i < assembly->metadatahdr->Streams; i++)
569 {
570 streamhdr = (METADATASTREAMHDR *)ptr;
571 ofs = rva_to_offset(assembly->nthdr, rva + streamhdr->Offset);
572
573 ptr += sizeof(METADATASTREAMHDR);
574 stream = (LPSTR)ptr;
575
576 if (!lstrcmpA(stream, "#~"))
577 {
578 hr = parse_clr_tables(assembly, ofs);
579 if (FAILED(hr))
580 return hr;
581 }
582 else if (!lstrcmpA(stream, "#Strings") || !lstrcmpA(stream, "Strings"))
583 assembly->strings = assembly_data_offset(assembly, ofs);
584 else if (!lstrcmpA(stream, "#Blob") || !lstrcmpA(stream, "Blob"))
585 assembly->blobs = assembly_data_offset(assembly, ofs);
586
587 ptr += ((lstrlenA(stream) + 1) + 3) & ~3; /* align on DWORD boundary */
588 }
589
590 return S_OK;
591 }
592
593 static HRESULT parse_pe_header(ASSEMBLY *assembly)
594 {
595 IMAGE_DATA_DIRECTORY *datadirs;
596
597 assembly->nthdr = ImageNtHeader(assembly->data);
598 if (!assembly->nthdr)
599 return E_FAIL;
600
601 if (assembly->nthdr->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC)
602 {
603 IMAGE_OPTIONAL_HEADER64 *opthdr =
604 (IMAGE_OPTIONAL_HEADER64 *)&assembly->nthdr->OptionalHeader;
605 datadirs = opthdr->DataDirectory;
606 }
607 else
608 {
609 IMAGE_OPTIONAL_HEADER32 *opthdr =
610 (IMAGE_OPTIONAL_HEADER32 *)&assembly->nthdr->OptionalHeader;
611 datadirs = opthdr->DataDirectory;
612 }
613
614 if (!datadirs)
615 return E_FAIL;
616
617 if (!datadirs[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].VirtualAddress ||
618 !datadirs[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].Size)
619 {
620 return E_FAIL;
621 }
622
623 assembly->corhdr = ImageRvaToVa(assembly->nthdr, assembly->data,
624 datadirs[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].VirtualAddress, NULL);
625 if (!assembly->corhdr)
626 return E_FAIL;
627
628 return S_OK;
629 }
630
631 HRESULT assembly_create(ASSEMBLY **out, LPCWSTR file)
632 {
633 ASSEMBLY *assembly;
634 HRESULT hr;
635
636 *out = NULL;
637
638 assembly = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ASSEMBLY));
639 if (!assembly)
640 return E_OUTOFMEMORY;
641
642 assembly->path = strdupW(file);
643 if (!assembly->path)
644 {
645 hr = E_OUTOFMEMORY;
646 goto failed;
647 }
648
649 assembly->hfile = CreateFileW(file, GENERIC_READ, FILE_SHARE_READ,
650 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
651 if (assembly->hfile == INVALID_HANDLE_VALUE)
652 {
653 hr = HRESULT_FROM_WIN32(GetLastError());
654 goto failed;
655 }
656
657 assembly->hmap = CreateFileMappingW(assembly->hfile, NULL, PAGE_READONLY,
658 0, 0, NULL);
659 if (!assembly->hmap)
660 {
661 hr = HRESULT_FROM_WIN32(GetLastError());
662 goto failed;
663 }
664
665 assembly->data = MapViewOfFile(assembly->hmap, FILE_MAP_READ, 0, 0, 0);
666 if (!assembly->data)
667 {
668 hr = HRESULT_FROM_WIN32(GetLastError());
669 goto failed;
670 }
671
672 hr = parse_pe_header(assembly);
673 if (FAILED(hr)) goto failed;
674
675 hr = parse_clr_metadata(assembly);
676 if (FAILED(hr)) goto failed;
677
678 *out = assembly;
679 return S_OK;
680
681 failed:
682 assembly_release(assembly);
683 return hr;
684 }
685
686 HRESULT assembly_release(ASSEMBLY *assembly)
687 {
688 if (!assembly)
689 return S_OK;
690
691 HeapFree(GetProcessHeap(), 0, assembly->metadatahdr);
692 HeapFree(GetProcessHeap(), 0, assembly->path);
693 UnmapViewOfFile(assembly->data);
694 CloseHandle(assembly->hmap);
695 CloseHandle(assembly->hfile);
696 HeapFree(GetProcessHeap(), 0, assembly);
697
698 return S_OK;
699 }
700
701 static LPWSTR assembly_dup_str(const ASSEMBLY *assembly, DWORD index)
702 {
703 int len;
704 LPWSTR cpy;
705 LPCSTR str = (LPCSTR)&assembly->strings[index];
706
707 len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
708
709 if ((cpy = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))))
710 MultiByteToWideChar(CP_ACP, 0, str, -1, cpy, len);
711
712 return cpy;
713 }
714
715 HRESULT assembly_get_name(ASSEMBLY *assembly, LPWSTR *name)
716 {
717 BYTE *ptr;
718 LONG offset;
719 DWORD stridx;
720
721 offset = assembly->tables[TableFromToken(mdtAssembly)].offset;
722 if (offset == -1)
723 return E_FAIL;
724
725 ptr = assembly_data_offset(assembly, offset);
726 if (!ptr)
727 return E_FAIL;
728
729 ptr += FIELD_OFFSET(ASSEMBLYTABLE, PublicKey) + assembly->blobsz;
730 if (assembly->stringsz == sizeof(DWORD))
731 stridx = *(DWORD *)ptr;
732 else
733 stridx = *(WORD *)ptr;
734
735 *name = assembly_dup_str(assembly, stridx);
736 if (!*name)
737 return E_OUTOFMEMORY;
738
739 return S_OK;
740 }
741
742 HRESULT assembly_get_path(const ASSEMBLY *assembly, LPWSTR *path)
743 {
744 LPWSTR cpy = HeapAlloc(GetProcessHeap(), 0, (strlenW(assembly->path) + 1) * sizeof(WCHAR));
745 *path = cpy;
746 if (cpy)
747 strcpyW(cpy, assembly->path);
748 else
749 return E_OUTOFMEMORY;
750
751 return S_OK;
752 }
753
754 HRESULT assembly_get_version(ASSEMBLY *assembly, LPWSTR *version)
755 {
756 static const WCHAR format[] = {'%','u','.','%','u','.','%','u','.','%','u',0};
757
758 ASSEMBLYTABLE *asmtbl;
759 LONG offset;
760
761 *version = NULL;
762
763 offset = assembly->tables[TableFromToken(mdtAssembly)].offset;
764 if (offset == -1)
765 return E_FAIL;
766
767 asmtbl = assembly_data_offset(assembly, offset);
768 if (!asmtbl)
769 return E_FAIL;
770
771 *version = HeapAlloc(GetProcessHeap(), 0, sizeof(format) + 4 * strlen("65535") * sizeof(WCHAR));
772 if (!*version)
773 return E_OUTOFMEMORY;
774
775 sprintfW(*version, format, asmtbl->MajorVersion, asmtbl->MinorVersion,
776 asmtbl->BuildNumber, asmtbl->RevisionNumber);
777
778 return S_OK;
779 }
780
781 PEKIND assembly_get_architecture(ASSEMBLY *assembly)
782 {
783 if ((assembly->corhdr->MajorRuntimeVersion == 2) && (assembly->corhdr->MinorRuntimeVersion == 0))
784 return peNone; /* .NET 1.x assembly */
785
786 if (assembly->nthdr->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC)
787 return peAMD64; /* AMD64/IA64 assembly */
788
789 if ((assembly->corhdr->Flags & COMIMAGE_FLAGS_ILONLY) && !(assembly->corhdr->Flags & COMIMAGE_FLAGS_32BITREQUIRED))
790 return peMSIL; /* MSIL assembly */
791
792 return peI386; /* x86 assembly */
793 }
794
795 static BYTE *assembly_get_blob(ASSEMBLY *assembly, DWORD index, ULONG *size)
796 {
797 return GetData(&assembly->blobs[index], size);
798 }
799
800 HRESULT assembly_get_pubkey_token(ASSEMBLY *assembly, LPWSTR *token)
801 {
802 ULONG i, size;
803 LONG offset;
804 BYTE *hashdata, *pubkey, *ptr;
805 HCRYPTPROV crypt;
806 HCRYPTHASH hash;
807 BYTE tokbytes[BYTES_PER_TOKEN];
808 HRESULT hr = E_FAIL;
809 LPWSTR tok;
810 DWORD idx;
811
812 *token = NULL;
813
814 offset = assembly->tables[TableFromToken(mdtAssembly)].offset;
815 if (offset == -1)
816 return E_FAIL;
817
818 ptr = assembly_data_offset(assembly, offset);
819 if (!ptr)
820 return E_FAIL;
821
822 ptr += FIELD_OFFSET(ASSEMBLYTABLE, PublicKey);
823 if (assembly->blobsz == sizeof(DWORD))
824 idx = *(DWORD *)ptr;
825 else
826 idx = *(WORD *)ptr;
827
828 pubkey = assembly_get_blob(assembly, idx, &size);
829
830 if (!CryptAcquireContextA(&crypt, NULL, NULL, PROV_RSA_FULL,
831 CRYPT_VERIFYCONTEXT))
832 return E_FAIL;
833
834 if (!CryptCreateHash(crypt, CALG_SHA1, 0, 0, &hash))
835 return E_FAIL;
836
837 if (!CryptHashData(hash, pubkey, size, 0))
838 return E_FAIL;
839
840 size = 0;
841 if (!CryptGetHashParam(hash, HP_HASHVAL, NULL, &size, 0))
842 return E_FAIL;
843
844 hashdata = HeapAlloc(GetProcessHeap(), 0, size);
845 if (!hashdata)
846 {
847 hr = E_OUTOFMEMORY;
848 goto done;
849 }
850
851 if (!CryptGetHashParam(hash, HP_HASHVAL, hashdata, &size, 0))
852 goto done;
853
854 for (i = size - 1; i >= size - 8; i--)
855 tokbytes[size - i - 1] = hashdata[i];
856
857 tok = HeapAlloc(GetProcessHeap(), 0, (TOKEN_LENGTH + 1) * sizeof(WCHAR));
858 if (!tok)
859 {
860 hr = E_OUTOFMEMORY;
861 goto done;
862 }
863
864 token_to_str(tokbytes, tok);
865
866 *token = tok;
867 hr = S_OK;
868
869 done:
870 HeapFree(GetProcessHeap(), 0, hashdata);
871 CryptDestroyHash(hash);
872 CryptReleaseContext(crypt, 0);
873
874 return hr;
875 }
876
877 HRESULT assembly_get_runtime_version(ASSEMBLY *assembly, LPSTR *version)
878 {
879 *version = assembly->metadatahdr->Version;
880 return S_OK;
881 }
882
883 HRESULT assembly_get_external_files(ASSEMBLY *assembly, LPWSTR **files, DWORD *count)
884 {
885 LONG offset;
886 INT i, num_rows;
887 WCHAR **ret;
888 BYTE *ptr;
889 DWORD idx;
890
891 *count = 0;
892
893 offset = assembly->tables[TableFromToken(mdtFile)].offset;
894 if (offset == -1)
895 return S_OK;
896
897 ptr = assembly_data_offset(assembly, offset);
898 if (!ptr)
899 return S_OK;
900
901 num_rows = assembly->tables[TableFromToken(mdtFile)].rows;
902 if (num_rows <= 0)
903 return S_OK;
904
905 ret = HeapAlloc(GetProcessHeap(), 0, num_rows * sizeof(WCHAR *));
906 if (!ret)
907 return E_OUTOFMEMORY;
908
909 for (i = 0; i < num_rows; i++)
910 {
911 ptr += sizeof(DWORD); /* skip Flags field */
912 if (assembly->stringsz == sizeof(DWORD))
913 idx = *(DWORD *)ptr;
914 else
915 idx = *(WORD *)ptr;
916
917 ret[i] = assembly_dup_str(assembly, idx);
918 if (!ret[i])
919 {
920 for (; i >= 0; i--) HeapFree(GetProcessHeap(), 0, ret[i]);
921 HeapFree(GetProcessHeap(), 0, ret);
922 return E_OUTOFMEMORY;
923 }
924 ptr += assembly->stringsz; /* skip Name field */
925 ptr += assembly->blobsz; /* skip Hash field */
926 }
927 *count = num_rows;
928 *files = ret;
929 return S_OK;
930 }