mscoree: Implement an override setting for .net libraries.

Signed-off-by: Vincent Povirk <vincent@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Vincent Povirk 2019-03-08 15:40:41 -06:00 committed by Alexandre Julliard
parent c8b50c98a3
commit 5baadda536
2 changed files with 303 additions and 23 deletions

View File

@ -39,6 +39,7 @@
#include "metahost.h"
#include "fusion.h"
#include "wine/list.h"
#include "wine/heap.h"
#include "mscoree_private.h"
#include "wine/debug.h"
@ -86,6 +87,7 @@ typedef void (CDECL *MonoProfilerRuntimeShutdownBeginCallback) (MonoProfiler *pr
MonoImage* (CDECL *mono_assembly_get_image)(MonoAssembly *assembly);
MonoAssembly* (CDECL *mono_assembly_load_from)(MonoImage *image, const char *fname, MonoImageOpenStatus *status);
const char* (CDECL *mono_assembly_name_get_name)(MonoAssemblyName *aname);
MonoAssembly* (CDECL *mono_assembly_open)(const char *filename, MonoImageOpenStatus *status);
void (CDECL *mono_callspec_set_assembly)(MonoAssembly *assembly);
MonoClass* (CDECL *mono_class_from_mono_type)(MonoType *type);
@ -193,6 +195,7 @@ static HRESULT load_mono(LPCWSTR mono_path)
LOAD_MONO_FUNCTION(mono_assembly_get_image);
LOAD_MONO_FUNCTION(mono_assembly_load_from);
LOAD_MONO_FUNCTION(mono_assembly_name_get_name);
LOAD_MONO_FUNCTION(mono_assembly_open);
LOAD_MONO_FUNCTION(mono_config_parse);
LOAD_MONO_FUNCTION(mono_class_from_mono_type);
@ -1180,6 +1183,274 @@ HRESULT CLRMetaHostPolicy_CreateInstance(REFIID riid, void **ppobj)
return ICLRMetaHostPolicy_QueryInterface(&GlobalCLRMetaHostPolicy.ICLRMetaHostPolicy_iface, riid, ppobj);
}
/*
* Assembly search override settings:
*
* WINE_MONO_OVERRIDES=*,Gac=n
* Never search the GAC for libraries.
*
* WINE_MONO_OVERRIDES=Microsoft.Xna.Framework,Gac=n
* Never search the GAC for Microsoft.Xna.Framework
*
* WINE_MONO_OVERRIDES=Microsoft.Xna.Framework.*,Gac=n;Microsoft.Xna.Framework.GamerServices,Gac=y
* Never search the GAC for Microsoft.Xna.Framework, or any library starting
* with Microsoft.Xna.Framework, except for Microsoft.Xna.Framework.GamerServices
*/
/* assembly search override flags */
#define ASSEMBLY_SEARCH_GAC 1
#define ASSEMBLY_SEARCH_UNDEFINED 2
#define ASSEMBLY_SEARCH_DEFAULT ASSEMBLY_SEARCH_GAC
typedef struct override_entry {
char *name;
DWORD flags;
struct list entry;
} override_entry;
static struct list env_overrides = LIST_INIT(env_overrides);
#define IS_OPTION_TRUE(ch) \
((ch) == 'y' || (ch) == 'Y' || (ch) == 't' || (ch) == 'T' || (ch) == '1')
#define IS_OPTION_FALSE(ch) \
((ch) == 'n' || (ch) == 'N' || (ch) == 'f' || (ch) == 'F' || (ch) == '0')
static void parse_override_entry(override_entry *entry, const char *string, int string_len)
{
const char *next_key, *equals, *value;
UINT kvp_len, key_len;
entry->flags = ASSEMBLY_SEARCH_DEFAULT;
while (string && string_len > 0)
{
next_key = memchr(string, ',', string_len);
if (next_key)
{
kvp_len = next_key - string;
next_key++;
}
else
kvp_len = string_len;
equals = memchr(string, '=', kvp_len);
if (equals)
{
key_len = equals - string;
value = equals + 1;
switch (key_len) {
case 3:
if (!strncasecmp(string, "gac", 3)) {
if (IS_OPTION_TRUE(*value))
entry->flags |= ASSEMBLY_SEARCH_GAC;
else if (IS_OPTION_FALSE(*value))
entry->flags &= ~ASSEMBLY_SEARCH_GAC;
}
break;
default:
break;
}
}
string = next_key;
string_len -= kvp_len + 1;
}
}
static BOOL WINAPI parse_env_overrides(INIT_ONCE *once, void *param, void **context)
{
const char *override_string = getenv("WINE_MONO_OVERRIDES");
struct override_entry *entry;
if (override_string)
{
const char *entry_start;
entry_start = override_string;
while (entry_start && *entry_start)
{
const char *next_entry, *basename_end;
UINT entry_len;
next_entry = strchr(entry_start, ';');
if (next_entry)
{
entry_len = next_entry - entry_start;
next_entry++;
}
else
entry_len = strlen(entry_start);
basename_end = memchr(entry_start, ',', entry_len);
if (!basename_end)
{
entry_start = next_entry;
continue;
}
entry = heap_alloc_zero(sizeof(*entry));
if (!entry)
{
ERR("out of memory\n");
break;
}
entry->name = heap_alloc_zero(basename_end - entry_start + 1);
if (!entry->name)
{
ERR("out of memory\n");
heap_free(entry);
break;
}
memcpy(entry->name, entry_start, basename_end - entry_start);
entry_len -= basename_end - entry_start + 1;
entry_start = basename_end + 1;
parse_override_entry(entry, entry_start, entry_len);
list_add_tail(&env_overrides, &entry->entry);
entry_start = next_entry;
}
}
return TRUE;
}
static DWORD get_basename_search_flags(const char *basename, MonoAssemblyName *aname, HKEY userkey, HKEY appkey)
{
struct override_entry *entry;
char buffer[256];
DWORD buffer_size;
static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT;
InitOnceExecuteOnce(&init_once, parse_env_overrides, NULL, NULL);
LIST_FOR_EACH_ENTRY(entry, &env_overrides, override_entry, entry)
{
if (strcmp(basename, entry->name) == 0)
{
return entry->flags;
}
}
buffer_size = sizeof(buffer);
if (appkey && !RegQueryValueExA(appkey, basename, 0, NULL, (LPBYTE)buffer, &buffer_size))
{
override_entry reg_entry;
memset(&reg_entry, 0, sizeof(reg_entry));
parse_override_entry(&reg_entry, buffer, strlen(buffer));
return reg_entry.flags;
}
buffer_size = sizeof(buffer);
if (userkey && !RegQueryValueExA(userkey, basename, 0, NULL, (LPBYTE)buffer, &buffer_size))
{
override_entry reg_entry;
memset(&reg_entry, 0, sizeof(reg_entry));
parse_override_entry(&reg_entry, buffer, strlen(buffer));
return reg_entry.flags;
}
return ASSEMBLY_SEARCH_UNDEFINED;
}
static HKEY get_app_overrides_key(void)
{
static const WCHAR subkeyW[] = {'\\','M','o','n','o','\\','A','s','m','O','v','e','r','r','i','d','e','s',0};
WCHAR bufferW[MAX_PATH+18];
HKEY appkey = 0;
DWORD len;
len = (GetModuleFileNameW( 0, bufferW, MAX_PATH ));
if (len && len < MAX_PATH)
{
HKEY tmpkey;
WCHAR *p, *appname = bufferW;
if ((p = strrchrW( appname, '/' ))) appname = p + 1;
if ((p = strrchrW( appname, '\\' ))) appname = p + 1;
strcatW( appname, subkeyW );
/* @@ Wine registry key: HKCU\Software\Wine\AppDefaults\app.exe\Mono\AsmOverrides */
if (!RegOpenKeyA( HKEY_CURRENT_USER, "Software\\Wine\\AppDefaults", &tmpkey ))
{
if (RegOpenKeyW( tmpkey, appname, &appkey )) appkey = 0;
RegCloseKey( tmpkey );
}
}
return appkey;
}
static DWORD get_assembly_search_flags(MonoAssemblyName *aname)
{
const char *name = mono_assembly_name_get_name(aname);
char *name_copy, *name_end;
DWORD result;
HKEY appkey = 0, userkey;
/* @@ Wine registry key: HKCU\Software\Wine\Mono\AsmOverrides */
if (RegOpenKeyA( HKEY_CURRENT_USER, "Software\\Wine\\Mono\\AsmOverrides", &userkey )) userkey = 0;
appkey = get_app_overrides_key();
result = get_basename_search_flags(name, aname, userkey, appkey);
if (result != ASSEMBLY_SEARCH_UNDEFINED)
{
if (userkey) RegCloseKey(userkey);
if (appkey) RegCloseKey(appkey);
return result;
}
name_copy = heap_alloc((strlen(name) + 3) * sizeof(WCHAR));
if (!name_copy)
{
ERR("out of memory\n");
if (userkey) RegCloseKey(userkey);
if (appkey) RegCloseKey(appkey);
return ASSEMBLY_SEARCH_DEFAULT;
}
strcpy(name_copy, name);
name_end = name_copy + strlen(name);
do
{
strcpy(name_end, ".*");
result = get_basename_search_flags(name_copy, aname, userkey, appkey);
if (result != ASSEMBLY_SEARCH_UNDEFINED) break;
*name_end = 0;
name_end = strrchr(name_copy, '.');
} while (name_end != NULL);
/* default flags */
if (result == ASSEMBLY_SEARCH_UNDEFINED)
{
result = get_basename_search_flags("*", aname, userkey, appkey);
if (result == ASSEMBLY_SEARCH_UNDEFINED)
result = ASSEMBLY_SEARCH_DEFAULT;
}
heap_free(name_copy);
if (appkey) RegCloseKey(appkey);
if (userkey) RegCloseKey(userkey);
return result;
}
HRESULT get_file_from_strongname(WCHAR* stringnameW, WCHAR* assemblies_path, int path_length)
{
HRESULT hr=S_OK;
@ -1229,6 +1500,7 @@ static MonoAssembly* CDECL mono_assembly_preload_hook_fn(MonoAssemblyName *aname
WCHAR path[MAX_PATH];
char *pathA;
MonoImageOpenStatus stat;
DWORD search_flags;
stringname = mono_stringify_assembly_name(aname);
@ -1236,38 +1508,45 @@ static MonoAssembly* CDECL mono_assembly_preload_hook_fn(MonoAssemblyName *aname
if (!stringname) return NULL;
search_flags = get_assembly_search_flags(aname);
/* FIXME: We should search the given paths before the GAC. */
stringnameW_size = MultiByteToWideChar(CP_UTF8, 0, stringname, -1, NULL, 0);
stringnameW = HeapAlloc(GetProcessHeap(), 0, stringnameW_size * sizeof(WCHAR));
if (stringnameW)
if ((search_flags & ASSEMBLY_SEARCH_GAC) != 0)
{
MultiByteToWideChar(CP_UTF8, 0, stringname, -1, stringnameW, stringnameW_size);
stringnameW_size = MultiByteToWideChar(CP_UTF8, 0, stringname, -1, NULL, 0);
hr = get_file_from_strongname(stringnameW, path, MAX_PATH);
HeapFree(GetProcessHeap(), 0, stringnameW);
}
else
hr = E_OUTOFMEMORY;
if (SUCCEEDED(hr))
{
TRACE("found: %s\n", debugstr_w(path));
pathA = WtoA(path);
if (pathA)
stringnameW = HeapAlloc(GetProcessHeap(), 0, stringnameW_size * sizeof(WCHAR));
if (stringnameW)
{
result = mono_assembly_open(pathA, &stat);
MultiByteToWideChar(CP_UTF8, 0, stringname, -1, stringnameW, stringnameW_size);
if (!result)
ERR("Failed to load %s, status=%u\n", debugstr_w(path), stat);
hr = get_file_from_strongname(stringnameW, path, MAX_PATH);
HeapFree(GetProcessHeap(), 0, pathA);
HeapFree(GetProcessHeap(), 0, stringnameW);
}
else
hr = E_OUTOFMEMORY;
if (SUCCEEDED(hr))
{
TRACE("found: %s\n", debugstr_w(path));
pathA = WtoA(path);
if (pathA)
{
result = mono_assembly_open(pathA, &stat);
if (!result)
ERR("Failed to load %s, status=%u\n", debugstr_w(path), stat);
HeapFree(GetProcessHeap(), 0, pathA);
}
}
}
else
TRACE("skipping Windows GAC search due to override setting\n");
mono_free(stringname);

View File

@ -144,6 +144,7 @@ extern BOOL is_mono_started DECLSPEC_HIDDEN;
extern MonoImage* (CDECL *mono_assembly_get_image)(MonoAssembly *assembly) DECLSPEC_HIDDEN;
extern MonoAssembly* (CDECL *mono_assembly_load_from)(MonoImage *image, const char *fname, MonoImageOpenStatus *status) DECLSPEC_HIDDEN;
extern const char* (CDECL *mono_assembly_name_get_name)(MonoAssemblyName *aname) DECLSPEC_HIDDEN;
extern MonoAssembly* (CDECL *mono_assembly_open)(const char *filename, MonoImageOpenStatus *status) DECLSPEC_HIDDEN;
extern void (CDECL *mono_callspec_set_assembly)(MonoAssembly *assembly) DECLSPEC_HIDDEN;
extern MonoClass* (CDECL *mono_class_from_mono_type)(MonoType *type) DECLSPEC_HIDDEN;