winebuild: Add support for building apiset data.

Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Alexandre Julliard 2022-02-18 10:13:58 +01:00
parent 6f1fd16f92
commit 8a3064899a
4 changed files with 296 additions and 5 deletions

View File

@ -105,6 +105,34 @@ typedef struct
} u; } u;
} ORDDEF; } 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 typedef struct
{ {
char *src_name; /* file name of the source spec file */ 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 **names; /* array of entry point names (points into entry_points) */
ORDDEF **ordinals; /* array of dll ordinals (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 resource *resources; /* array of dll resources (format differs between Win16/Win32) */
struct apiset apiset; /* list of defined api sets */
} DLLSPEC; } DLLSPEC;
extern char *target_alias; 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_fake_module( DLLSPEC *spec );
extern void output_data_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 output_apiset_lib( DLLSPEC *spec, const struct apiset *apiset );
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 );
extern void output_bin_res16_data( DLLSPEC *spec ); extern void output_bin_res16_data( DLLSPEC *spec );

View File

@ -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. * 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; unsigned int i, cpu_mask = 0;
const char *token;
do do
{ {
if (!(token = GetToken(0))) break; token++;
if (!strncmp( token, "arch=", 5)) if (!strncmp( token, "arch=", 5))
{ {
char *args = xstrdup( token + 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 = 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)) 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 ) static int name_compare( const void *ptr1, const void *ptr2 )
{ {
const ORDDEF *odp1 = *(const ORDDEF * const *)ptr1; const ORDDEF *odp1 = *(const ORDDEF * const *)ptr1;
@ -898,7 +1023,7 @@ int parse_spec_file( FILE *file, DLLSPEC *spec )
current_line = 0; current_line = 0;
comment_chars = "#;"; comment_chars = "#;";
separator_chars = "()-"; separator_chars = "()";
while (get_next_line()) while (get_next_line())
{ {
@ -911,6 +1036,10 @@ int parse_spec_file( FILE *file, DLLSPEC *spec )
{ {
if (!parse_spec_ordinal( atoi(token), spec )) continue; if (!parse_spec_ordinal( atoi(token), spec )) continue;
} }
else if (strcmp(token, "apiset") == 0)
{
if (!parse_spec_apiset( spec )) continue;
}
else else
{ {
error( "Expected ordinal declaration, got '%s'\n", token ); error( "Expected ordinal declaration, got '%s'\n", token );

View File

@ -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] ) static void output_pe_file( DLLSPEC *spec, const char signature[32] )
{ {
const unsigned int lfanew = 0x40 + 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(); pe.section_align = pe.file_align = get_page_size();
output_pe_exports( spec ); output_pe_exports( spec );
if (spec->apiset.count) output_apiset_section( &spec->apiset );
output_pe_file( spec, builtin_signature ); output_pe_file( spec, builtin_signature );
} }

View File

@ -539,6 +539,25 @@ This declaration defines an ordinal as an absolute value.
will be the name available for dynamic linking. will be the name available for dynamic linking.
.I data .I data
can be a decimal number or a hex number preceded by "0x". 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 .SH AUTHORS
.B winebuild .B winebuild
has been worked on by many people over the years. The main authors are has been worked on by many people over the years. The main authors are