Revert "winex11.drv: Optimise getting the bits of a DIB after calling SetDIBits."
[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
33 #include "fusionpriv.h"
34 #include "wine/debug.h"
35 #include "wine/unicode.h"
36
37 #define MAX_CLR_TABLES  64
38
39 typedef struct tagCLRTABLE
40 {
41     DWORD rows;
42     DWORD offset;
43 } CLRTABLE;
44
45 struct tagASSEMBLY
46 {
47     LPSTR path;
48
49     HANDLE hfile;
50     HANDLE hmap;
51     BYTE *data;
52
53     IMAGE_NT_HEADERS32 *nthdr;
54     IMAGE_COR20_HEADER *corhdr;
55
56     METADATAHDR *metadatahdr;
57
58     METADATATABLESHDR *tableshdr;
59     DWORD numtables;
60     DWORD *numrows;
61     CLRTABLE tables[MAX_CLR_TABLES];
62
63     BYTE *strings;
64     BYTE *blobs;
65 };
66
67 const DWORD COR_TABLE_SIZES[64] =
68 {
69     sizeof(MODULETABLE),
70     sizeof(TYPEREFTABLE),
71     sizeof(TYPEDEFTABLE),
72     0,
73     sizeof(FIELDTABLE),
74     0,
75     sizeof(METHODDEFTABLE),
76     0,
77     sizeof(PARAMTABLE),
78     sizeof(INTERFACEIMPLTABLE),
79     sizeof(MEMBERREFTABLE),
80     sizeof(CONSTANTTABLE),
81     sizeof(CUSTOMATTRIBUTETABLE),
82     sizeof(FIELDMARSHALTABLE),
83     sizeof(DECLSECURITYTABLE),
84     sizeof(CLASSLAYOUTTABLE),
85     sizeof(FIELDLAYOUTTABLE),
86     sizeof(STANDALONESIGTABLE),
87     sizeof(EVENTMAPTABLE),
88     0,
89     sizeof(EVENTTABLE),
90     sizeof(PROPERTYMAPTABLE),
91     0,
92     sizeof(PROPERTYTABLE),
93     sizeof(METHODSEMANTICSTABLE),
94     sizeof(METHODIMPLTABLE),
95     sizeof(MODULEREFTABLE),
96     sizeof(TYPESPECTABLE),
97     sizeof(IMPLMAPTABLE),
98     sizeof(FIELDRVATABLE),
99     0,
100     0,
101     sizeof(ASSEMBLYTABLE),
102     sizeof(ASSEMBLYPROCESSORTABLE),
103     sizeof(ASSEMBLYOSTABLE),
104     sizeof(ASSEMBLYREFTABLE),
105     sizeof(ASSEMBLYREFPROCESSORTABLE),
106     sizeof(ASSEMBLYREFOSTABLE),
107     sizeof(FILETABLE),
108     sizeof(EXPORTEDTYPETABLE),
109     sizeof(MANIFESTRESTABLE),
110     sizeof(NESTEDCLASSTABLE),
111     sizeof(GENERICPARAMTABLE),
112     sizeof(METHODSPECTABLE),
113     sizeof(GENERICPARAMCONSTRAINTTABLE),
114     0,
115     0,
116     0,
117     0,
118     0,
119     0,
120     0,
121     0,
122     0,
123     0,
124     0,
125     0,
126     0,
127     0,
128     0,
129     0,
130     0,
131     0,
132     0
133 };
134
135 static LPSTR strdupWtoA(LPCWSTR str)
136 {
137     LPSTR ret = NULL;
138     DWORD len;
139
140     if (!str)
141         return ret;
142
143     len = WideCharToMultiByte(CP_ACP, 0, str, -1, NULL, 0, NULL, NULL);
144     ret = HeapAlloc(GetProcessHeap(), 0, len);
145     if (ret)
146         WideCharToMultiByte(CP_ACP, 0, str, -1, ret, len, NULL, NULL);
147
148     return ret;
149 }
150
151 static DWORD rva_to_offset(IMAGE_NT_HEADERS *nthdrs, DWORD rva)
152 {
153     DWORD offset = rva, limit;
154     IMAGE_SECTION_HEADER *img;
155     WORD i;
156
157     img = IMAGE_FIRST_SECTION(nthdrs);
158
159     if (rva < img->PointerToRawData)
160         return rva;
161
162     for (i = 0; i < nthdrs->FileHeader.NumberOfSections; i++)
163     {
164         if (img[i].SizeOfRawData)
165             limit = img[i].SizeOfRawData;
166         else
167             limit = img[i].Misc.VirtualSize;
168
169         if (rva >= img[i].VirtualAddress &&
170             rva < (img[i].VirtualAddress + limit))
171         {
172             if (img[i].PointerToRawData != 0)
173             {
174                 offset -= img[i].VirtualAddress;
175                 offset += img[i].PointerToRawData;
176             }
177
178             return offset;
179         }
180     }
181
182     return 0;
183 }
184
185 static BYTE *GetData(BYTE *pData, ULONG *pLength)
186 {
187     if ((*pData & 0x80) == 0x00)
188     {
189         *pLength = (*pData & 0x7f);
190         return pData + 1;
191     }
192
193     if ((*pData & 0xC0) == 0x80)
194     {
195         *pLength = ((*pData & 0x3f) << 8 | *(pData + 1));
196         return pData + 2;
197     }
198
199     if ((*pData & 0xE0) == 0xC0)
200     {
201         *pLength = ((*pData & 0x1f) << 24 | *(pData + 1) << 16 |
202                     *(pData + 2) << 8 | *(pData + 3));
203         return pData + 4;
204     }
205
206     *pLength = (ULONG)-1;
207     return 0;
208 }
209
210 static VOID *assembly_data_offset(ASSEMBLY *assembly, ULONG offset)
211 {
212     return (VOID *)&assembly->data[offset];
213 }
214
215 static HRESULT parse_clr_tables(ASSEMBLY *assembly, ULONG offset)
216 {
217     DWORD i, previ, offidx;
218     ULONG currofs;
219
220     currofs = offset;
221     assembly->tableshdr = (METADATATABLESHDR *)assembly_data_offset(assembly, currofs);
222     if (!assembly->tableshdr)
223         return E_FAIL;
224
225     currofs += sizeof(METADATATABLESHDR);
226     assembly->numrows = (DWORD *)assembly_data_offset(assembly, currofs);
227     if (!assembly->numrows)
228         return E_FAIL;
229
230     assembly->numtables = 0;
231     for (i = 0; i < MAX_CLR_TABLES; i++)
232     {
233         if ((i < 32 && (assembly->tableshdr->MaskValid.u.LowPart >> i) & 1) ||
234             (i >= 32 && (assembly->tableshdr->MaskValid.u.HighPart >> i) & 1))
235         {
236             assembly->numtables++;
237         }
238     }
239
240     currofs += assembly->numtables * sizeof(DWORD);
241     memset(assembly->tables, -1, MAX_CLR_TABLES * sizeof(CLRTABLE));
242
243     if (assembly->tableshdr->MaskValid.u.LowPart & 1)
244     {
245         assembly->tables[0].offset = currofs;
246         assembly->tables[0].rows = assembly->numrows[0];
247     }
248
249     previ = 0;
250     offidx = 1;
251     for (i = 1; i < MAX_CLR_TABLES; i++)
252     {
253         if ((i < 32 && (assembly->tableshdr->MaskValid.u.LowPart >> i) & 1) ||
254             (i >= 32 && (assembly->tableshdr->MaskValid.u.HighPart >> i) & 1))
255         {
256             currofs += COR_TABLE_SIZES[previ] * assembly->numrows[offidx - 1];
257             assembly->tables[i].offset = currofs;
258             assembly->tables[i].rows = assembly->numrows[offidx];
259             offidx++;
260             previ = i;
261         }
262     }
263
264     return S_OK;
265 }
266
267 static HRESULT parse_clr_metadata(ASSEMBLY *assembly)
268 {
269     METADATASTREAMHDR *streamhdr;
270     ULONG rva, i, ofs;
271     LPSTR stream;
272     HRESULT hr;
273     BYTE *ptr;
274
275     rva = assembly->corhdr->MetaData.VirtualAddress;
276     assembly->metadatahdr = ImageRvaToVa(assembly->nthdr, assembly->data,
277                                          rva, NULL);
278     if (!assembly->metadatahdr)
279         return E_FAIL;
280
281     ptr = ImageRvaToVa(assembly->nthdr, assembly->data,
282                        rva + sizeof(METADATAHDR), NULL);
283     if (!ptr)
284         return E_FAIL;
285
286     for (i = 0; i < assembly->metadatahdr->Streams; i++)
287     {
288         streamhdr = (METADATASTREAMHDR *)ptr;
289         ofs = rva_to_offset(assembly->nthdr, rva + streamhdr->Offset);
290
291         ptr += sizeof(METADATASTREAMHDR);
292         stream = (LPSTR)ptr;
293
294         if (!lstrcmpA(stream, "#~"))
295         {
296             hr = parse_clr_tables(assembly, ofs);
297             if (FAILED(hr))
298                 return hr;
299         }
300         else if (!lstrcmpA(stream, "#Strings") || !lstrcmpA(stream, "Strings"))
301             assembly->strings = (BYTE *)assembly_data_offset(assembly, ofs);
302         else if (!lstrcmpA(stream, "#Blob"))
303             assembly->blobs = (BYTE *)assembly_data_offset(assembly, ofs);
304
305         ptr += lstrlenA(stream);
306         while (!*ptr) ptr++;
307     }
308
309     return S_OK;
310 }
311
312 static HRESULT parse_pe_header(ASSEMBLY *assembly)
313 {
314     IMAGE_DATA_DIRECTORY *datadirs;
315
316     assembly->nthdr = ImageNtHeader(assembly->data);
317     if (!assembly->nthdr)
318         return E_FAIL;
319
320     datadirs = assembly->nthdr->OptionalHeader.DataDirectory;
321     if (!datadirs)
322         return E_FAIL;
323
324     if (!datadirs[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].VirtualAddress ||
325         !datadirs[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].Size)
326     {
327         return E_FAIL;
328     }
329
330     assembly->corhdr = ImageRvaToVa(assembly->nthdr, assembly->data,
331         datadirs[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].VirtualAddress, NULL);
332     if (!assembly->corhdr)
333         return E_FAIL;
334
335     return S_OK;
336 }
337
338 HRESULT assembly_create(ASSEMBLY **out, LPCWSTR file)
339 {
340     ASSEMBLY *assembly;
341     HRESULT hr;
342
343     *out = NULL;
344
345     assembly = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ASSEMBLY));
346     if (!assembly)
347         return E_OUTOFMEMORY;
348
349     assembly->path = strdupWtoA(file);
350     if (!assembly->path)
351     {
352         hr = E_OUTOFMEMORY;
353         goto failed;
354     }
355
356     assembly->hfile = CreateFileW(file, GENERIC_READ, FILE_SHARE_READ,
357                                   NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
358     if (assembly->hfile == INVALID_HANDLE_VALUE)
359     {
360         hr = HRESULT_FROM_WIN32(GetLastError());
361         goto failed;
362     }
363
364     assembly->hmap = CreateFileMappingW(assembly->hfile, NULL, PAGE_READONLY,
365                                         0, 0, NULL);
366     if (!assembly->hmap)
367     {
368         hr = HRESULT_FROM_WIN32(GetLastError());
369         goto failed;
370     }
371
372     assembly->data = MapViewOfFile(assembly->hmap, FILE_MAP_READ, 0, 0, 0);
373     if (!assembly->data)
374     {
375         hr = HRESULT_FROM_WIN32(GetLastError());
376         goto failed;
377     }
378
379     hr = parse_pe_header(assembly);
380     if (FAILED(hr)) goto failed;
381
382     hr = parse_clr_metadata(assembly);
383     if (FAILED(hr)) goto failed;
384
385     *out = assembly;
386     return S_OK;
387
388 failed:
389     assembly_release( assembly );
390     return hr;
391 }
392
393 HRESULT assembly_release(ASSEMBLY *assembly)
394 {
395     if (!assembly)
396         return S_OK;
397
398     HeapFree(GetProcessHeap(), 0, assembly->path);
399     UnmapViewOfFile(assembly->data);
400     CloseHandle(assembly->hmap);
401     CloseHandle(assembly->hfile);
402     HeapFree(GetProcessHeap(), 0, assembly);
403
404     return S_OK;
405 }
406
407 static LPSTR assembly_dup_str(ASSEMBLY *assembly, WORD index)
408 {
409     LPSTR str = (LPSTR)&assembly->strings[index];
410     LPSTR cpy = HeapAlloc(GetProcessHeap(), 0, strlen(str)+1);
411     if (cpy)
412        strcpy(cpy, str);
413     return cpy;
414 }
415
416 HRESULT assembly_get_name(ASSEMBLY *assembly, LPSTR *name)
417 {
418     ASSEMBLYTABLE *asmtbl;
419     ULONG offset;
420
421     offset = assembly->tables[0x20].offset; /* FIXME: add constants */
422     if (offset == -1)
423         return E_FAIL;
424
425     asmtbl = (ASSEMBLYTABLE *)assembly_data_offset(assembly, offset);
426     if (!asmtbl)
427         return E_FAIL;
428
429     *name = assembly_dup_str(assembly, asmtbl->Name);
430     if (!*name)
431         return E_OUTOFMEMORY;
432
433     return S_OK;
434 }
435
436 HRESULT assembly_get_path(ASSEMBLY *assembly, LPSTR *path)
437 {
438     LPSTR cpy = HeapAlloc(GetProcessHeap(), 0, strlen(assembly->path)+1);
439     *path = cpy;
440     if (cpy)
441         strcpy(cpy, assembly->path);
442     else
443         return E_OUTOFMEMORY;
444
445     return S_OK;
446 }
447
448 HRESULT assembly_get_version(ASSEMBLY *assembly, LPSTR *version)
449 {
450     LPSTR verdata;
451     VS_FIXEDFILEINFO *ffi;
452     HRESULT hr = S_OK;
453     DWORD size;
454
455     size = GetFileVersionInfoSizeA(assembly->path, NULL);
456     if (!size)
457         return HRESULT_FROM_WIN32(GetLastError());
458
459     verdata = HeapAlloc(GetProcessHeap(), 0, size);
460     if (!verdata)
461         return E_OUTOFMEMORY;
462
463     if (!GetFileVersionInfoA(assembly->path, 0, size, verdata))
464     {
465         hr = HRESULT_FROM_WIN32(GetLastError());
466         goto done;
467     }
468
469     if (!VerQueryValueA(verdata, "\\", (LPVOID *)&ffi, &size))
470     {
471         hr = HRESULT_FROM_WIN32(GetLastError());
472         goto done;
473     }
474
475     *version = HeapAlloc(GetProcessHeap(), 0, MAX_PATH);
476     if (!*version)
477     {
478         hr = E_OUTOFMEMORY;
479         goto done;
480     }
481
482     sprintf(*version, "%d.%d.%d.%d", HIWORD(ffi->dwFileVersionMS),
483             LOWORD(ffi->dwFileVersionMS), HIWORD(ffi->dwFileVersionLS),
484             LOWORD(ffi->dwFileVersionLS));
485
486 done:
487     HeapFree(GetProcessHeap(), 0, verdata);
488     return hr;
489 }
490
491 HRESULT assembly_get_architecture(ASSEMBLY *assembly, DWORD fixme)
492 {
493     /* FIXME */
494     return S_OK;
495 }
496
497 static BYTE *assembly_get_blob(ASSEMBLY *assembly, WORD index, ULONG *size)
498 {
499     return GetData(&assembly->blobs[index], size);
500 }
501
502 static void bytes_to_str(BYTE *bytes, DWORD len, LPSTR str)
503 {
504     int i;
505
506     static const char hexval[16] = {
507         '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'
508     };
509
510     for(i = 0; i < len; i++)
511     {
512         str[i * 2] = hexval[((bytes[i] >> 4) & 0xF)];
513         str[i * 2 + 1] = hexval[(bytes[i]) & 0x0F];
514     }
515 }
516
517 #define BYTES_PER_TOKEN 8
518 #define CHARS_PER_BYTE 2
519 #define TOKEN_LENGTH (BYTES_PER_TOKEN * CHARS_PER_BYTE + 1)
520
521 HRESULT assembly_get_pubkey_token(ASSEMBLY *assembly, LPSTR *token)
522 {
523     ASSEMBLYTABLE *asmtbl;
524     ULONG i, offset, size;
525     BYTE *hashdata;
526     HCRYPTPROV crypt;
527     HCRYPTHASH hash;
528     BYTE *pubkey;
529     BYTE tokbytes[BYTES_PER_TOKEN];
530     HRESULT hr = E_FAIL;
531     LPSTR tok;
532
533     *token = NULL;
534
535     offset = assembly->tables[0x20].offset; /* FIXME: add constants */
536     if (offset == -1)
537         return E_FAIL;
538
539     asmtbl = (ASSEMBLYTABLE *)assembly_data_offset(assembly, offset);
540     if (!asmtbl)
541         return E_FAIL;
542
543     pubkey = assembly_get_blob(assembly, asmtbl->PublicKey, &size);
544
545     if (!CryptAcquireContextA(&crypt, NULL, NULL, PROV_RSA_FULL,
546                               CRYPT_VERIFYCONTEXT))
547         return E_FAIL;
548
549     if (!CryptCreateHash(crypt, CALG_SHA1, 0, 0, &hash))
550         return E_FAIL;
551
552     if (!CryptHashData(hash, pubkey, size, 0))
553         return E_FAIL;
554
555     size = 0;
556     if (!CryptGetHashParam(hash, HP_HASHVAL, NULL, &size, 0))
557         return E_FAIL;
558
559     hashdata = HeapAlloc(GetProcessHeap(), 0, size);
560     if (!hashdata)
561     {
562         hr = E_OUTOFMEMORY;
563         goto done;
564     }
565
566     if (!CryptGetHashParam(hash, HP_HASHVAL, hashdata, &size, 0))
567         goto done;
568
569     for (i = size - 1; i >= size - 8; i--)
570         tokbytes[size - i - 1] = hashdata[i];
571
572     tok = HeapAlloc(GetProcessHeap(), 0, TOKEN_LENGTH);
573     if (!tok)
574     {
575         hr = E_OUTOFMEMORY;
576         goto done;
577     }
578
579     bytes_to_str(tokbytes, BYTES_PER_TOKEN, tok);
580     tok[TOKEN_LENGTH - 1] = '\0';
581
582     *token = tok;
583     hr = S_OK;
584
585 done:
586     HeapFree(GetProcessHeap(), 0, hashdata);
587     CryptDestroyHash(hash);
588     CryptReleaseContext(crypt, 0);
589
590     return hr;
591 }