From: Vibhav Pant vibhavp@gmail.com
--- dlls/rometadata/Makefile.in | 1 + dlls/rometadata/assembly.c | 247 +++++++++++++++++++++++++++++ dlls/rometadata/main.c | 2 +- dlls/rometadata/mdtables.c | 48 ++++-- dlls/rometadata/rometadatapriv.h | 17 +- dlls/rometadata/tests/rometadata.c | 10 -- 6 files changed, 301 insertions(+), 24 deletions(-) create mode 100644 dlls/rometadata/assembly.c
diff --git a/dlls/rometadata/Makefile.in b/dlls/rometadata/Makefile.in index 52c39ba8f87..29edc10fb16 100644 --- a/dlls/rometadata/Makefile.in +++ b/dlls/rometadata/Makefile.in @@ -3,5 +3,6 @@ IMPORTLIB = rometadata IMPORTS = combase
SOURCES = \ + assembly.c \ main.c \ mdtables.c diff --git a/dlls/rometadata/assembly.c b/dlls/rometadata/assembly.c new file mode 100644 index 00000000000..cb95f3fde02 --- /dev/null +++ b/dlls/rometadata/assembly.c @@ -0,0 +1,247 @@ +/* + * CIL assembly parser + * + * Copyright 2025 Vibhav Pant + * + * 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 "rometadatapriv.h" + +#include <assert.h> +#include <wine/debug.h> + +WINE_DEFAULT_DEBUG_CHANNEL(rometadata); + +#pragma pack(push, 1) + +#define METADATA_MAGIC ('B' | ('S' << 8) | ('J' << 16) | ('B' << 24)) + +/* ECMA-335 Partition II.24.2.1, "Metadata root" */ +struct metadata_hdr +{ + UINT32 signature; + UINT16 major_version; + UINT16 minor_version; + UINT32 reserved; + UINT32 length; + char version[0]; +}; + +/* ECMA-335 Partition II.24.2.2, "Stream header" */ +struct stream_hdr +{ + UINT32 offset; + UINT32 size; + char name[0]; +}; + +#pragma pack(pop) + +struct metadata_stream +{ + UINT32 size; + const BYTE *start; +}; + +struct assembly +{ + HANDLE file; + HANDLE map; + const BYTE *data; + UINT64 size; + + struct metadata_stream stream_tables; + struct metadata_stream stream_strings; + struct metadata_stream stream_blobs; + struct metadata_stream stream_guids; + struct metadata_stream stream_user_strings; +}; + +static BOOL pe_rva_to_offset(const IMAGE_SECTION_HEADER *sections, UINT32 num_sections, UINT32 rva, UINT32 *offset) +{ + UINT32 i; + + for (i = 0; i < num_sections; i++) + { + if (rva >= sections[i].VirtualAddress && rva < (sections[i].VirtualAddress + sections[i].Misc.VirtualSize)) + { + *offset = rva - sections[i].VirtualAddress + sections[i].PointerToRawData; + return TRUE; + } + } + return FALSE; +} + +static HRESULT assembly_parse_headers(assembly_t *assembly) +{ + const IMAGE_DOS_HEADER *dos_hdr = (IMAGE_DOS_HEADER *)assembly->data; + const IMAGE_SECTION_HEADER *sections; + const IMAGE_NT_HEADERS32 *nt_hdrs; + const IMAGE_COR20_HEADER *cor_hdr; + const struct metadata_hdr *md_hdr; + const BYTE *streams_cur, *md_start; + UINT32 rva, num_sections, offset; + UINT8 num_streams, i; + + if (assembly->size < sizeof(IMAGE_DOS_HEADER) || dos_hdr->e_magic != IMAGE_DOS_SIGNATURE || + assembly->size < (dos_hdr->e_lfanew + sizeof(IMAGE_NT_HEADERS32))) + return E_INVALIDARG; + + nt_hdrs = (IMAGE_NT_HEADERS32 *)(assembly->data + dos_hdr->e_lfanew); + if (!(num_sections = nt_hdrs->FileHeader.NumberOfSections)) return E_INVALIDARG; + switch (nt_hdrs->OptionalHeader.Magic) + { + case IMAGE_NT_OPTIONAL_HDR32_MAGIC: + rva = nt_hdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].VirtualAddress; + sections = (IMAGE_SECTION_HEADER *)(assembly->data + dos_hdr->e_lfanew + sizeof(IMAGE_NT_HEADERS32)); + break; + case IMAGE_NT_OPTIONAL_HDR64_MAGIC: + { + const IMAGE_NT_HEADERS64 *hdr64 = (IMAGE_NT_HEADERS64 *)(assembly->data + dos_hdr->e_lfanew); + rva = hdr64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].VirtualAddress; + sections = (IMAGE_SECTION_HEADER *)(assembly->data + dos_hdr->e_lfanew + sizeof(IMAGE_NT_HEADERS64)); + break; + } + default: + return E_INVALIDARG; + } + + if (!pe_rva_to_offset(sections, num_sections, rva, &offset)) return E_INVALIDARG; + + cor_hdr = (IMAGE_COR20_HEADER *)(assembly->data + offset); + if (cor_hdr->cb != sizeof(IMAGE_COR20_HEADER)) return E_INVALIDARG; + if (!(pe_rva_to_offset(sections, num_sections, cor_hdr->MetaData.VirtualAddress, &offset))) return E_INVALIDARG; + + md_start = assembly->data + offset; + md_hdr = (struct metadata_hdr *)md_start; + if (md_hdr->signature != METADATA_MAGIC) return E_INVALIDARG; + + num_streams = *(UINT8 *)(md_start + offsetof(struct metadata_hdr, version[md_hdr->length]) + sizeof(UINT16)); /* Flags */ + streams_cur = md_start + offsetof(struct metadata_hdr, version[md_hdr->length]) + sizeof(UINT16) * 2; /* Flags + Streams */ + + for (i = 0; i < num_streams; i++) + { + const struct stream_hdr *md_stream_hdr = (struct stream_hdr *)streams_cur; + const struct + { + const char *name; + DWORD name_len; + struct metadata_stream *stream; + } streams[] = + { + { "#~", 2, &assembly->stream_tables }, + { "#Strings", 8, &assembly->stream_strings }, + { "#Blob", 5, &assembly->stream_blobs }, + { "#GUID", 5, &assembly->stream_guids }, + { "#US", 3, &assembly->stream_user_strings } + }; + HRESULT hr = E_INVALIDARG; + int j; + + for (j = 0; j < ARRAY_SIZE(streams); j++) + { + if (!strncmp(streams[j].name, md_stream_hdr->name, streams[j].name_len)) + { + if (md_stream_hdr->offset + md_stream_hdr->size <= assembly->size) + { + streams[j].stream->size = md_stream_hdr->size; + streams[j].stream->start = md_start + md_stream_hdr->offset; + hr = S_OK; + } + break; + } + } + if (FAILED(hr)) return hr; + /* The stream name is padded to the next 4-byte boundary (ECMA-335 Partition II.24.2.2) */ + streams_cur += offsetof(struct stream_hdr, name[(streams[j].name_len + 4) & ~3]); + } + + if (assembly->stream_strings.size) + { + /* IMetaDataTables::GetStringHeapSize returns the string heap size without the nul byte padding. */ + const BYTE *ptr = assembly->stream_strings.start + assembly->stream_strings.size - 1; + ULONG padding; + + for (padding = 0; !ptr[0] && !ptr[-1]; ptr--, padding++) /* nothing */; + assembly->stream_strings.size -= padding; + } + + return S_OK; +} + +HRESULT assembly_open_from_file(const WCHAR *path, assembly_t **ret) +{ + assembly_t *assembly; + HRESULT hr = S_OK; + + TRACE("(%s, %p)\n", debugstr_w(path), ret); + + if (!(assembly = calloc(1, sizeof(*assembly)))) return E_OUTOFMEMORY; + assembly->file = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (assembly->file == INVALID_HANDLE_VALUE) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + goto done; + } + if (!(assembly->map = CreateFileMappingW(assembly->file, NULL, PAGE_READONLY, 0, 0, NULL))) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + goto done; + } + if (!(assembly->data = MapViewOfFile(assembly->map, FILE_MAP_READ, 0, 0, 0))) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + goto done; + } + if (!GetFileSizeEx(assembly->file, (LARGE_INTEGER *)&assembly->size)) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + goto done; + } + + hr = assembly_parse_headers(assembly); + +done: + if (FAILED(hr)) + assembly_free(assembly); + else + *ret = assembly; + return hr; +} + +void assembly_free(assembly_t *assembly) +{ + if (assembly->map) UnmapViewOfFile(assembly->map); + CloseHandle(assembly->map); + CloseHandle(assembly->file); + free(assembly); +} + +ULONG assembly_get_heap_size(const assembly_t *assembly, enum heap_type heap) +{ + switch (heap) + { + case HEAP_BLOB: + return assembly->stream_blobs.size; + case HEAP_STRING: + return assembly->stream_strings.size; + case HEAP_GUID: + return assembly->stream_guids.size; + case HEAP_USER_STRING: + return assembly->stream_user_strings.size; + DEFAULT_UNREACHABLE; + } +} \ No newline at end of file diff --git a/dlls/rometadata/main.c b/dlls/rometadata/main.c index 6cded07c11f..346753009a7 100644 --- a/dlls/rometadata/main.c +++ b/dlls/rometadata/main.c @@ -96,7 +96,7 @@ static HRESULT WINAPI MetaDataDispenser_OpenScope(IMetaDataDispenserEx *iface, c FIXME("%p %s %lx %s %p semi-stub!\n", iface, debugstr_w(scope), open_flags, debugstr_guid(riid), obj);
*obj = NULL; - hr = IMetaDataTables_create(&tables); + hr = IMetaDataTables_create(scope, &tables); if (FAILED(hr)) return hr;
diff --git a/dlls/rometadata/mdtables.c b/dlls/rometadata/mdtables.c index 180dbb09b68..9affb80f5bf 100644 --- a/dlls/rometadata/mdtables.c +++ b/dlls/rometadata/mdtables.c @@ -32,6 +32,8 @@ struct metadata_tables { IMetaDataTables IMetaDataTables_iface; LONG ref; + + assembly_t *assembly; };
static inline struct metadata_tables *impl_from_IMetaDataTables(IMetaDataTables *iface) @@ -70,38 +72,53 @@ static ULONG WINAPI tables_Release(IMetaDataTables *iface)
TRACE("(%p)\n", iface);
- if (!ref) free(impl); + if (!ref) + { + assembly_free(impl->assembly); + free(impl); + } return ref; }
+static HRESULT get_heap_size(IMetaDataTables *iface, enum heap_type type, ULONG *size) +{ + struct metadata_tables *impl = impl_from_IMetaDataTables(iface); + *size = assembly_get_heap_size(impl->assembly, type); + return S_OK; +} + static HRESULT WINAPI tables_GetStringHeapSize(IMetaDataTables *iface, ULONG *size) { - FIXME("(%p, %p): stub!\n", iface, size); - return E_NOTIMPL; + TRACE("(%p, %p)\n", iface, size); + return get_heap_size(iface, HEAP_STRING, size); }
static HRESULT WINAPI tables_GetBlobHeapSize(IMetaDataTables *iface, ULONG *size) { - FIXME("(%p, %p): stub!\n", iface, size); - return E_NOTIMPL; + TRACE("(%p, %p)\n", iface, size); + return get_heap_size(iface, HEAP_BLOB, size); }
static HRESULT WINAPI tables_GetGuidHeapSize(IMetaDataTables *iface, ULONG *size) { - FIXME("(%p, %p): stub!\n", iface, size); - return E_NOTIMPL; + TRACE("(%p, %p)\n", iface, size); + return get_heap_size(iface, HEAP_GUID, size); }
static HRESULT WINAPI tables_GetUserStringHeapSize(IMetaDataTables *iface, ULONG *size) { - FIXME("(%p, %p): stub!\n", iface, size); - return E_NOTIMPL; + TRACE("(%p, %p)\n", iface, size); + return get_heap_size(iface, HEAP_USER_STRING, size); }
static HRESULT WINAPI tables_GetNumTables(IMetaDataTables *iface, ULONG *size) { - FIXME("(%p, %p): stub!\n", iface, size); - return E_NOTIMPL; + TRACE("(%p, %p)\n", iface, size); + + /* This returns the number of tables known to the metadata parser, not the number of tables that exist in this + * assembly. */ + *size = 45; + return S_OK; }
static HRESULT WINAPI tables_GetTableIndex(IMetaDataTables *iface, ULONG token, ULONG *idx) @@ -217,11 +234,18 @@ static const struct IMetaDataTablesVtbl tables_vtbl = tables_GetNextUserString, };
-HRESULT IMetaDataTables_create(IMetaDataTables **iface) +HRESULT IMetaDataTables_create(const WCHAR *path, IMetaDataTables **iface) { struct metadata_tables *impl; + HRESULT hr;
if (!(impl = calloc(1, sizeof(*impl)))) return E_OUTOFMEMORY; + if (FAILED(hr = assembly_open_from_file(path, &impl->assembly))) + { + free( impl ); + return hr; + } + impl->IMetaDataTables_iface.lpVtbl = &tables_vtbl; impl->ref = 1; *iface = &impl->IMetaDataTables_iface; diff --git a/dlls/rometadata/rometadatapriv.h b/dlls/rometadata/rometadatapriv.h index e6ff2ada18a..61878cff625 100644 --- a/dlls/rometadata/rometadatapriv.h +++ b/dlls/rometadata/rometadatapriv.h @@ -19,6 +19,21 @@ #ifndef __WINE_ROMETADATA_PRIVATE__ #define __WINE_ROMETADATA_PRIVATE__
-extern HRESULT IMetaDataTables_create(IMetaDataTables **iface); +#include <rometadataapi.h> + +extern HRESULT IMetaDataTables_create(const WCHAR *path, IMetaDataTables **iface); + +typedef struct assembly assembly_t; +enum heap_type +{ + HEAP_STRING = 0, + HEAP_GUID = 1, + HEAP_BLOB = 2, + HEAP_USER_STRING = 3 +}; + +extern HRESULT assembly_open_from_file(const WCHAR *path, assembly_t **out); +extern void assembly_free(assembly_t *assembly); +extern ULONG assembly_get_heap_size(const assembly_t *assembly, enum heap_type heap);
#endif /* __WINE_ROMETADATA_PRIVATE__ */ diff --git a/dlls/rometadata/tests/rometadata.c b/dlls/rometadata/tests/rometadata.c index c80714671d6..dc4452a3d34 100644 --- a/dlls/rometadata/tests/rometadata.c +++ b/dlls/rometadata/tests/rometadata.c @@ -287,37 +287,27 @@ static void test_MetaDataDispenser_OpenScope(void) IMetaDataDispenser_Release(dispenser);
hr = IMetaDataTables_GetStringHeapSize(md_tables, &val); - todo_wine ok(hr == S_OK, "got hr %#lx\n", hr); - todo_wine ok(val == 329, "got val %lu\n", val);
val = 0; hr = IMetaDataTables_GetBlobHeapSize(md_tables, &val); - todo_wine ok(hr == S_OK, "got hr %#lx\n", hr); - todo_wine ok(val == 156, "got val %lu\n", val);
val = 0; hr = IMetaDataTables_GetGuidHeapSize(md_tables, &val); - todo_wine ok(hr == S_OK, "got hr %#lx\n", hr); - todo_wine ok(val == 16, "got val %lu\n", val);
val = 0; hr = IMetaDataTables_GetUserStringHeapSize(md_tables, &val); - todo_wine ok(hr == S_OK, "got hr %#lx\n", hr); - todo_wine ok(val == 8, "got val %lu\n", val);
val = 0; hr = IMetaDataTables_GetNumTables(md_tables, &val); - todo_wine ok(hr == S_OK, "got hr %#lx\n", hr); - todo_wine ok(val == TABLE_MAX, "got val %lu\n", val);
for (i = 0; i < TABLE_MAX; i++)
From: Vibhav Pant vibhavp@gmail.com
--- dlls/rometadata/assembly.c | 558 ++++++++++++++++++++++++++++- dlls/rometadata/mdtables.c | 15 +- dlls/rometadata/rometadatapriv.h | 15 + dlls/rometadata/tests/rometadata.c | 11 - 4 files changed, 585 insertions(+), 14 deletions(-)
diff --git a/dlls/rometadata/assembly.c b/dlls/rometadata/assembly.c index cb95f3fde02..3c1f2799743 100644 --- a/dlls/rometadata/assembly.c +++ b/dlls/rometadata/assembly.c @@ -48,6 +48,19 @@ struct stream_hdr char name[0]; };
+/* ECMA-335 Partition II.24.2.6, "#~ stream" */ +struct stream_tables_hdr +{ + UINT32 reserved; + UINT8 major_version; + UINT8 minor_version; + UINT8 heap_size_bits; /* enum heap_type */ + UINT8 reserved2; + UINT64 valid_tables_bits; + UINT64 sorted_tables_bits; + UINT32 table_rows[0]; +}; + #pragma pack(pop)
struct metadata_stream @@ -56,6 +69,407 @@ struct metadata_stream const BYTE *start; };
+enum table +{ + TABLE_MODULE = 0x00, + TABLE_TYPEREF = 0x01, + TABLE_TYPEDEF = 0x02, + TABLE_FIELDPTR = 0x03, + TABLE_FIELD = 0x04, + TABLE_METHODPTR = 0x05, + TABLE_METHODDEF = 0x06, + TABLE_PARAMPTR = 0x07, + TABLE_PARAM = 0x08, + TABLE_INTERFACEIMPL = 0x09, + TABLE_MEMBERREF = 0x0a, + TABLE_CONSTANT = 0x0b, + TABLE_CUSTOMATTRIBUTE = 0x0c, + TABLE_FIELDMARSHAL = 0x0d, + TABLE_DECLSECURITY = 0x0e, + TABLE_CLASSLAYOUT = 0x0f, + TABLE_FIELDLAYOUT = 0x10, + TABLE_STANDALONESIG = 0x11, + TABLE_EVENTMAP = 0x12, + TABLE_EVENTPTR = 0x13, + TABLE_EVENT = 0x14, + TABLE_PROPERTYMAP = 0x15, + TABLE_PROPERTYPTR = 0x16, + TABLE_PROPERTY = 0x17, + TABLE_METHODSEMANTICS = 0x18, + TABLE_METHODIMPL = 0x19, + TABLE_MODULEREF = 0x1a, + TABLE_TYPESPEC = 0x1b, + TABLE_IMPLMAP = 0x1c, + TABLE_FIELDRVA = 0x1d, + TABLE_ENCLOG = 0x1e, + TABLE_ENCMAP = 0x1f, + TABLE_ASSEMBLY = 0x20, + TABLE_ASSEMBLYPROCESSOR = 0x21, + TABLE_ASSEMBLYOS = 0x22, + TABLE_ASSEMBLYREF = 0x23, + TABLE_ASSEMBLYREFPROCESSOR = 0x24, + TABLE_ASSEMBLYREFOS = 0x25, + TABLE_FILE = 0x26, + TABLE_EXPORTEDTYPE = 0x27, + TABLE_MANIFESTRESOURCE = 0x28, + TABLE_NESTEDCLASS = 0x29, + TABLE_GENERICPARAM = 0x2a, + TABLE_METHODSPEC = 0x2b, + TABLE_GENERICPARAMCONSTRAINT = 0x2c, + TABLE_MAX = 0x2d +}; + +struct table_coded_idx +{ + UINT8 len; + const enum table *tables; +}; + +enum table_column_type +{ + /* A basic data type */ + COLUMN_BASIC, + /* An index into one of the heaps */ + COLUMN_HEAP_IDX, + /* An index into a metadata table */ + COLUMN_TABLE_IDX, + /* A coded index */ + COLUMN_CODED_IDX +}; + +union table_column_size +{ + UINT8 basic; + enum heap_type heap; + enum table table; + struct table_coded_idx coded; +}; + +struct table_column +{ + enum table_column_type type; + union table_column_size size; + const char *name; + BOOL primary_key; +}; + +/* We use TABLE_MAX for unused tags (See CustomAttribute). */ +#define DEFINE_CODED_IDX(name, ...) static const enum table name##_tables[] = {__VA_ARGS__} + +DEFINE_CODED_IDX(TypeDefOrRef, TABLE_TYPEDEF, TABLE_TYPEREF, TABLE_TYPESPEC); +DEFINE_CODED_IDX(HasConstant, TABLE_FIELD, TABLE_PARAM, TABLE_PROPERTY); +DEFINE_CODED_IDX(HasCustomAttribute, + TABLE_METHODDEF, + TABLE_FIELD, + TABLE_TYPEREF, + TABLE_TYPEDEF, + TABLE_PARAM, + TABLE_INTERFACEIMPL, + TABLE_MEMBERREF, + TABLE_MODULE, + TABLE_PROPERTY, + TABLE_EVENT, + TABLE_STANDALONESIG, + TABLE_MODULEREF, + TABLE_TYPESPEC, + TABLE_ASSEMBLY, + TABLE_ASSEMBLYREF, + TABLE_FILE, + TABLE_EXPORTEDTYPE, + TABLE_MANIFESTRESOURCE, + TABLE_GENERICPARAM, + TABLE_GENERICPARAMCONSTRAINT, + TABLE_METHODSPEC); +DEFINE_CODED_IDX(HasFieldMarshal, TABLE_FIELD, TABLE_PARAM); +DEFINE_CODED_IDX(HasDeclSecurity, TABLE_TYPEDEF, TABLE_METHODDEF, TABLE_ASSEMBLY); +DEFINE_CODED_IDX(MemberRefParent, TABLE_TYPEDEF, TABLE_TYPEREF, TABLE_MODULEREF, TABLE_METHODDEF, TABLE_TYPESPEC); +DEFINE_CODED_IDX(HasSemantics, TABLE_EVENT, TABLE_PROPERTY); +DEFINE_CODED_IDX(MethodDefOrRef, TABLE_METHODDEF, TABLE_MEMBERREF); +DEFINE_CODED_IDX(MemberForwarded, TABLE_FIELD, TABLE_METHODDEF); +DEFINE_CODED_IDX(Implementation, TABLE_FILE, TABLE_ASSEMBLYREF, TABLE_EXPORTEDTYPE); +DEFINE_CODED_IDX(CustomAttributeType, TABLE_MAX, TABLE_MAX, TABLE_METHODDEF, TABLE_MEMBERREF, TABLE_MAX); +DEFINE_CODED_IDX(ResolutionScope, TABLE_MODULE, TABLE_MODULEREF, TABLE_ASSEMBLYREF, TABLE_TYPEREF); +DEFINE_CODED_IDX(TypeOrMethodDef, TABLE_TYPEDEF, TABLE_METHODDEF); + +#undef DEFINE_CODED_IDX + +struct table_schema +{ + ULONG columns_len; + const struct table_column *columns; + const char *name; +}; + +/* Partition II.22, "Metadata logical format: tables" */ +#define DEFINE_TABLE_SCHEMA(name, ...) \ + static const struct table_column name##_columns[] = {__VA_ARGS__}; \ + static const struct table_schema name##_schema = { ARRAY_SIZE(name##_columns), name##_columns, #name } + +#define BASIC(n, t) { COLUMN_BASIC, { .basic = sizeof(t) }, n } +#define HEAP(n, h) { COLUMN_HEAP_IDX, { .heap = (HEAP_##h) }, n } +#define TABLE(n, t) { COLUMN_TABLE_IDX, { .table = TABLE_##t }, n } +#define TABLE_PRIMARY(n, t) { COLUMN_TABLE_IDX, { .table= TABLE_##t }, n, TRUE } +#define CODED(n, c) { COLUMN_CODED_IDX, { .coded = { ARRAY_SIZE(c##_tables), c##_tables } }, n } +#define CODED_PRIMARY(n, c) { COLUMN_CODED_IDX, { .coded = { ARRAY_SIZE(c##_tables), c##_tables } }, n, TRUE } + +/* Partition II.22.2, "Assembly" */ +DEFINE_TABLE_SCHEMA(Assembly, + BASIC("HashAlgId", UINT32), + BASIC("MajorVersion", UINT16), + BASIC("MinorVersion", UINT16), + BASIC("BuildNumber", UINT16), + BASIC("RevisionNumber", UINT16), + BASIC("Flags", UINT32), + HEAP("PublicKey", BLOB), + HEAP("Name", STRING), + HEAP("Culture", STRING)); + +/* Partition II.22.3, "AssemblyOS" */ +DEFINE_TABLE_SCHEMA(AssemblyOS, + BASIC("OSPlatformID", UINT32), + BASIC("OSMajorVersion", UINT32), + BASIC("OSMinorVersion", UINT32)); + +/* Partition II.2.4, "AssemblyProcessor" */ +DEFINE_TABLE_SCHEMA(AssemblyProcessor, BASIC("Processor", UINT32)); + +/* Partition II.2.5, "AssemblyRef" */ +DEFINE_TABLE_SCHEMA(AssemblyRef, + BASIC("MajorVersion", UINT16), + BASIC("MinorVersion", UINT16), + BASIC("BuildNumber", UINT16), + BASIC("RevisionNumber", UINT16), + BASIC("Flags", UINT32), + HEAP("PublicKey", BLOB), + HEAP("Name", STRING), + HEAP("Culture", STRING), + HEAP("HashValue", BLOB)); + +/* Partition II.2.6, "AssemblyRefOS" */ +DEFINE_TABLE_SCHEMA(AssemblyRefOS, + BASIC("OSPlatformID", UINT32), + BASIC("OSMajorVersion", UINT32), + BASIC("OSMinorVersion", UINT32), + TABLE("AssemblyRef", ASSEMBLYREF)); + +/* Partition II.2.7, "AssemblyRefProcessor" */ +DEFINE_TABLE_SCHEMA(AssemblyRefProcessor, BASIC("Processor", UINT32), TABLE("AssemblyRef", ASSEMBLYREF)); + +/* Partition II.2.8, "ClassLayout" */ +DEFINE_TABLE_SCHEMA(ClassLayout, + BASIC("PackingSize", UINT16), + BASIC("ClassSize", UINT32), + TABLE_PRIMARY("Parent", TYPEDEF)); + +/* Partition II.22.9, "Constant" */ +DEFINE_TABLE_SCHEMA(Constant, BASIC("Type", UINT16), CODED_PRIMARY("Parent", HasConstant), HEAP("Value", BLOB)); + +/* Partition II.2.10, "CustomAttribute" */ +DEFINE_TABLE_SCHEMA(CustomAttribute, + CODED_PRIMARY("Parent", HasCustomAttribute), + CODED("Type", CustomAttributeType), + HEAP("Value", BLOB)); + +/* Partition II.22.11, "DeclSecurity" */ +DEFINE_TABLE_SCHEMA(DeclSecurity, + BASIC("Action", UINT16), + CODED_PRIMARY("Parent", HasDeclSecurity), + HEAP("PermissionSet", BLOB)); + +DEFINE_TABLE_SCHEMA(ENCLog, BASIC("Token", UINT32), BASIC("FuncCode", UINT32)); + +DEFINE_TABLE_SCHEMA(ENCMap, BASIC("Token", UINT32)); + +/* Partition II.22.12, "EventMap" */ +DEFINE_TABLE_SCHEMA(EventMap, TABLE("Parent", TYPEDEF), TABLE("EventList", EVENT)); + +/* Partition II.22.13, "Event" */ +DEFINE_TABLE_SCHEMA(Event, BASIC("EventFlags", UINT16), HEAP("Name", STRING), CODED("EventType", TypeDefOrRef)); + +DEFINE_TABLE_SCHEMA(EventPtr, TABLE("Event", EVENT)); + +/* Partition II.22.14, "ExportedType" */ +DEFINE_TABLE_SCHEMA(ExportedType, + BASIC("Flags", UINT32), + BASIC("TypeDefId", UINT32), + HEAP("TypeName", STRING), + HEAP("TypeNamespace", STRING), + CODED("Implementation", Implementation)); + +/* Partition II.22.15, "Field" */ +DEFINE_TABLE_SCHEMA(Field, BASIC("Flags", UINT16), HEAP("Name", STRING), HEAP("Signature", BLOB)); + +/* Partition II.22.16, "FieldLayout" */ +DEFINE_TABLE_SCHEMA(FieldLayout, BASIC("Offset", UINT32), TABLE_PRIMARY("Field", FIELD)); + +/* Partition II.22.17, "FieldMarshal" */ +DEFINE_TABLE_SCHEMA(FieldMarshal, CODED_PRIMARY("Parent", HasFieldMarshal), HEAP("NativeType", BLOB)); + +DEFINE_TABLE_SCHEMA(FieldPtr, TABLE("Field", FIELD)); + +/* Partition II.22.18, "FieldRVA" */ +DEFINE_TABLE_SCHEMA(FieldRVA, BASIC("RVA", UINT32), TABLE_PRIMARY("Field", FIELD)); + +/* Partition II.22.19, "File" */ +DEFINE_TABLE_SCHEMA(File, BASIC("Flags", UINT32), HEAP("Name", STRING), HEAP("HashValue", BLOB)); + +/* Partition II.22.20, "GenericParam" */ +DEFINE_TABLE_SCHEMA(GenericParam, + BASIC("Number", UINT16), + BASIC("Flags", UINT16), + CODED_PRIMARY("Owner", TypeOrMethodDef), + HEAP("Name", BLOB)); + +/* Partition II.22.21, "GenericParamConstraint" */ +DEFINE_TABLE_SCHEMA(GenericParamConstraint, TABLE_PRIMARY("Owner", GENERICPARAM), CODED("Constraint", TypeDefOrRef)); + +/* Partition II.22.22, "ImplMap" */ +DEFINE_TABLE_SCHEMA(ImplMap, + BASIC("MappingFlags", UINT16), + CODED_PRIMARY("MemberForwarded", MemberForwarded), + HEAP("ImportName", STRING), + TABLE("ImportScope", MODULEREF)); + +/* Partition II.22.23, "InterfaceImplMap" */ +DEFINE_TABLE_SCHEMA(InterfaceImpl, TABLE_PRIMARY("Class", TYPEDEF), CODED("Interface", TypeDefOrRef)); + +/* Partition II.22.24, "ManifestResource" */ +DEFINE_TABLE_SCHEMA(ManifestResource, + BASIC("Offset", UINT32), + BASIC("Flags", UINT32), + HEAP("Name", STRING), + CODED("Implementation", Implementation)); + +/* Partition II.22.25, "MemberRef" */ +DEFINE_TABLE_SCHEMA(MemberRef, CODED("Class", MemberRefParent), HEAP("Name", STRING), HEAP("Signature", BLOB)); + +/* Partition II.22.26, "MethodDef" */ +DEFINE_TABLE_SCHEMA(Method, + BASIC("RVA", UINT32), + BASIC("ImplFlags", UINT16), + BASIC("Flags", UINT16), + HEAP("Name", STRING), + HEAP("Signature", BLOB), + TABLE("ParamList", PARAM)); + +/* Partition II.22.27, "MethodImpl" */ +DEFINE_TABLE_SCHEMA(MethodImpl, + TABLE_PRIMARY("Class", TYPEDEF), + CODED("MethodBody", MethodDefOrRef), + CODED("MethodDeclaration", MethodDefOrRef)); + +DEFINE_TABLE_SCHEMA(MethodPtr, TABLE("Method", METHODDEF)); + +/* Partition II.22.28, "MethodSemantics" */ +DEFINE_TABLE_SCHEMA(MethodSemantics, + BASIC("Semantics", UINT16), + TABLE("Method", METHODDEF), + CODED_PRIMARY("Association", HasSemantics)); + +/* Partition II.22.29, "MethodSpec" */ +DEFINE_TABLE_SCHEMA(MethodSpec, CODED("Method", MethodDefOrRef), HEAP("Instantiation", BLOB)); + +/* Partition II.22.30, "Module" */ +DEFINE_TABLE_SCHEMA(Module, + BASIC("Generation", UINT16), + HEAP("Name", STRING), + HEAP("Mvid", GUID), + HEAP("EncId", GUID), + HEAP("EncBaseId", GUID)); + +/* Partition II.22.31, "ModuleRef"*/ +DEFINE_TABLE_SCHEMA(ModuleRef, HEAP("Name", STRING)); + +/* Partition II.22.32, "NestedClass" */ +DEFINE_TABLE_SCHEMA(NestedClass, TABLE_PRIMARY("NestedClass", TYPEDEF), TABLE("EnclosingClass", TYPEDEF)); + +/* Partition II.22.33, "Param" */ +DEFINE_TABLE_SCHEMA(Param, BASIC("Flags", UINT16), BASIC("Sequence", UINT16), HEAP("Name", STRING)); + +DEFINE_TABLE_SCHEMA(ParamPtr, TABLE("Param", PARAM)); + +/* Partition II.22.34, "Property" */ +DEFINE_TABLE_SCHEMA(Property, BASIC("Flags", UINT16), HEAP("Name", STRING), HEAP("Type", BLOB)); + +/* Partition II.22.35, "PropertyMap" */ +DEFINE_TABLE_SCHEMA(PropertyMap, TABLE("Parent", TYPEDEF), TABLE("PropertyList", PROPERTY)); + +DEFINE_TABLE_SCHEMA(PropertyPtr, TABLE("Property", PROPERTY)); + +/* Partition II.22.36, "StandAloneSig" */ +DEFINE_TABLE_SCHEMA(StandAloneSig, HEAP("Signature", BLOB)); + +/* Partition II.22.37, "TypeDef" */ +DEFINE_TABLE_SCHEMA(TypeDef, + BASIC("Flags", UINT32), + HEAP("TypeName", STRING), + HEAP("TypeNamespace", STRING), + CODED("Extends", TypeDefOrRef), + TABLE("FieldList", FIELD), + TABLE("MethodList", METHODDEF)); + +/* Partition II.22.38, "TypeRef" */ +DEFINE_TABLE_SCHEMA(TypeRef, CODED("ResolutionScope", ResolutionScope), HEAP("TypeName", STRING), HEAP("TypeNamespace", STRING)); + +/* Partition II.22.39, "TypeSpec" */ +DEFINE_TABLE_SCHEMA(TypeSpec, HEAP("Signature", BLOB)); + +#undef DEFINE_TABLE_SCHEMA +#undef BASIC +#undef HEAP +#undef TABLE +#undef TABLE_PRIMARY +#undef CODED +#undef CODED_PRIMARY + +static const struct table_schema *table_schemas[] = { + [TABLE_MODULE] = &Module_schema, + [TABLE_TYPEREF] = &TypeRef_schema, + [TABLE_TYPEDEF] = &TypeDef_schema, + [TABLE_FIELDPTR] = &FieldPtr_schema, + [TABLE_FIELD] = &Field_schema, + [TABLE_METHODPTR] = &MethodPtr_schema, + [TABLE_METHODDEF] = &Method_schema, + [TABLE_PARAMPTR] = &ParamPtr_schema, + [TABLE_PARAM] = &Param_schema, + [TABLE_INTERFACEIMPL] = &InterfaceImpl_schema, + [TABLE_MEMBERREF] = &MemberRef_schema, + [TABLE_CONSTANT] = &Constant_schema, + [TABLE_CUSTOMATTRIBUTE] = &CustomAttribute_schema, + [TABLE_FIELDMARSHAL] = &FieldMarshal_schema, + [TABLE_DECLSECURITY] = &DeclSecurity_schema, + [TABLE_CLASSLAYOUT] = &ClassLayout_schema, + [TABLE_FIELDLAYOUT] = &FieldLayout_schema, + [TABLE_STANDALONESIG] = &StandAloneSig_schema, + [TABLE_EVENTMAP] = &EventMap_schema, + [TABLE_EVENTPTR] = &EventPtr_schema, + [TABLE_EVENT] = &Event_schema, + [TABLE_PROPERTYMAP] = &PropertyMap_schema, + [TABLE_PROPERTYPTR] = &PropertyPtr_schema, + [TABLE_PROPERTY] = &Property_schema, + [TABLE_METHODSEMANTICS] = &MethodSemantics_schema, + [TABLE_METHODIMPL] = &MethodImpl_schema, + [TABLE_MODULEREF] = &ModuleRef_schema, + [TABLE_TYPESPEC] = &TypeSpec_schema, + [TABLE_IMPLMAP] = &ImplMap_schema, + [TABLE_FIELDRVA] = &FieldRVA_schema, + [TABLE_ENCLOG] = &ENCLog_schema, + [TABLE_ENCMAP] = &ENCMap_schema, + [TABLE_ASSEMBLY] = &Assembly_schema, + [TABLE_ASSEMBLYPROCESSOR] = &AssemblyProcessor_schema, + [TABLE_ASSEMBLYOS] = &AssemblyOS_schema, + [TABLE_ASSEMBLYREF] = &AssemblyRef_schema, + [TABLE_ASSEMBLYREFPROCESSOR] = &AssemblyRefProcessor_schema, + [TABLE_ASSEMBLYREFOS] = &AssemblyRefOS_schema, + [TABLE_FILE] = &File_schema, + [TABLE_EXPORTEDTYPE] = &ExportedType_schema, + [TABLE_MANIFESTRESOURCE] = &ManifestResource_schema, + [TABLE_NESTEDCLASS] = &NestedClass_schema, + [TABLE_GENERICPARAM] = &GenericParam_schema, + [TABLE_METHODSPEC] = &MethodSpec_schema, + [TABLE_GENERICPARAMCONSTRAINT] = &GenericParamConstraint_schema +}; + struct assembly { HANDLE file; @@ -68,6 +482,9 @@ struct assembly struct metadata_stream stream_blobs; struct metadata_stream stream_guids; struct metadata_stream stream_user_strings; + + const struct stream_tables_hdr *tables_hdr; + struct metadata_table_info tables[TABLE_MAX]; };
static BOOL pe_rva_to_offset(const IMAGE_SECTION_HEADER *sections, UINT32 num_sections, UINT32 rva, UINT32 *offset) @@ -85,6 +502,137 @@ static BOOL pe_rva_to_offset(const IMAGE_SECTION_HEADER *sections, UINT32 num_se return FALSE; }
+UINT8 assembly_heap_idx_size(const assembly_t *assembly, enum heap_type type) +{ + assert(type >= HEAP_STRING && type <= HEAP_BLOB); + /* ECMA-335 Partition II.24.2.6, "Heap size flag" */ + return (assembly->tables_hdr->heap_size_bits & (1 << type)) ? 4 : 2; +} + +static BOOL assembly_table_exists(const assembly_t *assembly, UINT8 idx) +{ + assert(idx < 64); + return !!(assembly->tables_hdr->valid_tables_bits & (1ULL << idx)); +} + +static ULONG index_size(ULONG n) +{ + return n < (1 << 16) ? 2 : 4; +} + +static ULONG assembly_get_table_index_size(const assembly_t *assembly, enum table table) +{ + assert(table < TABLE_MAX && table_schemas[table]); + return index_size(assembly->tables[table].num_rows); +} + +static ULONG bit_width(ULONG n) +{ + ULONG bits = 1; + + for (n = n - 1; n; n >>= 1) + bits++; + return bits; +} + +/* From Partition II.24.2.6, "#~stream": + * + * If e is a coded index that points into table t_i out of n possible tables t_0,...,t_{n-1}, then it + * is stored as e << (log n) | tag{ t_0, ..., t_{n-1} }[ t_i ] using 2 bytes if the maximum number + * of rows of tables t_0,...,t_{n-1}, is less than 2 (16 – (log n)), and using 4 bytes otherwise. + */ +static ULONG assembly_get_coded_index_size(const assembly_t *assembly, const struct table_coded_idx *coded) +{ + const ULONG tag_bits = bit_width(coded->len); + ULONG max_row_idx = 0, i; + + for (i = 0; i < coded->len; i++) + { + if (coded->tables[i] != TABLE_MAX) + max_row_idx = max(max_row_idx, assembly->tables[coded->tables[i]].num_rows); + } + return max_row_idx < (1 << (16 - tag_bits)) ? 2 : 4; +} + +static void assembly_calculate_table_sizes(assembly_t *assembly, enum table table) +{ + const struct table_schema *schema; + struct metadata_table_info *table_info; + int i; + + assert(table < TABLE_MAX); + schema = table_schemas[table]; + table_info = &assembly->tables[table]; + table_info->num_columns = schema->columns_len; + table_info->name = schema->name; + table_info->key_idx = -1; + + for (i = 0; i < schema->columns_len; i++) + { + if (schema->columns[i].primary_key) + { + table_info->key_idx = i; + break; + } + } + + for (i = 0; i < schema->columns_len; i++) + { + const struct table_column *column = &schema->columns[i]; + ULONG column_size; + + switch (column->type) + { + case COLUMN_BASIC: + column_size = column->size.basic; + break; + case COLUMN_HEAP_IDX: + column_size = assembly_heap_idx_size(assembly, column->size.heap); + break; + case COLUMN_TABLE_IDX: + column_size = assembly_get_table_index_size(assembly, table); + break; + case COLUMN_CODED_IDX: + column_size = assembly_get_coded_index_size(assembly, &column->size.coded); + break; + DEFAULT_UNREACHABLE; + } + table_info->column_size[i] = column_size; + table_info->row_size += column_size; + } +} + +static HRESULT assembly_parse_metadata_tables(assembly_t *assembly) +{ + const BYTE *cur_table; + ULONG num_tables = 0; + int i; + + for (i = 0; i < 64; i++) + { + if (!assembly_table_exists(assembly, i)) continue; + if (i >= TABLE_MAX) return E_INVALIDARG; + assert(table_schemas[i]); + assembly->tables[i].num_rows = assembly->tables_hdr->table_rows[num_tables++]; + } + + cur_table = assembly->stream_tables.start + offsetof(struct stream_tables_hdr, table_rows[num_tables]); + for (i = 0; i < TABLE_MAX; i++) + { + struct metadata_table_info *table = &assembly->tables[i]; + + assembly_calculate_table_sizes(assembly, i); + if (table->num_rows) + { + table->start = cur_table; + cur_table += (size_t)(table->num_rows * table->row_size); + if ((UINT_PTR)(cur_table - assembly->data) > assembly->size) return E_INVALIDARG; + } + } + + return S_OK; +} + static HRESULT assembly_parse_headers(assembly_t *assembly) { const IMAGE_DOS_HEADER *dos_hdr = (IMAGE_DOS_HEADER *)assembly->data; @@ -179,7 +727,10 @@ static HRESULT assembly_parse_headers(assembly_t *assembly) assembly->stream_strings.size -= padding; }
- return S_OK; + if (assembly->stream_tables.size < sizeof(struct stream_tables_hdr)) return E_INVALIDARG; + assembly->tables_hdr = (struct stream_tables_hdr *)assembly->stream_tables.start; + + return assembly_parse_metadata_tables(assembly); }
HRESULT assembly_open_from_file(const WCHAR *path, assembly_t **ret) @@ -230,6 +781,11 @@ void assembly_free(assembly_t *assembly) free(assembly); }
+const struct metadata_table_info *assembly_get_table(const assembly_t *assembly, ULONG table_idx) +{ + return table_idx < TABLE_MAX ? &assembly->tables[table_idx] : NULL; +} + ULONG assembly_get_heap_size(const assembly_t *assembly, enum heap_type heap) { switch (heap) diff --git a/dlls/rometadata/mdtables.c b/dlls/rometadata/mdtables.c index 9affb80f5bf..3ef49c7894e 100644 --- a/dlls/rometadata/mdtables.c +++ b/dlls/rometadata/mdtables.c @@ -130,8 +130,19 @@ static HRESULT WINAPI tables_GetTableIndex(IMetaDataTables *iface, ULONG token, static HRESULT WINAPI tables_GetTableInfo(IMetaDataTables *iface, ULONG idx_tbl, ULONG *row_size, ULONG *num_rows, ULONG *num_cols, ULONG *idx_key, const char **name) { - FIXME("(%p, %lu, %p, %p, %p, %p, %p): stub!\n", iface, idx_tbl, row_size, num_rows, num_cols, idx_key, name); - return E_NOTIMPL; + struct metadata_tables *impl = impl_from_IMetaDataTables(iface); + const struct metadata_table_info *table; + + TRACE("(%p, %lu, %p, %p, %p, %p, %p)\n", iface, idx_tbl, row_size, num_rows, num_cols, idx_key, name); + + if (!(table = assembly_get_table(impl->assembly, idx_tbl))) return E_INVALIDARG; + + *row_size = table->row_size; + *num_rows = table->num_rows; + *num_cols = table->num_columns; + *idx_key = table->key_idx; + *name = table->name; + return S_OK; }
static HRESULT WINAPI tables_GetColumnInfo(IMetaDataTables *iface, ULONG idx_tbl, ULONG idx_col, ULONG *offset, diff --git a/dlls/rometadata/rometadatapriv.h b/dlls/rometadata/rometadatapriv.h index 61878cff625..7b0b1f50de0 100644 --- a/dlls/rometadata/rometadatapriv.h +++ b/dlls/rometadata/rometadatapriv.h @@ -24,6 +24,20 @@ extern HRESULT IMetaDataTables_create(const WCHAR *path, IMetaDataTables **iface);
typedef struct assembly assembly_t; +struct metadata_table_info +{ + ULONG num_rows; + ULONG num_columns; + + ULONG key_idx; + + ULONG row_size; + ULONG column_size[9]; + + const char *name; + const BYTE *start; +}; + enum heap_type { HEAP_STRING = 0, @@ -34,6 +48,7 @@ enum heap_type
extern HRESULT assembly_open_from_file(const WCHAR *path, assembly_t **out); extern void assembly_free(assembly_t *assembly); +extern const struct metadata_table_info *assembly_get_table(const assembly_t *assembly, ULONG table_idx); extern ULONG assembly_get_heap_size(const assembly_t *assembly, enum heap_type heap);
#endif /* __WINE_ROMETADATA_PRIVATE__ */ diff --git a/dlls/rometadata/tests/rometadata.c b/dlls/rometadata/tests/rometadata.c index dc4452a3d34..c754368f212 100644 --- a/dlls/rometadata/tests/rometadata.c +++ b/dlls/rometadata/tests/rometadata.c @@ -319,23 +319,12 @@ static void test_MetaDataDispenser_OpenScope(void) winetest_push_context("tables[%lu]", i);
hr = IMetaDataTables_GetTableInfo(md_tables, i, &row_size, &rows, &cols, &key_idx, &name); - todo_wine ok(hr == S_OK, "got hr %#lx\n", hr); - if (FAILED(hr)) - { - winetest_pop_context(); - continue; - }
- todo_wine ok(row_size == table->exp_row_size, "got row_size %lu != %lu\n", row_size, table->exp_row_size); - todo_wine ok(rows == table->exp_rows, "got rows %lu != %lu\n", rows, table->exp_rows); - todo_wine ok(cols == table->exp_cols, "got cols %lu != %lu\n", cols, table->exp_cols); - todo_wine ok(key_idx == table->exp_key_idx, "got key_idx %lu != %lu\n", key_idx, table->exp_key_idx); - todo_wine ok(!strcmp(name, table->exp_name), "got name %s != %s\n", debugstr_a(name), debugstr_a(table->exp_name));
winetest_pop_context();
From: Vibhav Pant vibhavp@gmail.com
--- dlls/rometadata/mdtables.c | 16 ++++++++-- dlls/rometadata/tests/rometadata.c | 48 +++++++++++------------------- 2 files changed, 31 insertions(+), 33 deletions(-)
diff --git a/dlls/rometadata/mdtables.c b/dlls/rometadata/mdtables.c index 3ef49c7894e..2b21b6747e7 100644 --- a/dlls/rometadata/mdtables.c +++ b/dlls/rometadata/mdtables.c @@ -18,6 +18,8 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */
+#include <assert.h> + #define COBJMACROS #include "objbase.h" #include "cor.h" @@ -161,8 +163,18 @@ static HRESULT WINAPI tables_GetCodedTokenInfo(IMetaDataTables *iface, ULONG typ
static HRESULT WINAPI tables_GetRow(IMetaDataTables *iface, ULONG idx_tbl, ULONG idx_row, const BYTE *row) { - FIXME("(%p, %lu, %lu, %p): stub!\n", iface, idx_tbl, idx_row, row); - return E_NOTIMPL; + struct metadata_tables *impl = impl_from_IMetaDataTables(iface); + const struct metadata_table_info *table; + + TRACE("(%p, %lu, %lu, %p)\n", iface, idx_tbl, idx_row, row); + + if (!(table = assembly_get_table(impl->assembly, idx_tbl))) return E_INVALIDARG; + + assert(table->start); + idx_row--; /* Row indices are 1-based. */ + if (idx_row >= table->num_rows) return E_INVALIDARG; + *(const BYTE **)row = table->start + (size_t)(table->row_size * idx_row); + return S_OK; }
static HRESULT WINAPI tables_GetColumn(IMetaDataTables *iface, ULONG idx_tbl, ULONG idx_col, ULONG idx_row, ULONG *val) diff --git a/dlls/rometadata/tests/rometadata.c b/dlls/rometadata/tests/rometadata.c index c754368f212..b1dd9739eb6 100644 --- a/dlls/rometadata/tests/rometadata.c +++ b/dlls/rometadata/tests/rometadata.c @@ -332,22 +332,21 @@ static void test_MetaDataDispenser_OpenScope(void)
/* Read module information */ hr = IMetaDataTables_GetRow(md_tables, TABLE_MODULE, 1, (BYTE *)&module); - todo_wine ok(hr == S_OK, "got hr %#lx\n", hr); - todo_wine ok(!!module, "got module=%p\n", module);
- if (SUCCEEDED(hr)) - { - str = NULL; - hr = IMetaDataTables_GetString(md_tables, module->Name, &str); - ok(hr == S_OK, "got hr %#lx\n", hr); - ok(str &&!strcmp(str, "dlls/rometadata/tests/test-simple.winmd"), "got str %s\n", debugstr_a(str)); + str = NULL; + hr = IMetaDataTables_GetString(md_tables, module->Name, &str); + todo_wine + ok(hr == S_OK, "got hr %#lx\n", hr); + todo_wine + ok(str &&!strcmp(str, "dlls/rometadata/tests/test-simple.winmd"), "got str %s\n", debugstr_a(str));
- hr = IMetaDataTables_GetGuid(md_tables, module->Mvid, &guid); - ok(hr == S_OK, "got hr %#lx\n", hr); - ok(!!guid, "got guid %p\n", guid); - } + hr = IMetaDataTables_GetGuid(md_tables, module->Mvid, &guid); + todo_wine + ok(hr == S_OK, "got hr %#lx\n", hr); + todo_wine + ok(!!guid, "got guid %p\n", guid);
/* Read defined types. */ for (i = 0; i < ARRAY_SIZE(type_defs); i++) @@ -358,26 +357,24 @@ static void test_MetaDataDispenser_OpenScope(void)
type_def = NULL; hr = IMetaDataTables_GetRow(md_tables, TABLE_TYPEDEF, i + 1, (BYTE *)&type_def); - todo_wine ok(hr == S_OK, "got hr %#lx\n", hr); - todo_wine ok(!!type_def, "got type_def=%p\n", type_def); - if (FAILED(hr)) - { - winetest_pop_context(); - continue; - }
ok(type_def->Flags == type_info->exp_flags, "got Flags %#lx != %#lx\n", type_def->Flags, type_info->exp_flags); str = NULL; hr = IMetaDataTables_GetString(md_tables, type_def->Name, &str); + todo_wine ok(hr == S_OK, "got hr %#lx\n", hr); + todo_wine ok(str && !strcmp(str, type_info->exp_name), "got str %s != %s\n", debugstr_a(str), debugstr_a(type_info->exp_name)); if (type_info->exp_namespace) { str = NULL; hr = IMetaDataTables_GetString(md_tables, type_def->Namespace, &str); + todo_wine + ok(hr == S_OK, "got hr %#lx\n", hr); + todo_wine ok(str && !strcmp(str, type_info->exp_namespace), "got str %s != %s\n", debugstr_a(str), debugstr_a(type_info->exp_namespace)); if (!strcmp(type_info->exp_name, "ITest1") && !strcmp(type_info->exp_namespace, "Wine.Test")) @@ -395,15 +392,10 @@ static void test_MetaDataDispenser_OpenScope(void) winetest_push_context("i=%lu", i);
hr = IMetaDataTables_GetRow(md_tables, TABLE_MEMBERREF, i + 1, (BYTE *)&ref); - todo_wine ok(hr == S_OK, "got hr %#lx\n", hr); - if (FAILED(hr)) - { - winetest_pop_context(); - continue; - }
hr = IMetaDataTables_GetString(md_tables, ref->Name, &str); + todo_wine ok(hr == S_OK, "got hr %#lx\n", hr); if (str && !strcmp(str, ".ctor")) { @@ -442,13 +434,7 @@ static void test_MetaDataDispenser_OpenScope(void)
attr = NULL; hr = IMetaDataTables_GetRow(md_tables, TABLE_CUSTOMATTRIBUTE, i + 1, (BYTE *)&attr); - todo_wine ok(hr == S_OK, "got hr %#lx\n", hr); - if (FAILED(hr)) - { - winetest_pop_context(); - continue; - }
if (attr->Type == guid_ctor_idx && attr->Parent == itest1_def_idx) {
From: Vibhav Pant vibhavp@gmail.com
--- dlls/rometadata/assembly.c | 48 +++++++++++++++++++++++++++++- dlls/rometadata/mdtables.c | 15 ++++++---- dlls/rometadata/rometadatapriv.h | 3 ++ dlls/rometadata/tests/rometadata.c | 10 ------- 4 files changed, 59 insertions(+), 17 deletions(-)
diff --git a/dlls/rometadata/assembly.c b/dlls/rometadata/assembly.c index 3c1f2799743..ac58876816e 100644 --- a/dlls/rometadata/assembly.c +++ b/dlls/rometadata/assembly.c @@ -800,4 +800,50 @@ ULONG assembly_get_heap_size(const assembly_t *assembly, enum heap_type heap) return assembly->stream_user_strings.size; DEFAULT_UNREACHABLE; } -} \ No newline at end of file +} + +const char *assembly_get_string(const assembly_t *assembly, ULONG idx) +{ + return idx < assembly->stream_strings.size ? (const char *)&assembly->stream_strings.start[idx] : NULL; +} + +static HRESULT decode_int(const BYTE *encoded, ULONG *val, ULONG *len) +{ + if (!(encoded[0] & 0x80)) + { + *len = 1; + *val = encoded[0]; + } + else if (!(encoded[0] & 0x40)) + { + *len = 2; + *val = ((encoded[0] & ~0xc0) << 8) + encoded[1]; + } + else if (!(encoded[0] & 0x20)) + { + *len = 4; + *val = ((encoded[0] & ~0xe0) << 24) + (encoded[1] << 16) + (encoded[2] << 8) + encoded[3]; + } + else + return E_INVALIDARG; + return S_OK; +} + +HRESULT assembly_get_blob(const assembly_t *assembly, ULONG idx, const BYTE **blob, ULONG *size) +{ + const BYTE *ptr; + ULONG size_len; + HRESULT hr; + + if (idx >= assembly->stream_blobs.size) return E_INVALIDARG; + ptr = assembly->stream_blobs.start + idx; + if (FAILED(hr = decode_int(ptr, size, &size_len))) return hr; + *blob = ptr + size_len; + return S_OK; +} + +const GUID *assembly_get_guid(const assembly_t *assembly, ULONG idx) +{ + idx = (idx - 1) * sizeof(GUID); /* Indices into the GUID heap are 1-based */ + return idx < assembly->stream_guids.size ? (const GUID *)(assembly->stream_guids.start + idx) : NULL; +} diff --git a/dlls/rometadata/mdtables.c b/dlls/rometadata/mdtables.c index 2b21b6747e7..f4604ad1325 100644 --- a/dlls/rometadata/mdtables.c +++ b/dlls/rometadata/mdtables.c @@ -185,20 +185,23 @@ static HRESULT WINAPI tables_GetColumn(IMetaDataTables *iface, ULONG idx_tbl, UL
static HRESULT WINAPI tables_GetString(IMetaDataTables *iface, ULONG idx, const char **str) { - FIXME("(%p, %lu, %p): stub!\n", iface, idx, str); - return E_NOTIMPL; + struct metadata_tables *impl = impl_from_IMetaDataTables(iface); + TRACE("(%p, %lu, %p)\n", iface, idx, str); + return (*str = assembly_get_string(impl->assembly, idx)) ? S_OK : E_INVALIDARG; }
static HRESULT WINAPI tables_GetBlob(IMetaDataTables *iface, ULONG idx, ULONG *size, const BYTE **blob) { - FIXME("(%p, %lu, %p, %p): stub!\n", iface, idx, size, blob); - return E_NOTIMPL; + struct metadata_tables *impl = impl_from_IMetaDataTables(iface); + TRACE("(%p, %lu, %p, %p)\n", iface, idx, size, blob); + return assembly_get_blob(impl->assembly, idx, blob, size); }
static HRESULT WINAPI tables_GetGuid(IMetaDataTables *iface, ULONG idx, const GUID **guid) { - FIXME("(%p, %lu, %p): stub!\n", iface, idx, guid); - return E_NOTIMPL; + struct metadata_tables *impl = impl_from_IMetaDataTables(iface); + TRACE("(%p, %lu, %p)!\n", iface, idx, guid); + return (*guid = assembly_get_guid(impl->assembly, idx)) ? S_OK : E_INVALIDARG; }
static HRESULT WINAPI tables_GetUserString(IMetaDataTables *iface, ULONG idx, ULONG *size, const BYTE **string) diff --git a/dlls/rometadata/rometadatapriv.h b/dlls/rometadata/rometadatapriv.h index 7b0b1f50de0..8f1179d85eb 100644 --- a/dlls/rometadata/rometadatapriv.h +++ b/dlls/rometadata/rometadatapriv.h @@ -50,5 +50,8 @@ extern HRESULT assembly_open_from_file(const WCHAR *path, assembly_t **out); extern void assembly_free(assembly_t *assembly); extern const struct metadata_table_info *assembly_get_table(const assembly_t *assembly, ULONG table_idx); extern ULONG assembly_get_heap_size(const assembly_t *assembly, enum heap_type heap); +extern const char *assembly_get_string(const assembly_t *assembly, ULONG idx); +extern HRESULT assembly_get_blob(const assembly_t *assembly, ULONG idx, const BYTE **blob, ULONG *size); +extern const GUID *assembly_get_guid(const assembly_t *assembly, ULONG idx);
#endif /* __WINE_ROMETADATA_PRIVATE__ */ diff --git a/dlls/rometadata/tests/rometadata.c b/dlls/rometadata/tests/rometadata.c index b1dd9739eb6..03e39d61e77 100644 --- a/dlls/rometadata/tests/rometadata.c +++ b/dlls/rometadata/tests/rometadata.c @@ -337,15 +337,11 @@ static void test_MetaDataDispenser_OpenScope(void)
str = NULL; hr = IMetaDataTables_GetString(md_tables, module->Name, &str); - todo_wine ok(hr == S_OK, "got hr %#lx\n", hr); - todo_wine ok(str &&!strcmp(str, "dlls/rometadata/tests/test-simple.winmd"), "got str %s\n", debugstr_a(str));
hr = IMetaDataTables_GetGuid(md_tables, module->Mvid, &guid); - todo_wine ok(hr == S_OK, "got hr %#lx\n", hr); - todo_wine ok(!!guid, "got guid %p\n", guid);
/* Read defined types. */ @@ -363,18 +359,14 @@ static void test_MetaDataDispenser_OpenScope(void) ok(type_def->Flags == type_info->exp_flags, "got Flags %#lx != %#lx\n", type_def->Flags, type_info->exp_flags); str = NULL; hr = IMetaDataTables_GetString(md_tables, type_def->Name, &str); - todo_wine ok(hr == S_OK, "got hr %#lx\n", hr); - todo_wine ok(str && !strcmp(str, type_info->exp_name), "got str %s != %s\n", debugstr_a(str), debugstr_a(type_info->exp_name)); if (type_info->exp_namespace) { str = NULL; hr = IMetaDataTables_GetString(md_tables, type_def->Namespace, &str); - todo_wine ok(hr == S_OK, "got hr %#lx\n", hr); - todo_wine ok(str && !strcmp(str, type_info->exp_namespace), "got str %s != %s\n", debugstr_a(str), debugstr_a(type_info->exp_namespace)); if (!strcmp(type_info->exp_name, "ITest1") && !strcmp(type_info->exp_namespace, "Wine.Test")) @@ -395,7 +387,6 @@ static void test_MetaDataDispenser_OpenScope(void) ok(hr == S_OK, "got hr %#lx\n", hr);
hr = IMetaDataTables_GetString(md_tables, ref->Name, &str); - todo_wine ok(hr == S_OK, "got hr %#lx\n", hr); if (str && !strcmp(str, ".ctor")) { @@ -422,7 +413,6 @@ static void test_MetaDataDispenser_OpenScope(void) winetest_pop_context(); }
- todo_wine ok(!!guid_ctor_idx, "got guid_ctor_coded_idx %lu\n", guid_ctor_idx);
/* Verify ITest1 has the correct GuidAttribute value. */
Nikolay Sivov (@nsivov) commented about dlls/rometadata/assembly.c:
- default:
return E_INVALIDARG;- }
- if (!pe_rva_to_offset(sections, num_sections, rva, &offset)) return E_INVALIDARG;
- cor_hdr = (IMAGE_COR20_HEADER *)(assembly->data + offset);
- if (cor_hdr->cb != sizeof(IMAGE_COR20_HEADER)) return E_INVALIDARG;
- if (!(pe_rva_to_offset(sections, num_sections, cor_hdr->MetaData.VirtualAddress, &offset))) return E_INVALIDARG;
- md_start = assembly->data + offset;
- md_hdr = (struct metadata_hdr *)md_start;
- if (md_hdr->signature != METADATA_MAGIC) return E_INVALIDARG;
- num_streams = *(UINT8 *)(md_start + offsetof(struct metadata_hdr, version[md_hdr->length]) + sizeof(UINT16)); /* Flags */
- streams_cur = md_start + offsetof(struct metadata_hdr, version[md_hdr->length]) + sizeof(UINT16) * 2; /* Flags + Streams */
I haven't looked at the whole thing, but this section looks as if we were going to assume that file contents are always sensible. And if that's what this is doing, I think it's wrong and reader should always validate offsets and sizes.
On Thu Oct 9 17:30:00 2025 +0000, Nikolay Sivov wrote:
I haven't looked at the whole thing, but this section looks as if we were going to assume that file contents are always sensible. And if that's what this is doing, I think it's wrong and reader should always validate offsets and sizes.
Yes, there are a few places where I need to add bounds checks, but I generally do check for them, thanks.
On Thu Oct 9 17:38:35 2025 +0000, Vibhav Pant wrote:
Yes, there are a few places where I need to add bounds checks, but I generally do check for them, thanks.
There are a few examples of that pattern in d3d10/effect.c or dwrite/opentype.c, where we basically check that reading next structured fragment makes sense with given {offset,size} vs {position,overall_size}, something like that. I'm sure reading shader blobs is also done in a similar way.
And if API matches data format closely, we won't really need to allocate copies of data elements on heap, but instead could simply navigate mapped memory with offsets (I think you're already doing that).
And if API matches data format closely, we won't really need to allocate copies of data elements on heap,
We only keep a copy of the number of rows per table and some schema-related data in metadata_table_info for convenience, but I'll see if I can also reduce those allocations.