winebuild: Add generation of system call thunks.

Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Alexandre Julliard 2020-07-07 10:42:23 +02:00
parent a0266339c7
commit b2d09cbb21
5 changed files with 183 additions and 21 deletions

View file

@ -276,6 +276,7 @@ extern void free_dll_spec( DLLSPEC *spec );
extern char *make_c_identifier( const char *str );
extern const char *get_stub_name( const ORDDEF *odp, const DLLSPEC *spec );
extern const char *get_link_name( const ORDDEF *odp );
extern int sort_func_list( ORDDEF **list, int count, int (*compare)(const void *, const void *) );
extern int get_cpu_from_name( const char *name );
extern unsigned int get_alignment(unsigned int align);
extern unsigned int get_page_size(void);
@ -303,6 +304,7 @@ extern int has_imports(void);
extern void output_get_pc_thunk(void);
extern void output_module( DLLSPEC *spec );
extern void output_stubs( DLLSPEC *spec );
extern void output_syscalls( DLLSPEC *spec );
extern void output_imports( DLLSPEC *spec );
extern void output_static_lib( DLLSPEC *spec, char **argv );
extern void output_exports( DLLSPEC *spec );

View file

@ -563,6 +563,7 @@ static void check_undefined_exports( DLLSPEC *spec )
spec->src_name, odp->lineno, odp->link_name );
break;
default:
if (!strcmp( odp->link_name, "__wine_syscall_dispatcher" )) break;
error( "%s:%d: external symbol '%s' is not a function\n",
spec->src_name, odp->lineno, odp->link_name );
break;
@ -1402,6 +1403,169 @@ void output_stubs( DLLSPEC *spec )
}
}
static int cmp_link_name( const void *e1, const void *e2 )
{
const ORDDEF *odp1 = *(const ORDDEF * const *)e1;
const ORDDEF *odp2 = *(const ORDDEF * const *)e2;
return strcmp( odp1->link_name, odp2->link_name );
}
/* output the functions for system calls */
void output_syscalls( DLLSPEC *spec )
{
const unsigned int invalid_param = 0xc000000d; /* STATUS_INVALID_PARAMETER */
int i, count;
ORDDEF **syscalls = NULL;
for (i = count = 0; i < spec->nb_entry_points; i++)
{
ORDDEF *odp = &spec->entry_points[i];
if (!(odp->flags & FLAG_SYSCALL)) continue;
if (!syscalls) syscalls = xmalloc( (spec->nb_entry_points - i) * sizeof(*syscalls) );
syscalls[count++] = odp;
}
if (!count) return;
count = sort_func_list( syscalls, count, cmp_link_name );
output( "\n/* system calls */\n\n" );
output( "\t.text\n" );
if (unix_lib)
{
output( "\t.align %d\n", get_alignment(4) );
output( "\t%s\n", func_declaration("__wine_syscall_dispatcher") );
output( "%s\n", asm_globl("__wine_syscall_dispatcher") );
output_cfi( ".cfi_startproc" );
switch (target_cpu)
{
case CPU_x86:
output( "\tcmpl $%u,%%eax\n", count );
output( "\tjae 1f\n" );
output( "\taddl $4,%%esp\n" );
if (UsePIC)
{
output( "\tmovl %%eax,%%edx\n" );
output( "\tcall %s\n", asm_name("__wine_spec_get_pc_thunk_eax") );
output( "1:\tjmp *.Lsyscall_table-1b(%%eax,%%edx,4)\n" );
needs_get_pc_thunk = 1;
}
else output( "\tjmp *.Lsyscall_table(,%%eax,4)\n" );
output( "1:\tmovl $0x%x,%%eax\n", invalid_param );
output( "\tret\n" );
break;
case CPU_x86_64:
output( "\tcmpq $%u,%%rax\n", count );
output( "\tjae 1f\n" );
output( "\tleaq .Lsyscall_table(%%rip),%%r10\n" );
output( "\tjmpq *(%%r10,%%rax,8)\n" );
output( "1:\tmovl $0x%x,%%eax\n", invalid_param );
output( "\tret\n" );
break;
case CPU_ARM:
output( "\tldr r1, 4f\n" );
output( "\tcmp r0, r1\n" );
output( "\tbcs 2f\n" );
output( "\tldr r1, 3f\n");
output( "\tadd r1, pc\n");
output( "\tldr ip, [r1, r0, lsl #2]\n");
output( "1:\tpop {r0-r1}\n" );
output( "\tbx ip\n");
output( "2:\tpop {r0-r1}\n" );
output( "\tldr r0,5f\n" );
output( "bx lr\n" );
output( "3:\t.long .Lsyscall_table-1b\n" );
output( "4:\t.long %u\n", count );
output( "5:\t.long 0x%x\n", invalid_param );
break;
case CPU_ARM64:
output( "\tcmp x8, %u\n", count );
output( "\tbcs 1f\n" );
output( "\tadrp x16, .Lsyscall_table\n" );
output( "\tadd x16, x16, #:lo12:.Lsyscall_table\n" );
output( "\tldr x16, [x16, x8, lsl 3]\n" );
output( "\tbr x16\n" );
output( "1:\tmov x0, #0x%x\n", invalid_param & 0xffff0000 );
output( "\tmovk x0, #0x%x\n", invalid_param & 0x0000ffff );
output( "\tret\n" );
break;
default:
assert(0);
}
output_cfi( ".cfi_endproc" );
output_function_size( "__wine_syscall_dispatcher" );
output( "\t.data\n" );
output( "\t.align %d\n", get_alignment( get_ptr_size() ) );
output( ".Lsyscall_table:\n" );
for (i = 0; i < count; i++)
output( "\t%s %s\n", get_asm_ptr_keyword(), asm_name( get_link_name( syscalls[i] )));
return;
}
for (i = 0; i < count; i++)
{
ORDDEF *odp = syscalls[i];
const char *name = get_link_name(odp);
output( "\t.align %d\n", get_alignment(16) );
output( "\t%s\n", func_declaration(name) );
output( "%s\n", asm_globl(name) );
output_cfi( ".cfi_startproc" );
switch (target_cpu)
{
case CPU_x86:
/* FIXME: syscall thunks not binary-compatible yet */
if (UsePIC)
{
output( "\tcall %s\n", asm_name("__wine_spec_get_pc_thunk_eax") );
output( "1:\tmovl %s-1b(%%eax),%%edx\n", asm_name("__wine_syscall_dispatcher") );
output( "\tmovl $%u,%%eax\n", i );
output( "\tcall *%%edx\n" );
needs_get_pc_thunk = 1;
}
else
{
output( "\tmovl $%u,%%eax\n", i );
output( "\tcall *(%s)\n", asm_name("__wine_syscall_dispatcher") );
}
output( "\tret $%u\n", get_args_size( odp ));
break;
case CPU_x86_64:
/* FIXME: syscall thunks not binary-compatible yet */
output( "\tmovl $%u,%%eax\n", i );
output( "\tjmpq *%s(%%rip)\n", asm_name("__wine_syscall_dispatcher") );
break;
case CPU_ARM:
output( "\tpush {r0-r1}\n" );
output( "\tldr r0, 3f\n");
output( "\tldr r1, 2f\n");
output( "\tadd r1, pc\n");
output( "\tldr ip, [r1]\n");
output( "1:\tbx ip\n");
output( "2:\t.long %s-1b\n", asm_name("__wine_syscall_dispatcher") );
output( "3:\t.long %u\n", i );
break;
case CPU_ARM64:
output( "\tmov x8, #%u\n", i );
output( "\tadrp x16, %s\n", asm_name("__wine_syscall_dispatcher") );
output( "\tldr x16, [x16, #:lo12:%s]\n", asm_name("__wine_syscall_dispatcher") );
output( "\tbr x16\n");
break;
default:
assert(0);
}
output_cfi( ".cfi_endproc" );
output_function_size( name );
}
output( "\t.data\n" );
output( "\t.align %d\n", get_alignment( get_ptr_size() ) );
output( "%s\n", asm_globl("__wine_syscall_dispatcher") );
output( "\t%s 0\n", get_asm_ptr_keyword() );
}
/* output the import and delayed import tables of a Win32 module */
void output_imports( DLLSPEC *spec )
{

View file

@ -495,27 +495,6 @@ static int relay_type_compare( const void *e1, const void *e2 )
}
/*******************************************************************
* sort_func_list
*
* Sort a list of functions, removing duplicates.
*/
static int sort_func_list( ORDDEF **list, int count,
int (*compare)(const void *, const void *) )
{
int i, j;
if (!count) return 0;
qsort( list, count, sizeof(*list), compare );
for (i = j = 0; i < count; i++)
{
if (compare( &list[j], &list[i] )) list[++j] = list[i];
}
return j + 1;
}
/*******************************************************************
* output_module16
*

View file

@ -739,6 +739,7 @@ void output_spec32_file( DLLSPEC *spec )
output_stubs( spec );
output_exports( spec );
output_imports( spec );
output_syscalls( spec );
if (needs_get_pc_thunk) output_get_pc_thunk();
output_resources( spec );
output_gnu_stack_note();

View file

@ -960,6 +960,22 @@ const char *get_link_name( const ORDDEF *odp )
return ret;
}
/*******************************************************************
* sort_func_list
*
* Sort a list of functions, removing duplicates.
*/
int sort_func_list( ORDDEF **list, int count, int (*compare)(const void *, const void *) )
{
int i, j;
if (!count) return 0;
qsort( list, count, sizeof(*list), compare );
for (i = j = 0; i < count; i++) if (compare( &list[j], &list[i] )) list[++j] = list[i];
return j + 1;
}
/* parse a cpu name and return the corresponding value */
int get_cpu_from_name( const char *name )
{