winebuild: Add support for generating data-only DLLs.

Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Alexandre Julliard 2022-02-16 14:58:49 +01:00
parent 3eca3516ad
commit 39f6ab55a9
6 changed files with 199 additions and 79 deletions

View File

@ -283,6 +283,7 @@ extern void output_resources( DLLSPEC *spec );
extern void output_bin_resources( DLLSPEC *spec, unsigned int start_rva ); extern void output_bin_resources( DLLSPEC *spec, unsigned int start_rva );
extern void output_spec32_file( DLLSPEC *spec ); extern void output_spec32_file( DLLSPEC *spec );
extern void output_fake_module( 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 output_def_file( DLLSPEC *spec, int import_only );
extern void load_res16_file( const char *name, DLLSPEC *spec ); extern void load_res16_file( const char *name, DLLSPEC *spec );
extern void output_res16_data( DLLSPEC *spec ); extern void output_res16_data( DLLSPEC *spec );
@ -329,6 +330,7 @@ extern int use_msvcrt;
extern int unix_lib; extern int unix_lib;
extern int safe_seh; extern int safe_seh;
extern int prefer_native; extern int prefer_native;
extern int data_only;
extern char *input_file_name; extern char *input_file_name;
extern char *spec_file_name; extern char *spec_file_name;

View File

@ -46,6 +46,7 @@ int use_msvcrt = 0;
int unix_lib = 0; int unix_lib = 0;
int safe_seh = 0; int safe_seh = 0;
int prefer_native = 0; int prefer_native = 0;
int data_only = 0;
struct target target = { 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, --target=TARGET Specify target CPU and platform for cross-compiling\n"
" -B PREFIX Look for build tools in the PREFIX directory\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" " --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, --delay-lib=LIB Import the specified library in delayed mode\n"
" -D SYM Ignored for C flags compatibility\n" " -D SYM Ignored for C flags compatibility\n"
" -e, --entry=FUNC Set the DLL entry point function (default: DllMain)\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_BUILTIN,
LONG_OPT_ASCMD, LONG_OPT_ASCMD,
LONG_OPT_CCCMD, LONG_OPT_CCCMD,
LONG_OPT_DATA_ONLY,
LONG_OPT_EXTERNAL_SYMS, LONG_OPT_EXTERNAL_SYMS,
LONG_OPT_FAKE_MODULE, LONG_OPT_FAKE_MODULE,
LONG_OPT_FIXUP_CTORS, LONG_OPT_FIXUP_CTORS,
@ -284,6 +287,7 @@ static const struct long_option long_options[] =
/* other long options */ /* other long options */
{ "as-cmd", 1, LONG_OPT_ASCMD }, { "as-cmd", 1, LONG_OPT_ASCMD },
{ "cc-cmd", 1, LONG_OPT_CCCMD }, { "cc-cmd", 1, LONG_OPT_CCCMD },
{ "data-only", 0, LONG_OPT_DATA_ONLY },
{ "external-symbols", 0, LONG_OPT_EXTERNAL_SYMS }, { "external-symbols", 0, LONG_OPT_EXTERNAL_SYMS },
{ "fake-module", 0, LONG_OPT_FAKE_MODULE }, { "fake-module", 0, LONG_OPT_FAKE_MODULE },
{ "large-address-aware", 0, LONG_OPT_LARGE_ADDRESS_AWARE }, { "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: case LONG_OPT_CCCMD:
cc_command = strarray_fromstring( optarg, " " ); cc_command = strarray_fromstring( optarg, " " );
break; break;
case LONG_OPT_DATA_ONLY:
data_only = 1;
break;
case LONG_OPT_FAKE_MODULE: case LONG_OPT_FAKE_MODULE:
fake_module = 1; fake_module = 1;
break; break;
@ -637,6 +644,11 @@ int main(int argc, char **argv)
output_fake_module( spec ); output_fake_module( spec );
break; break;
} }
if (data_only)
{
output_data_module( spec );
break;
}
if (!is_pe()) if (!is_pe())
{ {
files = load_import_libs( files ); files = load_import_libs( files );

View File

@ -628,6 +628,12 @@ static int parse_spec_ordinal( int ordinal, DLLSPEC *spec )
return 1; 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 != -1)
{ {
if (!ordinal) if (!ordinal)

View File

@ -796,6 +796,7 @@ static struct
unsigned int file_align; unsigned int file_align;
unsigned int sec_count; unsigned int sec_count;
unsigned int exp_count; unsigned int exp_count;
unsigned int code_size;
struct dir_data dir[16]; struct dir_data dir[16];
struct sec_data sec[8]; struct sec_data sec[8];
struct exp_data exp[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; return sec->size;
} }
/******************************************************************* static void output_pe_exports( DLLSPEC *spec )
* 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 */ unsigned int i, exp_count = get_exports_count( spec );
0xc2, 0x0c, 0x00 }; /* ret $12 */ 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 */ if (!spec->nb_entry_points) return;
0xc2, 0x04, 0x00 }; /* ret $4 */
const unsigned int page_size = get_page_size();
const unsigned int lfanew = 0x40 + sizeof(fakedll_signature);
unsigned int i;
resolve_imports( spec );
init_output_buffer(); init_output_buffer();
put_dword( 0 ); /* Characteristics */
pe.section_align = page_size; put_dword( hash_filename(spec->file_name) ); /* TimeDateStamp */
pe.file_align = 0x200; put_word( 0 ); /* MajorVersion */
put_word( 0 ); /* MinorVersion */
/* .text section */ put_dword( str_rva ); /* Name */
if (spec->characteristics & IMAGE_FILE_DLL) put_data( dll_code_section, sizeof(dll_code_section) ); put_dword( spec->base ); /* Base */
else put_data( exe_code_section, sizeof(exe_code_section) ); put_dword( exp_count ); /* NumberOfFunctions */
flush_output_to_section( ".text", -1, 0x60000020 /* CNT_CODE|MEM_EXECUTE|MEM_READ */ ); put_dword( spec->nb_names ); /* NumberOfNames */
put_dword( exp_rva ); /* AddressOfFunctions */
if (spec->type == SPEC_WIN16) if (spec->nb_names)
{ {
add_export( current_rva(), "__wine_spec_dos_header" ); put_dword( exp_rva + 4 * exp_count ); /* AddressOfNames */
put_dword( exp_rva + 4 * exp_count + 4 * spec->nb_names ); /* AddressOfNameOrdinals */
/* .rodata section */ }
output_fake_module16( spec ); else
if (spec->main_module) {
{ put_dword( 0 ); /* AddressOfNames */
add_export( current_rva() + output_buffer_pos, "__wine_spec_main_module" ); put_dword( 0 ); /* AddressOfNameOrdinals */
put_data( spec->main_module, strlen(spec->main_module) + 1 );
}
flush_output_to_section( ".rodata", -1, 0x40000040 /* CNT_INITIALIZED_DATA|MEM_READ */ );
} }
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 */ ORDDEF *odp = spec->ordinals[i];
unsigned int exp_rva = current_rva() + 40; /* sizeof(IMAGE_EXPORT_DIRECTORY) */ if (odp && (odp->flags & FLAG_FORWARD))
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 ); put_dword( pos );
pos += strlen( pe.exp[i].name ) + 1; pos += strlen(odp->link_name) + 1;
} }
/* ordinals */ else put_dword( 0 );
for (i = 0; i < pe.exp_count; i++) put_word( i ); }
/* strings */
put_data( spec->file_name, strlen(spec->file_name) + 1 ); /* names */
for (i = 0; i < pe.exp_count; i++) put_data( pe.exp[i].name, strlen(pe.exp[i].name) + 1 ); for (i = 0, pos = str_rva + strlen(spec->file_name) + 1; i < spec->nb_names; i++)
flush_output_to_section( ".edata", 0 /* IMAGE_DIRECTORY_ENTRY_EXPORT */, {
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 */ ); 0x40000040 /* CNT_INITIALIZED_DATA|MEM_READ */ );
} }
/* .reloc section */ /* .reloc section */
put_dword( 0 ); /* VirtualAddress */ if (pe.code_size)
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)
{ {
/* .rsrc section */ put_dword( 0 ); /* VirtualAddress */
output_bin_resources( spec, current_rva() ); put_dword( 0 ); /* Size */
flush_output_to_section( ".rsrc", 2 /* IMAGE_DIRECTORY_ENTRY_RESOURCE */, flush_output_to_section( ".reloc", 5 /* IMAGE_DIRECTORY_ENTRY_BASERELOC */,
0x40000040 /* CNT_INITIALIZED_DATA|MEM_READ */ ); 0x42000040 /* CNT_INITIALIZED_DATA|MEM_DISCARDABLE|MEM_READ */ );
} }
put_word( 0x5a4d ); /* e_magic */ put_word( 0x5a4d ); /* e_magic */
@ -961,7 +965,7 @@ void output_fake_module( DLLSPEC *spec )
put_dword( 0 ); put_dword( 0 );
put_dword( lfanew ); put_dword( lfanew );
put_data( fakedll_signature, sizeof(fakedll_signature) ); put_data( signature, 32 );
put_dword( 0x4550 ); /* Signature */ put_dword( 0x4550 ); /* Signature */
switch (target.cpu) switch (target.cpu)
@ -984,11 +988,11 @@ void output_fake_module( DLLSPEC *spec )
IMAGE_NT_OPTIONAL_HDR32_MAGIC ); /* Magic */ IMAGE_NT_OPTIONAL_HDR32_MAGIC ); /* Magic */
put_byte( 7 ); /* MajorLinkerVersion */ put_byte( 7 ); /* MajorLinkerVersion */
put_byte( 10 ); /* MinorLinkerVersion */ put_byte( 10 ); /* MinorLinkerVersion */
put_dword( pe.sec[0].size ); /* SizeOfCode */ put_dword( pe.code_size ); /* SizeOfCode */
put_dword( 0 ); /* SizeOfInitializedData */ put_dword( 0 ); /* SizeOfInitializedData */
put_dword( 0 ); /* SizeOfUninitializedData */ put_dword( 0 ); /* SizeOfUninitializedData */
put_dword( pe.sec[0].rva ); /* AddressOfEntryPoint */ put_dword( pe.code_size ? pe.sec[0].rva : 0 ); /* AddressOfEntryPoint */
put_dword( pe.sec[0].rva ); /* BaseOfCode */ put_dword( pe.code_size ? pe.sec[0].rva : 0 ); /* BaseOfCode */
if (get_ptr_size() == 4) put_dword( 0 ); /* BaseOfData */ if (get_ptr_size() == 4) put_dword( 0 ); /* BaseOfData */
put_pword( 0x10000000 ); /* ImageBase */ put_pword( 0x10000000 ); /* ImageBase */
put_dword( pe.section_align ); /* SectionAlignment */ put_dword( pe.section_align ); /* SectionAlignment */
@ -1006,9 +1010,9 @@ void output_fake_module( DLLSPEC *spec )
put_word( spec->subsystem ); /* Subsystem */ put_word( spec->subsystem ); /* Subsystem */
put_word( spec->dll_characteristics ); /* DllCharacteristics */ put_word( spec->dll_characteristics ); /* DllCharacteristics */
put_pword( (spec->stack_size ? spec->stack_size : 1024) * 1024 ); /* SizeOfStackReserve */ 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( (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( 0 ); /* LoaderFlags */
put_dword( 16 ); /* NumberOfRvaAndSizes */ 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 ); 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 * output_def_file

View File

@ -644,7 +644,7 @@ DLLSPEC *alloc_dll_spec(void)
spec->type = SPEC_WIN32; spec->type = SPEC_WIN32;
spec->base = MAX_ORDINALS; spec->base = MAX_ORDINALS;
spec->characteristics = IMAGE_FILE_EXECUTABLE_IMAGE; spec->characteristics = IMAGE_FILE_EXECUTABLE_IMAGE;
spec->subsystem = 0; spec->subsystem = IMAGE_SUBSYSTEM_WINDOWS_CUI;
spec->subsystem_major = 4; spec->subsystem_major = 4;
spec->subsystem_minor = 0; spec->subsystem_minor = 0;
spec->syscall_table = 0; spec->syscall_table = 0;

View File

@ -89,6 +89,11 @@ like \fBas\fR, \fBnm\fR and \fBld\fR.
Specify the C compiler to use to compile assembly files; the default Specify the C compiler to use to compile assembly files; the default
is to instead use the assembler specified with \fB--as-cmd\fR. is to instead use the assembler specified with \fB--as-cmd\fR.
.TP .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 .BI \-d,\ --delay-lib= name
Set the delayed import mode for the specified library, which must be 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 one of the libraries imported with the \fB-l\fR option. Delayed mode