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);