From 39f6ab55a970dc242304621026b90444037a3daa Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Wed, 16 Feb 2022 14:58:49 +0100 Subject: [PATCH] winebuild: Add support for generating data-only DLLs. Signed-off-by: Alexandre Julliard --- tools/winebuild/build.h | 2 + tools/winebuild/main.c | 12 ++ tools/winebuild/parser.c | 6 + tools/winebuild/spec32.c | 251 +++++++++++++++++++++---------- tools/winebuild/utils.c | 2 +- tools/winebuild/winebuild.man.in | 5 + 6 files changed, 199 insertions(+), 79 deletions(-) diff --git a/tools/winebuild/build.h b/tools/winebuild/build.h index 85cbc100b59..aa6f31342c7 100644 --- a/tools/winebuild/build.h +++ b/tools/winebuild/build.h @@ -283,6 +283,7 @@ extern void output_resources( DLLSPEC *spec ); extern void output_bin_resources( DLLSPEC *spec, unsigned int start_rva ); extern void output_spec32_file( DLLSPEC *spec ); extern void output_fake_module( DLLSPEC *spec ); +extern void output_data_module( DLLSPEC *spec ); extern void output_def_file( DLLSPEC *spec, int import_only ); extern void load_res16_file( const char *name, DLLSPEC *spec ); extern void output_res16_data( DLLSPEC *spec ); @@ -329,6 +330,7 @@ extern int use_msvcrt; extern int unix_lib; extern int safe_seh; extern int prefer_native; +extern int data_only; extern char *input_file_name; extern char *spec_file_name; diff --git a/tools/winebuild/main.c b/tools/winebuild/main.c index 77f13b0fa8a..cfccee6cf8b 100644 --- a/tools/winebuild/main.c +++ b/tools/winebuild/main.c @@ -46,6 +46,7 @@ int use_msvcrt = 0; int unix_lib = 0; int safe_seh = 0; int prefer_native = 0; +int data_only = 0; struct target target = { 0 }; @@ -198,6 +199,7 @@ static const char usage_str[] = " -b, --target=TARGET Specify target CPU and platform for cross-compiling\n" " -B PREFIX Look for build tools in the PREFIX directory\n" " --cc-cmd=CC C compiler to use for assembling (default: fall back to --as-cmd)\n" +" --data-only Generate a data-only dll (i.e. without any executable code)\n" " -d, --delay-lib=LIB Import the specified library in delayed mode\n" " -D SYM Ignored for C flags compatibility\n" " -e, --entry=FUNC Set the DLL entry point function (default: DllMain)\n" @@ -251,6 +253,7 @@ enum long_options_values LONG_OPT_BUILTIN, LONG_OPT_ASCMD, LONG_OPT_CCCMD, + LONG_OPT_DATA_ONLY, LONG_OPT_EXTERNAL_SYMS, LONG_OPT_FAKE_MODULE, LONG_OPT_FIXUP_CTORS, @@ -284,6 +287,7 @@ static const struct long_option long_options[] = /* other long options */ { "as-cmd", 1, LONG_OPT_ASCMD }, { "cc-cmd", 1, LONG_OPT_CCCMD }, + { "data-only", 0, LONG_OPT_DATA_ONLY }, { "external-symbols", 0, LONG_OPT_EXTERNAL_SYMS }, { "fake-module", 0, LONG_OPT_FAKE_MODULE }, { "large-address-aware", 0, LONG_OPT_LARGE_ADDRESS_AWARE }, @@ -483,6 +487,9 @@ static void option_callback( int optc, char *optarg ) case LONG_OPT_CCCMD: cc_command = strarray_fromstring( optarg, " " ); break; + case LONG_OPT_DATA_ONLY: + data_only = 1; + break; case LONG_OPT_FAKE_MODULE: fake_module = 1; break; @@ -637,6 +644,11 @@ int main(int argc, char **argv) output_fake_module( spec ); break; } + if (data_only) + { + output_data_module( spec ); + break; + } if (!is_pe()) { files = load_import_libs( files ); diff --git a/tools/winebuild/parser.c b/tools/winebuild/parser.c index 0ee07bda231..a9b6bf2e5a6 100644 --- a/tools/winebuild/parser.c +++ b/tools/winebuild/parser.c @@ -628,6 +628,12 @@ static int parse_spec_ordinal( int ordinal, DLLSPEC *spec ) return 1; } + if (data_only && !(odp->flags & FLAG_FORWARD)) + { + error( "Only forwarded entry points are allowed in data-only mode\n" ); + goto error; + } + if (ordinal != -1) { if (!ordinal) diff --git a/tools/winebuild/spec32.c b/tools/winebuild/spec32.c index a2fb6ad25de..6475b74e37d 100644 --- a/tools/winebuild/spec32.c +++ b/tools/winebuild/spec32.c @@ -796,6 +796,7 @@ static struct unsigned int file_align; unsigned int sec_count; unsigned int exp_count; + unsigned int code_size; struct dir_data dir[16]; struct sec_data sec[8]; struct exp_data exp[8]; @@ -846,94 +847,97 @@ static unsigned int flush_output_to_section( const char *name, int dir_idx, unsi return sec->size; } -/******************************************************************* - * output_fake_module - * - * Build a fake binary module from a spec file. - */ -void output_fake_module( DLLSPEC *spec ) +static void output_pe_exports( DLLSPEC *spec ) { - static const unsigned char dll_code_section[] = { 0x31, 0xc0, /* xor %eax,%eax */ - 0xc2, 0x0c, 0x00 }; /* ret $12 */ + unsigned int i, exp_count = get_exports_count( spec ); + unsigned int exp_rva = current_rva() + 40; /* sizeof(IMAGE_EXPORT_DIRECTORY) */ + unsigned int pos, str_rva = exp_rva + 4 * exp_count + 6 * spec->nb_names; - static const unsigned char exe_code_section[] = { 0xb8, 0x01, 0x00, 0x00, 0x00, /* movl $1,%eax */ - 0xc2, 0x04, 0x00 }; /* ret $4 */ - const unsigned int page_size = get_page_size(); - const unsigned int lfanew = 0x40 + sizeof(fakedll_signature); - unsigned int i; + if (!spec->nb_entry_points) return; - resolve_imports( spec ); init_output_buffer(); - - pe.section_align = page_size; - pe.file_align = 0x200; - - /* .text section */ - if (spec->characteristics & IMAGE_FILE_DLL) put_data( dll_code_section, sizeof(dll_code_section) ); - else put_data( exe_code_section, sizeof(exe_code_section) ); - flush_output_to_section( ".text", -1, 0x60000020 /* CNT_CODE|MEM_EXECUTE|MEM_READ */ ); - - if (spec->type == SPEC_WIN16) + put_dword( 0 ); /* Characteristics */ + put_dword( hash_filename(spec->file_name) ); /* TimeDateStamp */ + put_word( 0 ); /* MajorVersion */ + put_word( 0 ); /* MinorVersion */ + put_dword( str_rva ); /* Name */ + put_dword( spec->base ); /* Base */ + put_dword( exp_count ); /* NumberOfFunctions */ + put_dword( spec->nb_names ); /* NumberOfNames */ + put_dword( exp_rva ); /* AddressOfFunctions */ + if (spec->nb_names) { - add_export( current_rva(), "__wine_spec_dos_header" ); - - /* .rodata section */ - output_fake_module16( spec ); - if (spec->main_module) - { - add_export( current_rva() + output_buffer_pos, "__wine_spec_main_module" ); - put_data( spec->main_module, strlen(spec->main_module) + 1 ); - } - flush_output_to_section( ".rodata", -1, 0x40000040 /* CNT_INITIALIZED_DATA|MEM_READ */ ); + put_dword( exp_rva + 4 * exp_count ); /* AddressOfNames */ + put_dword( exp_rva + 4 * exp_count + 4 * spec->nb_names ); /* AddressOfNameOrdinals */ + } + else + { + put_dword( 0 ); /* AddressOfNames */ + put_dword( 0 ); /* AddressOfNameOrdinals */ } - if (pe.exp_count) + /* functions */ + for (i = 0, pos = str_rva + strlen(spec->file_name) + 1; i < spec->nb_names; i++) + pos += strlen( spec->names[i]->name ) + 1; + for (i = spec->base; i <= spec->limit; i++) { - /* .edata section */ - unsigned int exp_rva = current_rva() + 40; /* sizeof(IMAGE_EXPORT_DIRECTORY) */ - unsigned int pos, str_rva = exp_rva + 10 * pe.exp_count; - - put_dword( 0 ); /* Characteristics */ - put_dword( hash_filename(spec->file_name) ); /* TimeDateStamp */ - put_word( 0 ); /* MajorVersion */ - put_word( 0 ); /* MinorVersion */ - put_dword( str_rva ); /* Name */ - put_dword( 1 ); /* Base */ - put_dword( pe.exp_count ); /* NumberOfFunctions */ - put_dword( pe.exp_count ); /* NumberOfNames */ - put_dword( exp_rva ); /* AddressOfFunctions */ - put_dword( exp_rva + 4 * pe.exp_count ); /* AddressOfNames */ - put_dword( exp_rva + 8 * pe.exp_count ); /* AddressOfNameOrdinals */ - - /* functions */ - for (i = 0; i < pe.exp_count; i++) put_dword( pe.exp[i].rva ); - /* names */ - for (i = 0, pos = str_rva + strlen(spec->file_name) + 1; i < pe.exp_count; i++) + ORDDEF *odp = spec->ordinals[i]; + if (odp && (odp->flags & FLAG_FORWARD)) { put_dword( pos ); - pos += strlen( pe.exp[i].name ) + 1; + pos += strlen(odp->link_name) + 1; } - /* ordinals */ - for (i = 0; i < pe.exp_count; i++) put_word( i ); - /* strings */ - put_data( spec->file_name, strlen(spec->file_name) + 1 ); - for (i = 0; i < pe.exp_count; i++) put_data( pe.exp[i].name, strlen(pe.exp[i].name) + 1 ); - flush_output_to_section( ".edata", 0 /* IMAGE_DIRECTORY_ENTRY_EXPORT */, + else put_dword( 0 ); + } + + /* names */ + for (i = 0, pos = str_rva + strlen(spec->file_name) + 1; i < spec->nb_names; i++) + { + put_dword( pos ); + pos += strlen(spec->names[i]->name) + 1; + } + + /* ordinals */ + for (i = 0; i < spec->nb_names; i++) put_word( spec->names[i]->ordinal - spec->base ); + + /* strings */ + put_data( spec->file_name, strlen(spec->file_name) + 1 ); + for (i = 0; i < spec->nb_names; i++) + put_data( spec->names[i]->name, strlen(spec->names[i]->name) + 1 ); + + for (i = spec->base; i <= spec->limit; i++) + { + ORDDEF *odp = spec->ordinals[i]; + if (odp && (odp->flags & FLAG_FORWARD)) put_data( odp->link_name, strlen(odp->link_name) + 1 ); + } + + flush_output_to_section( ".edata", 0 /* IMAGE_DIRECTORY_ENTRY_EXPORT */, + 0x40000040 /* CNT_INITIALIZED_DATA|MEM_READ */ ); +} + + +static void output_pe_file( DLLSPEC *spec, const char signature[32] ) +{ + const unsigned int lfanew = 0x40 + 32; + unsigned int i; + + init_output_buffer(); + + /* .rsrc section */ + if (spec->type == SPEC_WIN32) + { + output_bin_resources( spec, current_rva() ); + flush_output_to_section( ".rsrc", 2 /* IMAGE_DIRECTORY_ENTRY_RESOURCE */, 0x40000040 /* CNT_INITIALIZED_DATA|MEM_READ */ ); } /* .reloc section */ - put_dword( 0 ); /* VirtualAddress */ - put_dword( 0 ); /* Size */ - flush_output_to_section( ".reloc", 5 /* IMAGE_DIRECTORY_ENTRY_BASERELOC */, - 0x42000040 /* CNT_INITIALIZED_DATA|MEM_DISCARDABLE|MEM_READ */ ); - - if (spec->type == SPEC_WIN32) + if (pe.code_size) { - /* .rsrc section */ - output_bin_resources( spec, current_rva() ); - flush_output_to_section( ".rsrc", 2 /* IMAGE_DIRECTORY_ENTRY_RESOURCE */, - 0x40000040 /* CNT_INITIALIZED_DATA|MEM_READ */ ); + put_dword( 0 ); /* VirtualAddress */ + put_dword( 0 ); /* Size */ + flush_output_to_section( ".reloc", 5 /* IMAGE_DIRECTORY_ENTRY_BASERELOC */, + 0x42000040 /* CNT_INITIALIZED_DATA|MEM_DISCARDABLE|MEM_READ */ ); } put_word( 0x5a4d ); /* e_magic */ @@ -961,7 +965,7 @@ void output_fake_module( DLLSPEC *spec ) put_dword( 0 ); put_dword( lfanew ); - put_data( fakedll_signature, sizeof(fakedll_signature) ); + put_data( signature, 32 ); put_dword( 0x4550 ); /* Signature */ switch (target.cpu) @@ -984,11 +988,11 @@ void output_fake_module( DLLSPEC *spec ) IMAGE_NT_OPTIONAL_HDR32_MAGIC ); /* Magic */ put_byte( 7 ); /* MajorLinkerVersion */ put_byte( 10 ); /* MinorLinkerVersion */ - put_dword( pe.sec[0].size ); /* SizeOfCode */ + put_dword( pe.code_size ); /* SizeOfCode */ put_dword( 0 ); /* SizeOfInitializedData */ put_dword( 0 ); /* SizeOfUninitializedData */ - put_dword( pe.sec[0].rva ); /* AddressOfEntryPoint */ - put_dword( pe.sec[0].rva ); /* BaseOfCode */ + put_dword( pe.code_size ? pe.sec[0].rva : 0 ); /* AddressOfEntryPoint */ + put_dword( pe.code_size ? pe.sec[0].rva : 0 ); /* BaseOfCode */ if (get_ptr_size() == 4) put_dword( 0 ); /* BaseOfData */ put_pword( 0x10000000 ); /* ImageBase */ put_dword( pe.section_align ); /* SectionAlignment */ @@ -1006,9 +1010,9 @@ void output_fake_module( DLLSPEC *spec ) put_word( spec->subsystem ); /* Subsystem */ put_word( spec->dll_characteristics ); /* DllCharacteristics */ put_pword( (spec->stack_size ? spec->stack_size : 1024) * 1024 ); /* SizeOfStackReserve */ - put_pword( page_size ); /* SizeOfStackCommit */ + put_pword( pe.section_align ); /* SizeOfStackCommit */ put_pword( (spec->heap_size ? spec->heap_size : 1024) * 1024 ); /* SizeOfHeapReserve */ - put_pword( page_size ); /* SizeOfHeapCommit */ + put_pword( pe.section_align ); /* SizeOfHeapCommit */ put_dword( 0 ); /* LoaderFlags */ put_dword( 16 ); /* NumberOfRvaAndSizes */ @@ -1044,6 +1048,97 @@ void output_fake_module( DLLSPEC *spec ) flush_output_buffer( output_file_name ? output_file_name : spec->file_name ); } +/******************************************************************* + * output_fake_module + * + * Build a fake binary module from a spec file. + */ +void output_fake_module( DLLSPEC *spec ) +{ + static const unsigned char dll_code_section[] = { 0x31, 0xc0, /* xor %eax,%eax */ + 0xc2, 0x0c, 0x00 }; /* ret $12 */ + + static const unsigned char exe_code_section[] = { 0xb8, 0x01, 0x00, 0x00, 0x00, /* movl $1,%eax */ + 0xc2, 0x04, 0x00 }; /* ret $4 */ + unsigned int i; + + resolve_imports( spec ); + pe.section_align = get_page_size(); + pe.file_align = 0x200; + init_output_buffer(); + + /* .text section */ + if (spec->characteristics & IMAGE_FILE_DLL) put_data( dll_code_section, sizeof(dll_code_section) ); + else put_data( exe_code_section, sizeof(exe_code_section) ); + pe.code_size = output_buffer_pos; + flush_output_to_section( ".text", -1, 0x60000020 /* CNT_CODE|MEM_EXECUTE|MEM_READ */ ); + + if (spec->type == SPEC_WIN16) + { + add_export( current_rva(), "__wine_spec_dos_header" ); + + /* .rdata section */ + output_fake_module16( spec ); + if (spec->main_module) + { + add_export( current_rva() + output_buffer_pos, "__wine_spec_main_module" ); + put_data( spec->main_module, strlen(spec->main_module) + 1 ); + } + flush_output_to_section( ".rdata", -1, 0x40000040 /* CNT_INITIALIZED_DATA|MEM_READ */ ); + } + + /* .edata section */ + if (pe.exp_count) + { + unsigned int exp_rva = current_rva() + 40; /* sizeof(IMAGE_EXPORT_DIRECTORY) */ + unsigned int pos, str_rva = exp_rva + 10 * pe.exp_count; + + put_dword( 0 ); /* Characteristics */ + put_dword( hash_filename(spec->file_name) ); /* TimeDateStamp */ + put_word( 0 ); /* MajorVersion */ + put_word( 0 ); /* MinorVersion */ + put_dword( str_rva ); /* Name */ + put_dword( 1 ); /* Base */ + put_dword( pe.exp_count ); /* NumberOfFunctions */ + put_dword( pe.exp_count ); /* NumberOfNames */ + put_dword( exp_rva ); /* AddressOfFunctions */ + put_dword( exp_rva + 4 * pe.exp_count ); /* AddressOfNames */ + put_dword( exp_rva + 8 * pe.exp_count ); /* AddressOfNameOrdinals */ + + /* functions */ + for (i = 0; i < pe.exp_count; i++) put_dword( pe.exp[i].rva ); + /* names */ + for (i = 0, pos = str_rva + strlen(spec->file_name) + 1; i < pe.exp_count; i++) + { + put_dword( pos ); + pos += strlen( pe.exp[i].name ) + 1; + } + /* ordinals */ + for (i = 0; i < pe.exp_count; i++) put_word( i ); + /* strings */ + put_data( spec->file_name, strlen(spec->file_name) + 1 ); + for (i = 0; i < pe.exp_count; i++) put_data( pe.exp[i].name, strlen(pe.exp[i].name) + 1 ); + flush_output_to_section( ".edata", 0 /* IMAGE_DIRECTORY_ENTRY_EXPORT */, + 0x40000040 /* CNT_INITIALIZED_DATA|MEM_READ */ ); + } + + output_pe_file( spec, fakedll_signature ); +} + + +/******************************************************************* + * output_data_module + * + * Build a data-only module from a spec file. + */ +void output_data_module( DLLSPEC *spec ) +{ + pe.section_align = pe.file_align = get_page_size(); + + output_pe_exports( spec ); + output_pe_file( spec, builtin_signature ); +} + /******************************************************************* * output_def_file diff --git a/tools/winebuild/utils.c b/tools/winebuild/utils.c index 5549691ef10..4f1bdf7cd2d 100644 --- a/tools/winebuild/utils.c +++ b/tools/winebuild/utils.c @@ -644,7 +644,7 @@ DLLSPEC *alloc_dll_spec(void) spec->type = SPEC_WIN32; spec->base = MAX_ORDINALS; spec->characteristics = IMAGE_FILE_EXECUTABLE_IMAGE; - spec->subsystem = 0; + spec->subsystem = IMAGE_SUBSYSTEM_WINDOWS_CUI; spec->subsystem_major = 4; spec->subsystem_minor = 0; spec->syscall_table = 0; diff --git a/tools/winebuild/winebuild.man.in b/tools/winebuild/winebuild.man.in index d9a05fc99d9..1e2c0bbde43 100644 --- a/tools/winebuild/winebuild.man.in +++ b/tools/winebuild/winebuild.man.in @@ -89,6 +89,11 @@ like \fBas\fR, \fBnm\fR and \fBld\fR. Specify the C compiler to use to compile assembly files; the default is to instead use the assembler specified with \fB--as-cmd\fR. .TP +.B \--data-only +Build a module that contains only data and resources, and no +executable code. With this option, \fBwinebuild\fR directly outputs a +PE file, instead of an assembly or object file. +.TP .BI \-d,\ --delay-lib= name Set the delayed import mode for the specified library, which must be one of the libraries imported with the \fB-l\fR option. Delayed mode