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