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

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.
*/
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 );

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] )
{
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 );
}

View File

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