From 8a3064899a94b801ac765d592f338a66d1356058 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Fri, 18 Feb 2022 10:13:58 +0100 Subject: [PATCH] winebuild: Add support for building apiset data. Signed-off-by: Alexandre Julliard --- tools/winebuild/build.h | 30 +++++++ tools/winebuild/parser.c | 139 +++++++++++++++++++++++++++++-- tools/winebuild/spec32.c | 113 +++++++++++++++++++++++++ tools/winebuild/winebuild.man.in | 19 +++++ 4 files changed, 296 insertions(+), 5 deletions(-) diff --git a/tools/winebuild/build.h b/tools/winebuild/build.h index aa6f31342c7..9918c2341f4 100644 --- a/tools/winebuild/build.h +++ b/tools/winebuild/build.h @@ -105,6 +105,34 @@ typedef struct } u; } ORDDEF; +struct apiset_entry +{ + unsigned int name_off; + unsigned int name_len; + unsigned int hash; + unsigned int hash_len; + unsigned int val_count; + struct apiset_value + { + unsigned int name_off; + unsigned int name_len; + unsigned int val_off; + unsigned int val_len; + } values[4]; +}; + +struct apiset +{ + unsigned int count; + unsigned int size; + struct apiset_entry *entries; + unsigned int str_pos; + unsigned int str_size; + char *strings; +}; + +static const unsigned int apiset_hash_factor = 31; + typedef struct { char *src_name; /* file name of the source spec file */ @@ -133,6 +161,7 @@ typedef struct ORDDEF **names; /* array of entry point names (points into entry_points) */ ORDDEF **ordinals; /* array of dll ordinals (points into entry_points) */ struct resource *resources; /* array of dll resources (format differs between Win16/Win32) */ + struct apiset apiset; /* list of defined api sets */ } DLLSPEC; extern char *target_alias; @@ -285,6 +314,7 @@ 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 output_apiset_lib( DLLSPEC *spec, const struct apiset *apiset ); extern void load_res16_file( const char *name, DLLSPEC *spec ); extern void output_res16_data( DLLSPEC *spec ); extern void output_bin_res16_data( DLLSPEC *spec ); diff --git a/tools/winebuild/parser.c b/tools/winebuild/parser.c index a9b6bf2e5a6..11b422bd435 100644 --- a/tools/winebuild/parser.c +++ b/tools/winebuild/parser.c @@ -471,14 +471,13 @@ static int parse_spec_extern( ORDDEF *odp, DLLSPEC *spec ) * * Parse the optional flags for an entry point in a .spec file. */ -static const char *parse_spec_flags( DLLSPEC *spec, ORDDEF *odp ) +static const char *parse_spec_flags( DLLSPEC *spec, ORDDEF *odp, const char *token ) { unsigned int i, cpu_mask = 0; - const char *token; do { - if (!(token = GetToken(0))) break; + token++; if (!strncmp( token, "arch=", 5)) { char *args = xstrdup( token + 5 ); @@ -578,7 +577,7 @@ static int parse_spec_ordinal( int ordinal, DLLSPEC *spec ) } if (!(token = GetToken(0))) goto error; - if (*token == '-' && !(token = parse_spec_flags( spec, odp ))) goto error; + if (*token == '-' && !(token = parse_spec_flags( spec, odp, token ))) goto error; if (ordinal == -1 && spec->type != SPEC_WIN32 && !(odp->flags & FLAG_EXPORT32)) { @@ -703,6 +702,132 @@ error: } +static unsigned int apiset_hash_len( const char *str ) +{ + return strrchr( str, '-' ) - str; +} + +static unsigned int apiset_hash( const char *str ) +{ + unsigned int ret = 0, len = apiset_hash_len( str ); + while (len--) ret = ret * apiset_hash_factor + *str++; + return ret; +} + +static unsigned int apiset_add_str( struct apiset *apiset, const char *str, unsigned int len ) +{ + char *ret; + + if (!apiset->strings || !(ret = strstr( apiset->strings, str ))) + { + if (apiset->str_pos + len >= apiset->str_size) + { + apiset->str_size = max( apiset->str_size * 2, 1024 ); + apiset->strings = xrealloc( apiset->strings, apiset->str_size ); + } + ret = apiset->strings + apiset->str_pos; + memcpy( ret, str, len ); + ret[len] = 0; + apiset->str_pos += len; + } + return ret - apiset->strings; +} + +static void add_apiset( struct apiset *apiset, const char *api ) +{ + struct apiset_entry *entry; + + if (apiset->count == apiset->size) + { + apiset->size = max( apiset->size * 2, 64 ); + apiset->entries = xrealloc( apiset->entries, apiset->size * sizeof(*apiset->entries) ); + } + entry = &apiset->entries[apiset->count++]; + entry->name_len = strlen( api ); + entry->name_off = apiset_add_str( apiset, api, entry->name_len ); + entry->hash = apiset_hash( api ); + entry->hash_len = apiset_hash_len( api ); + entry->val_count = 0; +} + +static void add_apiset_value( struct apiset *apiset, const char *value ) +{ + struct apiset_entry *entry = &apiset->entries[apiset->count - 1]; + + if (entry->val_count < ARRAY_SIZE(entry->values) - 1) + { + struct apiset_value *val = &entry->values[entry->val_count++]; + char *sep = strchr( value, ':' ); + + if (sep) + { + val->name_len = sep - value; + val->name_off = apiset_add_str( apiset, value, val->name_len ); + val->val_len = strlen( sep + 1 ); + val->val_off = apiset_add_str( apiset, sep + 1, val->val_len ); + } + else + { + val->name_len = val->name_off = 0; + val->val_len = strlen( value ); + val->val_off = apiset_add_str( apiset, value, val->val_len ); + } + } + else error( "Too many values for api '%.*s'\n", entry->name_len, apiset->strings + entry->name_off ); +} + +/******************************************************************* + * parse_spec_apiset + */ +static int parse_spec_apiset( DLLSPEC *spec ) +{ + struct apiset_entry *entry; + const char *token; + unsigned int i, hash; + + if (!data_only) + { + error( "Apiset definitions are only allowed in data-only mode\n" ); + return 0; + } + + if (!(token = GetToken(0))) return 0; + + if (!strncmp( token, "api-", 4 ) && !strncmp( token, "ext-", 4 )) + { + error( "Unrecognized API set name '%s'\n", token ); + return 0; + } + + hash = apiset_hash( token ); + for (i = 0, entry = spec->apiset.entries; i < spec->apiset.count; i++, entry++) + { + if (entry->name_len == strlen( token ) && + !strncmp( spec->apiset.strings + entry->name_off, token, entry->name_len )) + { + error( "Duplicate API set '%s'\n", token ); + return 0; + } + if (entry->hash == hash) + { + error( "Duplicate hash code '%.*s' and '%s'\n", + entry->name_len, spec->apiset.strings + entry->name_off, token ); + return 0; + } + } + add_apiset( &spec->apiset, token ); + + if (!(token = GetToken(0)) || strcmp( token, "=" )) + { + error( "Syntax error near '%s'\n", token ); + return 0; + } + + while ((token = GetToken(1))) add_apiset_value( &spec->apiset, token ); + return 1; +} + + static int name_compare( const void *ptr1, const void *ptr2 ) { const ORDDEF *odp1 = *(const ORDDEF * const *)ptr1; @@ -898,7 +1023,7 @@ int parse_spec_file( FILE *file, DLLSPEC *spec ) current_line = 0; comment_chars = "#;"; - separator_chars = "()-"; + separator_chars = "()"; while (get_next_line()) { @@ -911,6 +1036,10 @@ int parse_spec_file( FILE *file, DLLSPEC *spec ) { if (!parse_spec_ordinal( atoi(token), spec )) continue; } + else if (strcmp(token, "apiset") == 0) + { + if (!parse_spec_apiset( spec )) continue; + } else { error( "Expected ordinal declaration, got '%s'\n", token ); diff --git a/tools/winebuild/spec32.c b/tools/winebuild/spec32.c index 6475b74e37d..ca3c861e60f 100644 --- a/tools/winebuild/spec32.c +++ b/tools/winebuild/spec32.c @@ -916,6 +916,118 @@ static void output_pe_exports( DLLSPEC *spec ) } +/* apiset hash table */ +struct apiset_hash_entry +{ + unsigned int hash; + unsigned int index; +}; + +static int apiset_hash_cmp( const void *h1, const void *h2 ) +{ + const struct apiset_hash_entry *entry1 = h1; + const struct apiset_hash_entry *entry2 = h2; + + if (entry1->hash > entry2->hash) return 1; + if (entry1->hash < entry2->hash) return -1; + return 0; +} + +static void output_apiset_section( const struct apiset *apiset ) +{ + struct apiset_hash_entry *hash; + struct apiset_entry *e; + unsigned int i, j, str_pos, value_pos, hash_pos, size; + + init_output_buffer(); + + value_pos = 0x1c /* header */ + apiset->count * 0x18; /* names */ + str_pos = value_pos; + for (i = 0, e = apiset->entries; i < apiset->count; i++, e++) + str_pos += 0x14 * max( 1, e->val_count ); /* values */ + + hash_pos = str_pos + ((apiset->str_pos * 2 + 3) & ~3); + size = hash_pos + apiset->count * 8; /* hashes */ + + /* header */ + + put_dword( 6 ); /* Version */ + put_dword( size ); /* Size */ + put_dword( 0 ); /* Flags */ + put_dword( apiset->count ); /* Count */ + put_dword( 0x1c ); /* EntryOffset */ + put_dword( hash_pos ); /* HashOffset */ + put_dword( apiset_hash_factor ); /* HashFactor */ + + /* name entries */ + + value_pos = 0x1c /* header */ + apiset->count * 0x18; /* names */ + for (i = 0, e = apiset->entries; i < apiset->count; i++, e++) + { + put_dword( 1 ); /* Flags */ + put_dword( str_pos + e->name_off * 2 ); /* NameOffset */ + put_dword( e->name_len * 2 ); /* NameLength */ + put_dword( e->hash_len * 2 ); /* HashedLength */ + put_dword( value_pos ); /* ValueOffset */ + put_dword( max( 1, e->val_count )); /* ValueCount */ + value_pos += 0x14 * max( 1, e->val_count ); + } + + /* values */ + + for (i = 0, e = apiset->entries; i < apiset->count; i++, e++) + { + if (!e->val_count) + { + put_dword( 0 ); /* Flags */ + put_dword( 0 ); /* NameOffset */ + put_dword( 0 ); /* NameLength */ + put_dword( 0 ); /* ValueOffset */ + put_dword( 0 ); /* ValueLength */ + } + else for (j = 0; j < e->val_count; j++) + { + put_dword( 0 ); /* Flags */ + if (e->values[j].name_off) + { + put_dword( str_pos + e->values[j].name_off * 2 ); /* NameOffset */ + put_dword( e->values[j].name_len * 2 ); /* NameLength */ + } + else + { + put_dword( 0 ); /* NameOffset */ + put_dword( 0 ); /* NameLength */ + } + put_dword( str_pos + e->values[j].val_off * 2 ); /* ValueOffset */ + put_dword( e->values[j].val_len * 2 ); /* ValueLength */ + } + } + + /* strings */ + + for (i = 0; i < apiset->str_pos; i++) put_word( apiset->strings[i] ); + align_output( 4 ); + + /* hash table */ + + hash = xmalloc( apiset->count * sizeof(*hash) ); + for (i = 0, e = apiset->entries; i < apiset->count; i++, e++) + { + hash[i].hash = e->hash; + hash[i].index = i; + } + qsort( hash, apiset->count, sizeof(*hash), apiset_hash_cmp ); + for (i = 0; i < apiset->count; i++) + { + put_dword( hash[i].hash ); + put_dword( hash[i].index ); + } + free( hash ); + + flush_output_to_section( ".apiset", -1, 0x40000040 /* CNT_INITIALIZED_DATA|MEM_READ */ ); +} + + static void output_pe_file( DLLSPEC *spec, const char signature[32] ) { const unsigned int lfanew = 0x40 + 32; @@ -1136,6 +1248,7 @@ void output_data_module( DLLSPEC *spec ) pe.section_align = pe.file_align = get_page_size(); output_pe_exports( spec ); + if (spec->apiset.count) output_apiset_section( &spec->apiset ); output_pe_file( spec, builtin_signature ); } diff --git a/tools/winebuild/winebuild.man.in b/tools/winebuild/winebuild.man.in index 1e2c0bbde43..2637b5beb5f 100644 --- a/tools/winebuild/winebuild.man.in +++ b/tools/winebuild/winebuild.man.in @@ -539,6 +539,25 @@ This declaration defines an ordinal as an absolute value. will be the name available for dynamic linking. .I data can be a decimal number or a hex number preceded by "0x". +.SS "Api sets" +Syntax: +.br +.BI apiset\ apiset_dll\ =\ target.dll\ [host.dll:target.dll] +.PP +This declaration defines that the \fIapiset_dll\fR (of the form +api-ms-*) resolves to the \fItarget\fR dll. Optionally other targets +can be specified to resolve differently for specific host dlls. For +example: +.IP +api-ms-win-core-processenvironment-l1-1-0 = kernelbase.dll +.br +api-ms-win-core-processthreads-l1-1-0 = kernel32.dll \\ +.br + kernel32.dll:kernelbase.dll +.PP +If apisets are defined, a corresponding .apiset section will be +generated in the PE binary. This requires building the module with the +--data-only option. .SH AUTHORS .B winebuild has been worked on by many people over the years. The main authors are