ddraw: Add full implementation of IDirect3DDevice7_Load.
[wine.git] / dlls / fusion / assembly.c
blob2110ba51e0d609c0f8879097ec200d454c071aa8
1 /*
2 * assembly parser
4 * Copyright 2008 James Hawkins
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.
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.
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
21 #include <stdarg.h>
22 #include <stdio.h>
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"
33 #include "fusionpriv.h"
34 #include "wine/debug.h"
35 #include "wine/unicode.h"
37 #define MAX_CLR_TABLES 64
39 typedef struct tagCLRTABLE
41 DWORD rows;
42 DWORD offset;
43 } CLRTABLE;
45 struct tagASSEMBLY
47 LPSTR path;
49 HANDLE hfile;
50 HANDLE hmap;
51 BYTE *data;
53 IMAGE_NT_HEADERS32 *nthdr;
54 IMAGE_COR20_HEADER *corhdr;
56 METADATAHDR *metadatahdr;
58 METADATATABLESHDR *tableshdr;
59 DWORD numtables;
60 DWORD *numrows;
61 CLRTABLE tables[MAX_CLR_TABLES];
63 BYTE *strings;
64 BYTE *blobs;
67 const DWORD COR_TABLE_SIZES[64] =
69 sizeof(MODULETABLE),
70 sizeof(TYPEREFTABLE),
71 sizeof(TYPEDEFTABLE),
73 sizeof(FIELDTABLE),
75 sizeof(METHODDEFTABLE),
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),
89 sizeof(EVENTTABLE),
90 sizeof(PROPERTYMAPTABLE),
92 sizeof(PROPERTYTABLE),
93 sizeof(METHODSEMANTICSTABLE),
94 sizeof(METHODIMPLTABLE),
95 sizeof(MODULEREFTABLE),
96 sizeof(TYPESPECTABLE),
97 sizeof(IMPLMAPTABLE),
98 sizeof(FIELDRVATABLE),
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),
135 static LPSTR strdupWtoA(LPCWSTR str)
137 LPSTR ret = NULL;
138 DWORD len;
140 if (!str)
141 return ret;
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);
148 return ret;
151 static DWORD rva_to_offset(IMAGE_NT_HEADERS *nthdrs, DWORD rva)
153 DWORD offset = rva, limit;
154 IMAGE_SECTION_HEADER *img;
155 WORD i;
157 img = IMAGE_FIRST_SECTION(nthdrs);
159 if (rva < img->PointerToRawData)
160 return rva;
162 for (i = 0; i < nthdrs->FileHeader.NumberOfSections; i++)
164 if (img[i].SizeOfRawData)
165 limit = img[i].SizeOfRawData;
166 else
167 limit = img[i].Misc.VirtualSize;
169 if (rva >= img[i].VirtualAddress &&
170 rva < (img[i].VirtualAddress + limit))
172 if (img[i].PointerToRawData != 0)
174 offset -= img[i].VirtualAddress;
175 offset += img[i].PointerToRawData;
178 return offset;
182 return 0;
185 static BYTE *GetData(BYTE *pData, ULONG *pLength)
187 if ((*pData & 0x80) == 0x00)
189 *pLength = (*pData & 0x7f);
190 return pData + 1;
193 if ((*pData & 0xC0) == 0x80)
195 *pLength = ((*pData & 0x3f) << 8 | *(pData + 1));
196 return pData + 2;
199 if ((*pData & 0xE0) == 0xC0)
201 *pLength = ((*pData & 0x1f) << 24 | *(pData + 1) << 16 |
202 *(pData + 2) << 8 | *(pData + 3));
203 return pData + 4;
206 *pLength = (ULONG)-1;
207 return 0;
210 static VOID *assembly_data_offset(ASSEMBLY *assembly, ULONG offset)
212 return (VOID *)&assembly->data[offset];
215 static HRESULT parse_clr_tables(ASSEMBLY *assembly, ULONG offset)
217 DWORD i, previ, offidx;
218 ULONG currofs;
220 currofs = offset;
221 assembly->tableshdr = (METADATATABLESHDR *)assembly_data_offset(assembly, currofs);
222 if (!assembly->tableshdr)
223 return E_FAIL;
225 currofs += sizeof(METADATATABLESHDR);
226 assembly->numrows = (DWORD *)assembly_data_offset(assembly, currofs);
227 if (!assembly->numrows)
228 return E_FAIL;
230 assembly->numtables = 0;
231 for (i = 0; i < MAX_CLR_TABLES; i++)
233 if ((i < 32 && (assembly->tableshdr->MaskValid.LowPart >> i) & 1) ||
234 (i >= 32 && (assembly->tableshdr->MaskValid.HighPart >> i) & 1))
236 assembly->numtables++;
240 currofs += assembly->numtables * sizeof(DWORD);
241 memset(assembly->tables, -1, MAX_CLR_TABLES * sizeof(CLRTABLE));
243 if (assembly->tableshdr->MaskValid.LowPart & 1)
245 assembly->tables[0].offset = currofs;
246 assembly->tables[0].rows = assembly->numrows[0];
249 previ = 0;
250 offidx = 1;
251 for (i = 1; i < MAX_CLR_TABLES; i++)
253 if ((i < 32 && (assembly->tableshdr->MaskValid.LowPart >> i) & 1) ||
254 (i >= 32 && (assembly->tableshdr->MaskValid.HighPart >> i) & 1))
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;
264 return S_OK;
267 static HRESULT parse_clr_metadata(ASSEMBLY *assembly)
269 METADATASTREAMHDR *streamhdr;
270 ULONG rva, i, ofs;
271 LPSTR stream;
272 HRESULT hr;
273 BYTE *ptr;
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;
281 ptr = ImageRvaToVa(assembly->nthdr, assembly->data,
282 rva + sizeof(METADATAHDR), NULL);
283 if (!ptr)
284 return E_FAIL;
286 for (i = 0; i < assembly->metadatahdr->Streams; i++)
288 streamhdr = (METADATASTREAMHDR *)ptr;
289 ofs = rva_to_offset(assembly->nthdr, rva + streamhdr->Offset);
291 ptr += sizeof(METADATASTREAMHDR);
292 stream = (LPSTR)ptr;
294 if (!lstrcmpA(stream, "#~"))
296 hr = parse_clr_tables(assembly, ofs);
297 if (FAILED(hr))
298 return hr;
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);
305 ptr += lstrlenA(stream);
306 while (!*ptr) ptr++;
309 return S_OK;
312 static HRESULT parse_pe_header(ASSEMBLY *assembly)
314 IMAGE_DATA_DIRECTORY *datadirs;
316 assembly->nthdr = ImageNtHeader(assembly->data);
317 if (!assembly->nthdr)
318 return E_FAIL;
320 datadirs = assembly->nthdr->OptionalHeader.DataDirectory;
321 if (!datadirs)
322 return E_FAIL;
324 if (!datadirs[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].VirtualAddress ||
325 !datadirs[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].Size)
327 return E_FAIL;
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;
335 return S_OK;
338 HRESULT assembly_create(ASSEMBLY **out, LPCWSTR file)
340 ASSEMBLY *assembly;
341 HRESULT hr;
343 *out = NULL;
345 assembly = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ASSEMBLY));
346 if (!assembly)
347 return E_OUTOFMEMORY;
349 assembly->path = strdupWtoA(file);
350 if (!assembly->path)
352 hr = E_OUTOFMEMORY;
353 goto failed;
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)
360 hr = HRESULT_FROM_WIN32(GetLastError());
361 goto failed;
364 assembly->hmap = CreateFileMappingW(assembly->hfile, NULL, PAGE_READONLY,
365 0, 0, NULL);
366 if (!assembly->hmap)
368 hr = HRESULT_FROM_WIN32(GetLastError());
369 goto failed;
372 assembly->data = MapViewOfFile(assembly->hmap, FILE_MAP_READ, 0, 0, 0);
373 if (!assembly->data)
375 hr = HRESULT_FROM_WIN32(GetLastError());
376 goto failed;
379 hr = parse_pe_header(assembly);
380 if (FAILED(hr)) goto failed;
382 hr = parse_clr_metadata(assembly);
383 if (FAILED(hr)) goto failed;
385 *out = assembly;
386 return S_OK;
388 failed:
389 assembly_release( assembly );
390 return hr;
393 HRESULT assembly_release(ASSEMBLY *assembly)
395 if (!assembly)
396 return S_OK;
398 HeapFree(GetProcessHeap(), 0, assembly->path);
399 UnmapViewOfFile(assembly->data);
400 CloseHandle(assembly->hmap);
401 CloseHandle(assembly->hfile);
402 HeapFree(GetProcessHeap(), 0, assembly);
404 return S_OK;
407 static LPSTR assembly_dup_str(ASSEMBLY *assembly, WORD index)
409 return strdup((LPSTR)&assembly->strings[index]);
412 HRESULT assembly_get_name(ASSEMBLY *assembly, LPSTR *name)
414 ASSEMBLYTABLE *asmtbl;
415 ULONG offset;
417 offset = assembly->tables[0x20].offset; /* FIXME: add constants */
418 if (offset == -1)
419 return E_FAIL;
421 asmtbl = (ASSEMBLYTABLE *)assembly_data_offset(assembly, offset);
422 if (!asmtbl)
423 return E_FAIL;
425 *name = assembly_dup_str(assembly, asmtbl->Name);
426 if (!*name)
427 return E_OUTOFMEMORY;
429 return S_OK;
432 HRESULT assembly_get_path(ASSEMBLY *assembly, LPSTR *path)
434 *path = strdup(assembly->path);
435 if (!*path)
436 return E_OUTOFMEMORY;
438 return S_OK;
441 HRESULT assembly_get_version(ASSEMBLY *assembly, LPSTR *version)
443 LPSTR verdata;
444 VS_FIXEDFILEINFO *ffi;
445 HRESULT hr = S_OK;
446 DWORD size;
448 size = GetFileVersionInfoSizeA(assembly->path, NULL);
449 if (!size)
450 return HRESULT_FROM_WIN32(GetLastError());
452 verdata = HeapAlloc(GetProcessHeap(), 0, size);
453 if (!verdata)
454 return E_OUTOFMEMORY;
456 if (!GetFileVersionInfoA(assembly->path, 0, size, verdata))
458 hr = HRESULT_FROM_WIN32(GetLastError());
459 goto done;
462 if (!VerQueryValueA(verdata, "\\", (LPVOID *)&ffi, &size))
464 hr = HRESULT_FROM_WIN32(GetLastError());
465 goto done;
468 *version = HeapAlloc(GetProcessHeap(), 0, MAX_PATH);
469 if (!*version)
471 hr = E_OUTOFMEMORY;
472 goto done;
475 sprintf(*version, "%d.%d.%d.%d", HIWORD(ffi->dwFileVersionMS),
476 LOWORD(ffi->dwFileVersionMS), HIWORD(ffi->dwFileVersionLS),
477 LOWORD(ffi->dwFileVersionLS));
479 done:
480 HeapFree(GetProcessHeap(), 0, verdata);
481 return hr;
484 HRESULT assembly_get_architecture(ASSEMBLY *assembly, DWORD fixme)
486 /* FIXME */
487 return S_OK;
490 static BYTE *assembly_get_blob(ASSEMBLY *assembly, WORD index, ULONG *size)
492 return GetData(&assembly->blobs[index], size);
495 static void bytes_to_str(BYTE *bytes, DWORD len, LPSTR str)
497 int i;
499 static const char hexval[16] = {
500 '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'
503 for(i = 0; i < len; i++)
505 str[i * 2] = hexval[((bytes[i] >> 4) & 0xF)];
506 str[i * 2 + 1] = hexval[(bytes[i]) & 0x0F];
510 #define BYTES_PER_TOKEN 8
511 #define CHARS_PER_BYTE 2
512 #define TOKEN_LENGTH (BYTES_PER_TOKEN * CHARS_PER_BYTE + 1)
514 HRESULT assembly_get_pubkey_token(ASSEMBLY *assembly, LPSTR *token)
516 ASSEMBLYTABLE *asmtbl;
517 ULONG i, offset, size;
518 BYTE *hashdata;
519 HCRYPTPROV crypt;
520 HCRYPTHASH hash;
521 BYTE *pubkey;
522 BYTE tokbytes[BYTES_PER_TOKEN];
523 HRESULT hr = E_FAIL;
524 LPSTR tok;
526 *token = NULL;
528 offset = assembly->tables[0x20].offset; /* FIXME: add constants */
529 if (offset == -1)
530 return E_FAIL;
532 asmtbl = (ASSEMBLYTABLE *)assembly_data_offset(assembly, offset);
533 if (!asmtbl)
534 return E_FAIL;
536 pubkey = assembly_get_blob(assembly, asmtbl->PublicKey, &size);
538 if (!CryptAcquireContextA(&crypt, NULL, NULL, PROV_RSA_FULL,
539 CRYPT_VERIFYCONTEXT))
540 return E_FAIL;
542 if (!CryptCreateHash(crypt, CALG_SHA1, 0, 0, &hash))
543 return E_FAIL;
545 if (!CryptHashData(hash, pubkey, size, 0))
546 return E_FAIL;
548 size = 0;
549 if (!CryptGetHashParam(hash, HP_HASHVAL, NULL, &size, 0))
550 return E_FAIL;
552 hashdata = HeapAlloc(GetProcessHeap(), 0, size);
553 if (!hashdata)
555 hr = E_OUTOFMEMORY;
556 goto done;
559 if (!CryptGetHashParam(hash, HP_HASHVAL, hashdata, &size, 0))
560 goto done;
562 for (i = size - 1; i >= size - 8; i--)
563 tokbytes[size - i - 1] = hashdata[i];
565 tok = HeapAlloc(GetProcessHeap(), 0, TOKEN_LENGTH);
566 if (!tok)
568 hr = E_OUTOFMEMORY;
569 goto done;
572 bytes_to_str(tokbytes, BYTES_PER_TOKEN, tok);
573 tok[TOKEN_LENGTH - 1] = '\0';
575 *token = tok;
576 hr = S_OK;
578 done:
579 HeapFree(GetProcessHeap(), 0, hashdata);
580 CryptDestroyHash(hash);
581 CryptReleaseContext(crypt, 0);
583 return hr;