From 4b84757bba03d22cb225103cc7e52805a8d223f0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Michael=20M=C3=BCller?= <michael@fds-team.de>
Date: Mon, 15 May 2017 16:27:56 +0200
Subject: winebuild: Add stub functions in fake dlls.

---
 dlls/kernel32/tests/loader.c |   8 +-
 dlls/ntdll/signal_i386.c     |  40 +++++++
 include/winternl.h           |   2 +-
 tools/winebuild/build.h      |   1 +
 tools/winebuild/spec32.c     | 209 +++++++++++++++++++++++++++++++++--
 tools/winebuild/utils.c      |  10 +-
 6 files changed, 255 insertions(+), 15 deletions(-)

diff --git a/dlls/kernel32/tests/loader.c b/dlls/kernel32/tests/loader.c
index 589e9f66..4a35a592 100644
--- a/dlls/kernel32/tests/loader.c
+++ b/dlls/kernel32/tests/loader.c
@@ -1550,9 +1550,7 @@ static void test_FakeDLL(void)
     ok(ptr != NULL, "MapViewOfFile failed with error %u\n", GetLastError());
 
     dir = RtlImageDirectoryEntryToData(ptr, TRUE, IMAGE_DIRECTORY_ENTRY_EXPORT, &size);
-todo_wine
     ok(dir != NULL, "RtlImageDirectoryEntryToData failed\n");
-    if (dir == NULL) goto done;
 
     names    = RVAToAddr(dir->AddressOfNames, ptr);
     ordinals = RVAToAddr(dir->AddressOfNameOrdinals, ptr);
@@ -1581,17 +1579,20 @@ todo_wine
         /* check position in memory */
         dll_rva = (DWORD_PTR)dll_func - (DWORD_PTR)module;
         map_rva = funcs[ordinals[i]];
+    todo_wine
         ok(map_rva == dll_rva, "%s: Rva of mapped function (0x%x) does not match dll (0x%x)\n",
            func_name, dll_rva, map_rva);
 
         /* check position in file */
         map_offset = (DWORD_PTR)RtlImageRvaToVa(RtlImageNtHeader(ptr),    ptr,    map_rva, NULL) - (DWORD_PTR)ptr;
         dll_offset = (DWORD_PTR)RtlImageRvaToVa(RtlImageNtHeader(module), module, dll_rva, NULL) - (DWORD_PTR)module;
+    todo_wine
         ok(map_offset == dll_offset, "%s: File offset of mapped function (0x%x) does not match dll (0x%x)\n",
            func_name, map_offset, dll_offset);
 
         /* check function content */
         map_func = RVAToAddr(map_rva, ptr);
+    todo_wine
         ok(!memcmp(map_func, dll_func, 0x20), "%s: Function content does not match!\n", func_name);
 
         if (!strcmp(func_name, "NtSetEvent"))
@@ -1605,10 +1606,11 @@ todo_wine
         ok(event != NULL, "CreateEvent failed with error %u\n", GetLastError());
         pNtSetEvent(event, 0);
         ok(WaitForSingleObject(event, 0) == WAIT_OBJECT_0, "Event was not signaled\n");
+        pNtSetEvent(event, 0);
+        ok(WaitForSingleObject(event, 0) == WAIT_OBJECT_0, "Event was not signaled\n");
         CloseHandle(event);
     }
 
-done:
     UnmapViewOfFile(ptr);
     CloseHandle(map);
     CloseHandle(file);
diff --git a/dlls/ntdll/signal_i386.c b/dlls/ntdll/signal_i386.c
index 8dfcd0b5..4a4eb047 100644
--- a/dlls/ntdll/signal_i386.c
+++ b/dlls/ntdll/signal_i386.c
@@ -430,6 +430,45 @@ static wine_signal_handler handlers[256];
 extern void DECLSPEC_NORETURN __wine_syscall_dispatcher( void );
 extern NTSTATUS WINAPI __syscall_NtGetContextThread( HANDLE handle, CONTEXT *context );
 
+/* convert from straight ASCII to Unicode without depending on the current codepage */
+static inline void ascii_to_unicode( WCHAR *dst, const char *src, size_t len )
+{
+    while (len--) *dst++ = (unsigned char)*src++;
+}
+
+static void* WINAPI __wine_fakedll_dispatcher( const char *module, ULONG ord )
+{
+    UNICODE_STRING name;
+    NTSTATUS status;
+    HMODULE base;
+    WCHAR *moduleW;
+    void *proc = NULL;
+    DWORD len = strlen(module);
+
+    TRACE( "(%s, %u)\n", debugstr_a(module), ord );
+
+    if (!(moduleW = RtlAllocateHeap( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) )))
+        return NULL;
+
+    ascii_to_unicode( moduleW, module, len );
+    moduleW[ len ] = 0;
+    RtlInitUnicodeString( &name, moduleW );
+
+    status = LdrGetDllHandle( NULL, 0, &name, &base );
+    if (status == STATUS_DLL_NOT_FOUND)
+        status = LdrLoadDll( NULL, 0, &name, &base );
+    if (status == STATUS_SUCCESS)
+        status = LdrAddRefDll( LDR_ADDREF_DLL_PIN, base );
+    if (status == STATUS_SUCCESS)
+        status = LdrGetProcedureAddress( base, NULL, ord, &proc );
+
+    if (status)
+        FIXME( "No procedure address found for %s.#%u, status %x\n", debugstr_a(module), ord, status );
+
+    RtlFreeHeap( GetProcessHeap(), 0, moduleW );
+    return proc;
+}
+
 enum i386_trap_code
 {
     TRAP_x86_UNKNOWN    = -1,  /* Unknown fault (TRAP_sig not defined) */
@@ -2321,6 +2360,7 @@ NTSTATUS signal_alloc_thread( TEB **teb )
         (*teb)->Tib.Self = &(*teb)->Tib;
         (*teb)->Tib.ExceptionList = (void *)~0UL;
         (*teb)->WOW32Reserved = __wine_syscall_dispatcher;
+        (*teb)->Spare3 = __wine_fakedll_dispatcher;
         thread_data = (struct x86_thread_data *)(*teb)->SystemReserved2;
         if (!(thread_data->fs = wine_ldt_alloc_fs()))
         {
diff --git a/include/winternl.h b/include/winternl.h
index 5d128b46..64640f4b 100644
--- a/include/winternl.h
+++ b/include/winternl.h
@@ -397,7 +397,7 @@ typedef struct _TEB
     PVOID                        WinSockData;                       /* f6c/1738 */
     ULONG                        GdiBatchCount;                     /* f70/1740 */
     ULONG                        Spare2;                            /* f74/1744 */
-    PVOID                        Spare3;                            /* f78/1748 */
+    PVOID                        Spare3;                            /* f78/1748 used for fakedll thunks */
     PVOID                        Spare4;                            /* f7c/1750 */
     PVOID                        ReservedForOle;                    /* f80/1758 */
     ULONG                        WaitingOnLoaderLock;               /* f84/1760 */
diff --git a/tools/winebuild/build.h b/tools/winebuild/build.h
index 56e9af3d..6b12d496 100644
--- a/tools/winebuild/build.h
+++ b/tools/winebuild/build.h
@@ -344,6 +344,7 @@ extern void put_word( unsigned short val );
 extern void put_dword( unsigned int val );
 extern void put_qword( unsigned int val );
 extern void put_pword( unsigned int val );
+extern void put_str( const char *str );
 extern void align_output( unsigned int align );
 extern void align_output_rva( unsigned int file_align, unsigned int rva_align );
 extern size_t label_pos( const char *name );
diff --git a/tools/winebuild/spec32.c b/tools/winebuild/spec32.c
index 55587cb6..2f172033 100644
--- a/tools/winebuild/spec32.c
+++ b/tools/winebuild/spec32.c
@@ -794,6 +794,163 @@ void BuildSpec32File( DLLSPEC *spec )
 }
 
 
+static int needs_stub_exports( DLLSPEC *spec )
+{
+    if (target_cpu != CPU_x86)
+        return 0;
+    if (!(spec->characteristics & IMAGE_FILE_DLL))
+        return 0;
+    if (!spec->nb_entry_points)
+        return 0;
+    return 1;
+}
+
+
+static void create_stub_exports_text( DLLSPEC *spec )
+{
+    int i, nr_exports = spec->base <= spec->limit ? spec->limit - spec->base + 1 : 0;
+    size_t rva, thunk;
+
+    /* output stub code for exports */
+    for (i = 0; i < spec->nb_entry_points; i++)
+    {
+        ORDDEF *odp = &spec->entry_points[i];
+        const char *name = get_stub_name( odp, spec );
+
+        align_output_rva( 16, 16 );
+        put_label( name );
+        put_byte( 0x8b ); put_byte( 0xff );                           /* mov edi, edi */
+        put_byte( 0x55 );                                             /* push ebp */
+        put_byte( 0x8b ); put_byte( 0xec );                           /* mov ebp, esp */
+        put_byte( 0x68 ); put_dword( 0 );                             /* push dword 0 */
+        put_byte( 0x68 ); put_dword( odp->ordinal );                  /* push ORDINAL */
+        rva = output_buffer_rva + 5;
+        put_byte( 0xe8 ); put_dword( label_rva("_forward") - rva );   /* call _forward */
+        put_byte( 0x89 ); put_byte( 0xec );                           /* mov esp, ebp */
+        put_byte( 0x5d );                                             /* pop ebp */
+        if (odp->type == TYPE_STDCALL)
+        {
+            put_byte( 0xc2 ); put_word( get_args_size(odp) );         /* ret X */
+        }
+        else
+        {
+            put_byte( 0xc3 );                                         /* ret */
+        }
+    }
+
+    /* output entry point */
+    align_output_rva( 16, 16 );
+    put_label( "entrypoint" );
+    put_byte( 0xb8 ); put_dword( 1 );                                 /* mov eax, 1 */
+    put_byte( 0xc2 ); put_word( 12 );                                 /* ret 12 */
+
+    /* output forward function */
+    align_output_rva( 16, 16 );
+    put_label( "_forward" );
+    put_byte( 0x8b ); put_byte( 0x6d ); put_byte( 0x00 );             /* mov ebp, dword[ebp] */
+    put_byte( 0x89 ); put_byte( 0x44 );                               /* mov dword[esp+8], eax */
+    put_byte( 0x24 ); put_byte( 0x08 );
+    put_byte( 0x89 ); put_byte( 0x14 ); put_byte( 0x24 );             /* mov dword[esp], edx */
+    put_byte( 0x8b ); put_byte( 0x54 );                               /* mov edx, dword[esp+4] */
+    put_byte( 0x24 ); put_byte( 0x04 );
+    put_byte( 0x89 ); put_byte( 0x4c );                               /* mov dword[esp+4], ecx */
+    put_byte( 0x24 ); put_byte( 0x04 );
+    put_byte( 0xe8 ); put_dword( 0 );                                 /* call 1f */
+    thunk = output_buffer_rva;
+    put_byte( 0x59 );                                                 /* pop ecx */
+    put_byte( 0x8b ); put_byte( 0x84 ); put_byte( 0x91 );             /* mov eax, dword[_functions + 4 * (edx - BASE)] */
+    put_dword( label_rva("_functions") - thunk - 4 * spec->base );
+    put_byte( 0x09 ); put_byte( 0xc0 );                               /* or eax, eax */
+    rva = output_buffer_rva + 2;
+    put_byte( 0x74 ); put_byte( label_rva("_forward_load") - rva );   /* je _forward_load */
+
+    put_label( "_forward_done" );
+    put_byte( 0x89 ); put_byte( 0x44 );                               /* mov dword[esp+12], eax */
+    put_byte( 0x24 ); put_byte( 0x0c );
+    put_byte( 0x5a );                                                 /* pop edx */
+    put_byte( 0x59 );                                                 /* pop ecx */
+    put_byte( 0x58 );                                                 /* pop eax */
+    put_byte( 0xc3 );                                                 /* ret */
+
+    align_output_rva( 16, 16 );
+    put_label( "_forward_load" );
+    put_byte( 0x8d ); put_byte( 0x84 ); put_byte( 0x91 );             /* lea eax, [_functions + 4 * (edx - BASE)] */
+    put_dword( label_rva("_functions") - thunk - 4 * spec->base );
+    put_byte( 0x50 );                                                 /* push eax */
+    put_byte( 0x52 );                                                 /* push edx */
+    put_byte( 0x8d ); put_byte( 0x81 );                               /* lea eax, [dll_name] */
+    put_dword( label_rva("dll_name") - thunk );
+    put_byte( 0x50 );                                                 /* push eax */
+    put_byte( 0x64 ); put_byte( 0xff );                               /* call dword ptr fs:[0F78h] */
+    put_byte( 0x15 ); put_dword( 0xf78 );
+    put_byte( 0x5a );                                                 /* pop edx */
+    put_byte( 0x89 ); put_byte( 0x02 );                               /* mov dword[edx], eax */
+    rva = output_buffer_rva + 2;
+    put_byte( 0xeb ); put_byte( label_rva("_forward_done") - rva );   /* jmp _forward_done */
+
+    /* export directory */
+    align_output_rva( 16, 16 );
+    put_label( "export_start" );
+    put_dword( 0 );                             /* Characteristics */
+    put_dword( 0 );                             /* TimeDateStamp */
+    put_dword( 0 );                             /* MajorVersion/MinorVersion */
+    put_dword( label_rva("dll_name") );         /* Name */
+    put_dword( spec->base );                    /* Base */
+    put_dword( nr_exports );                    /* NumberOfFunctions */
+    put_dword( spec->nb_names );                /* NumberOfNames */
+    put_dword( label_rva("export_funcs") );     /* AddressOfFunctions */
+    put_dword( label_rva("export_names") );     /* AddressOfNames */
+    put_dword( label_rva("export_ordinals") );  /* AddressOfNameOrdinals */
+
+    put_label( "export_funcs" );
+    for (i = spec->base; i <= spec->limit; i++)
+    {
+        ORDDEF *odp = spec->ordinals[i];
+        if (odp)
+        {
+            const char *name = get_stub_name( odp, spec );
+            put_dword( label_rva( name ) );
+        }
+        else
+            put_dword( 0 );
+    }
+
+    if (spec->nb_names)
+    {
+        put_label( "export_names" );
+        for (i = 0; i < spec->nb_names; i++)
+            put_dword( label_rva(strmake("str_%s", get_stub_name(spec->names[i], spec))) );
+
+        put_label( "export_ordinals" );
+        for (i = 0; i < spec->nb_names; i++)
+            put_word( spec->names[i]->ordinal - spec->base );
+        if (spec->nb_names % 2)
+            put_word( 0 );
+    }
+
+    put_label( "dll_name" );
+    put_str( spec->file_name );
+
+    for (i = 0; i < spec->nb_names; i++)
+    {
+        put_label( strmake("str_%s", get_stub_name(spec->names[i], spec)) );
+        put_str( spec->names[i]->name );
+    }
+
+    put_label( "export_end" );
+}
+
+
+static void create_stub_exports_data( DLLSPEC *spec )
+{
+    int i;
+
+    put_label( "_functions" );
+    for (i = spec->base; i <= spec->limit; i++)
+        put_dword( 0 );
+}
+
+
 /*******************************************************************
  *         output_fake_module_pass
  *
@@ -812,7 +969,7 @@ static void output_fake_module_pass( DLLSPEC *spec )
     const unsigned int section_align = page_size;
     const unsigned int file_align = 0x200;
     const unsigned int lfanew = (0x40 + sizeof(fakedll_signature) + 15) & ~15;
-    const unsigned int nb_sections = 2 + (spec->nb_resources != 0);
+    const unsigned int nb_sections = 2 + (needs_stub_exports( spec ) != 0) + (spec->nb_resources != 0);
 
     put_word( 0x5a4d );       /* e_magic */
     put_word( 0x40 );         /* e_cblp */
@@ -869,7 +1026,7 @@ static void output_fake_module_pass( DLLSPEC *spec )
     put_dword( 0 );                                  /* SizeOfUninitializedData */
     put_dword( label_rva("entrypoint") );            /* AddressOfEntryPoint */
     put_dword( label_rva("text_start") );            /* BaseOfCode */
-    if (get_ptr_size() == 4) put_dword( 0 );         /* BaseOfData */
+    if (get_ptr_size() == 4) put_dword( label_rva("data_start") ); /* BaseOfData */
     put_pword( 0x10000000 );                         /* ImageBase */
     put_dword( section_align );                      /* SectionAlignment */
     put_dword( file_align );                         /* FileAlignment */
@@ -892,7 +1049,8 @@ static void output_fake_module_pass( DLLSPEC *spec )
     put_dword( 0 );                                  /* LoaderFlags */
     put_dword( 16 );                                 /* NumberOfRvaAndSizes */
 
-    put_dword( 0 ); put_dword( 0 );   /* DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT] */
+    put_dword( label_rva("export_start") ); /* DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT] */
+    put_dword( label_pos("export_end") - label_pos("export_start") );
     put_dword( 0 ); put_dword( 0 );   /* DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT] */
     if (spec->nb_resources)           /* DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE] */
     {
@@ -932,6 +1090,21 @@ static void output_fake_module_pass( DLLSPEC *spec )
     put_word( 0 );                                                        /* NumberOfLinenumbers */
     put_dword( 0x60000020 /* CNT_CODE|MEM_EXECUTE|MEM_READ */ );          /* Characteristics  */
 
+    /* .data section */
+    if (needs_stub_exports( spec ))
+    {
+        put_data( ".data\0\0", 8 );                                       /* Name */
+        put_dword( label_rva_align("data_end") - label_rva("data_start") ); /* VirtualSize */
+        put_dword( label_rva("data_start") );                             /* VirtualAddress */
+        put_dword( label_pos("data_end") - label_pos("data_start") );     /* SizeOfRawData */
+        put_dword( label_pos("data_start") );                             /* PointerToRawData */
+        put_dword( 0 );                                                   /* PointerToRelocations */
+        put_dword( 0 );                                                   /* PointerToLinenumbers */
+        put_word( 0 );                                                    /* NumberOfRelocations */
+        put_word( 0 );                                                    /* NumberOfLinenumbers */
+        put_dword( 0xc0000040 /* CNT_INITIALIZED_DATA|MEM_READ|MEM_WRITE */ ); /* Characteristics  */
+    }
+
     /* .reloc section */
     put_data( ".reloc\0", 8 );                                            /* Name */
     put_dword( label_rva_align("reloc_end") - label_rva("reloc_start") ); /* VirtualSize */
@@ -964,13 +1137,31 @@ static void output_fake_module_pass( DLLSPEC *spec )
 
     /* .text contents */
     align_output_rva( file_align, section_align );
-    put_label( "text_start" );
-    put_label( "entrypoint" );
-    if (spec->characteristics & IMAGE_FILE_DLL)
-        put_data( dll_code_section, sizeof(dll_code_section) );
+    if (needs_stub_exports( spec ))
+    {
+        put_label( "text_start" );
+        create_stub_exports_text( spec );
+        put_label( "text_end" );
+    }
     else
-        put_data( exe_code_section, sizeof(exe_code_section) );
-    put_label( "text_end" );
+    {
+        put_label( "text_start" );
+        put_label( "entrypoint" );
+        if (spec->characteristics & IMAGE_FILE_DLL)
+            put_data( dll_code_section, sizeof(dll_code_section) );
+        else
+            put_data( exe_code_section, sizeof(exe_code_section) );
+        put_label( "text_end" );
+    }
+
+    /* .data contents */
+    align_output_rva( file_align, section_align );
+    if (needs_stub_exports( spec ))
+    {
+        put_label( "data_start" );
+        create_stub_exports_data( spec );
+        put_label( "data_end" );
+    }
 
     /* .reloc contents */
     align_output_rva( file_align, section_align );
diff --git a/tools/winebuild/utils.c b/tools/winebuild/utils.c
index b9c2b329..f4ed3261 100644
--- a/tools/winebuild/utils.c
+++ b/tools/winebuild/utils.c
@@ -528,7 +528,7 @@ size_t output_buffer_size;
 struct label
 {
     struct list entry;
-    const char *name;
+    char *name;
     size_t pos;
     size_t rva;
 };
@@ -546,7 +546,7 @@ struct label *get_label( const char *name )
     }
 
     label = xmalloc( sizeof(*label) );
-    label->name = name;
+    label->name = xstrdup( name );
     label->pos = 0;
     label->rva = 0;
 
@@ -598,6 +598,7 @@ void free_labels( void )
     LIST_FOR_EACH_ENTRY_SAFE( label, label2, &labels, struct label, entry )
     {
         list_remove( &label->entry );
+        free( label->name );
         free( label );
     }
 }
@@ -732,6 +733,11 @@ void put_pword( unsigned int val )
     else put_dword( val );
 }
 
+void put_str( const char *str )
+{
+    put_data( str, strlen(str) + 1 );
+}
+
 void align_output( unsigned int align )
 {
     size_t size = align - (output_buffer_pos % align);
-- 
2.20.1

