526 lines
14 KiB
C
526 lines
14 KiB
C
/*
|
|
* Module/Library loadorder
|
|
*
|
|
* Copyright 1999 Bertho Stultiens
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
|
|
#include "config.h"
|
|
#include "windef.h"
|
|
#include "options.h"
|
|
#include "loadorder.h"
|
|
#include "heap.h"
|
|
#include "module.h"
|
|
#include "elfdll.h"
|
|
#include "debugtools.h"
|
|
|
|
DEFAULT_DEBUG_CHANNEL(module)
|
|
|
|
|
|
/* #define DEBUG_LOADORDER */
|
|
|
|
#define LOADORDER_ALLOC_CLUSTER 32 /* Allocate with 32 entries at a time */
|
|
|
|
static module_loadorder_t default_loadorder;
|
|
static module_loadorder_t *module_loadorder = NULL;
|
|
static int nmodule_loadorder = 0;
|
|
static int nmodule_loadorder_alloc = 0;
|
|
|
|
static struct tagDllOverride {
|
|
char *key,*value;
|
|
} DefaultDllOverrides[] = {
|
|
{"kernel32,gdi32,user32", "builtin"},
|
|
{"krnl386,gdi,user", "builtin"},
|
|
{"toolhelp", "builtin"},
|
|
{"comdlg32,commdlg", "elfdll,builtin,native"},
|
|
{"version,ver", "elfdll,builtin,native"},
|
|
{"shell32,shell", "builtin,native"},
|
|
{"shlwapi", "native,builtin"},
|
|
{"lz32,lzexpand", "builtin,native"},
|
|
{"commctrl,comctl32", "builtin,native"},
|
|
{"wsock32,winsock", "builtin"},
|
|
{"advapi32,crtdll,ntdll", "builtin,native"},
|
|
{"mpr,winspool.drv", "builtin,native"},
|
|
{"ddraw,dinput,dsound", "builtin,native"},
|
|
{"winmm, mmsystem", "builtin"},
|
|
{"msvideo, msvfw32", "builtin, native"},
|
|
{"mcicda.drv, mciseq.drv", "builtin, native"},
|
|
{"mciwave.drv", "builtin, native"},
|
|
{"mciavi.drv, mcianim.drv", "native, builtin"},
|
|
{"msacm.drv, midimap.drv", "builtin, native"},
|
|
{"w32skrnl", "builtin"},
|
|
{"wnaspi32,wow32", "builtin"},
|
|
{"system,display,wprocs ", "builtin"},
|
|
{"wineps", "builtin"},
|
|
{"icmp", "builtin"},
|
|
/* we have to use libglide2x.so instead of glide2x.dll ... */
|
|
{"glide2x", "so,native"},
|
|
{"odbc32", "builtin"},
|
|
{"opengl32", "builtin,native"},
|
|
{NULL,NULL},
|
|
};
|
|
|
|
static const struct tagDllPair {
|
|
const char *dll1, *dll2;
|
|
} DllPairs[] = {
|
|
{ "krnl386", "kernel32" },
|
|
{ "gdi", "gdi32" },
|
|
{ "user", "user32" },
|
|
{ "commdlg", "comdlg32" },
|
|
{ "commctrl", "comctl32" },
|
|
{ "ver", "version" },
|
|
{ "shell", "shell32" },
|
|
{ "lzexpand", "lz32" },
|
|
{ "mmsystem", "winmm" },
|
|
{ "msvideo", "msvfw32" },
|
|
{ "winsock", "wsock32" },
|
|
{ NULL, NULL }
|
|
};
|
|
|
|
/***************************************************************************
|
|
* cmp_sort_func (internal, static)
|
|
*
|
|
* Sorting and comparing function used in sort and search of loadorder
|
|
* entries.
|
|
*/
|
|
static int cmp_sort_func(const void *s1, const void *s2)
|
|
{
|
|
return strcasecmp(((module_loadorder_t *)s1)->modulename, ((module_loadorder_t *)s2)->modulename);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
* get_tok (internal, static)
|
|
*
|
|
* strtok wrapper for non-destructive buffer writing.
|
|
* NOTE: strtok is not reentrant and therefore this code is neither.
|
|
*/
|
|
static char *get_tok(const char *str, const char *delim)
|
|
{
|
|
static char *buf = NULL;
|
|
char *cptr;
|
|
|
|
if(!str && !buf)
|
|
return NULL;
|
|
|
|
if(str && buf)
|
|
{
|
|
HeapFree(GetProcessHeap(), 0, buf);
|
|
buf = NULL;
|
|
}
|
|
|
|
if(str && !buf)
|
|
{
|
|
buf = HEAP_strdupA(GetProcessHeap(), 0, str);
|
|
cptr = strtok(buf, delim);
|
|
}
|
|
else
|
|
{
|
|
cptr = strtok(NULL, delim);
|
|
}
|
|
|
|
if(!cptr)
|
|
{
|
|
HeapFree(GetProcessHeap(), 0, buf);
|
|
buf = NULL;
|
|
}
|
|
return cptr;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
* ParseLoadOrder (internal, static)
|
|
*
|
|
* Parses the loadorder options from the configuration and puts it into
|
|
* a structure.
|
|
*/
|
|
static BOOL ParseLoadOrder(char *order, module_loadorder_t *mlo)
|
|
{
|
|
char *cptr;
|
|
int n = 0;
|
|
|
|
memset(mlo->loadorder, 0, sizeof(mlo->loadorder));
|
|
|
|
cptr = get_tok(order, ", \t");
|
|
while(cptr)
|
|
{
|
|
char type = MODULE_LOADORDER_INVALID;
|
|
|
|
if(n >= MODULE_LOADORDER_NTYPES)
|
|
{
|
|
ERR("More than existing %d module-types specified, rest ignored", MODULE_LOADORDER_NTYPES);
|
|
break;
|
|
}
|
|
|
|
switch(*cptr)
|
|
{
|
|
case 'N': /* Native */
|
|
case 'n': type = MODULE_LOADORDER_DLL; break;
|
|
|
|
case 'E': /* Elfdll */
|
|
case 'e': type = MODULE_LOADORDER_ELFDLL; break;
|
|
|
|
case 'S': /* So */
|
|
case 's': type = MODULE_LOADORDER_SO; break;
|
|
|
|
case 'B': /* Builtin */
|
|
case 'b': type = MODULE_LOADORDER_BI; break;
|
|
|
|
default:
|
|
ERR("Invalid load order module-type '%s', ignored\n", cptr);
|
|
}
|
|
|
|
if(type != MODULE_LOADORDER_INVALID)
|
|
{
|
|
mlo->loadorder[n++] = type;
|
|
}
|
|
cptr = get_tok(NULL, ", \t");
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
* AddLoadOrder (internal, static)
|
|
*
|
|
* Adds an entry in the list of overrides. If the entry exists, then the
|
|
* override parameter determines whether it will be overwritten.
|
|
*/
|
|
static BOOL AddLoadOrder(module_loadorder_t *plo, BOOL override)
|
|
{
|
|
int i;
|
|
|
|
/* TRACE(module, "'%s' -> %08lx\n", plo->modulename, *(DWORD *)(plo->loadorder)); */
|
|
|
|
for(i = 0; i < nmodule_loadorder; i++)
|
|
{
|
|
if(!cmp_sort_func(plo, &module_loadorder[i]))
|
|
{
|
|
if(!override)
|
|
ERR("Module '%s' is already in the list of overrides, using first definition\n", plo->modulename);
|
|
else
|
|
memcpy(module_loadorder[i].loadorder, plo->loadorder, sizeof(plo->loadorder));
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
if(nmodule_loadorder >= nmodule_loadorder_alloc)
|
|
{
|
|
/* No space in current array, make it larger */
|
|
nmodule_loadorder_alloc += LOADORDER_ALLOC_CLUSTER;
|
|
module_loadorder = (module_loadorder_t *)HeapReAlloc(GetProcessHeap(),
|
|
0,
|
|
module_loadorder,
|
|
nmodule_loadorder_alloc * sizeof(module_loadorder_t));
|
|
if(!module_loadorder)
|
|
{
|
|
MESSAGE("Virtual memory exhausted\n");
|
|
exit(1);
|
|
}
|
|
}
|
|
memcpy(module_loadorder[nmodule_loadorder].loadorder, plo->loadorder, sizeof(plo->loadorder));
|
|
module_loadorder[nmodule_loadorder].modulename = HEAP_strdupA(GetProcessHeap(), 0, plo->modulename);
|
|
nmodule_loadorder++;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
* AddLoadOrderSet (internal, static)
|
|
*
|
|
* Adds a set of entries in the list of overrides from the key parameter.
|
|
* If the entry exists, then the override parameter determines whether it
|
|
* will be overwritten.
|
|
*/
|
|
static BOOL AddLoadOrderSet(char *key, char *order, BOOL override)
|
|
{
|
|
module_loadorder_t ldo;
|
|
char *cptr;
|
|
|
|
/* Parse the loadorder before the rest because strtok is not reentrant */
|
|
if(!ParseLoadOrder(order, &ldo))
|
|
return FALSE;
|
|
|
|
cptr = get_tok(key, ", \t");
|
|
while(cptr)
|
|
{
|
|
char *ext = strrchr(cptr, '.');
|
|
if(ext)
|
|
{
|
|
if(strlen(ext) == 4 && (!strcasecmp(ext, ".dll") || !strcasecmp(ext, ".exe")))
|
|
MESSAGE("Warning: Loadorder override '%s' contains an extension and might not be found during lookup\n", cptr);
|
|
}
|
|
|
|
ldo.modulename = cptr;
|
|
if(!AddLoadOrder(&ldo, override))
|
|
return FALSE;
|
|
cptr = get_tok(NULL, ", \t");
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
* ParseCommandlineOverrides (internal, static)
|
|
*
|
|
* The commandline is in the form:
|
|
* name[,name,...]=native[,b,...][:...]
|
|
*/
|
|
static BOOL ParseCommandlineOverrides(void)
|
|
{
|
|
char *cpy;
|
|
char *key;
|
|
char *next;
|
|
char *value;
|
|
BOOL retval = TRUE;
|
|
|
|
if(!Options.dllFlags)
|
|
return TRUE;
|
|
|
|
cpy = HEAP_strdupA(GetProcessHeap(), 0, Options.dllFlags);
|
|
key = cpy;
|
|
next = key;
|
|
for(; next; key = next)
|
|
{
|
|
next = strchr(key, ':');
|
|
if(next)
|
|
{
|
|
*next = '\0';
|
|
next++;
|
|
}
|
|
value = strchr(key, '=');
|
|
if(!value)
|
|
{
|
|
retval = FALSE;
|
|
goto endit;
|
|
}
|
|
*value = '\0';
|
|
value++;
|
|
|
|
TRACE("Commandline override '%s' = '%s'\n", key, value);
|
|
|
|
if(!AddLoadOrderSet(key, value, TRUE))
|
|
{
|
|
retval = FALSE;
|
|
goto endit;
|
|
}
|
|
}
|
|
endit:
|
|
HeapFree(GetProcessHeap(), 0, cpy);
|
|
return retval;;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
* MODULE_InitLoadOrder (internal)
|
|
*
|
|
* Initialize the load order from the wine.conf file.
|
|
* The section has the following format:
|
|
* Section:
|
|
* [DllDefaults]
|
|
*
|
|
* Keys:
|
|
* EXTRA_LD_LIBRARY_PATH=/usr/local/lib/wine[:/more/path/to/search[:...]]
|
|
* The path will be appended to any existing LD_LIBRARY_PATH from the
|
|
* environment (see note in code below).
|
|
*
|
|
* DefaultLoadOrder=native,elfdll,so,builtin
|
|
* A comma separated list of module types to try to load in that specific
|
|
* order. The DefaultLoadOrder key is used as a fallback when a module is
|
|
* not specified explicitly. If the DefaultLoadOrder key is not found,
|
|
* then the order "dll,elfdll,so,bi" is used
|
|
* The possible module types are:
|
|
* - native Native windows dll files
|
|
* - elfdll Dlls encapsulated in .so libraries
|
|
* - so Native .so libraries mapped to dlls
|
|
* - builtin Built-in modules
|
|
*
|
|
* Case is not important and only the first letter of each type is enough to
|
|
* identify the type n[ative], e[lfdll], s[o], b[uiltin]. Also whitespace is
|
|
* ignored.
|
|
* E.g.:
|
|
* n,el ,s , b
|
|
* is equal to:
|
|
* native,elfdll,so,builtin
|
|
*
|
|
* Section:
|
|
* [DllOverrides]
|
|
*
|
|
* Keys:
|
|
* There are no explicit keys defined other than module/library names. A comma
|
|
* separated list of modules is followed by an assignment of the load-order
|
|
* for these specific modules. See above for possible types. You should not
|
|
* specify an extension.
|
|
* Examples:
|
|
* kernel32, gdi32, user32 = builtin
|
|
* kernel, gdi, user = builtin
|
|
* comdlg32 = elfdll, native, builtin
|
|
* commdlg = native, builtin
|
|
* version, ver = elfdll, native, builtin
|
|
*
|
|
*/
|
|
|
|
#define BUFFERSIZE 1024
|
|
|
|
BOOL MODULE_InitLoadOrder(void)
|
|
{
|
|
char buffer[BUFFERSIZE];
|
|
char key[256];
|
|
int nbuffer;
|
|
int idx;
|
|
const struct tagDllPair *dllpair;
|
|
|
|
#if defined(HAVE_DL_API)
|
|
/* Get/set the new LD_LIBRARY_PATH */
|
|
nbuffer = PROFILE_GetWineIniString("DllDefaults", "EXTRA_LD_LIBRARY_PATH", "", buffer, sizeof(buffer));
|
|
|
|
if(nbuffer)
|
|
{
|
|
extra_ld_library_path = HEAP_strdupA(GetProcessHeap(), 0, buffer);
|
|
TRACE("Setting extra LD_LIBRARY_PATH=%s\n", buffer);
|
|
}
|
|
#endif
|
|
|
|
/* Get the default load order */
|
|
nbuffer = PROFILE_GetWineIniString("DllDefaults", "DefaultLoadOrder", "n,b,e,s", buffer, sizeof(buffer));
|
|
if(!nbuffer)
|
|
{
|
|
MESSAGE("MODULE_InitLoadOrder: mysteriously read nothing from default loadorder\n");
|
|
return FALSE;
|
|
}
|
|
|
|
TRACE("Setting default loadorder=%s\n", buffer);
|
|
|
|
if(!ParseLoadOrder(buffer, &default_loadorder))
|
|
return FALSE;
|
|
default_loadorder.modulename = "<none>";
|
|
|
|
{
|
|
int i;
|
|
for (i=0;DefaultDllOverrides[i].key;i++)
|
|
AddLoadOrderSet(
|
|
DefaultDllOverrides[i].key,
|
|
DefaultDllOverrides[i].value,
|
|
FALSE
|
|
);
|
|
}
|
|
|
|
/* Read the explicitely defined orders for specific modules as an entire section */
|
|
idx = 0;
|
|
while (PROFILE_EnumWineIniString( "DllOverrides", idx++, key, sizeof(key),
|
|
buffer, sizeof(buffer)))
|
|
{
|
|
TRACE("Key '%s' uses override '%s'\n", key, buffer);
|
|
if(!AddLoadOrderSet(key, buffer, TRUE))
|
|
return FALSE;
|
|
}
|
|
|
|
/* Add the commandline overrides to the pool */
|
|
if(!ParseCommandlineOverrides())
|
|
{
|
|
MESSAGE( "Syntax: -dll name[,name[,...]]={native|elfdll|so|builtin}[,{n|e|s|b}[,...]][:...]\n"
|
|
" - 'name' is the name of any dll without extension\n"
|
|
" - the order of loading (native, elfdll, so and builtin) can be abbreviated\n"
|
|
" with the first letter\n"
|
|
" - different loadorders for different dlls can be specified by seperating the\n"
|
|
" commandline entries with a ':'\n"
|
|
" Example:\n"
|
|
" -dll comdlg32,commdlg=n:shell,shell32=b\n"
|
|
);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Sort the array for quick lookup */
|
|
qsort(module_loadorder, nmodule_loadorder, sizeof(module_loadorder[0]), cmp_sort_func);
|
|
|
|
/* Check the pairs of dlls */
|
|
dllpair = DllPairs;
|
|
while (dllpair->dll1)
|
|
{
|
|
module_loadorder_t *plo1, *plo2;
|
|
plo1 = MODULE_GetLoadOrder(dllpair->dll1);
|
|
plo2 = MODULE_GetLoadOrder(dllpair->dll2);
|
|
assert(plo1 && plo2);
|
|
if(memcmp(plo1->loadorder, plo2->loadorder, sizeof(plo1->loadorder)))
|
|
MESSAGE("Warning: Modules '%s' and '%s' have different loadorder which may cause trouble\n", dllpair->dll1, dllpair->dll2);
|
|
dllpair++;
|
|
}
|
|
|
|
if(TRACE_ON(module))
|
|
{
|
|
int i, j;
|
|
static char types[6] = "-NESB";
|
|
|
|
for(i = 0; i < nmodule_loadorder; i++)
|
|
{
|
|
DPRINTF("%3d: %-12s:", i, module_loadorder[i].modulename);
|
|
for(j = 0; j < MODULE_LOADORDER_NTYPES; j++)
|
|
DPRINTF(" %c", types[module_loadorder[i].loadorder[j] % (MODULE_LOADORDER_NTYPES+1)]);
|
|
DPRINTF("\n");
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
* MODULE_GetLoadOrder (internal)
|
|
*
|
|
* Locate the loadorder of a module.
|
|
* Any path is stripped from the path-argument and so are the extension
|
|
* '.dll' and '.exe'. A lookup in the table can yield an override for
|
|
* the specific dll. Otherwise the default load order is returned.
|
|
*/
|
|
module_loadorder_t *MODULE_GetLoadOrder(const char *path)
|
|
{
|
|
module_loadorder_t lo, *tmp;
|
|
char fname[256];
|
|
char *cptr;
|
|
char *name;
|
|
int len;
|
|
|
|
TRACE("looking for %s\n", path);
|
|
|
|
assert(path != NULL);
|
|
|
|
/* Strip path information */
|
|
cptr = strrchr(path, '\\');
|
|
if(!cptr)
|
|
name = strrchr(path, '/');
|
|
else
|
|
name = strrchr(cptr, '/');
|
|
|
|
if(!name)
|
|
name = cptr ? cptr+1 : (char *)path;
|
|
else
|
|
name++;
|
|
|
|
if((cptr = strchr(name, ':')) != NULL) /* Also strip drive if in format 'C:MODULE.DLL' */
|
|
name = cptr+1;
|
|
|
|
len = strlen(name);
|
|
if(len >= sizeof(fname) || len <= 0)
|
|
{
|
|
ERR("Path '%s' -> '%s' reduces to zilch or just too large...\n", path, name);
|
|
return &default_loadorder;
|
|
}
|
|
|
|
strcpy(fname, name);
|
|
if(len >= 4 && (!lstrcmpiA(fname+len-4, ".dll") || !lstrcmpiA(fname+len-4, ".exe")))
|
|
fname[len-4] = '\0';
|
|
|
|
lo.modulename = fname;
|
|
tmp = bsearch(&lo, module_loadorder, nmodule_loadorder, sizeof(module_loadorder[0]), cmp_sort_func);
|
|
|
|
TRACE("Looking for '%s' (%s), found '%s'\n", path, fname, tmp ? tmp->modulename : "<nothing>");
|
|
|
|
if(!tmp)
|
|
return &default_loadorder;
|
|
return tmp;
|
|
}
|
|
|