mscoree: Implement vtable fixups.

Vincent Povirk madewokherd at gmail.com
Wed May 9 08:59:39 CDT 2012


Silenced the warning in 64-bit by using PtrToUint, and added an ERR
message for that case.
-------------- next part --------------
From ae3997c2c0d1ec3fab326c9a296d408c25a430a8 Mon Sep 17 00:00:00 2001
From: Vincent Povirk <vincent at codeweavers.com>
Date: Mon, 7 May 2012 10:48:33 -0500
Subject: [PATCH] mscoree: Implement vtable fixups.

---
 dlls/mscoree/corruntimehost.c  |  232 +++++++++++++++++++++++++++++++++++++++-
 dlls/mscoree/metahost.c        |    1 +
 dlls/mscoree/mscoree_main.c    |    3 +
 dlls/mscoree/mscoree_private.h |    4 +
 4 files changed, 237 insertions(+), 3 deletions(-)

diff --git a/dlls/mscoree/corruntimehost.c b/dlls/mscoree/corruntimehost.c
index ec68ea1..181da26 100644
--- a/dlls/mscoree/corruntimehost.c
+++ b/dlls/mscoree/corruntimehost.c
@@ -19,6 +19,7 @@
 
 #define COBJMACROS
 
+#include <assert.h>
 #include <stdarg.h>
 
 #include "windef.h"
@@ -39,6 +40,7 @@
 
 #include "wine/debug.h"
 #include "wine/unicode.h"
+#include "wine/list.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL( mscoree );
 
@@ -52,6 +54,21 @@ struct DomainEntry
     MonoDomain *domain;
 };
 
+static HANDLE dll_fixup_heap; /* using a separate heap so we can have execute permission */
+
+static struct list dll_fixups;
+
+struct dll_fixup
+{
+    struct list entry;
+    int done;
+    HMODULE dll;
+    void *thunk_code; /* pointer into dll_fixup_heap */
+    VTableFixup *fixup;
+    void *vtable;
+    void *tokens; /* pointer into process heap */
+};
+
 static HRESULT RuntimeHost_AddDomain(RuntimeHost *This, MonoDomain **result)
 {
     struct DomainEntry *entry;
@@ -803,19 +820,207 @@ static void get_utf8_args(int *argc, char ***argv)
     HeapFree(GetProcessHeap(), 0, argvw);
 }
 
+#if __i386__
+
+# define CAN_FIXUP_VTABLE 1
+
+#include "pshpack1.h"
+
+struct vtable_fixup_thunk
+{
+    /* sub $0x4,%esp */
+    BYTE i1[3];
+    /* mov fixup,(%esp) */
+    BYTE i2[3];
+    struct dll_fixup *fixup;
+    /* mov function,%eax */
+    BYTE i3;
+    void (CDECL *function)(struct dll_fixup *);
+    /* call *%eax */
+    BYTE i4[2];
+    /* pop %eax */
+    BYTE i5;
+    /* jmp *vtable_entry */
+    BYTE i6[2];
+    void *vtable_entry;
+};
+
+static const struct vtable_fixup_thunk thunk_template = {
+    {0x83,0xec,0x04},
+    {0xc7,0x04,0x24},
+    NULL,
+    0xb8,
+    NULL,
+    {0xff,0xd0},
+    0x58,
+    {0xff,0x25},
+    NULL
+};
+
+#include "poppack.h"
+
+#else /* !defined(__i386__) */
+
+# define CAN_FIXUP_VTABLE 0
+
+struct vtable_fixup_thunk
+{
+    struct dll_fixup *fixup;
+    void (CDECL *function)(struct dll_fixup *fixup);
+    void *vtable_entry;
+};
+
+static const struct vtable_fixup_thunk thunk_template = {0};
+
+#endif
+
+static void CDECL ReallyFixupVTable(struct dll_fixup *fixup)
+{
+    HRESULT hr=S_OK;
+    WCHAR filename[MAX_PATH];
+    ICLRRuntimeInfo *info=NULL;
+    RuntimeHost *host;
+    char *filenameA;
+    MonoImage *image=NULL;
+    MonoAssembly *assembly=NULL;
+    MonoImageOpenStatus status=0;
+    MonoDomain *domain;
+
+    if (fixup->done) return;
+
+    /* It's possible we'll have two threads doing this at once. This is
+     * considered preferable to the potential deadlock if we use a mutex. */
+
+    GetModuleFileNameW(fixup->dll, filename, MAX_PATH);
+
+    TRACE("%p,%p,%s\n", fixup, fixup->dll, debugstr_w(filename));
+
+    filenameA = WtoA(filename);
+    if (!filenameA)
+        hr = E_OUTOFMEMORY;
+
+    if (SUCCEEDED(hr))
+        hr = get_runtime_info(filename, NULL, NULL, 0, 0, FALSE, &info);
+
+    if (SUCCEEDED(hr))
+        hr = ICLRRuntimeInfo_GetRuntimeHost(info, &host);
+
+    if (SUCCEEDED(hr))
+        hr = RuntimeHost_GetDefaultDomain(host, &domain);
+
+    if (SUCCEEDED(hr))
+    {
+        host->mono->mono_thread_attach(domain);
+
+        image = host->mono->mono_image_open_from_module_handle(fixup->dll,
+            filenameA, 1, &status);
+    }
+
+    if (image)
+        assembly = host->mono->mono_assembly_load_from(image, filenameA, &status);
+
+    if (assembly)
+    {
+        int i;
+
+        /* Mono needs an image that belongs to an assembly. */
+        image = host->mono->mono_assembly_get_image(assembly);
+
+        if (fixup->fixup->type & COR_VTABLE_32BIT)
+        {
+            DWORD *vtable = fixup->vtable;
+            DWORD *tokens = fixup->tokens;
+            for (i=0; i<fixup->fixup->count; i++)
+            {
+                TRACE("%x\n", tokens[i]);
+                vtable[i] = PtrToUint(host->mono->mono_marshal_get_vtfixup_ftnptr(
+                    image, tokens[i], fixup->fixup->type));
+            }
+        }
+
+        fixup->done = 1;
+    }
+
+    if (info != NULL)
+        ICLRRuntimeHost_Release(info);
+
+    HeapFree(GetProcessHeap(), 0, filenameA);
+
+    if (!fixup->done)
+    {
+        ERR("unable to fixup vtable, hr=%x, status=%d\n", hr, status);
+        /* If we returned now, we'd get an infinite loop. */
+        assert(0);
+    }
+}
+
+static void FixupVTableEntry(HMODULE hmodule, VTableFixup *vtable_fixup)
+{
+    /* We can't actually generate code for the functions without loading mono,
+     * and loading mono inside DllMain is a terrible idea. So we make thunks
+     * that call ReallyFixupVTable, which will load the runtime and fill in the
+     * vtable, then do an indirect jump using the (now filled in) vtable. Note
+     * that we have to keep the thunks around forever, as one of them may get
+     * called while we're filling in the table, and we can never be sure all 
+     * threads are clear. */
+    struct dll_fixup *fixup;
+
+    fixup = HeapAlloc(GetProcessHeap(), 0, sizeof(*fixup));
+
+    fixup->dll = hmodule;
+    fixup->thunk_code = HeapAlloc(dll_fixup_heap, 0, sizeof(struct vtable_fixup_thunk) * vtable_fixup->count);
+    fixup->fixup = vtable_fixup;
+    fixup->vtable = (BYTE*)hmodule + vtable_fixup->rva;
+    fixup->done = 0;
+
+    if (vtable_fixup->type & COR_VTABLE_32BIT)
+    {
+        DWORD *vtable = fixup->vtable;
+        DWORD *tokens;
+        int i;
+        struct vtable_fixup_thunk *thunks = fixup->thunk_code;
+
+        if (sizeof(void*) > 4)
+            ERR("32-bit fixup in 64-bit mode; broken image?\n");
+
+        tokens = fixup->tokens = HeapAlloc(GetProcessHeap(), 0, sizeof(*tokens) * vtable_fixup->count);
+        memcpy(tokens, vtable, sizeof(*tokens) * vtable_fixup->count);
+        for (i=0; i<vtable_fixup->count; i++)
+        {
+            memcpy(&thunks[i], &thunk_template, sizeof(thunk_template));
+            thunks[i].fixup = fixup;
+            thunks[i].function = ReallyFixupVTable;
+            thunks[i].vtable_entry = &vtable[i];
+            vtable[i] = PtrToUint(&thunks[i]);
+        }
+    }
+    else
+    {
+        ERR("unsupported vtable fixup flags %x\n", vtable_fixup->type);
+        HeapFree(dll_fixup_heap, 0, fixup->thunk_code);
+        HeapFree(GetProcessHeap(), 0, fixup);
+        return;
+    }
+
+    list_add_tail(&dll_fixups, &fixup->entry);
+}
+
 static void FixupVTable(HMODULE hmodule)
 {
     ASSEMBLY *assembly;
     HRESULT hr;
     VTableFixup *vtable_fixups;
-    ULONG vtable_fixup_count;
+    ULONG vtable_fixup_count, i;
 
     hr = assembly_from_hmodule(&assembly, hmodule);
     if (SUCCEEDED(hr))
     {
         hr = assembly_get_vtable_fixups(assembly, &vtable_fixups, &vtable_fixup_count);
-        if (vtable_fixup_count)
-            FIXME("vtable fixups are not implemented; expect a crash\n");
+        if (CAN_FIXUP_VTABLE)
+            for (i=0; i<vtable_fixup_count; i++)
+                FixupVTableEntry(hmodule, &vtable_fixups[i]);
+        else if (vtable_fixup_count)
+            FIXME("cannot fixup vtable; expect a crash\n");
 
         assembly_release(assembly);
     }
@@ -909,11 +1114,32 @@ BOOL WINAPI _CorDllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
         FixupVTable(hinstDLL);
         break;
     case DLL_PROCESS_DETACH:
+        /* FIXME: clean up the vtables */
         break;
     }
     return TRUE;
 }
 
+/* called from DLL_PROCESS_ATTACH */
+void runtimehost_init(void)
+{
+    dll_fixup_heap = HeapCreate(HEAP_CREATE_ENABLE_EXECUTE, 0, 0);
+    list_init(&dll_fixups);
+}
+
+/* called from DLL_PROCESS_DETACH */
+void runtimehost_uninit(void)
+{
+    struct dll_fixup *fixup, *fixup2;
+
+    HeapDestroy(dll_fixup_heap);
+    LIST_FOR_EACH_ENTRY_SAFE(fixup, fixup2, &dll_fixups, struct dll_fixup, entry)
+    {
+        HeapFree(GetProcessHeap(), 0, fixup->tokens);
+        HeapFree(GetProcessHeap(), 0, fixup);
+    }
+}
+
 HRESULT RuntimeHost_Construct(const CLRRuntimeInfo *runtime_version,
     loaded_mono *loaded_mono, RuntimeHost** result)
 {
diff --git a/dlls/mscoree/metahost.c b/dlls/mscoree/metahost.c
index 2c3322a..3736dc0 100644
--- a/dlls/mscoree/metahost.c
+++ b/dlls/mscoree/metahost.c
@@ -177,6 +177,7 @@ static HRESULT load_mono(CLRRuntimeInfo *This, loaded_mono **result)
         LOAD_MONO_FUNCTION(mono_jit_exec);
         LOAD_MONO_FUNCTION(mono_jit_init);
         LOAD_MONO_FUNCTION(mono_jit_set_trace_options);
+        LOAD_MONO_FUNCTION(mono_marshal_get_vtfixup_ftnptr);
         LOAD_MONO_FUNCTION(mono_object_get_domain);
         LOAD_MONO_FUNCTION(mono_object_new);
         LOAD_MONO_FUNCTION(mono_object_unbox);
diff --git a/dlls/mscoree/mscoree_main.c b/dlls/mscoree/mscoree_main.c
index 09f6dad..0aa736f 100644
--- a/dlls/mscoree/mscoree_main.c
+++ b/dlls/mscoree/mscoree_main.c
@@ -227,10 +227,13 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
     case DLL_WINE_PREATTACH:
         return FALSE;  /* prefer native version */
     case DLL_PROCESS_ATTACH:
+        runtimehost_init();
         DisableThreadLibraryCalls(hinstDLL);
         break;
     case DLL_PROCESS_DETACH:
         expect_no_runtimes();
+        if (lpvReserved) break; /* process is terminating */
+        runtimehost_uninit();
         break;
     }
     return TRUE;
diff --git a/dlls/mscoree/mscoree_private.h b/dlls/mscoree/mscoree_private.h
index 69f285c..ef9079e 100644
--- a/dlls/mscoree/mscoree_private.h
+++ b/dlls/mscoree/mscoree_private.h
@@ -159,6 +159,7 @@ struct loaded_mono
     int (CDECL *mono_jit_exec)(MonoDomain *domain, MonoAssembly *assembly, int argc, char *argv[]);
     MonoDomain* (CDECL *mono_jit_init)(const char *file);
     int (CDECL *mono_jit_set_trace_options)(const char* options);
+    void* (CDECL *mono_marshal_get_vtfixup_ftnptr)(MonoImage *image, DWORD token, WORD type);
     MonoDomain* (CDECL *mono_object_get_domain)(MonoObject *obj);
     MonoObject* (CDECL *mono_object_new)(MonoDomain *domain, MonoClass *klass);
     void* (CDECL *mono_object_unbox)(MonoObject *obj);
@@ -200,4 +201,7 @@ extern HRESULT CorDebug_Create(ICLRRuntimeHost *runtimehost, IUnknown** ppUnk) D
 
 extern HRESULT create_monodata(REFIID riid, LPVOID *ppObj) DECLSPEC_HIDDEN;
 
+extern void runtimehost_init(void);
+extern void runtimehost_uninit(void);
+
 #endif   /* __MSCOREE_PRIVATE__ */
-- 
1.7.9.5


More information about the wine-patches mailing list