diff --git a/dlls/mscoree/corruntimehost.c b/dlls/mscoree/corruntimehost.c index ec68ea19cce..3f2b9a28d30 100644 --- a/dlls/mscoree/corruntimehost.c +++ b/dlls/mscoree/corruntimehost.c @@ -19,6 +19,7 @@ #define COBJMACROS +#include #include #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; ifixup->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; icount; 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; itokens); + 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 2c3322a17d2..3736dc0ace1 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 09f6dad586d..0aa736f93df 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 69f285c3405..ef9079ebdef 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__ */