mscoree: Implement vtable fixups.
This commit is contained in:
parent
f0be44c82d
commit
a629ba4949
|
@ -19,6 +19,7 @@
|
||||||
|
|
||||||
#define COBJMACROS
|
#define COBJMACROS
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
|
||||||
#include "windef.h"
|
#include "windef.h"
|
||||||
|
@ -39,6 +40,7 @@
|
||||||
|
|
||||||
#include "wine/debug.h"
|
#include "wine/debug.h"
|
||||||
#include "wine/unicode.h"
|
#include "wine/unicode.h"
|
||||||
|
#include "wine/list.h"
|
||||||
|
|
||||||
WINE_DEFAULT_DEBUG_CHANNEL( mscoree );
|
WINE_DEFAULT_DEBUG_CHANNEL( mscoree );
|
||||||
|
|
||||||
|
@ -52,6 +54,21 @@ struct DomainEntry
|
||||||
MonoDomain *domain;
|
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)
|
static HRESULT RuntimeHost_AddDomain(RuntimeHost *This, MonoDomain **result)
|
||||||
{
|
{
|
||||||
struct DomainEntry *entry;
|
struct DomainEntry *entry;
|
||||||
|
@ -803,19 +820,207 @@ static void get_utf8_args(int *argc, char ***argv)
|
||||||
HeapFree(GetProcessHeap(), 0, argvw);
|
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)
|
static void FixupVTable(HMODULE hmodule)
|
||||||
{
|
{
|
||||||
ASSEMBLY *assembly;
|
ASSEMBLY *assembly;
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
VTableFixup *vtable_fixups;
|
VTableFixup *vtable_fixups;
|
||||||
ULONG vtable_fixup_count;
|
ULONG vtable_fixup_count, i;
|
||||||
|
|
||||||
hr = assembly_from_hmodule(&assembly, hmodule);
|
hr = assembly_from_hmodule(&assembly, hmodule);
|
||||||
if (SUCCEEDED(hr))
|
if (SUCCEEDED(hr))
|
||||||
{
|
{
|
||||||
hr = assembly_get_vtable_fixups(assembly, &vtable_fixups, &vtable_fixup_count);
|
hr = assembly_get_vtable_fixups(assembly, &vtable_fixups, &vtable_fixup_count);
|
||||||
if (vtable_fixup_count)
|
if (CAN_FIXUP_VTABLE)
|
||||||
FIXME("vtable fixups are not implemented; expect a crash\n");
|
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);
|
assembly_release(assembly);
|
||||||
}
|
}
|
||||||
|
@ -909,11 +1114,32 @@ BOOL WINAPI _CorDllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
|
||||||
FixupVTable(hinstDLL);
|
FixupVTable(hinstDLL);
|
||||||
break;
|
break;
|
||||||
case DLL_PROCESS_DETACH:
|
case DLL_PROCESS_DETACH:
|
||||||
|
/* FIXME: clean up the vtables */
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return TRUE;
|
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,
|
HRESULT RuntimeHost_Construct(const CLRRuntimeInfo *runtime_version,
|
||||||
loaded_mono *loaded_mono, RuntimeHost** result)
|
loaded_mono *loaded_mono, RuntimeHost** result)
|
||||||
{
|
{
|
||||||
|
|
|
@ -177,6 +177,7 @@ static HRESULT load_mono(CLRRuntimeInfo *This, loaded_mono **result)
|
||||||
LOAD_MONO_FUNCTION(mono_jit_exec);
|
LOAD_MONO_FUNCTION(mono_jit_exec);
|
||||||
LOAD_MONO_FUNCTION(mono_jit_init);
|
LOAD_MONO_FUNCTION(mono_jit_init);
|
||||||
LOAD_MONO_FUNCTION(mono_jit_set_trace_options);
|
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_get_domain);
|
||||||
LOAD_MONO_FUNCTION(mono_object_new);
|
LOAD_MONO_FUNCTION(mono_object_new);
|
||||||
LOAD_MONO_FUNCTION(mono_object_unbox);
|
LOAD_MONO_FUNCTION(mono_object_unbox);
|
||||||
|
|
|
@ -227,10 +227,13 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
|
||||||
case DLL_WINE_PREATTACH:
|
case DLL_WINE_PREATTACH:
|
||||||
return FALSE; /* prefer native version */
|
return FALSE; /* prefer native version */
|
||||||
case DLL_PROCESS_ATTACH:
|
case DLL_PROCESS_ATTACH:
|
||||||
|
runtimehost_init();
|
||||||
DisableThreadLibraryCalls(hinstDLL);
|
DisableThreadLibraryCalls(hinstDLL);
|
||||||
break;
|
break;
|
||||||
case DLL_PROCESS_DETACH:
|
case DLL_PROCESS_DETACH:
|
||||||
expect_no_runtimes();
|
expect_no_runtimes();
|
||||||
|
if (lpvReserved) break; /* process is terminating */
|
||||||
|
runtimehost_uninit();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
|
@ -159,6 +159,7 @@ struct loaded_mono
|
||||||
int (CDECL *mono_jit_exec)(MonoDomain *domain, MonoAssembly *assembly, int argc, char *argv[]);
|
int (CDECL *mono_jit_exec)(MonoDomain *domain, MonoAssembly *assembly, int argc, char *argv[]);
|
||||||
MonoDomain* (CDECL *mono_jit_init)(const char *file);
|
MonoDomain* (CDECL *mono_jit_init)(const char *file);
|
||||||
int (CDECL *mono_jit_set_trace_options)(const char* options);
|
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);
|
MonoDomain* (CDECL *mono_object_get_domain)(MonoObject *obj);
|
||||||
MonoObject* (CDECL *mono_object_new)(MonoDomain *domain, MonoClass *klass);
|
MonoObject* (CDECL *mono_object_new)(MonoDomain *domain, MonoClass *klass);
|
||||||
void* (CDECL *mono_object_unbox)(MonoObject *obj);
|
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 HRESULT create_monodata(REFIID riid, LPVOID *ppObj) DECLSPEC_HIDDEN;
|
||||||
|
|
||||||
|
extern void runtimehost_init(void);
|
||||||
|
extern void runtimehost_uninit(void);
|
||||||
|
|
||||||
#endif /* __MSCOREE_PRIVATE__ */
|
#endif /* __MSCOREE_PRIVATE__ */
|
||||||
|
|
Loading…
Reference in New Issue