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

View File

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

View File

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

View File

@ -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

View File

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

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