[FUSION] Sync with Wine Staging 4.18. CORE-16441
[reactos.git] / dll / win32 / fusion / assembly.c
1 /*
2 * assembly parser
3 *
4 * Copyright 2008 James Hawkins
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 */
20
21 #include <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 }