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