From b34e0ac3eee5865a66316f28316b4134c615b078 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Michael=20M=C3=BCller?= <michael@fds-team.de>
Date: Thu, 11 May 2017 05:32:55 +0200
Subject: [PATCH] winebuild: Generate syscall thunks for ntdll exports.

Based on a patch by Erich E. Hoover.
---
 dlls/ntdll/signal_i386.c     |   6 +-
 dlls/ntdll/tests/exception.c |   2 +
 include/winternl.h           |   2 +-
 tools/winebuild/build.h      |   8 ++-
 tools/winebuild/import.c     |  10 ++--
 tools/winebuild/parser.c     |  59 ++++++++++++++++++++
 tools/winebuild/spec16.c     |  22 +-------
 tools/winebuild/spec32.c     | 104 +++++++++++++++++++++++++++++++++++
 tools/winebuild/utils.c      |  21 +++++++
 9 files changed, 206 insertions(+), 28 deletions(-)

diff --git a/dlls/ntdll/signal_i386.c b/dlls/ntdll/signal_i386.c
index 7c6e1b50c..a62fbd4bf 100644
--- a/dlls/ntdll/signal_i386.c
+++ b/dlls/ntdll/signal_i386.c
@@ -427,6 +427,9 @@ static size_t signal_stack_size;
 
 static wine_signal_handler handlers[256];
 
+extern void DECLSPEC_NORETURN __wine_syscall_dispatcher( void );
+extern NTSTATUS WINAPI __syscall_NtGetContextThread( HANDLE handle, CONTEXT *context );
+
 enum i386_trap_code
 {
     TRAP_x86_UNKNOWN    = -1,  /* Unknown fault (TRAP_sig not defined) */
@@ -1448,7 +1451,7 @@ NTSTATUS CDECL DECLSPEC_HIDDEN __regs_NtGetContextThread( DWORD edi, DWORD esi,
         {
             context->Ebp    = ebp;
             context->Esp    = (DWORD)&retaddr;
-            context->Eip    = *(&edi - 1);
+            context->Eip    = (DWORD)__syscall_NtGetContextThread + 18;
             context->SegCs  = wine_get_cs();
             context->SegSs  = wine_get_ss();
             context->EFlags = eflags;
@@ -2335,6 +2338,7 @@ NTSTATUS signal_alloc_thread( TEB **teb )
         *teb = addr;
         (*teb)->Tib.Self = &(*teb)->Tib;
         (*teb)->Tib.ExceptionList = (void *)~0UL;
+        (*teb)->WOW32Reserved = __wine_syscall_dispatcher;
         thread_data = (struct x86_thread_data *)(*teb)->SystemReserved2;
         if (!(thread_data->fs = wine_ldt_alloc_fs()))
         {
diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c
index ada28c91e..3ed8fa082 100644
--- a/dlls/ntdll/tests/exception.c
+++ b/dlls/ntdll/tests/exception.c
@@ -1651,6 +1651,8 @@ static void test_thread_context(void)
     ok( (char *)context.Eip >= (char *)pNtGetContextThread - 0x10000 &&
         (char *)context.Eip <= (char *)pNtGetContextThread + 0x10000,
         "wrong Eip %08x/%08x\n", context.Eip, (DWORD)pNtGetContextThread );
+    ok( *(WORD *)context.Eip == 0xc483 || *(WORD *)context.Eip == 0x08c2 || *(WORD *)context.Eip == 0x8dc3,
+        "expected 0xc483 or 0x08c2 or 0x8dc3, got %04x\n", *(WORD *)context.Eip );
     /* segment registers clear the high word */
     ok( context.SegCs == LOWORD(expect.SegCs), "wrong SegCs %08x/%08x\n", context.SegCs, expect.SegCs );
     ok( context.SegDs == LOWORD(expect.SegDs), "wrong SegDs %08x/%08x\n", context.SegDs, expect.SegDs );
diff --git a/include/winternl.h b/include/winternl.h
index 0e8cf30d7..4ec65ca63 100644
--- a/include/winternl.h
+++ b/include/winternl.h
@@ -357,7 +357,7 @@ typedef struct _TEB
     PVOID                        CsrClientThread;                   /* 03c/0070 */
     PVOID                        Win32ThreadInfo;                   /* 040/0078 */
     ULONG                        Win32ClientInfo[31];               /* 044/0080 used for user32 private data in Wine */
-    PVOID                        WOW32Reserved;                     /* 0c0/0100 */
+    PVOID                        WOW32Reserved;                     /* 0c0/0100 used for ntdll syscall thunks */
     ULONG                        CurrentLocale;                     /* 0c4/0108 */
     ULONG                        FpSoftwareStatusRegister;          /* 0c8/010c */
     PVOID                        SystemReserved1[54];               /* 0cc/0110 used for krnl386.exe16 private data in Wine */
diff --git a/tools/winebuild/build.h b/tools/winebuild/build.h
index 49473c768..0b7cb370b 100644
--- a/tools/winebuild/build.h
+++ b/tools/winebuild/build.h
@@ -103,6 +103,7 @@ typedef struct
     int         flags;
     char       *name;         /* public name of this function */
     char       *link_name;    /* name of the C symbol to link to */
+    char       *impl_name;    /* name of the C symbol of the real implementation (thunks only) */
     char       *export_name;  /* name exported under for noname exports */
     union
     {
@@ -129,6 +130,7 @@ typedef struct
     int              alloc_entry_points; /* number of allocated entry points */
     int              nb_names;           /* number of entry points with names */
     unsigned int     nb_resources;       /* number of resources */
+    int              nb_syscalls;        /* number of syscalls */
     int              characteristics;    /* characteristics for the PE header */
     int              dll_characteristics;/* DLL characteristics for the PE header */
     int              subsystem;          /* subsystem id */
@@ -138,6 +140,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) */
+    ORDDEF         **syscalls;           /* array of syscalls (points into entry_points) */
 } DLLSPEC;
 
 enum target_cpu
@@ -179,8 +182,9 @@ struct strarray
 #define FLAG_FORWARD   0x200  /* function is a forwarded name */
 #define FLAG_EXT_LINK  0x400  /* function links to an external symbol */
 #define FLAG_EXPORT32  0x800  /* 32-bit export in 16-bit spec file */
+#define FLAG_SYSCALL   0x1000  /* function should be called through a syscall thunk */
 
-#define FLAG_CPU(cpu)  (0x01000 << (cpu))
+#define FLAG_CPU(cpu)  (0x02000 << (cpu))
 #define FLAG_CPU_MASK  (FLAG_CPU(CPU_LAST + 1) - FLAG_CPU(0))
 #define FLAG_CPU_WIN64 (FLAG_CPU(CPU_x86_64) | FLAG_CPU(CPU_ARM64))
 #define FLAG_CPU_WIN32 (FLAG_CPU_MASK & ~FLAG_CPU_WIN64)
@@ -323,6 +327,8 @@ extern void add_16bit_exports( DLLSPEC *spec32, DLLSPEC *spec16 );
 extern int parse_spec_file( FILE *file, DLLSPEC *spec );
 extern int parse_def_file( FILE *file, DLLSPEC *spec );
 
+extern int sort_func_list( ORDDEF **list, int count, int (*compare)(const void *, const void *) );
+
 /* buffer management */
 
 extern int byte_swapped;
diff --git a/tools/winebuild/import.c b/tools/winebuild/import.c
index d8abc9d03..9c7c605b6 100644
--- a/tools/winebuild/import.c
+++ b/tools/winebuild/import.c
@@ -454,6 +454,7 @@ static void check_undefined_forwards( DLLSPEC *spec )
 /* flag the dll exports that link to an undefined symbol */
 static void check_undefined_exports( DLLSPEC *spec )
 {
+    const char *name;
     int i;
 
     for (i = 0; i < spec->nb_entry_points; i++)
@@ -461,7 +462,8 @@ static void check_undefined_exports( DLLSPEC *spec )
         ORDDEF *odp = &spec->entry_points[i];
         if (odp->type == TYPE_STUB || odp->type == TYPE_ABS || odp->type == TYPE_VARIABLE) continue;
         if (odp->flags & FLAG_FORWARD) continue;
-        if (find_name( odp->link_name, &undef_symbols ))
+        name = odp->impl_name ? odp->impl_name : odp->link_name;
+        if (find_name( name, &undef_symbols ))
         {
             switch(odp->type)
             {
@@ -472,14 +474,14 @@ static void check_undefined_exports( DLLSPEC *spec )
                 if (link_ext_symbols)
                 {
                     odp->flags |= FLAG_EXT_LINK;
-                    strarray_add( &ext_link_imports, odp->link_name, NULL );
+                    strarray_add( &ext_link_imports, name, NULL );
                 }
                 else error( "%s:%d: function '%s' not defined\n",
-                            spec->src_name, odp->lineno, odp->link_name );
+                            spec->src_name, odp->lineno, name );
                 break;
             default:
                 error( "%s:%d: external symbol '%s' is not a function\n",
-                       spec->src_name, odp->lineno, odp->link_name );
+                       spec->src_name, odp->lineno, name );
                 break;
             }
         }
diff --git a/tools/winebuild/parser.c b/tools/winebuild/parser.c
index bec9f523e..da832de0e 100644
--- a/tools/winebuild/parser.c
+++ b/tools/winebuild/parser.c
@@ -540,6 +540,24 @@ static const char *parse_spec_flags( DLLSPEC *spec, ORDDEF *odp )
 }
 
 
+static int needs_syscall( ORDDEF *odp, DLLSPEC *spec )
+{
+    if (target_cpu != CPU_x86)
+        return 0;
+    if (odp->flags & (FLAG_FORWARD | FLAG_REGISTER))
+        return 0;
+    if (odp->type != TYPE_STDCALL)
+        return 0;
+    if (!spec->dll_name || strcmp(spec->dll_name, "ntdll"))
+        return 0;
+    if (!odp->name)
+        return 0;
+    if (strncmp(odp->name, "Nt", 2) && strncmp(odp->name, "Zw", 2))
+        return 0;
+    return 1;
+}
+
+
 /*******************************************************************
  *         parse_spec_ordinal
  *
@@ -615,6 +633,14 @@ static int parse_spec_ordinal( int ordinal, DLLSPEC *spec )
         assert( 0 );
     }
 
+    if (needs_syscall( odp, spec ))
+    {
+        char *link_name = strmake( "__syscall_%s", odp->link_name );
+        odp->impl_name = odp->link_name;
+        odp->link_name = link_name;
+        odp->flags |= FLAG_SYSCALL;
+    }
+
     if ((odp->flags & FLAG_CPU_MASK) && !(odp->flags & FLAG_CPU(target_cpu)))
     {
         /* ignore this entry point */
@@ -812,6 +838,37 @@ static void assign_ordinals( DLLSPEC *spec )
 }
 
 
+static int link_name_compare( 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);
+}
+
+
+static void assign_syscalls( DLLSPEC *spec )
+{
+    int i;
+
+    spec->syscalls = xmalloc( (spec->limit - spec->base + 1) * sizeof(*spec->syscalls) );
+    spec->nb_syscalls = 0;
+
+    for (i = 0; i <= spec->limit; i++)
+    {
+        ORDDEF *odp = spec->ordinals[i];
+        if (!odp || !(odp->flags & FLAG_SYSCALL)) continue;
+        spec->syscalls[spec->nb_syscalls++] = odp;
+    }
+
+    spec->nb_syscalls = sort_func_list( spec->syscalls, spec->nb_syscalls, link_name_compare );
+    if (!spec->nb_syscalls)
+    {
+        free( spec->syscalls );
+        spec->syscalls = NULL;
+    }
+}
+
+
 /*******************************************************************
  *         add_16bit_exports
  *
@@ -913,6 +970,8 @@ int parse_spec_file( FILE *file, DLLSPEC *spec )
     current_line = 0;  /* no longer parsing the input file */
     assign_names( spec );
     assign_ordinals( spec );
+    assign_syscalls( spec );
+
     return !nb_errors;
 }
 
diff --git a/tools/winebuild/spec16.c b/tools/winebuild/spec16.c
index b21f47277..24a8f1054 100644
--- a/tools/winebuild/spec16.c
+++ b/tools/winebuild/spec16.c
@@ -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
  *
@@ -544,6 +523,7 @@ static void output_module16( DLLSPEC *spec )
         entry_point->flags = FLAG_REGISTER;
         entry_point->name = NULL;
         entry_point->link_name = xstrdup( spec->init_func );
+        entry_point->impl_name = NULL;
         entry_point->export_name = NULL;
         entry_point->u.func.nb_args = 0;
         assert( !spec->ordinals[0] );
diff --git a/tools/winebuild/spec32.c b/tools/winebuild/spec32.c
index a0aa01754..fea45e398 100644
--- a/tools/winebuild/spec32.c
+++ b/tools/winebuild/spec32.c
@@ -371,6 +371,109 @@ static void output_relay_debug( DLLSPEC *spec )
     }
 }
 
+/*******************************************************************
+ *         output_syscall_thunks
+ *
+ * Output entry points for system call functions
+ */
+static void output_syscall_thunks( DLLSPEC *spec )
+{
+    int i;
+
+    if (!spec->nb_syscalls)
+        return;
+
+    output( "\n/* syscall thunks */\n\n" );
+    output( "\t.text\n" );
+
+    for (i = 0; i < spec->nb_syscalls; i++)
+    {
+        ORDDEF *odp = spec->syscalls[i];
+        const char *name = odp->link_name;
+
+        /* Chromium attempts to hook system call thunks. It expects them to
+         * have a very specific form, or it will fail. The below matches what
+         * Chromium expects from 64-bit Windows 8. */
+
+        output( "\t.balign 16, 0\n" );
+        output( "\t%s\n", func_declaration(name) );
+        output( "%s\n", asm_globl(name) );
+        output_cfi( ".cfi_startproc" );
+        output( "\t.byte 0xb8\n" );                               /* mov eax, SYSCALL */
+        output( "\t.long %d\n", i );
+        output( "\t.byte 0x64,0xff,0x15,0xc0,0x00,0x00,0x00\n" ); /* call dword ptr fs:[0C0h] */
+        output( "\t.byte 0xc2\n" );                               /* ret X */
+        output( "\t.short %d\n", get_args_size(odp) );
+        output_cfi( ".cfi_endproc" );
+        output_function_size( name );
+    }
+
+    for (i = 0; i < 0x20; i++)
+        output( "\t.byte 0\n" );
+
+    output( "\n/* syscall table */\n\n" );
+    output( "\t.data\n" );
+    output( "%s\n", asm_globl("__wine_syscall_table") );
+    for (i = 0; i < spec->nb_syscalls; i++)
+    {
+        ORDDEF *odp = spec->syscalls[i];
+        output ("\t%s %s\n", get_asm_ptr_keyword(), asm_name(odp->impl_name) );
+    }
+
+    output( "\n/* syscall argument stack size table */\n\n" );
+    output( "\t.data\n" );
+    output( "%s\n", asm_globl("__wine_syscall_stack_size") );
+    for (i = 0; i < spec->nb_syscalls; i++)
+    {
+        ORDDEF *odp = spec->syscalls[i];
+        output( "\t.byte %d\n", get_args_size(odp) );
+    }
+
+    output( "\n/* syscall dispatcher */\n\n" );
+    output( "\t.text\n" );
+    output( "\t.align %d\n", get_alignment(16) );
+    output( "\t%s\n", func_declaration("__wine_syscall_dispatcher") );
+    output( "%s\n", asm_globl("__wine_syscall_dispatcher") );
+    output_cfi( ".cfi_startproc" );
+    output( "\tpushl %%ebp\n" );
+    output_cfi( ".cfi_adjust_cfa_offset 4\n" );
+    output_cfi( ".cfi_rel_offset %ebp,0\n" );
+    output( "\tmovl %%esp,%%ebp\n" );
+    output_cfi( ".cfi_def_cfa_register %ebp\n" );
+    output( "\tpushl %%esi\n" );
+    output_cfi( ".cfi_rel_offset %esi,-4\n" );
+    output( "\tpushl %%edi\n" );
+    output_cfi( ".cfi_rel_offset %edi,-8\n" );
+    output( "\tleal 12(%%ebp),%%esi\n" );
+    if (UsePIC)
+    {
+        output( "\tcall 1f\n" );
+        output( "1:\tpopl %%edx\n" );
+        output( "movzbl (%s-1b)(%%edx,%%eax,1),%%ecx\n", asm_name("__wine_syscall_stack_size") );
+    }
+    else
+        output( "movzbl %s(%%eax),%%ecx\n", asm_name("__wine_syscall_stack_size") );
+
+    output( "\tsubl %%ecx,%%esp\n" );
+    output( "\tshrl $2,%%ecx\n" );
+    output( "\tmovl %%esp,%%edi\n" );
+    output( "\trep; movsl\n" );
+    if (UsePIC)
+        output( "\tcall *(%s-1b)(%%edx,%%eax,%d)\n", asm_name("__wine_syscall_table"), get_ptr_size() );
+    else
+        output( "\tcall *%s(,%%eax,%d)\n", asm_name("__wine_syscall_table"), get_ptr_size() );
+    output( "\tpop %%edi\n" );
+    output_cfi( ".cfi_same_value %edi\n" );
+    output( "\tpop %%esi\n" );
+    output_cfi( ".cfi_same_value %esi\n" );
+    output( "\tleave\n" );
+    output_cfi( ".cfi_def_cfa %esp,4\n" );
+    output_cfi( ".cfi_same_value %ebp\n" );
+    output( "\tret\n" );
+    output_cfi( ".cfi_endproc" );
+    output_function_size( "__wine_syscall_dispatcher" );
+}
+
 /*******************************************************************
  *         output_exports
  *
@@ -714,6 +817,7 @@ void output_spec32_file( DLLSPEC *spec )
     open_output_file();
     output_standard_file_header();
     output_module( spec );
+    output_syscall_thunks( spec );
     output_stubs( spec );
     output_exports( spec );
     output_imports( spec );
diff --git a/tools/winebuild/utils.c b/tools/winebuild/utils.c
index dbfe7a4ba..5796af2f6 100644
--- a/tools/winebuild/utils.c
+++ b/tools/winebuild/utils.c
@@ -838,6 +838,7 @@ void free_dll_spec( DLLSPEC *spec )
         free( odp->name );
         free( odp->export_name );
         free( odp->link_name );
+        free( odp->impl_name );
     }
     free( spec->file_name );
     free( spec->dll_name );
@@ -847,6 +848,7 @@ void free_dll_spec( DLLSPEC *spec )
     free( spec->names );
     free( spec->ordinals );
     free( spec->resources );
+    free( spec->syscalls );
     free( spec );
 }
 
@@ -1265,3 +1267,22 @@ const char *get_asm_string_section(void)
     default:             return ".section .rodata";
     }
 }
+
+/*******************************************************************
+ *         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;
+}
-- 
2.21.0

