-- v2: widl: Write the guid stream. widl: Write the blob stream. widl: Write the user string stream. widl: Write the string stream. widl: Initial support for generating Windows Runtime metadata.
From: Hans Leidekker hans@codeweavers.com
This adds a --winmd option that converts a runtime IDL file into a metadata file (.winmd). --- tools/widl/Makefile.in | 1 + tools/widl/metadata.c | 257 +++++++++++++++++++++++++++++++++++++++++ tools/widl/parser.y | 1 + tools/widl/widl.c | 26 ++++- tools/widl/widl.h | 3 + 5 files changed, 286 insertions(+), 2 deletions(-) create mode 100644 tools/widl/metadata.c
diff --git a/tools/widl/Makefile.in b/tools/widl/Makefile.in index 4caabd87ba7..39ed0ec7fbe 100644 --- a/tools/widl/Makefile.in +++ b/tools/widl/Makefile.in @@ -7,6 +7,7 @@ SOURCES = \ expr.c \ hash.c \ header.c \ + metadata.c \ parser.l \ parser.y \ ppl.l \ diff --git a/tools/widl/metadata.c b/tools/widl/metadata.c new file mode 100644 index 00000000000..c8f76f47444 --- /dev/null +++ b/tools/widl/metadata.c @@ -0,0 +1,257 @@ +/* + * Copyright 2024, 2025 Hans Leidekker for CodeWeavers + * + * 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 "config.h" +#include <stdarg.h> +#include <stdio.h> + +#include "windef.h" +#include "winnt.h" + +#include "widl.h" +#include "utils.h" +#include "typetree.h" + +static const IMAGE_DOS_HEADER dos_header = +{ + .e_magic = IMAGE_DOS_SIGNATURE, + .e_lfanew = sizeof(dos_header), +}; + +#define FILE_ALIGNMENT 0x200 +#define SECTION_ALIGNMENT 0x1000 +static IMAGE_NT_HEADERS32 nt_header = +{ + .Signature = IMAGE_NT_SIGNATURE, + .FileHeader = + { + .Machine = IMAGE_FILE_MACHINE_I386, + .NumberOfSections = 1, + .TimeDateStamp = 0, + .PointerToSymbolTable = 0, + .NumberOfSymbols = 0, + .SizeOfOptionalHeader = sizeof(IMAGE_OPTIONAL_HEADER32), + .Characteristics = IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_32BIT_MACHINE | IMAGE_FILE_DLL + }, + .OptionalHeader = + { + .Magic = IMAGE_NT_OPTIONAL_HDR32_MAGIC, + .MajorLinkerVersion = 11, + .MinorLinkerVersion = 0, + .SizeOfCode = 0, + .SizeOfInitializedData = 0, + .SizeOfUninitializedData = 0, + .AddressOfEntryPoint = 0, + .BaseOfCode = 0, + .BaseOfData = 0, + .ImageBase = 0x400000, + .SectionAlignment = SECTION_ALIGNMENT, + .FileAlignment = FILE_ALIGNMENT, + .MajorOperatingSystemVersion = 6, + .MinorOperatingSystemVersion = 2, + .MajorImageVersion = 0, + .MinorImageVersion = 0, + .MajorSubsystemVersion = 6, + .MinorSubsystemVersion = 2, + .Win32VersionValue = 0, + .SizeOfImage = 0, + .SizeOfHeaders = FILE_ALIGNMENT, + .CheckSum = 0, + .Subsystem = IMAGE_SUBSYSTEM_WINDOWS_CUI, + .DllCharacteristics = IMAGE_DLLCHARACTERISTICS_NO_SEH | IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE | + IMAGE_DLLCHARACTERISTICS_NX_COMPAT, + .SizeOfStackReserve = 0x100000, + .SizeOfStackCommit = 0, + .SizeOfHeapReserve = 0x1000, + .SizeOfHeapCommit = 0, + .LoaderFlags = 0x100000, + .NumberOfRvaAndSizes = IMAGE_NUMBEROF_DIRECTORY_ENTRIES, + .DataDirectory = + { + { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, + { .VirtualAddress = SECTION_ALIGNMENT, .Size = sizeof(IMAGE_COR20_HEADER) }, { 0 } + } + } +}; + +static IMAGE_SECTION_HEADER section_header = +{ + .Name = ".text", + { .VirtualSize = 0 }, + .VirtualAddress = 0, + .SizeOfRawData = 0, + .PointerToRawData = 0, + .PointerToRelocations = 0, + .PointerToLinenumbers = 0, + .NumberOfRelocations = 0, + .NumberOfLinenumbers = 0, + .Characteristics = IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_READ +}; + +static IMAGE_COR20_HEADER cor_header = +{ + .cb = sizeof(IMAGE_COR20_HEADER), + .MajorRuntimeVersion = 2, + .MinorRuntimeVersion = 5, + .MetaData = { 0 }, + .Flags = COMIMAGE_FLAGS_ILONLY, + .EntryPointToken = 0, + .Resources = { 0 }, + .StrongNameSignature = { 0 }, + .CodeManagerTable = { 0 }, + .VTableFixups = { 0 }, + .ExportAddressTableJumps = { 0 }, + .ManagedNativeHeader = { 0 } +}; + +#define METADATA_MAGIC ('B' | ('S' << 8) | ('J' << 16) | ('B' << 24)) +static struct +{ + UINT signature; + USHORT major_version; + USHORT minor_version; + UINT reserved; + UINT length; + char version[20]; + USHORT flags; + USHORT num_streams; +} +metadata_header = +{ + METADATA_MAGIC, + 1, + 1, + 0, + 20, + "WindowsRuntime 1.4", + 0, + 0 +}; + +enum +{ + STREAM_TABLE, + STREAM_STRING, + STREAM_USERSTRING, + STREAM_GUID, + STREAM_BLOB, + STREAM_MAX +}; + +static struct +{ + UINT data_offset; + UINT data_size; + char name[12]; + UINT header_size; + const BYTE *data; +} +streams[] = +{ + { 0, 0, "#~", 12 }, + { 0, 0, "#Strings", 20 }, + { 0, 0, "#US", 12 }, + { 0, 0, "#GUID", 16 }, + { 0, 0, "#Blob", 16 } +}; + +static void write_data( FILE *file, const void *buf, size_t size ) +{ + fwrite( buf, size, 1, file ); +} + +static void write_headers( FILE *file, UINT image_size ) +{ + static const BYTE pad[8]; + UINT i, stream_headers_size = 0, streams_size = 0; + USHORT num_streams = 0; + + write_data( file, &dos_header, sizeof(dos_header) ); + + image_size += nt_header.OptionalHeader.SizeOfHeaders + sizeof(section_header); + nt_header.OptionalHeader.SizeOfImage = (image_size + 0x1fff) & ~0x1fff; + + write_data( file, &nt_header, sizeof(nt_header) ); + + for (i = 0; i < STREAM_MAX; i++) + { + if (!streams[i].data_size) continue; + streams_size += streams[i].header_size + streams[i].data_size; + num_streams++; + } + + section_header.PointerToRawData = FILE_ALIGNMENT; + section_header.VirtualAddress = SECTION_ALIGNMENT; + section_header.Misc.VirtualSize = sizeof(cor_header) + sizeof(metadata_header) + streams_size + 8; + section_header.SizeOfRawData = (section_header.Misc.VirtualSize + FILE_ALIGNMENT - 1) & ~(FILE_ALIGNMENT - 1); + + write_data( file, §ion_header, sizeof(section_header) ); + + for (i = 0; i < FILE_ALIGNMENT - sizeof(dos_header) - sizeof(nt_header) - sizeof(section_header); i++) + write_data( file, pad, 1 ); + + cor_header.MetaData.VirtualAddress = section_header.VirtualAddress + sizeof(cor_header) + 8; + cor_header.MetaData.Size = sizeof(metadata_header) + streams_size; + + write_data( file, &cor_header, sizeof(cor_header) ); + write_data( file, pad, 8 ); + + metadata_header.num_streams = num_streams; + write_data( file, &metadata_header, sizeof(metadata_header) ); + for (i = 0; i < STREAM_MAX; i++) + { + if (!streams[i].data_size) continue; + write_data( file, &streams[i], streams[i].header_size ); + stream_headers_size += streams[i].header_size; + } +} + +static void write_streams( FILE *file ) +{ + UINT i; + for (i = 0; i < STREAM_MAX; i++) + { + if (!streams[i].data_size) continue; + write_data( file, streams[i].data, streams[i].data_size ); + } +} + +void write_metadata( const statement_list_t *stmts ) +{ + static const BYTE pad[FILE_ALIGNMENT]; + UINT image_size, file_size, i; + FILE *file; + + if (!do_metadata) return; + + if (!(file = fopen( metadata_name, "wb" ))) + { + error( "Could not open %s for output\n", metadata_name ); + return; + } + + image_size = FILE_ALIGNMENT + sizeof(cor_header) + 8 + sizeof(metadata_header); + for (i = 0; i < STREAM_MAX; i++) image_size += streams[i].header_size + streams[i].data_size; + + write_headers( file, image_size ); + write_streams( file ); + + file_size = (image_size + FILE_ALIGNMENT - 1) & ~(FILE_ALIGNMENT - 1); + write_data( file, pad, file_size - image_size ); + fclose( file ); +} diff --git a/tools/widl/parser.y b/tools/widl/parser.y index 985f1108ce4..54b143637cf 100644 --- a/tools/widl/parser.y +++ b/tools/widl/parser.y @@ -379,6 +379,7 @@ input: gbl_statements m_acf { $1 = append_parameterized_type_stmts($1); write_typelib_regscript($1); write_dlldata($1); write_local_stubs($1); + write_metadata($1); (void)parser_nerrs; /* avoid unused variable warning */ } ; diff --git a/tools/widl/widl.c b/tools/widl/widl.c index b1ede89934f..19b85633831 100644 --- a/tools/widl/widl.c +++ b/tools/widl/widl.c @@ -77,6 +77,7 @@ static const char usage[] = " -W Enable pedantic warnings\n" " --win32, --win64 Set the target architecture (Win32 or Win64)\n" " --winrt Enable Windows Runtime mode\n" +" --winmd Generate metadata (implies --winrt)\n" "Debug level 'n' is a bitmask with following meaning:\n" " * 0x01 Tell which resource is parsed (verbose mode)\n" " * 0x02 Dump internal structures\n" @@ -105,6 +106,7 @@ int do_server = 0; int do_regscript = 0; int do_idfile = 0; int do_dlldata = 0; +int do_metadata = 0; static int no_preprocess = 0; int old_names = 0; int old_typelib = 0; @@ -120,6 +122,7 @@ char *header_name; char *local_stubs_name; char *header_token; char *typelib_name; +char *metadata_name; char *dlldata_name; char *proxy_name; char *proxy_token; @@ -168,6 +171,7 @@ enum { SYSROOT_OPTION, WIN32_OPTION, WIN64_OPTION, + WINMD_OPTION, };
static const char short_options[] = @@ -195,6 +199,7 @@ static const struct long_option long_options[] = { { "winrt", 0, RT_OPTION }, { "win32", 0, WIN32_OPTION }, { "win64", 0, WIN64_OPTION }, + { "winmd", 0, WINMD_OPTION }, { NULL } };
@@ -263,6 +268,7 @@ static void set_everything(int x) do_regscript = x; do_idfile = x; do_dlldata = x; + do_metadata = x; }
void start_cplusplus_guard(FILE *fp) @@ -519,6 +525,11 @@ static void option_callback( int optc, char *optarg ) case WIN64_OPTION: pointer_size = 8; break; + case WINMD_OPTION: + do_everything = 0; + winrt_mode = 1; + do_metadata = 1; + break; case PACKING_OPTION: packing = strtol(optarg, NULL, 0); if(packing != 2 && packing != 4 && packing != 8) @@ -719,11 +730,16 @@ int main(int argc,char *argv[])
/* if nothing specified, try to guess output type from the output file name */ if (output_name && do_everything && !do_header && !do_typelib && !do_proxies && - !do_client && !do_server && !do_regscript && !do_idfile && !do_dlldata) + !do_client && !do_server && !do_regscript && !do_idfile && !do_dlldata && !do_metadata) { do_everything = 0; if (strendswith( output_name, ".h" )) do_header = 1; else if (strendswith( output_name, ".tlb" )) do_typelib = 1; + else if (strendswith( output_name, ".winmd" )) + { + winrt_mode = 1; + do_metadata = 1; + } else if (strendswith( output_name, "_p.c" )) do_proxies = 1; else if (strendswith( output_name, "_c.c" )) do_client = 1; else if (strendswith( output_name, "_s.c" )) do_server = 1; @@ -740,10 +756,11 @@ int main(int argc,char *argv[]) }
if (do_header + do_typelib + do_proxies + do_client + - do_server + do_regscript + do_idfile + do_dlldata == 1 && output_name) + do_server + do_regscript + do_idfile + do_dlldata + do_metadata == 1 && output_name) { if (do_header && !header_name) header_name = output_name; else if (do_typelib && !typelib_name) typelib_name = output_name; + else if (do_metadata && !metadata_name) metadata_name = output_name; else if (do_proxies && !proxy_name) proxy_name = output_name; else if (do_client && !client_name) client_name = output_name; else if (do_server && !server_name) server_name = output_name; @@ -813,6 +830,9 @@ int main(int argc,char *argv[]) if (!idfile_name && do_idfile) idfile_name = replace_extension( get_basename(input_name), ".idl", "_i.c" );
+ if (!metadata_name && do_metadata) + metadata_name = replace_extension( get_basename(input_name), ".idl", ".winmd" ); + if (do_proxies) proxy_token = dup_basename_token(proxy_name,"_p.c"); if (do_client) client_token = dup_basename_token(client_name,"_c.c"); if (do_server) server_token = dup_basename_token(server_name,"_s.c"); @@ -857,6 +877,8 @@ static void rm_tempfile(void) unlink(proxy_name); if (do_typelib) unlink(typelib_name); + if (do_metadata) + unlink(metadata_name); remove_temp_files(); }
diff --git a/tools/widl/widl.h b/tools/widl/widl.h index 31bb2c83694..08071e8fdf8 100644 --- a/tools/widl/widl.h +++ b/tools/widl/widl.h @@ -39,6 +39,7 @@ extern int pedantic; extern int do_everything; extern int do_header; extern int do_typelib; +extern int do_metadata; extern int do_proxies; extern int do_client; extern int do_server; @@ -58,6 +59,7 @@ extern char *header_name; extern char *header_token; extern char *local_stubs_name; extern char *typelib_name; +extern char *metadata_name; extern char *dlldata_name; extern char *proxy_name; extern char *proxy_token; @@ -86,6 +88,7 @@ extern void write_typelib_regscript(const statement_list_t *stmts); extern void output_typelib_regscript( const typelib_t *typelib ); extern void write_local_stubs(const statement_list_t *stmts); extern void write_dlldata(const statement_list_t *stmts); +extern void write_metadata(const statement_list_t *stmts);
extern void start_cplusplus_guard(FILE *fp); extern void end_cplusplus_guard(FILE *fp);
From: Hans Leidekker hans@codeweavers.com
--- tools/widl/metadata.c | 125 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+)
diff --git a/tools/widl/metadata.c b/tools/widl/metadata.c index c8f76f47444..5767e05d3f4 100644 --- a/tools/widl/metadata.c +++ b/tools/widl/metadata.c @@ -221,6 +221,129 @@ static void write_headers( FILE *file, UINT image_size ) } }
+static struct buffer +{ + UINT offset; /* write position */ + UINT allocated; /* allocated size in bytes */ + UINT count; /* number of entries written */ + BYTE *ptr; +} strings, strings_idx; + +static void *grow_buffer( struct buffer *buf, UINT size ) +{ + UINT new_size; + + if (buf->allocated - buf->offset >= size) return buf->ptr; + + new_size = max( buf->offset + size, buf->allocated * 2 ); + buf->ptr = xrealloc( buf->ptr, new_size ); + buf->allocated = new_size; + return buf->ptr; +} + +struct index +{ + UINT offset; /* offset into corresponding data buffer */ + UINT size; /* size of data entry */ +}; + +static inline int cmp_data( const BYTE *data, UINT size, const BYTE *data2, UINT size2 ) +{ + if (size < size2) return -1; + else if (size > size2) return 1; + return memcmp( data, data2, size ); +} + +/* return index struct if found, NULL and insert index if not found */ +static const struct index *find_index( const struct buffer *buf_idx, const struct buffer *buf_data, const BYTE *data, + UINT size, UINT *insert_idx ) +{ + int i, c, min = 0, max = buf_idx->count - 1, len = 0; + const struct index *idx, *base = (const struct index *)buf_idx->ptr; + + while (min <= max) + { + i = (min + max) / 2; + idx = &base[i]; + + c = cmp_data( data, size, buf_data->ptr + idx->offset + len, idx->size ); + + if (c < 0) max = i - 1; + else if (c > 0) min = i + 1; + else return idx; + } + + if (insert_idx) *insert_idx = max + 1; + return NULL; +} + +static void insert_index( struct buffer *buf_idx, UINT idx, UINT offset, UINT size ) +{ + struct index new = { offset, size }, *base = grow_buffer( buf_idx, sizeof(new) ); + + memmove( &base[idx] + 1, &base[idx], (buf_idx->count - idx) * sizeof(new) ); + base[idx] = new; + buf_idx->offset += sizeof(new); + buf_idx->count++; +} + +static UINT add_string( const char *str ) +{ + UINT insert_idx, size, offset = strings.offset; + const struct index *idx; + + if (!str) return 0; + size = strlen( str ) + 1; + if ((idx = find_index( &strings_idx, &strings, (const BYTE *)str, size, &insert_idx ))) + return idx->offset; + + grow_buffer( &strings, size ); + memcpy( strings.ptr + offset, str, size ); + strings.offset += size; + strings.count++; + + insert_index( &strings_idx, insert_idx, offset, size ); + return offset; +} + +static void add_bytes( struct buffer *buf, const BYTE *data, UINT size ) +{ + grow_buffer( buf, size ); + memcpy( buf->ptr + buf->offset, data, size ); + buf->offset += size; +} + +static void build_table_stream( const statement_list_t *stmts ) +{ + add_string( "" ); +} + +static void build_streams( const statement_list_t *stmts ) +{ + static const BYTE pad[4]; + UINT i, len, offset = sizeof(metadata_header); + + build_table_stream( stmts ); + + len = (strings.offset + 3) & ~3; + add_bytes( &strings, pad, len - strings.offset ); + + streams[STREAM_STRING].data_size = strings.offset; + streams[STREAM_STRING].data = strings.ptr; + + for (i = 0; i < STREAM_MAX; i++) + { + if (!streams[i].data_size) continue; + offset += streams[i].header_size; + } + for (i = 0; i < STREAM_MAX; i++) + { + if (!streams[i].data_size) continue; + streams[i].data_offset = offset; + offset += streams[i].data_size; + } +} + static void write_streams( FILE *file ) { UINT i; @@ -245,6 +368,8 @@ void write_metadata( const statement_list_t *stmts ) return; }
+ build_streams( stmts ); + image_size = FILE_ALIGNMENT + sizeof(cor_header) + 8 + sizeof(metadata_header); for (i = 0; i < STREAM_MAX; i++) image_size += streams[i].header_size + streams[i].data_size;
From: Hans Leidekker hans@codeweavers.com
--- tools/widl/metadata.c | 86 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 83 insertions(+), 3 deletions(-)
diff --git a/tools/widl/metadata.c b/tools/widl/metadata.c index 5767e05d3f4..61429a0864d 100644 --- a/tools/widl/metadata.c +++ b/tools/widl/metadata.c @@ -227,7 +227,7 @@ static struct buffer UINT allocated; /* allocated size in bytes */ UINT count; /* number of entries written */ BYTE *ptr; -} strings, strings_idx; +} strings, strings_idx, userstrings, userstrings_idx;
static void *grow_buffer( struct buffer *buf, UINT size ) { @@ -241,6 +241,40 @@ static void *grow_buffer( struct buffer *buf, UINT size ) return buf->ptr; }
+static UINT encode_int( UINT value, BYTE *encoded ) +{ + if (value < 0x80) + { + encoded[0] = value; + return 1; + } + if (value < 0x4000) + { + encoded[0] = value >> 8 | 0x80; + encoded[1] = value & 0xff; + return 2; + } + if (value < 0x20000000) + { + encoded[0] = value >> 24 | 0xc0; + encoded[1] = value >> 16 & 0xff; + encoded[2] = value >> 8 & 0xff; + encoded[3] = value & 0xff; + return 4; + } + fprintf( stderr, "Value too large to encode.\n" ); + exit( 1 ); +} + +static inline UINT len_encoded_int( const BYTE *encoded ) +{ + if (!(encoded[0] & 0x80)) return 1; + if (!(encoded[0] & 0x40)) return 2; + if (!(encoded[0] & 0x20)) return 4; + fprintf( stderr, "Invalid encoding.\n" ); + exit( 1 ); +} + struct index { UINT offset; /* offset into corresponding data buffer */ @@ -256,7 +290,7 @@ static inline int cmp_data( const BYTE *data, UINT size, const BYTE *data2, UINT
/* return index struct if found, NULL and insert index if not found */ static const struct index *find_index( const struct buffer *buf_idx, const struct buffer *buf_data, const BYTE *data, - UINT size, UINT *insert_idx ) + UINT size, BOOL is_blob, UINT *insert_idx ) { int i, c, min = 0, max = buf_idx->count - 1, len = 0; const struct index *idx, *base = (const struct index *)buf_idx->ptr; @@ -265,6 +299,7 @@ static const struct index *find_index( const struct buffer *buf_idx, const struc { i = (min + max) / 2; idx = &base[i]; + if (is_blob) len = len_encoded_int( buf_data->ptr + idx->offset );
c = cmp_data( data, size, buf_data->ptr + idx->offset + len, idx->size );
@@ -294,7 +329,7 @@ static UINT add_string( const char *str )
if (!str) return 0; size = strlen( str ) + 1; - if ((idx = find_index( &strings_idx, &strings, (const BYTE *)str, size, &insert_idx ))) + if ((idx = find_index( &strings_idx, &strings, (const BYTE *)str, size, FALSE, &insert_idx ))) return idx->offset;
grow_buffer( &strings, size ); @@ -306,6 +341,41 @@ static UINT add_string( const char *str ) return offset; }
+static inline int is_special_char( USHORT c ) +{ + return (c >= 0x100 || (c >= 0x01 && c <= 0x08) || (c >= 0x0e && c <= 0x1f) || c == 0x27 || c == 0x2d || c == 0x7f); +} + +static UINT add_userstring( const USHORT *str, UINT size ) +{ + BYTE encoded[4], terminal = 0; + UINT i, insert_idx, offset = userstrings.offset, len = encode_int( size + (str ? 1 : 0), encoded ); + const struct index *idx; + + if (!str && offset) return 0; + + if ((idx = find_index( &userstrings_idx, &userstrings, (const BYTE *)str, size, TRUE, &insert_idx ))) + return idx->offset; + + grow_buffer( &userstrings, len + size + 1 ); + memcpy( userstrings.ptr + userstrings.offset, encoded, len ); + userstrings.offset += len; + if (str) + { + for (i = 0; i < size / sizeof(USHORT); i++) + { + *(USHORT *)(userstrings.ptr + userstrings.offset) = str[i]; + userstrings.offset += sizeof(USHORT); + if (is_special_char( str[i] )) terminal = 1; + } + userstrings.ptr[userstrings.offset++] = terminal; + } + userstrings.count++; + + insert_index( &userstrings_idx, insert_idx, offset, size ); + return offset; +} + static void add_bytes( struct buffer *buf, const BYTE *data, UINT size ) { grow_buffer( buf, size ); @@ -315,7 +385,11 @@ static void add_bytes( struct buffer *buf, const BYTE *data, UINT size )
static void build_table_stream( const statement_list_t *stmts ) { + static const USHORT space = 0x20; + add_string( "" ); + add_userstring( NULL, 0 ); + add_userstring( &space, sizeof(space) ); }
static void build_streams( const statement_list_t *stmts ) @@ -331,6 +405,12 @@ static void build_streams( const statement_list_t *stmts ) streams[STREAM_STRING].data_size = strings.offset; streams[STREAM_STRING].data = strings.ptr;
+ len = (userstrings.offset + 3) & ~3; + add_bytes( &userstrings, pad, len - userstrings.offset ); + + streams[STREAM_USERSTRING].data_size = userstrings.offset; + streams[STREAM_USERSTRING].data = userstrings.ptr; + for (i = 0; i < STREAM_MAX; i++) { if (!streams[i].data_size) continue;
From: Hans Leidekker hans@codeweavers.com
--- tools/widl/metadata.c | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-)
diff --git a/tools/widl/metadata.c b/tools/widl/metadata.c index 61429a0864d..c025fbf19d6 100644 --- a/tools/widl/metadata.c +++ b/tools/widl/metadata.c @@ -227,7 +227,7 @@ static struct buffer UINT allocated; /* allocated size in bytes */ UINT count; /* number of entries written */ BYTE *ptr; -} strings, strings_idx, userstrings, userstrings_idx; +} strings, strings_idx, userstrings, userstrings_idx, blobs, blobs_idx;
static void *grow_buffer( struct buffer *buf, UINT size ) { @@ -376,6 +376,29 @@ static UINT add_userstring( const USHORT *str, UINT size ) return offset; }
+static UINT add_blob( const BYTE *blob, UINT size ) +{ + BYTE encoded[4]; + UINT insert_idx, offset = blobs.offset, len = encode_int( size, encoded ); + const struct index *idx; + + if (!blob && offset) return 0; + if ((idx = find_index( &blobs_idx, &blobs, blob, size, TRUE, &insert_idx ))) return idx->offset; + + grow_buffer( &blobs, len + size ); + memcpy( blobs.ptr + blobs.offset, encoded, len ); + blobs.offset += len; + if (blob) + { + memcpy( blobs.ptr + blobs.offset, blob, size ); + blobs.offset += size; + } + blobs.count++; + + insert_index( &blobs_idx, insert_idx, offset, size ); + return offset; +} + static void add_bytes( struct buffer *buf, const BYTE *data, UINT size ) { grow_buffer( buf, size ); @@ -390,6 +413,7 @@ static void build_table_stream( const statement_list_t *stmts ) add_string( "" ); add_userstring( NULL, 0 ); add_userstring( &space, sizeof(space) ); + add_blob( NULL, 0 ); }
static void build_streams( const statement_list_t *stmts ) @@ -411,6 +435,12 @@ static void build_streams( const statement_list_t *stmts ) streams[STREAM_USERSTRING].data_size = userstrings.offset; streams[STREAM_USERSTRING].data = userstrings.ptr;
+ len = (blobs.offset + 3) & ~3; + add_bytes( &blobs, pad, len - blobs.offset ); + + streams[STREAM_BLOB].data_size = blobs.offset; + streams[STREAM_BLOB].data = blobs.ptr; + for (i = 0; i < STREAM_MAX; i++) { if (!streams[i].data_size) continue;
From: Hans Leidekker hans@codeweavers.com
--- tools/widl/metadata.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-)
diff --git a/tools/widl/metadata.c b/tools/widl/metadata.c index c025fbf19d6..0845318de55 100644 --- a/tools/widl/metadata.c +++ b/tools/widl/metadata.c @@ -227,7 +227,7 @@ static struct buffer UINT allocated; /* allocated size in bytes */ UINT count; /* number of entries written */ BYTE *ptr; -} strings, strings_idx, userstrings, userstrings_idx, blobs, blobs_idx; +} strings, strings_idx, userstrings, userstrings_idx, blobs, blobs_idx, guids;
static void *grow_buffer( struct buffer *buf, UINT size ) { @@ -399,6 +399,14 @@ static UINT add_blob( const BYTE *blob, UINT size ) return offset; }
+static UINT add_guid( const GUID *guid ) +{ + grow_buffer( &guids, sizeof(*guid) ); + memcpy( guids.ptr + guids.offset, guid, sizeof(*guid) ); + guids.offset += sizeof(*guid); + return ++guids.count; +} + static void add_bytes( struct buffer *buf, const BYTE *data, UINT size ) { grow_buffer( buf, size ); @@ -408,12 +416,14 @@ static void add_bytes( struct buffer *buf, const BYTE *data, UINT size )
static void build_table_stream( const statement_list_t *stmts ) { + static const GUID guid = { 0x9ddc04c6, 0x04ca, 0x04cc, { 0x52, 0x85, 0x4b, 0x50, 0xb2, 0x60, 0x1d, 0xa8 } }; static const USHORT space = 0x20;
add_string( "" ); add_userstring( NULL, 0 ); add_userstring( &space, sizeof(space) ); add_blob( NULL, 0 ); + add_guid( &guid ); }
static void build_streams( const statement_list_t *stmts ) @@ -441,6 +451,9 @@ static void build_streams( const statement_list_t *stmts ) streams[STREAM_BLOB].data_size = blobs.offset; streams[STREAM_BLOB].data = blobs.ptr;
+ streams[STREAM_GUID].data_size = guids.offset; + streams[STREAM_GUID].data = guids.ptr; + for (i = 0; i < STREAM_MAX; i++) { if (!streams[i].data_size) continue;
v2: Addressed comments
Rémi Bernon (@rbernon) commented about tools/widl/metadata.c:
- { 0, 0, "#~", 12 },
- { 0, 0, "#Strings", 20 },
- { 0, 0, "#US", 12 },
- { 0, 0, "#GUID", 16 },
- { 0, 0, "#Blob", 16 }
+};
+static void write_data( FILE *file, const void *buf, size_t size ) +{
- fwrite( buf, size, 1, file );
+}
+static void write_headers( FILE *file, UINT image_size ) +{
- static const BYTE pad[8];
- UINT i, stream_headers_size = 0, streams_size = 0;
`stream_headers_size` is not actually used.
Rémi Bernon (@rbernon) commented about tools/widl/metadata.c:
- UINT header_size;
- const BYTE *data;
+} +streams[] = +{
- { 0, 0, "#~", 12 },
- { 0, 0, "#Strings", 20 },
- { 0, 0, "#US", 12 },
- { 0, 0, "#GUID", 16 },
- { 0, 0, "#Blob", 16 }
+};
+static void write_data( FILE *file, const void *buf, size_t size ) +{
- fwrite( buf, size, 1, file );
+}
There's `put_data` in tools.h which should probably be used, saving the need to pass a FILE param around, and `flush_output_buffer` to write the data to the output file.
Rémi Bernon (@rbernon) commented about tools/widl/metadata.c:
- 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 "config.h" +#include <stdarg.h> +#include <stdio.h>
+#include "windef.h" +#include "winnt.h"
I'm wondering if it's alright to use Win32 types in `widl`? Other unix-only programs seem to prefer using C builtin types instead (`unsigned int`, `size_t`, `bool` from `stdbool`, etc). I don't have a strong opinion but using Win32 types will more tightly couple the code with Wine/Win32 headers.
Rémi Bernon (@rbernon) commented about tools/widl/metadata.c:
{ i = (min + max) / 2; idx = &base[i];
if (is_blob) len = len_encoded_int( buf_data->ptr + idx->offset ); c = cmp_data( data, size, buf_data->ptr + idx->offset + len, idx->size );
Shouldn't we check that the stored blob length matches the searched blob length?
Rémi Bernon (@rbernon) commented about tools/widl/metadata.c:
static void build_table_stream( const statement_list_t *stmts ) {
static const GUID guid = { 0x9ddc04c6, 0x04ca, 0x04cc, { 0x52, 0x85, 0x4b, 0x50, 0xb2, 0x60, 0x1d, 0xa8 } }; static const USHORT space = 0x20;
add_string( "" ); add_userstring( NULL, 0 ); add_userstring( &space, sizeof(space) ); add_blob( NULL, 0 );
add_guid( &guid );
Fwiw, not a big issue that early but I gave monodis a try, and it expects a GUID entry first and asserts when missing, then some "#~" data, and segfaults when missing. I'm not sure that should determine the order of priority we should output these sections, but is the current code supposed to work, in some minimal way, with another tool?
On Fri May 16 11:14:01 2025 +0000, Rémi Bernon wrote:
I'm wondering if it's alright to use Win32 types in `widl`? Other unix-only programs seem to prefer using C builtin types instead (`unsigned int`, `size_t`, `bool` from `stdbool`, etc). I don't have a strong opinion but using Win32 types will more tightly couple the code with Wine/Win32 headers.
I don't know if there's such a requirement. Like winedump I included Windows headers to get the PE header definitions, but they could be redefined of course.
On Fri May 16 11:13:37 2025 +0000, Rémi Bernon wrote:
Shouldn't we check that the stored blob length matches the searched blob length?
Yes, thanks.
On Fri May 16 11:13:37 2025 +0000, Rémi Bernon wrote:
There's `put_data` in tools.h which should probably be used, saving the need to pass a FILE param around, and `flush_output_buffer` to write the data to the output file.
Right, I'll switch to put_data().
On Fri May 16 11:13:37 2025 +0000, Rémi Bernon wrote:
Fwiw, not a big issue that early but I gave monodis a try, and it expects a GUID entry first and asserts when missing, then some "#~" data, and segfaults when missing. I'm not sure that should determine the order of priority we should output these sections, but is the current code supposed to work, in some minimal way, with another tool?
Streams are written in the same order as midlrt writes them. I don't know of any ordering requirement for WinRT metadata files but perhaps .NET assemblies do require a certain order? The streams are essentially empty now. In followup patches the table stream (#~) will be added and the other streams will be filled too.
On Fri May 16 13:31:27 2025 +0000, Hans Leidekker wrote:
Streams are written in the same order as midlrt writes them. I don't know of any ordering requirement for WinRT metadata files but perhaps .NET assemblies do require a certain order? The streams are essentially empty now. In followup patches the table stream (#~) will be added and the other streams will be filled too.
Yeah I didn't mean the order they are being written to the file, rather the order we implement them (to be able to use dump tools like monodis and confirm the winmd is being valid). But sure, this is still early and probably fine that it doesn't work yet.
On Fri May 16 13:37:53 2025 +0000, Rémi Bernon wrote:
Yeah I didn't mean the order they are being written to the file, rather the order we implement them (to be able to use dump tools like monodis and confirm the winmd is being valid). But sure, this is still early and probably fine that it doesn't work yet.
It's too early still but there's winmidl.exe which does the reverse. It takes a winmd file and generates an idl file. It's not identical to the original because of formatting but if you generate a winmd file with midlrt from the same source idl and convert back with winmidl then the diff is very helpful for debugging :-).
On Fri May 16 14:01:44 2025 +0000, Hans Leidekker wrote:
It's too early still but there's winmidl.exe which does the reverse. It takes a winmd file and generates an idl file. It's not identical to the original because of formatting but if you generate a winmd file with midlrt from the same source idl and convert back with winmidl then the diff is very helpful for debugging :-).
Sorry, that should read winmdidl.exe.