winebuild: Add support for building apiset data.
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
6f1fd16f92
commit
8a3064899a
|
@ -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 );
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue