wine/dlls/fusion/assembly.c
2009-01-08 12:54:29 +01:00

939 lines
28 KiB
C

/*
* assembly parser
*
* Copyright 2008 James Hawkins
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <stdarg.h>
#include <stdio.h>
#include "windef.h"
#include "winbase.h"
#include "winuser.h"
#include "winver.h"
#include "wincrypt.h"
#include "dbghelp.h"
#include "ole2.h"
#include "fusion.h"
#include "corhdr.h"
#include "fusionpriv.h"
#include "wine/debug.h"
#include "wine/unicode.h"
#define TableFromToken(tk) (TypeFromToken(tk) >> 24)
#define TokenFromTable(idx) (idx << 24)
#define MAX_CLR_TABLES 64
#define MD_STRINGS_BIT 0x1
#define MD_GUIDS_BIT 0x2
#define MD_BLOBS_BIT 0x4
typedef struct tagCLRTABLE
{
INT rows;
DWORD offset;
} CLRTABLE;
struct tagASSEMBLY
{
LPSTR path;
HANDLE hfile;
HANDLE hmap;
BYTE *data;
IMAGE_NT_HEADERS *nthdr;
IMAGE_COR20_HEADER *corhdr;
METADATAHDR *metadatahdr;
METADATATABLESHDR *tableshdr;
DWORD numtables;
DWORD *numrows;
CLRTABLE tables[MAX_CLR_TABLES];
DWORD stringsz;
DWORD guidsz;
DWORD blobsz;
BYTE *strings;
BYTE *blobs;
};
static LPSTR strdupWtoA(LPCWSTR str)
{
LPSTR ret = NULL;
DWORD len;
if (!str)
return ret;
len = WideCharToMultiByte(CP_ACP, 0, str, -1, NULL, 0, NULL, NULL);
ret = HeapAlloc(GetProcessHeap(), 0, len);
if (ret)
WideCharToMultiByte(CP_ACP, 0, str, -1, ret, len, NULL, NULL);
return ret;
}
static DWORD rva_to_offset(IMAGE_NT_HEADERS *nthdrs, DWORD rva)
{
DWORD offset = rva, limit;
IMAGE_SECTION_HEADER *img;
WORD i;
img = IMAGE_FIRST_SECTION(nthdrs);
if (rva < img->PointerToRawData)
return rva;
for (i = 0; i < nthdrs->FileHeader.NumberOfSections; i++)
{
if (img[i].SizeOfRawData)
limit = img[i].SizeOfRawData;
else
limit = img[i].Misc.VirtualSize;
if (rva >= img[i].VirtualAddress &&
rva < (img[i].VirtualAddress + limit))
{
if (img[i].PointerToRawData != 0)
{
offset -= img[i].VirtualAddress;
offset += img[i].PointerToRawData;
}
return offset;
}
}
return 0;
}
static BYTE *GetData(BYTE *pData, ULONG *pLength)
{
if ((*pData & 0x80) == 0x00)
{
*pLength = (*pData & 0x7f);
return pData + 1;
}
if ((*pData & 0xC0) == 0x80)
{
*pLength = ((*pData & 0x3f) << 8 | *(pData + 1));
return pData + 2;
}
if ((*pData & 0xE0) == 0xC0)
{
*pLength = ((*pData & 0x1f) << 24 | *(pData + 1) << 16 |
*(pData + 2) << 8 | *(pData + 3));
return pData + 4;
}
*pLength = (ULONG)-1;
return 0;
}
static VOID *assembly_data_offset(ASSEMBLY *assembly, ULONG offset)
{
return &assembly->data[offset];
}
#define MAX_TABLES_WORD 0xFFFF
#define MAX_TABLES_1BIT_ENCODE 32767
#define MAX_TABLES_2BIT_ENCODE 16383
#define MAX_TABLES_3BIT_ENCODE 8191
#define MAX_TABLES_5BIT_ENCODE 2047
static inline ULONG get_table_size(ASSEMBLY *assembly, DWORD index)
{
DWORD size;
INT tables;
switch (TokenFromTable(index))
{
case mdtModule:
{
size = sizeof(MODULETABLE) + (assembly->stringsz - sizeof(WORD)) +
2 * (assembly->guidsz - sizeof(WORD));
break;
}
case mdtTypeRef:
{
size = sizeof(TYPEREFTABLE) + 2 * (assembly->stringsz - sizeof(WORD));
/* ResolutionScope:ResolutionScope */
tables = max(assembly->tables[TableFromToken(mdtModule)].rows,
assembly->tables[TableFromToken(mdtModuleRef)].rows);
tables = max(tables, assembly->tables[TableFromToken(mdtAssemblyRef)].rows);
tables = max(tables, assembly->tables[TableFromToken(mdtTypeRef)].rows);
size += (tables > MAX_TABLES_2BIT_ENCODE) ? sizeof(WORD) : 0;
break;
}
case mdtTypeDef:
{
size = sizeof(TYPEDEFTABLE) + 2 * (assembly->stringsz - sizeof(WORD));
/* Extends:TypeDefOrRef */
tables = max(assembly->tables[TableFromToken(mdtTypeDef)].rows,
assembly->tables[TableFromToken(mdtTypeRef)].rows);
tables = max(tables, assembly->tables[TableFromToken(mdtTypeSpec)].rows);
size += (tables > MAX_TABLES_2BIT_ENCODE) ? sizeof(WORD) : 0;
size += (assembly->tables[TableFromToken(mdtFieldDef)].rows >
MAX_TABLES_WORD) ? sizeof(WORD) : 0;
size += (assembly->tables[TableFromToken(mdtMethodDef)].rows >
MAX_TABLES_WORD) ? sizeof(WORD) : 0;
break;
}
case mdtFieldDef:
{
size = sizeof(FIELDTABLE) + (assembly->stringsz - sizeof(WORD)) +
(assembly->blobsz - sizeof(WORD));
break;
}
case mdtMethodDef:
{
size = sizeof(METHODDEFTABLE) + (assembly->stringsz - sizeof(WORD)) +
(assembly->blobsz - sizeof(WORD));
size += (assembly->tables[TableFromToken(mdtParamDef)].rows >
MAX_TABLES_WORD) ? sizeof(WORD) : 0;
break;
}
case mdtParamDef:
{
size = sizeof(PARAMTABLE) + (assembly->stringsz - sizeof(WORD));
break;
}
case mdtInterfaceImpl:
{
size = sizeof(INTERFACEIMPLTABLE);
/* Interface:TypeDefOrRef */
tables = max(assembly->tables[TableFromToken(mdtTypeDef)].rows,
assembly->tables[TableFromToken(mdtTypeRef)].rows);
tables = max(tables, assembly->tables[TableFromToken(mdtTypeSpec)].rows);
size += (tables > MAX_TABLES_2BIT_ENCODE) ? sizeof(WORD) : 0;
break;
}
case mdtMemberRef:
{
size = sizeof(MEMBERREFTABLE) + (assembly->stringsz - sizeof(WORD)) +
(assembly->blobsz - sizeof(WORD));
/* Class:MemberRefParent */
tables = max(assembly->tables[TableFromToken(mdtTypeRef)].rows,
assembly->tables[TableFromToken(mdtModuleRef)].rows);
tables = max(tables, assembly->tables[TableFromToken(mdtMethodDef)].rows);
tables = max(tables, assembly->tables[TableFromToken(mdtTypeSpec)].rows);
tables = max(tables, assembly->tables[TableFromToken(mdtTypeDef)].rows);
size += (tables > MAX_TABLES_3BIT_ENCODE) ? sizeof(WORD) : 0;
break;
}
case 0x0B000000: /* FIXME */
{
size = sizeof(CONSTANTTABLE) + (assembly->blobsz - sizeof(WORD));
/* Parent:HasConstant */
tables = max(assembly->tables[TableFromToken(mdtParamDef)].rows,
assembly->tables[TableFromToken(mdtFieldDef)].rows);
tables = max(tables, assembly->tables[TableFromToken(mdtProperty)].rows);
size += (tables > MAX_TABLES_2BIT_ENCODE) ? sizeof(WORD) : 0;
break;
}
case mdtCustomAttribute:
{
size = sizeof(CUSTOMATTRIBUTETABLE) + (assembly->blobsz - sizeof(WORD));
/* Parent:HasCustomAttribute */
tables = max(assembly->tables[TableFromToken(mdtMethodDef)].rows,
assembly->tables[TableFromToken(mdtFieldDef)].rows);
tables = max(tables, assembly->tables[TableFromToken(mdtTypeRef)].rows);
tables = max(tables, assembly->tables[TableFromToken(mdtTypeDef)].rows);
tables = max(tables, assembly->tables[TableFromToken(mdtParamDef)].rows);
tables = max(tables, assembly->tables[TableFromToken(mdtInterfaceImpl)].rows);
tables = max(tables, assembly->tables[TableFromToken(mdtMemberRef)].rows);
tables = max(tables, assembly->tables[TableFromToken(mdtPermission)].rows);
tables = max(tables, assembly->tables[TableFromToken(mdtProperty)].rows);
tables = max(tables, assembly->tables[TableFromToken(mdtEvent)].rows);
tables = max(tables, assembly->tables[TableFromToken(mdtSignature)].rows);
tables = max(tables, assembly->tables[TableFromToken(mdtModuleRef)].rows);
tables = max(tables, assembly->tables[TableFromToken(mdtTypeSpec)].rows);
tables = max(tables, assembly->tables[TableFromToken(mdtAssembly)].rows);
tables = max(tables, assembly->tables[TableFromToken(mdtFile)].rows);
tables = max(tables, assembly->tables[TableFromToken(mdtExportedType)].rows);
tables = max(tables, assembly->tables[TableFromToken(mdtManifestResource)].rows);
size += (tables > MAX_TABLES_5BIT_ENCODE) ? sizeof(WORD) : 0;
/* Type:CustomAttributeType */
tables = max(assembly->tables[TableFromToken(mdtMethodDef)].rows,
assembly->tables[TableFromToken(mdtMemberRef)].rows);
size += (tables > MAX_TABLES_3BIT_ENCODE) ? sizeof(WORD) : 0;
break;
}
case 0x0D000000: /* FIXME */
{
size = sizeof(FIELDMARSHALTABLE) + (assembly->blobsz - sizeof(WORD));
/* Parent:HasFieldMarshal */
tables = max(assembly->tables[TableFromToken(mdtFieldDef)].rows,
assembly->tables[TableFromToken(mdtParamDef)].rows);
size += (tables > MAX_TABLES_1BIT_ENCODE) ? sizeof(WORD) : 0;
break;
}
case mdtPermission:
{
size = sizeof(DECLSECURITYTABLE) + (assembly->blobsz - sizeof(WORD));
/* Parent:HasDeclSecurity */
tables = max(assembly->tables[TableFromToken(mdtTypeDef)].rows,
assembly->tables[TableFromToken(mdtMethodDef)].rows);
tables = max(tables, assembly->tables[TableFromToken(mdtAssembly)].rows);
size += (tables > MAX_TABLES_2BIT_ENCODE) ? sizeof(WORD) : 0;
break;
}
case 0x0F000000: /* FIXME */
{
size = sizeof(CLASSLAYOUTTABLE);
size += (assembly->tables[TableFromToken(mdtTypeDef)].rows >
MAX_TABLES_WORD) ? sizeof(WORD) : 0;
break;
}
case 0x10000000: /* FIXME */
{
size = sizeof(FIELDLAYOUTTABLE);
size += (assembly->tables[TableFromToken(mdtFieldDef)].rows >
MAX_TABLES_WORD) ? sizeof(WORD) : 0;
break;
}
case mdtSignature:
{
size = sizeof(STANDALONESIGTABLE) + (assembly->blobsz - sizeof(WORD));
break;
}
case 0x12000000: /* FIXME */
{
size = sizeof(EVENTMAPTABLE);
size += (assembly->tables[TableFromToken(mdtTypeDef)].rows >
MAX_TABLES_WORD) ? sizeof(WORD) : 0;
size += (assembly->tables[TableFromToken(mdtEvent)].rows >
MAX_TABLES_WORD) ? sizeof(WORD) : 0;
break;
}
case mdtEvent:
{
size = sizeof(EVENTTABLE) + (assembly->stringsz - sizeof(WORD));
/* EventType:TypeDefOrRef */
tables = max(assembly->tables[TableFromToken(mdtTypeDef)].rows,
assembly->tables[TableFromToken(mdtTypeRef)].rows);
tables = max(tables, assembly->tables[TableFromToken(mdtTypeSpec)].rows);
size += (tables > MAX_TABLES_2BIT_ENCODE) ? sizeof(WORD) : 0;
break;
}
case 0x15000000:/* FIXME */
{
size = sizeof(PROPERTYMAPTABLE);
size += (assembly->tables[TableFromToken(mdtTypeDef)].rows >
MAX_TABLES_WORD) ? sizeof(WORD) : 0;
size += (assembly->tables[TableFromToken(mdtProperty)].rows >
MAX_TABLES_WORD) ? sizeof(WORD) : 0;
break;
}
case mdtProperty:
{
size = sizeof(PROPERTYTABLE) + (assembly->stringsz - sizeof(WORD)) +
(assembly->blobsz - sizeof(WORD));
break;
}
case 0x18000000: /* FIXME */
{
size = sizeof(METHODSEMANTICSTABLE);
/* Association:HasSemantics */
tables = max(assembly->tables[TableFromToken(mdtEvent)].rows,
assembly->tables[TableFromToken(mdtProperty)].rows);
size += (tables > MAX_TABLES_1BIT_ENCODE) ? sizeof(WORD) : 0;
size += (assembly->tables[TableFromToken(mdtMethodDef)].rows >
MAX_TABLES_WORD) ? sizeof(WORD) : 0;
break;
}
case 0x19000000: /* FIXME */
{
size = sizeof(METHODIMPLTABLE);
/* MethodBody:MethodDefOrRef, MethodDeclaration:MethodDefOrRef */
tables = max(assembly->tables[TableFromToken(mdtMethodDef)].rows,
assembly->tables[TableFromToken(mdtMemberRef)].rows);
size += (tables > MAX_TABLES_1BIT_ENCODE) ? 2 * sizeof(WORD) : 0;
size += (assembly->tables[TableFromToken(mdtTypeDef)].rows >
MAX_TABLES_WORD) ? sizeof(WORD) : 0;
break;
}
case mdtModuleRef:
{
size = sizeof(MODULEREFTABLE) + (assembly->stringsz - sizeof(WORD));
break;
}
case mdtTypeSpec:
{
size = sizeof(TYPESPECTABLE) + (assembly->blobsz - sizeof(WORD));
break;
}
case 0x1C000000: /* FIXME */
{
size = sizeof(IMPLMAPTABLE) + (assembly->stringsz - sizeof(WORD));
/* MemberForwarded:MemberForwarded */
tables = max(assembly->tables[TableFromToken(mdtFieldDef)].rows,
assembly->tables[TableFromToken(mdtMethodDef)].rows);
size += (tables > MAX_TABLES_1BIT_ENCODE) ? sizeof(WORD) : 0;
size += (assembly->tables[TableFromToken(mdtModuleRef)].rows >
MAX_TABLES_WORD) ? sizeof(WORD) : 0;
break;
}
case 0x1D000000: /* FIXME */
{
size = sizeof(FIELDRVATABLE);
size += (assembly->tables[TableFromToken(mdtFieldDef)].rows >
MAX_TABLES_WORD) ? sizeof(WORD) : 0;
break;
}
case mdtAssembly:
{
size = sizeof(ASSEMBLYTABLE) + 2 * (assembly->stringsz - sizeof(WORD)) +
(assembly->blobsz - sizeof(WORD));
break;
}
case 0x20000001: /* FIXME */
{
size = sizeof(ASSEMBLYPROCESSORTABLE);
break;
}
case 0x22000000: /* FIXME */
{
size = sizeof(ASSEMBLYOSTABLE);
break;
}
case mdtAssemblyRef:
{
size = sizeof(ASSEMBLYREFTABLE) + 2 * (assembly->stringsz - sizeof(WORD)) +
2 * (assembly->blobsz - sizeof(WORD));
break;
}
case 0x24000000: /* FIXME */
{
size = sizeof(ASSEMBLYREFPROCESSORTABLE);
size += (assembly->tables[TableFromToken(mdtAssemblyRef)].rows >
MAX_TABLES_WORD) ? sizeof(WORD) : 0;
break;
}
case 0x25000000: /* FIXME */
{
size = sizeof(ASSEMBLYREFOSTABLE);
size += (assembly->tables[TableFromToken(mdtAssemblyRef)].rows >
MAX_TABLES_WORD) ? sizeof(WORD) : 0;
break;
}
case mdtFile:
{
size = sizeof(FILETABLE) + (assembly->stringsz - sizeof(WORD)) +
(assembly->blobsz - sizeof(WORD));
break;
}
case mdtExportedType:
{
size = sizeof(EXPORTEDTYPETABLE) + 2 * (assembly->stringsz - sizeof(WORD));
/* Implementation:Implementation */
tables = max(assembly->tables[TableFromToken(mdtFile)].rows,
assembly->tables[TableFromToken(mdtMethodDef)].rows);
size += (tables > MAX_TABLES_2BIT_ENCODE) ? sizeof(WORD) : 0;
break;
}
case mdtManifestResource:
{
size = sizeof(MANIFESTRESTABLE) + (assembly->stringsz - sizeof(WORD));
/* Implementation:Implementation */
tables = max(assembly->tables[TableFromToken(mdtFile)].rows,
assembly->tables[TableFromToken(mdtAssemblyRef)].rows);
size += (tables > MAX_TABLES_2BIT_ENCODE) ? sizeof(WORD) : 0;
break;
}
case 0x29000000: /* FIXME */
{
size = sizeof(NESTEDCLASSTABLE);
size += (assembly->tables[TableFromToken(mdtTypeDef)].rows >
MAX_TABLES_WORD) ? 2 * sizeof(WORD) : 0;
break;
}
default:
return 0;
}
return size;
}
static HRESULT parse_clr_tables(ASSEMBLY *assembly, ULONG offset)
{
DWORD i, previ, offidx;
ULONG currofs;
currofs = offset;
assembly->tableshdr = assembly_data_offset(assembly, currofs);
if (!assembly->tableshdr)
return E_FAIL;
assembly->stringsz = (assembly->tableshdr->HeapOffsetSizes & MD_STRINGS_BIT) ?
sizeof(DWORD) : sizeof(WORD);
assembly->guidsz = (assembly->tableshdr->HeapOffsetSizes & MD_GUIDS_BIT) ?
sizeof(DWORD) : sizeof(WORD);
assembly->blobsz = (assembly->tableshdr->HeapOffsetSizes & MD_BLOBS_BIT) ?
sizeof(DWORD) : sizeof(WORD);
currofs += sizeof(METADATATABLESHDR);
assembly->numrows = assembly_data_offset(assembly, currofs);
if (!assembly->numrows)
return E_FAIL;
assembly->numtables = 0;
for (i = 0; i < MAX_CLR_TABLES; i++)
{
if ((i < 32 && (assembly->tableshdr->MaskValid.u.LowPart >> i) & 1) ||
(i >= 32 && (assembly->tableshdr->MaskValid.u.HighPart >> i) & 1))
{
assembly->numtables++;
}
}
currofs += assembly->numtables * sizeof(DWORD);
memset(assembly->tables, -1, MAX_CLR_TABLES * sizeof(CLRTABLE));
if (assembly->tableshdr->MaskValid.u.LowPart & 1)
assembly->tables[0].offset = currofs;
offidx = 0;
for (i = 0; i < MAX_CLR_TABLES; i++)
{
if ((i < 32 && (assembly->tableshdr->MaskValid.u.LowPart >> i) & 1) ||
(i >= 32 && (assembly->tableshdr->MaskValid.u.HighPart >> i) & 1))
{
assembly->tables[i].rows = assembly->numrows[offidx];
offidx++;
}
}
previ = 0;
offidx = 1;
for (i = 1; i < MAX_CLR_TABLES; i++)
{
if ((i < 32 && (assembly->tableshdr->MaskValid.u.LowPart >> i) & 1) ||
(i >= 32 && (assembly->tableshdr->MaskValid.u.HighPart >> i) & 1))
{
currofs += get_table_size(assembly, previ) * assembly->numrows[offidx - 1];
assembly->tables[i].offset = currofs;
offidx++;
previ = i;
}
}
return S_OK;
}
static HRESULT parse_metadata_header(ASSEMBLY *assembly, DWORD *hdrsz)
{
METADATAHDR *metadatahdr;
BYTE *ptr, *dest;
DWORD size, ofs;
ULONG rva;
rva = assembly->corhdr->MetaData.VirtualAddress;
ptr = ImageRvaToVa(assembly->nthdr, assembly->data, rva, NULL);
if (!ptr)
return E_FAIL;
metadatahdr = (METADATAHDR *)ptr;
assembly->metadatahdr = HeapAlloc(GetProcessHeap(), 0, sizeof(METADATAHDR));
if (!assembly->metadatahdr)
return E_OUTOFMEMORY;
size = FIELD_OFFSET(METADATAHDR, Version);
memcpy(assembly->metadatahdr, metadatahdr, size);
/* we don't care about the version string */
ofs = FIELD_OFFSET(METADATAHDR, Flags);
ptr += FIELD_OFFSET(METADATAHDR, Version) + metadatahdr->VersionLength + 1;
dest = (BYTE *)assembly->metadatahdr + ofs;
memcpy(dest, ptr, sizeof(METADATAHDR) - ofs);
*hdrsz = sizeof(METADATAHDR) - sizeof(LPSTR) + metadatahdr->VersionLength + 1;
return S_OK;
}
static HRESULT parse_clr_metadata(ASSEMBLY *assembly)
{
METADATASTREAMHDR *streamhdr;
ULONG rva, i, ofs;
LPSTR stream;
HRESULT hr;
DWORD hdrsz;
BYTE *ptr;
hr = parse_metadata_header(assembly, &hdrsz);
if (FAILED(hr))
return hr;
rva = assembly->corhdr->MetaData.VirtualAddress;
ptr = ImageRvaToVa(assembly->nthdr, assembly->data, rva + hdrsz, NULL);
if (!ptr)
return E_FAIL;
for (i = 0; i < assembly->metadatahdr->Streams; i++)
{
streamhdr = (METADATASTREAMHDR *)ptr;
ofs = rva_to_offset(assembly->nthdr, rva + streamhdr->Offset);
ptr += sizeof(METADATASTREAMHDR);
stream = (LPSTR)ptr;
if (!lstrcmpA(stream, "#~"))
{
hr = parse_clr_tables(assembly, ofs);
if (FAILED(hr))
return hr;
}
else if (!lstrcmpA(stream, "#Strings") || !lstrcmpA(stream, "Strings"))
assembly->strings = assembly_data_offset(assembly, ofs);
else if (!lstrcmpA(stream, "#Blob") || !lstrcmpA(stream, "Blob"))
assembly->blobs = assembly_data_offset(assembly, ofs);
ptr += lstrlenA(stream) + 1;
ptr = (BYTE *)(((UINT_PTR)ptr + 3) & ~3); /* align on DWORD boundary */
}
return S_OK;
}
static HRESULT parse_pe_header(ASSEMBLY *assembly)
{
IMAGE_DATA_DIRECTORY *datadirs;
assembly->nthdr = ImageNtHeader(assembly->data);
if (!assembly->nthdr)
return E_FAIL;
if (assembly->nthdr->FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64)
{
IMAGE_OPTIONAL_HEADER64 *opthdr =
(IMAGE_OPTIONAL_HEADER64 *)&assembly->nthdr->OptionalHeader;
datadirs = opthdr->DataDirectory;
}
else
datadirs = assembly->nthdr->OptionalHeader.DataDirectory;
if (!datadirs)
return E_FAIL;
if (!datadirs[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].VirtualAddress ||
!datadirs[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].Size)
{
return E_FAIL;
}
assembly->corhdr = ImageRvaToVa(assembly->nthdr, assembly->data,
datadirs[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].VirtualAddress, NULL);
if (!assembly->corhdr)
return E_FAIL;
return S_OK;
}
HRESULT assembly_create(ASSEMBLY **out, LPCWSTR file)
{
ASSEMBLY *assembly;
HRESULT hr;
*out = NULL;
assembly = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ASSEMBLY));
if (!assembly)
return E_OUTOFMEMORY;
assembly->path = strdupWtoA(file);
if (!assembly->path)
{
hr = E_OUTOFMEMORY;
goto failed;
}
assembly->hfile = CreateFileW(file, GENERIC_READ, FILE_SHARE_READ,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (assembly->hfile == INVALID_HANDLE_VALUE)
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto failed;
}
assembly->hmap = CreateFileMappingW(assembly->hfile, NULL, PAGE_READONLY,
0, 0, NULL);
if (!assembly->hmap)
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto failed;
}
assembly->data = MapViewOfFile(assembly->hmap, FILE_MAP_READ, 0, 0, 0);
if (!assembly->data)
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto failed;
}
hr = parse_pe_header(assembly);
if (FAILED(hr)) goto failed;
hr = parse_clr_metadata(assembly);
if (FAILED(hr)) goto failed;
*out = assembly;
return S_OK;
failed:
assembly_release(assembly);
return hr;
}
HRESULT assembly_release(ASSEMBLY *assembly)
{
if (!assembly)
return S_OK;
HeapFree(GetProcessHeap(), 0, assembly->metadatahdr);
HeapFree(GetProcessHeap(), 0, assembly->path);
UnmapViewOfFile(assembly->data);
CloseHandle(assembly->hmap);
CloseHandle(assembly->hfile);
HeapFree(GetProcessHeap(), 0, assembly);
return S_OK;
}
static LPSTR assembly_dup_str(ASSEMBLY *assembly, DWORD index)
{
LPSTR str = (LPSTR)&assembly->strings[index];
LPSTR cpy = HeapAlloc(GetProcessHeap(), 0, strlen(str)+1);
if (cpy)
strcpy(cpy, str);
return cpy;
}
HRESULT assembly_get_name(ASSEMBLY *assembly, LPSTR *name)
{
BYTE *ptr;
LONG offset;
DWORD stridx;
offset = assembly->tables[TableFromToken(mdtAssembly)].offset;
if (offset == -1)
return E_FAIL;
ptr = assembly_data_offset(assembly, offset);
if (!ptr)
return E_FAIL;
ptr += FIELD_OFFSET(ASSEMBLYTABLE, PublicKey) + assembly->blobsz;
if (assembly->stringsz == sizeof(DWORD))
stridx = *((DWORD *)ptr);
else
stridx = *((WORD *)ptr);
*name = assembly_dup_str(assembly, stridx);
if (!*name)
return E_OUTOFMEMORY;
return S_OK;
}
HRESULT assembly_get_path(ASSEMBLY *assembly, LPSTR *path)
{
LPSTR cpy = HeapAlloc(GetProcessHeap(), 0, strlen(assembly->path)+1);
*path = cpy;
if (cpy)
strcpy(cpy, assembly->path);
else
return E_OUTOFMEMORY;
return S_OK;
}
HRESULT assembly_get_version(ASSEMBLY *assembly, LPSTR *version)
{
LPSTR verdata;
VS_FIXEDFILEINFO *ffi;
HRESULT hr = S_OK;
DWORD size;
size = GetFileVersionInfoSizeA(assembly->path, NULL);
if (!size)
return HRESULT_FROM_WIN32(GetLastError());
verdata = HeapAlloc(GetProcessHeap(), 0, size);
if (!verdata)
return E_OUTOFMEMORY;
if (!GetFileVersionInfoA(assembly->path, 0, size, verdata))
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto done;
}
if (!VerQueryValueA(verdata, "\\", (LPVOID *)&ffi, &size))
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto done;
}
*version = HeapAlloc(GetProcessHeap(), 0, MAX_PATH);
if (!*version)
{
hr = E_OUTOFMEMORY;
goto done;
}
sprintf(*version, "%d.%d.%d.%d", HIWORD(ffi->dwFileVersionMS),
LOWORD(ffi->dwFileVersionMS), HIWORD(ffi->dwFileVersionLS),
LOWORD(ffi->dwFileVersionLS));
done:
HeapFree(GetProcessHeap(), 0, verdata);
return hr;
}
HRESULT assembly_get_architecture(ASSEMBLY *assembly, DWORD fixme)
{
/* FIXME */
return S_OK;
}
static BYTE *assembly_get_blob(ASSEMBLY *assembly, WORD index, ULONG *size)
{
return GetData(&assembly->blobs[index], size);
}
static void bytes_to_str(BYTE *bytes, DWORD len, LPSTR str)
{
DWORD i;
static const char hexval[16] = {
'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'
};
for(i = 0; i < len; i++)
{
str[i * 2] = hexval[((bytes[i] >> 4) & 0xF)];
str[i * 2 + 1] = hexval[(bytes[i]) & 0x0F];
}
}
#define BYTES_PER_TOKEN 8
#define CHARS_PER_BYTE 2
#define TOKEN_LENGTH (BYTES_PER_TOKEN * CHARS_PER_BYTE + 1)
HRESULT assembly_get_pubkey_token(ASSEMBLY *assembly, LPSTR *token)
{
ASSEMBLYTABLE *asmtbl;
ULONG i, size;
LONG offset;
BYTE *hashdata;
HCRYPTPROV crypt;
HCRYPTHASH hash;
BYTE *pubkey;
BYTE tokbytes[BYTES_PER_TOKEN];
HRESULT hr = E_FAIL;
LPSTR tok;
*token = NULL;
offset = assembly->tables[TableFromToken(mdtAssembly)].offset;
if (offset == -1)
return E_FAIL;
asmtbl = assembly_data_offset(assembly, offset);
if (!asmtbl)
return E_FAIL;
pubkey = assembly_get_blob(assembly, asmtbl->PublicKey, &size);
if (!CryptAcquireContextA(&crypt, NULL, NULL, PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT))
return E_FAIL;
if (!CryptCreateHash(crypt, CALG_SHA1, 0, 0, &hash))
return E_FAIL;
if (!CryptHashData(hash, pubkey, size, 0))
return E_FAIL;
size = 0;
if (!CryptGetHashParam(hash, HP_HASHVAL, NULL, &size, 0))
return E_FAIL;
hashdata = HeapAlloc(GetProcessHeap(), 0, size);
if (!hashdata)
{
hr = E_OUTOFMEMORY;
goto done;
}
if (!CryptGetHashParam(hash, HP_HASHVAL, hashdata, &size, 0))
goto done;
for (i = size - 1; i >= size - 8; i--)
tokbytes[size - i - 1] = hashdata[i];
tok = HeapAlloc(GetProcessHeap(), 0, TOKEN_LENGTH);
if (!tok)
{
hr = E_OUTOFMEMORY;
goto done;
}
bytes_to_str(tokbytes, BYTES_PER_TOKEN, tok);
tok[TOKEN_LENGTH - 1] = '\0';
*token = tok;
hr = S_OK;
done:
HeapFree(GetProcessHeap(), 0, hashdata);
CryptDestroyHash(hash);
CryptReleaseContext(crypt, 0);
return hr;
}