mscoree: Implement vtable fixups.
Vincent Povirk
madewokherd at gmail.com
Tue May 8 08:41:24 CDT 2012
-------------- next part --------------
From 06a1be8951d541aea7bb9b7884f5a531bbe92e23 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 | 229 +++++++++++++++++++++++++++++++++++++++-
dlls/mscoree/metahost.c | 1 +
dlls/mscoree/mscoree_main.c | 3 +
dlls/mscoree/mscoree_private.h | 4 +
4 files changed, 234 insertions(+), 3 deletions(-)
diff --git a/dlls/mscoree/corruntimehost.c b/dlls/mscoree/corruntimehost.c
index ec68ea1..e7449e1 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,204 @@ 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] = (DWORD)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;
+
+ 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] = (DWORD)&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 +1111,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