xref: /reactos/dll/win32/fusion/assembly.c (revision c2c66aff)
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 }
931