PATCH: loading and using windows kernel drivers

Marcus Meissner marcus at jet.franken.de
Sun Feb 15 15:51:54 CST 2004


Hi,

This implements some very basic and functional kernel driver loading
and execution.

Currently only tested with one driver and with just DeviceIoControl
support.

wdm.c is a new file, attached.

Ciao, Marcus

Changelog:
	Added a possibility to load Windows WDM kernel drivers,
	a bit hackish.

	Implemented a seperate PE loader for WDMs, but reuse
	common functionality from normal PE loader (relocation
	fixup, relayhandling, module override.) Uses NtLoadDriver.

	Implemented DeviceIoControl passing to kernel drivers. 

Index: Makefile.in
===================================================================
RCS file: /home/wine/wine/dlls/ntdll/Makefile.in,v
retrieving revision 1.94
diff -u -r1.94 Makefile.in
--- dlls/ntdll/Makefile.in	11 Nov 2003 22:21:29 -0000	1.94
+++ dlls/ntdll/Makefile.in	15 Feb 2004 20:32:21 -0000
@@ -40,7 +40,8 @@
 	thread.c \
 	time.c \
 	virtual.c \
-	wcstring.c
+	wcstring.c \
+	wdm.c
 
 ASM_SRCS = relay32.s
 
Index: file.c
===================================================================
RCS file: /home/wine/wine/dlls/ntdll/file.c,v
retrieving revision 1.37
diff -u -r1.37 file.c
--- dlls/ntdll/file.c	23 Jan 2004 01:51:34 -0000	1.37
+++ dlls/ntdll/file.c	15 Feb 2004 20:32:22 -0000
@@ -686,14 +686,24 @@
                               UserApcRoutine, UserApcContext,
                               IoStatusBlock, IoControlCode,
                               InputBuffer, InputBufferSize,
-                              OutputBuffer, OutputBufferSize) == STATUS_NO_SUCH_DEVICE)
-    {
-        /* it wasn't a CDROM */
-        FIXME("Unimplemented dwIoControlCode=%08lx\n", IoControlCode);
-        IoStatusBlock->u.Status = STATUS_NOT_IMPLEMENTED;
-        IoStatusBlock->Information = 0;
-        if (hEvent) NtSetEvent(hEvent, NULL);
-    }
+                              OutputBuffer, OutputBufferSize) == STATUS_SUCCESS)
+        return IoStatusBlock->u.Status;
+
+    /* It wasn't a CDROM */
+
+    if (WDM_DeviceIoControl(clientID, DeviceHandle, hEvent,
+                            UserApcRoutine, UserApcContext,
+                            IoStatusBlock, IoControlCode,
+                            InputBuffer, InputBufferSize,
+                            OutputBuffer, OutputBufferSize) == STATUS_SUCCESS)
+        return IoStatusBlock->u.Status;
+
+    /* It was no WDM either. */
+
+    FIXME("Unimplemented dwIoControlCode=%08lx, clientid %lx\n", IoControlCode, clientID);
+    IoStatusBlock->u.Status = STATUS_NOT_IMPLEMENTED;
+    IoStatusBlock->Information = 0;
+    if (hEvent) NtSetEvent(hEvent, NULL);
     return IoStatusBlock->u.Status;
 }
 
Index: loader.c
===================================================================
RCS file: /home/wine/wine/dlls/ntdll/loader.c,v
retrieving revision 1.63
diff -u -r1.63 loader.c
--- dlls/ntdll/loader.c	7 Feb 2004 01:11:54 -0000	1.63
+++ dlls/ntdll/loader.c	15 Feb 2004 20:32:23 -0000
@@ -67,14 +67,6 @@
 
 static const WCHAR dllW[] = {'.','d','l','l',0};
 
-/* internal representation of 32bit modules. per process. */
-typedef struct _wine_modref
-{
-    LDR_MODULE            ldr;
-    int                   nDeps;
-    struct _wine_modref **deps;
-} WINE_MODREF;
-
 /* info about the current builtin dll load */
 /* used to keep track of things across the register_dll constructor call */
 struct builtin_load_info
@@ -128,12 +120,12 @@
 }
 
 /*************************************************************************
- *		get_modref
+ *		PE_get_modref
  *
  * Looks for the referenced HMODULE in the current process
  * The loader_section must be locked while calling this function.
  */
-static WINE_MODREF *get_modref( HMODULE hmod )
+WINE_MODREF *PE_get_modref( HMODULE hmod )
 {
     PLIST_ENTRY mark, entry;
     PLDR_MODULE mod;
@@ -229,7 +221,7 @@
     if (!(wm = find_basename_module( mod_name )))
     {
         ERR("module not found for forward '%s' used by %s\n",
-            forward, debugstr_w(get_modref(module)->ldr.FullDllName.Buffer) );
+            forward, debugstr_w(PE_get_modref(module)->ldr.FullDllName.Buffer) );
         return NULL;
     }
     if ((exports = RtlImageDirectoryEntryToData( wm->ldr.BaseAddress, TRUE,
@@ -240,8 +232,8 @@
     {
         ERR("function not found for forward '%s' used by %s."
             " If you are using builtin %s, try using the native one instead.\n",
-            forward, debugstr_w(get_modref(module)->ldr.FullDllName.Buffer),
-            debugstr_w(get_modref(module)->ldr.BaseDllName.Buffer) );
+            forward, debugstr_w(PE_get_modref(module)->ldr.FullDllName.Buffer),
+            debugstr_w(PE_get_modref(module)->ldr.BaseDllName.Buffer) );
     }
     return proc;
 }
@@ -874,7 +866,7 @@
 
     RtlEnterCriticalSection( &loader_section );
 
-    wm = get_modref( hModule );
+    wm = PE_get_modref( hModule );
     if (!wm || wm->ldr.TlsIndex != -1)
         ret = STATUS_DLL_NOT_FOUND;
     else
@@ -1027,7 +1019,7 @@
     else
     {
         /* check if the module itself is invalid to return the proper error */
-        if (!get_modref( module )) ret = STATUS_DLL_NOT_FOUND;
+        if (!PE_get_modref( module )) ret = STATUS_DLL_NOT_FOUND;
     }
 
     RtlLeaveCriticalSection( &loader_section );
@@ -1060,6 +1052,11 @@
         builtin_load_info->status = STATUS_INVALID_IMAGE_FORMAT;
         return;
     }
+
+    extern void VXD_load_builtin_callback(void *module, const char *filename);
+    if (nt->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_NATIVE)
+	return VXD_load_builtin_callback(module,filename);
+
     if (!(nt->FileHeader.Characteristics & IMAGE_FILE_DLL))
     {
         /* if we already have an executable, ignore this one */
@@ -1429,7 +1426,7 @@
         return STATUS_SUCCESS;
     }
 
-    main_exe = get_modref( NtCurrentTeb()->Peb->ImageBaseAddress );
+    main_exe = PE_get_modref( NtCurrentTeb()->Peb->ImageBaseAddress );
     MODULE_GetLoadOrderW( loadorder, main_exe ? main_exe->ldr.BaseDllName.Buffer : NULL, filename );
 
     nts = STATUS_DLL_NOT_FOUND;
@@ -1705,7 +1702,7 @@
         WINE_MODREF *wm;
 
         free_lib_count++;
-        if ((wm = get_modref( hModule )) != NULL)
+        if ((wm = PE_get_modref( hModule )) != NULL)
         {
             TRACE("(%s) - START\n", debugstr_w(wm->ldr.BaseDllName.Buffer));
 
Index: ntdll.spec
===================================================================
RCS file: /home/wine/wine/dlls/ntdll/ntdll.spec,v
retrieving revision 1.144
diff -u -r1.144 ntdll.spec
--- dlls/ntdll/ntdll.spec	18 Jan 2004 22:11:52 -0000	1.144
+++ dlls/ntdll/ntdll.spec	15 Feb 2004 20:32:23 -0000
@@ -121,7 +121,7 @@
 @ stub NtImpersonateThread
 @ stub NtInitializeRegistry
 @ stdcall NtListenPort(ptr ptr)
-@ stub NtLoadDriver
+@ stdcall NtLoadDriver(ptr ptr)
 @ stdcall NtLoadKey(ptr ptr)
 @ stdcall NtLockFile(long long ptr ptr ptr ptr ptr ptr long long)
 @ stdcall NtLockVirtualMemory(long ptr ptr long)
Index: ntdll_misc.h
===================================================================
RCS file: /home/wine/wine/dlls/ntdll/ntdll_misc.h,v
retrieving revision 1.37
diff -u -r1.37 ntdll_misc.h
--- dlls/ntdll/ntdll_misc.h	18 Jan 2004 22:11:52 -0000	1.37
+++ dlls/ntdll/ntdll_misc.h	15 Feb 2004 20:32:23 -0000
@@ -37,6 +37,18 @@
 #define SIGNAL_STACK_SIZE  0  /* we don't need a signal stack on non-i386 */
 #endif
 
+/* internal representation of 32bit modules. per process. */
+typedef struct _wine_modref
+{
+    LDR_MODULE            ldr;
+    int                   nDeps;
+    struct _wine_modref **deps;
+} WINE_MODREF;
+
+WINE_MODREF *PE_get_modref( HMODULE hmod );
+int PE_do_relocations( char*, const IMAGE_DATA_DIRECTORY*, int, DWORD);
+
+
 /* debug helper */
 extern LPCSTR debugstr_us( const UNICODE_STRING *str );
 extern void dump_ObjectAttributes (const OBJECT_ATTRIBUTES *ObjectAttributes);
@@ -96,6 +108,15 @@
                                       ULONG IoControlCode,
                                       LPVOID lpInBuffer, DWORD nInBufferSize,
                                       LPVOID lpOutBuffer, DWORD nOutBufferSize);
+
+/* ntdll/wdm.c */
+extern NTSTATUS WDM_DeviceIoControl(DWORD clientID, HANDLE hDevice, 
+                                    HANDLE hEvent, PIO_APC_ROUTINE UserApcRoutine,
+                                    PVOID UserApcContext, 
+                                    PIO_STATUS_BLOCK piosb, 
+                                    ULONG IoControlCode,
+                                    LPVOID lpInBuffer, DWORD nInBufferSize,
+                                    LPVOID lpOutBuffer, DWORD nOutBufferSize);
 
 /* memory/virtual.c */
 typedef BOOL (*HANDLERPROC)(LPVOID, LPCVOID);
Index: relay.c
===================================================================
RCS file: /home/wine/wine/dlls/ntdll/relay.c,v
retrieving revision 1.8
diff -u -r1.8 relay.c
--- dlls/ntdll/relay.c	1 Dec 2003 22:49:33 -0000	1.8
+++ dlls/ntdll/relay.c	15 Feb 2004 20:32:24 -0000
@@ -390,17 +392,33 @@
             break;
         }
     }
+    if (entry == mark) {
+	extern PEB_LDR_DATA driverldr;
 
+	mark = &driverldr.InLoadOrderModuleList;
+	for (entry = mark->Flink; entry && (entry != mark); entry = entry->Flink)
+	{
+	    mod = CONTAINING_RECORD(entry, LDR_MODULE, InLoadOrderModuleList);
+	    if (!(mod->Flags & LDR_WINE_INTERNAL)) continue;
+	    exp = RtlImageDirectoryEntryToData( mod->BaseAddress, TRUE, IMAGE_DIRECTORY_ENTRY_EXPORT, &size );
+	    if (!exp) continue;
+	    debug = (DEBUG_ENTRY_POINT *)((char *)exp + size);
+	    if (debug <= relay && relay < debug + exp->NumberOfFunctions)
+	    {
+		ordinal = relay - debug;
+		break;
+	    }
+	}
+    }
     /* Now find the function */
-
     strcpy( buffer, (char *)mod->BaseAddress + exp->Name );
     p = buffer + strlen(buffer);
     if (p > buffer + 4 && !strcasecmp( p - 4, ".dll" )) p -= 4;
 
     if ((name = find_exported_name( mod->BaseAddress, exp, ordinal + exp->Base )))
-        sprintf( p, ".%s", name );
+	sprintf( p, ".%s", name );
     else
-        sprintf( p, ".%ld", ordinal + exp->Base );
+	sprintf( p, ".%ld", ordinal + exp->Base );
 }
 
 
Index: virtual.c
===================================================================
RCS file: /home/wine/wine/dlls/ntdll/virtual.c,v
retrieving revision 1.27
diff -u -r1.27 virtual.c
--- dlls/ntdll/virtual.c	11 Feb 2004 23:56:52 -0000	1.27
+++ dlls/ntdll/virtual.c	15 Feb 2004 20:32:25 -0000
@@ -506,11 +506,11 @@
 
 
 /***********************************************************************
- *           do_relocations
+ *           PE_do_relocations
  *
  * Apply the relocations to a mapped PE image
  */
-static int do_relocations( char *base, const IMAGE_DATA_DIRECTORY *dir,
+int PE_do_relocations( char *base, const IMAGE_DATA_DIRECTORY *dir,
                            int delta, DWORD total_size )
 {
     IMAGE_BASE_RELOCATION *rel;
@@ -739,7 +739,7 @@
         if ((nt->OptionalHeader.ImageBase & 0x80000000) && !((DWORD)base & 0x80000000))
             ERR( "Forced to relocate system DLL (base > 2GB). This is not good.\n" );
 
-        if (!do_relocations( ptr, relocs, ptr - base, total_size ))
+        if (!PE_do_relocations( ptr, relocs, ptr - base, total_size ))
         {
             goto error;
         }
-- 
-------------- next part --------------
/*
 * WMD Loader functions
 *
 * Copyright 1995, 2003 Alexandre Julliard
 * Copyright 2002 Dmitry Timoshkov for CodeWeavers
 * Copyright 2004 Marcus Meissner
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "config.h"
#include "wine/port.h"

#include <assert.h>
#include <stdarg.h>
#include <ctype.h>

#include "windef.h"
#include "winbase.h"
#include "winnt.h"
#include "winreg.h"
#include "winternl.h"

#include "module.h"
#include "wine/exception.h"
#include "excpt.h"
#include "wine/unicode.h"
#include "wine/debug.h"
#include "wine/server.h"
#include "ntdll_misc.h"

WINE_DEFAULT_DEBUG_CHANNEL(module);
WINE_DECLARE_DEBUG_CHANNEL(relay);
WINE_DECLARE_DEBUG_CHANNEL(snoop);
WINE_DECLARE_DEBUG_CHANNEL(loaddll);

static const WCHAR dllW[] = {'.','d','l','l',0};

/* info about the current builtin dll load */
/* used to keep track of things across the register_dll constructor call */
struct builtin_load_info
{
    const WCHAR *load_path;
    NTSTATUS     status;
    WINE_MODREF *wm;
};

PEB_LDR_DATA driverldr;
static struct builtin_load_info default_load_info;
static struct builtin_load_info *builtin_load_info = &default_load_info;

static UNICODE_STRING system_dir;  /* system directory */

static CRITICAL_SECTION loader_section;
static CRITICAL_SECTION_DEBUG critsect_debug =
{
    0, 0, &loader_section,
    { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
      0, 0, { 0, (DWORD)(__FILE__ ": loader_section") }
};
static CRITICAL_SECTION loader_section = { &critsect_debug, -1, 0, 0, 0, 0 };

static WINE_MODREF *cached_modref;
static WINE_MODREF *current_modref;

static NTSTATUS load_dll( LPCWSTR load_path, LPCWSTR libname, DWORD flags, WINE_MODREF** pwm );
static FARPROC find_named_export( HMODULE module, IMAGE_EXPORT_DIRECTORY *exports,
                                  DWORD exp_size, const char *name, int hint );

/* Internal WDM structures gleamed from ReactOS and Captive NTFS.
 * For more complete information look there.
 *
 * Thanks guys!
 */
enum
{
     IRP_MJ_CREATE,
     IRP_MJ_CREATE_NAMED_PIPE,
     IRP_MJ_CLOSE,
     IRP_MJ_READ,
     IRP_MJ_WRITE,
     IRP_MJ_QUERY_INFORMATION,
     IRP_MJ_SET_INFORMATION,
     IRP_MJ_QUERY_EA,
     IRP_MJ_SET_EA,
     IRP_MJ_FLUSH_BUFFERS,
     IRP_MJ_QUERY_VOLUME_INFORMATION,
     IRP_MJ_SET_VOLUME_INFORMATION,
     IRP_MJ_DIRECTORY_CONTROL,
     IRP_MJ_FILE_SYSTEM_CONTROL,
     IRP_MJ_DEVICE_CONTROL,
     IRP_MJ_INTERNAL_DEVICE_CONTROL,
     IRP_MJ_SHUTDOWN,
     IRP_MJ_LOCK_CONTROL,
     IRP_MJ_CLEANUP,
     IRP_MJ_CREATE_MAILSLOT,
     IRP_MJ_QUERY_SECURITY,
     IRP_MJ_SET_SECURITY,
     IRP_MJ_POWER,
     IRP_MJ_SYSTEM_CONTROL,
     IRP_MJ_DEVICE_CHANGE,
     IRP_MJ_QUERY_QUOTA,
     IRP_MJ_SET_QUOTA,
     IRP_MJ_PNP,
     IRP_MJ_MAXIMUM_FUNCTION,
};

/* IO request thing */
typedef struct _xirp {
	DWORD				a1[6];
	IO_STATUS_BLOCK			iostatusblock;
	DWORD				a2[7];
	LPVOID				userbuffer;
	DWORD				a3[8];
	struct _io_stack_location	*iostack;
} IRP;

/* the device object for the opened files. We do not need 
 * a full structure.
 */
typedef struct _device_object {
	DWORD emptyfornow;
} DEVICE_OBJECT;

/* generic driver dispatch for the IRP_MJ_ calls */
typedef NTSTATUS WINAPI (*PDRIVER_DISPATCH)(DEVICE_OBJECT*,IRP*);

/* The driver object passed back by the driver.
 * Filled out to a large degree by the driver itself.
 */
typedef struct _driver_object {
	DWORD	a1[6];
	struct _driver_extension *drvext;
	DWORD	a2[7];
	PDRIVER_DISPATCH drvdsp[IRP_MJ_MAXIMUM_FUNCTION];
} DRIVER_OBJECT;

typedef struct _driver_extension {
	struct _driver_object *dob;
	DWORD adddevice;
	ULONG cnt;
	UNICODE_STRING servname;
} DRIVER_EXTENSION;

typedef struct _io_stack_location {
	UCHAR majorfunc;
	UCHAR minorfunc;
	UCHAR flags;
	UCHAR control;
	union {
		struct _devioctl {
			ULONG obuflen;
			ULONG ibuflen;
			ULONG ioctrlcode;
			PVOID type3input;
		} devioctrl;
	} u;
} IO_STACK_LOCATION;

/* our linked lists of loaded windows drivers. */
static struct loadeddriver {
	UNICODE_STRING	ustr;
	DWORD		id;
	DRIVER_OBJECT	drvob;
} *loadeddrivers = NULL;
static int nrofloadeddrivers = 0;

static DWORD driverid = 0x4243;

typedef DWORD WINAPI (*driverfunc)(struct _driver_object*,void*);

static void
WDM_Init() {
    static int initialised = 0;

    if (initialised) return;
    initialised = 1;
    InitializeListHead( &driverldr.InLoadOrderModuleList );
    InitializeListHead( &driverldr.InMemoryOrderModuleList );
    InitializeListHead( &driverldr.InInitializationOrderModuleList );
}

/* convert PE image VirtualAddress to Real Address */
inline static void *get_rva( HMODULE module, DWORD va )
{
    return (void *)((char *)module + va);
}

/* check whether the file name contains a path */
inline static int contains_path( LPCWSTR name )
{
    return ((*name && (name[1] == ':')) || strchrW(name, '/') || strchrW(name, '\\'));
}

/* convert from straight ASCII to Unicode without depending on the current codepage */
inline static void ascii_to_unicode( WCHAR *dst, const char *src, size_t len )
{
    while (len--) *dst++ = (unsigned char)*src++;
}

/*************************************************************************
 *		get_modref
 *
 * Looks for the referenced HMODULE in the current process
 * The loader_section must be locked while calling this function.
 */
static WINE_MODREF *get_modref( HMODULE hmod )
{
    PLIST_ENTRY mark, entry;
    PLDR_MODULE mod;

    if (cached_modref && cached_modref->ldr.BaseAddress == hmod) return cached_modref;

    mark = &driverldr.InMemoryOrderModuleList;
    for (entry = mark->Flink; entry && (entry != mark); entry = entry->Flink)
    {
        mod = CONTAINING_RECORD(entry, LDR_MODULE, InMemoryOrderModuleList);
        if (mod->BaseAddress == hmod)
            return cached_modref = CONTAINING_RECORD(mod, WINE_MODREF, ldr);
        if (mod->BaseAddress > (void*)hmod) break;
    }
    return NULL;
}


/**********************************************************************
 *	    find_basename_module
 *
 * Find a module from its base name.
 * The loader_section must be locked while calling this function
 */
static WINE_MODREF *find_basename_module( LPCWSTR name )
{
    PLIST_ENTRY mark, entry;

    if (cached_modref && !strcmpiW( name, cached_modref->ldr.BaseDllName.Buffer ))
        return cached_modref;

    mark = &driverldr.InLoadOrderModuleList;
    for (entry = mark->Flink; entry && (entry != mark); entry = entry->Flink)
    {
        LDR_MODULE *mod = CONTAINING_RECORD(entry, LDR_MODULE, InLoadOrderModuleList);
        if (!strcmpiW( name, mod->BaseDllName.Buffer ))
        {
            cached_modref = CONTAINING_RECORD(mod, WINE_MODREF, ldr);
            return cached_modref;
        }
    }
    return NULL;
}


/**********************************************************************
 *	    find_fullname_module
 *
 * Find a module from its full path name.
 * The loader_section must be locked while calling this function
 */
static WINE_MODREF *find_fullname_module( LPCWSTR name )
{
    PLIST_ENTRY mark, entry;


    if (cached_modref && !strcmpiW( name, cached_modref->ldr.FullDllName.Buffer ))
        return cached_modref;

    mark = &driverldr.InLoadOrderModuleList;
    for (entry = mark->Flink; entry && (entry != mark); entry = entry->Flink)
    {
        LDR_MODULE *mod = CONTAINING_RECORD(entry, LDR_MODULE, InLoadOrderModuleList);
        if (!strcmpiW( name, mod->FullDllName.Buffer ))
        {
            cached_modref = CONTAINING_RECORD(mod, WINE_MODREF, ldr);
            return cached_modref;
        }
    }
    return NULL;
}


/*************************************************************************
 *		find_forwarded_export
 *
 * Find the final function pointer for a forwarded function.
 * The loader_section must be locked while calling this function.
 */
static FARPROC find_forwarded_export( HMODULE module, const char *forward )
{
    IMAGE_EXPORT_DIRECTORY *exports;
    DWORD exp_size;
    WINE_MODREF *wm;
    WCHAR mod_name[32];
    char *end = strchr(forward, '.');
    FARPROC proc = NULL;

    if (!end) return NULL;
    if ((end - forward) * sizeof(WCHAR) >= sizeof(mod_name) - sizeof(dllW)) return NULL;
    ascii_to_unicode( mod_name, forward, end - forward );
    memcpy( mod_name + (end - forward), dllW, sizeof(dllW) );

    if (!(wm = find_basename_module( mod_name )))
    {
        ERR("module not found for forward '%s' used by %s\n",
            forward, debugstr_w(get_modref(module)->ldr.FullDllName.Buffer) );
        return NULL;
    }
    if ((exports = RtlImageDirectoryEntryToData( wm->ldr.BaseAddress, TRUE,
                                                 IMAGE_DIRECTORY_ENTRY_EXPORT, &exp_size )))
        proc = find_named_export( wm->ldr.BaseAddress, exports, exp_size, end + 1, -1 );

    if (!proc)
    {
        ERR("function not found for forward '%s' used by %s."
            " If you are using builtin %s, try using the native one instead.\n",
            forward, debugstr_w(get_modref(module)->ldr.FullDllName.Buffer),
            debugstr_w(get_modref(module)->ldr.BaseDllName.Buffer) );
    }
    return proc;
}


/*************************************************************************
 *		find_ordinal_export
 *
 * Find an exported function by ordinal.
 * The exports base must have been subtracted from the ordinal already.
 * The loader_section must be locked while calling this function.
 */
static FARPROC find_ordinal_export( HMODULE module, IMAGE_EXPORT_DIRECTORY *exports,
                                    DWORD exp_size, int ordinal )
{
    FARPROC proc;
    DWORD *functions = get_rva( module, exports->AddressOfFunctions );

    if (ordinal >= exports->NumberOfFunctions)
    {
        TRACE("	ordinal %ld out of range!\n", ordinal + exports->Base );
        return NULL;
    }
    if (!functions[ordinal]) return NULL;

    proc = get_rva( module, functions[ordinal] );

    /* if the address falls into the export dir, it's a forward */
    if (((char *)proc >= (char *)exports) && ((char *)proc < (char *)exports + exp_size))
        return find_forwarded_export( module, (char *)proc );

    if (TRACE_ON(snoop))
    {
        proc = SNOOP_GetProcAddress( module, exports, exp_size, proc, ordinal );
    }
    if (TRACE_ON(relay) && current_modref)
    {
        proc = RELAY_GetProcAddress( module, exports, exp_size, proc,
                                     current_modref->ldr.BaseDllName.Buffer );
    }
    return proc;
}

/*************************************************************************
 *               no_at_strcmp
 *
 * This function compares 2 function names, skipping @args notifications
 * if present. WDM drivers occasionaly have those. Be aware that @
 * might be present in regular C++ names.
 */
static int no_at_strcmp(const char *a, const char *b) {
	do {
		if (((*a == '\0') && (*b == '\0')))
			return 0;

		if (((*a == '@') && (*b == '\0'))) {
			const char *xa = a+1;

			/* check if we just have numbers afterwards
			 * (think C++ mangling) */
			while (*xa && isdigit(*xa))
				xa++;
			if (!*xa)
				return 0;
			/* else: fallthrough */
		}

		if (((*b == '@') && (*a == '\0'))) {
			const char *xb = b+1;

			/* check if we just have numbers afterwards
			 * (think C++ mangling) */
			while (*xb && isdigit(*xb))
				xb++;
			if (!*xb)
				return 0;
			/* else: fallthrough */
		}

		if (*a != *b)
			return *a - *b;

		a++;
		b++;
	} while (1);
}

/*************************************************************************
 *		find_named_export
 *
 * Find an exported function by name.
 * The loader_section must be locked while calling this function.
 */
static FARPROC find_named_export( HMODULE module, IMAGE_EXPORT_DIRECTORY *exports,
                                  DWORD exp_size, const char *name, int hint )
{
    WORD *ordinals = get_rva( module, exports->AddressOfNameOrdinals );
    DWORD *names = get_rva( module, exports->AddressOfNames );
    int min = 0, max = exports->NumberOfNames - 1;

    /* first check the hint */
    if (hint >= 0 && hint <= max)
    {
        char *ename = get_rva( module, names[hint] );
        if (!no_at_strcmp( ename, name ))
            return find_ordinal_export( module, exports, exp_size, ordinals[hint] );
    }

    /* then do a binary search */
    while (min <= max)
    {
        int res, pos = (min + max) / 2;
        char *ename = get_rva( module, names[pos] );
        if (!(res = no_at_strcmp( ename, name )))
            return find_ordinal_export( module, exports, exp_size, ordinals[pos] );
        if (res > 0) max = pos - 1;
        else min = pos + 1;
    }
    return NULL;

}


/*************************************************************************
 *		VXD_import_dll
 *
 * Import the dll specified by the given import descriptor.
 * The loader_section must be locked while calling this function.
 */
static WINE_MODREF *VXD_import_dll( HMODULE module, IMAGE_IMPORT_DESCRIPTOR *descr, LPCWSTR load_path )
{
    NTSTATUS status;
    WINE_MODREF *wmImp;
    HMODULE imp_mod;
    IMAGE_EXPORT_DIRECTORY *exports;
    DWORD exp_size;
    IMAGE_THUNK_DATA *import_list, *thunk_list;
    WCHAR buffer[32];
    char *name = get_rva( module, descr->Name );
    DWORD len = strlen(name) + 1;

    thunk_list = get_rva( module, (DWORD)descr->FirstThunk );
    if (descr->u.OriginalFirstThunk)
        import_list = get_rva( module, (DWORD)descr->u.OriginalFirstThunk );
    else
        import_list = thunk_list;

    if (len * sizeof(WCHAR) <= sizeof(buffer))
    {
        ascii_to_unicode( buffer, name, len );
        status = load_dll( load_path, buffer, 0, &wmImp );
    }
    else  /* need to allocate a larger buffer */
    {
        WCHAR *ptr = RtlAllocateHeap( GetProcessHeap(), 0, len * sizeof(WCHAR) );
        if (!ptr) return NULL;
        ascii_to_unicode( ptr, name, len );
        status = load_dll( load_path, ptr, 0, &wmImp );
        RtlFreeHeap( GetProcessHeap(), 0, ptr );
    }

    if (status)
    {
        if (status == STATUS_DLL_NOT_FOUND)
            ERR("Module (file) %s (which is needed by %s) not found\n",
                name, debugstr_w(current_modref->ldr.FullDllName.Buffer));
        else
            ERR("Loading module (file) %s (which is needed by %s) failed (error %lx).\n",
                name, debugstr_w(current_modref->ldr.FullDllName.Buffer), status);
        imp_mod = NULL;
        exports = NULL;
    }
    else
    {
        imp_mod = wmImp->ldr.BaseAddress;
        exports = RtlImageDirectoryEntryToData( imp_mod, TRUE, IMAGE_DIRECTORY_ENTRY_EXPORT, &exp_size );
    }

    if (!exports)
    {
        /* set all imported function to deadbeef */
        while (import_list->u1.Ordinal)
        {
            if (IMAGE_SNAP_BY_ORDINAL(import_list->u1.Ordinal))
            {
                ERR("No implementation for %s.%ld", name, IMAGE_ORDINAL(import_list->u1.Ordinal));
            }
            else
            {
                IMAGE_IMPORT_BY_NAME *pe_name = get_rva( module, (DWORD)import_list->u1.AddressOfData );
                ERR("No implementation for %s.%s", name, pe_name->Name );
            }
            ERR(" imported from %s, setting to 0xdeadbeef\n",
                debugstr_w(current_modref->ldr.FullDllName.Buffer) );
            thunk_list->u1.Function = (PDWORD)0xdeadbeef;

            import_list++;
            thunk_list++;
        }
        return wmImp;
    }

    while (import_list->u1.Ordinal)
    {
        if (IMAGE_SNAP_BY_ORDINAL(import_list->u1.Ordinal))
        {
            int ordinal = IMAGE_ORDINAL(import_list->u1.Ordinal);

            thunk_list->u1.Function = (PDWORD)find_ordinal_export( imp_mod, exports, exp_size,
                                                                   ordinal - exports->Base );
            if (!thunk_list->u1.Function)
            {
                ERR("No implementation for %s.%d imported from %s, setting to 0xdeadbeef\n",
                    name, ordinal, debugstr_w(current_modref->ldr.FullDllName.Buffer) );
                thunk_list->u1.Function = (PDWORD)0xdeadbeef;
            }
            TRACE("--- Ordinal %s.%d = %p\n", name, ordinal, thunk_list->u1.Function );
        }
        else  /* import by name */
        {
            IMAGE_IMPORT_BY_NAME *pe_name;
            pe_name = get_rva( module, (DWORD)import_list->u1.AddressOfData );

            thunk_list->u1.Function = (PDWORD)find_named_export( imp_mod, exports, exp_size,
                                                                 pe_name->Name, pe_name->Hint );
            if (!thunk_list->u1.Function)
            {
                ERR("No implementation for %s.%s imported from %s, setting to 0xdeadbeef\n",
                    name, pe_name->Name, debugstr_w(current_modref->ldr.FullDllName.Buffer) );
                thunk_list->u1.Function = (PDWORD)0xdeadbeef;
            }
            TRACE("--- %s %s.%d = %p\n", pe_name->Name, name, pe_name->Hint, thunk_list->u1.Function);
        }
        import_list++;
        thunk_list++;
    }
    return wmImp;
}


/****************************************************************
 *       fixup_imports
 *
 * Fixup all imports of a given module.
 * The loader_section must be locked while calling this function.
 */
static NTSTATUS fixup_imports( WINE_MODREF *wm, LPCWSTR load_path )
{
    int i, nb_imports;
    IMAGE_IMPORT_DESCRIPTOR *imports;
    WINE_MODREF *prev;
    DWORD size;
    NTSTATUS status;

    if (!(imports = RtlImageDirectoryEntryToData( wm->ldr.BaseAddress, TRUE,
                                                  IMAGE_DIRECTORY_ENTRY_IMPORT, &size )))
        return STATUS_SUCCESS;

    nb_imports = size / sizeof(*imports);
    for (i = 0; i < nb_imports; i++)
    {
        if (!imports[i].Name)
        {
            nb_imports = i;
            break;
        }
    }
    if (!nb_imports) return STATUS_SUCCESS;  /* no imports */

    /* Allocate module dependency list */
    wm->nDeps = nb_imports;
    wm->deps  = RtlAllocateHeap( ntdll_get_process_heap(), 0, nb_imports*sizeof(WINE_MODREF *) );

    /* load the imported modules. They are automatically
     * added to the modref list of the process.
     */
    prev = current_modref;
    current_modref = wm;
    status = STATUS_SUCCESS;
    for (i = 0; i < nb_imports; i++)
    {
        if (!(wm->deps[i] = VXD_import_dll( wm->ldr.BaseAddress, &imports[i], load_path )))
            status = STATUS_DLL_NOT_FOUND;
    }
    current_modref = prev;
    return status;
}


/*************************************************************************
 *		alloc_module
 *
 * Allocate a WINE_MODREF structure and add it to the process list
 * The loader_section must be locked while calling this function.
 */
static WINE_MODREF *alloc_module( HMODULE hModule, LPCWSTR filename )
{
    WINE_MODREF *wm;
    WCHAR *p;
    IMAGE_NT_HEADERS *nt = RtlImageNtHeader(hModule);
    PLIST_ENTRY entry, mark;

    if (!(wm = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*wm) ))) return NULL;

    wm->nDeps    = 0;
    wm->deps     = NULL;

    wm->ldr.BaseAddress   = hModule;
    wm->ldr.EntryPoint    = NULL;
    wm->ldr.SizeOfImage   = nt->OptionalHeader.SizeOfImage;
    wm->ldr.Flags         = 0;
    wm->ldr.LoadCount     = 0;
    wm->ldr.TlsIndex      = -1;
    wm->ldr.SectionHandle = NULL;
    wm->ldr.CheckSum      = 0;
    wm->ldr.TimeDateStamp = 0;

    RtlCreateUnicodeString( &wm->ldr.FullDllName, filename );
    if ((p = strrchrW( wm->ldr.FullDllName.Buffer, '\\' ))) p++;
    else p = wm->ldr.FullDllName.Buffer;
    RtlInitUnicodeString( &wm->ldr.BaseDllName, p );

    if (nt->FileHeader.Characteristics & IMAGE_FILE_DLL)
    {
        wm->ldr.Flags |= LDR_IMAGE_IS_DLL;
        if (nt->OptionalHeader.AddressOfEntryPoint)
            wm->ldr.EntryPoint = (char *)hModule + nt->OptionalHeader.AddressOfEntryPoint;
    }

    InsertTailList(&driverldr.InLoadOrderModuleList,
                   &wm->ldr.InLoadOrderModuleList);

    /* insert module in MemoryList, sorted in increasing base addresses */
    mark = &driverldr.InMemoryOrderModuleList;
    for (entry = mark->Flink; entry != mark; entry = entry->Flink)
    {
        if (CONTAINING_RECORD(entry, LDR_MODULE, InMemoryOrderModuleList)->BaseAddress > wm->ldr.BaseAddress)
            break;
    }
    entry->Blink->Flink = &wm->ldr.InMemoryOrderModuleList;
    wm->ldr.InMemoryOrderModuleList.Blink = entry->Blink;
    wm->ldr.InMemoryOrderModuleList.Flink = entry;
    entry->Blink = &wm->ldr.InMemoryOrderModuleList;

    /* wait until init is called for inserting into this list */
    wm->ldr.InInitializationOrderModuleList.Flink = NULL;
    wm->ldr.InInitializationOrderModuleList.Blink = NULL;
    return wm;
}

/***********************************************************************
 *           load_builtin_callback
 *
 * Load a library in memory; callback function for wine_dll_register
 *
 * We use the way the PE loader does it, because the builtin dlls/drivers
 * are matching the standard PE constraints.
 */
void VXD_load_builtin_callback( void *module, const char *filename )
{
    static const WCHAR emptyW[1];
    void *addr;
    IMAGE_NT_HEADERS *nt;
    WINE_MODREF *wm;
    WCHAR *fullname, *p;
    const WCHAR *load_path;

	FIXME("(%s)\n", filename);
    if (!module)
    {
        ERR("could not map image for %s\n", filename ? filename : "main exe" );
        return;
    }
    if (!(nt = RtlImageNtHeader( module )))
    {
        ERR( "bad module for %s\n", filename ? filename : "main exe" );
        builtin_load_info->status = STATUS_INVALID_IMAGE_FORMAT;
        return;
    }

    /* create the MODREF */

    if (!(fullname = RtlAllocateHeap( GetProcessHeap(), 0,
                                      system_dir.MaximumLength + (strlen(filename) + 1) * sizeof(WCHAR) )))
    {
        ERR( "can't load %s\n", filename );
        builtin_load_info->status = STATUS_NO_MEMORY;
        return;
    }
    memcpy( fullname, system_dir.Buffer, system_dir.Length );
    p = fullname + system_dir.Length / sizeof(WCHAR);
    if (p > fullname && p[-1] != '\\') *p++ = '\\';
    ascii_to_unicode( p, filename, strlen(filename) + 1 );

    wm = alloc_module( module, fullname );
    RtlFreeHeap( GetProcessHeap(), 0, fullname );
    if (!wm)
    {
        ERR( "can't load %s\n", filename );
        builtin_load_info->status = STATUS_NO_MEMORY;
        return;
    }
    wm->ldr.Flags |= LDR_WINE_INTERNAL;
    NtAllocateVirtualMemory( GetCurrentProcess(), &addr, module, &nt->OptionalHeader.SizeOfImage,
                             MEM_SYSTEM | MEM_IMAGE, PAGE_EXECUTE_WRITECOPY );

    /* fixup imports */

    load_path = builtin_load_info->load_path;
    if (!load_path) load_path = NtCurrentTeb()->Peb->ProcessParameters->DllPath.Buffer;
    if (!load_path) load_path = emptyW;
    if (fixup_imports( wm, load_path ) != STATUS_SUCCESS)
    {
        /* the module has only be inserted in the load & memory order lists */
        RemoveEntryList(&wm->ldr.InLoadOrderModuleList);
        RemoveEntryList(&wm->ldr.InMemoryOrderModuleList);
        /* FIXME: free the modref */
        builtin_load_info->status = STATUS_DLL_NOT_FOUND;
        return;
    }
    builtin_load_info->wm = wm;
    TRACE( "loaded %s %p %p\n", filename, wm, module );

    /* send the DLL load event */

    SERVER_START_REQ( load_dll )
    {
        req->handle     = 0;
        req->base       = module;
        req->size       = nt->OptionalHeader.SizeOfImage;
        req->dbg_offset = nt->FileHeader.PointerToSymbolTable;
        req->dbg_size   = nt->FileHeader.NumberOfSymbols;
        req->name       = &wm->ldr.FullDllName.Buffer;
        wine_server_add_data( req, wm->ldr.FullDllName.Buffer, wm->ldr.FullDllName.Length );
        wine_server_call( req );
    }
    SERVER_END_REQ;

    /* setup relay debugging entry points */
    if (TRACE_ON(relay)) RELAY_SetupDLL( module );
}


/******************************************************************************
 *	load_native_dll  (internal)
 *
 * We do all things by hand, since we are not running page aligned and
 * do not want to pollute the standard PE loader. However we register the
 * dll / driver as standard PE dll in the server, so the debugger can pick it
 * up. (It does not get confused by it.)
 */
static NTSTATUS load_native_dll( LPCWSTR load_path, LPCWSTR name, HANDLE file,
                                 DWORD flags, WINE_MODREF** pwm )
{
    void *module;
    int i;
    ULONG size;
    IMAGE_NT_HEADERS *nt;
    WINE_MODREF *wm;
    NTSTATUS status;
    IMAGE_DOS_HEADER mzh;
    IMAGE_NT_HEADERS nthdr;
    LARGE_INTEGER off;
    IO_STATUS_BLOCK ioblock;
    IMAGE_SECTION_HEADER *sechdrs;

    TRACE( "loading %s\n", debugstr_w(name) );

    memset(&off, 0, sizeof(off));
    status = NtReadFile(file,0,NULL,NULL,&ioblock,&mzh,sizeof(mzh),&off,NULL);
    if (status) {
	FIXME("read of MZ header failed?\n");
	return status;
    }
    if (mzh.e_magic != IMAGE_DOS_SIGNATURE) {
	FIXME("MZ header has bad signature %04x?\n",mzh.e_magic);
	return STATUS_UNSUCCESSFUL;
    }
    off.u.LowPart = mzh.e_lfanew;
    status = NtReadFile(file,0,NULL,NULL,&ioblock,&nthdr,sizeof(nthdr),&off,NULL);
    if (status) {
	FIXME("read of NT header failed?\n");
	return status;
    }
    if (nthdr.Signature != IMAGE_NT_SIGNATURE) {
	FIXME("NT header has bad signature %08lx?\n",nthdr.Signature);
	return STATUS_UNSUCCESSFUL;
    }
    FIXME("Reading %d section headers.\n", nthdr.FileHeader.NumberOfSections);
    sechdrs = RtlAllocateHeap( GetProcessHeap(), 0, IMAGE_SIZEOF_SECTION_HEADER * nthdr.FileHeader.NumberOfSections);
    off.u.LowPart =	mzh.e_lfanew 			+
			sizeof(nthdr.FileHeader)	+
			sizeof(nthdr.Signature)		+
			nthdr.FileHeader.SizeOfOptionalHeader;
    status = NtReadFile(file,0,NULL,NULL,&ioblock,sechdrs,IMAGE_SIZEOF_SECTION_HEADER * nthdr.FileHeader.NumberOfSections,&off,NULL);
    if (status) {
	FIXME("read of PE section entries failed?\n");
	return status;
    }

    size = nthdr.FileHeader.SizeOfOptionalHeader + mzh.e_lfanew + IMAGE_SIZEOF_SECTION_HEADER * nthdr.FileHeader.NumberOfSections;

    for (i = 0; i < nthdr.FileHeader.NumberOfSections; i++) {
        if (sechdrs[i].VirtualAddress + sechdrs[i].Misc.VirtualSize > size)
	   size = sechdrs[i].VirtualAddress + sechdrs[i].Misc.VirtualSize;
    }

    FIXME("allocating %ld bytes.\n", size);
    status = NtAllocateVirtualMemory( GetCurrentProcess(), &module, NULL, &size, MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    if (status != STATUS_SUCCESS) {
	FIXME("NtAllocVirtMem failed with %lx\n",status);
	return status;
    }
    /* Copy MZ and PE header stuff. */
    memcpy((char*)module, &mzh, sizeof(mzh));
    memcpy((char*)module+mzh.e_lfanew, &nthdr, sizeof(nthdr));
    memcpy((char*)module+mzh.e_lfanew+sizeof(nthdr.Signature)+sizeof(nthdr.FileHeader)+nthdr.FileHeader.SizeOfOptionalHeader, sechdrs, nthdr.FileHeader.NumberOfSections * IMAGE_SIZEOF_SECTION_HEADER);
    for (i = 0; i < nthdr.FileHeader.NumberOfSections; i++) {
	ULONG size;
	if (sechdrs[i].Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA)
	    continue;
	size = sechdrs[i].SizeOfRawData;
	if (size > sechdrs[i].Misc.VirtualSize)
		size = sechdrs[i].Misc.VirtualSize;

	off.u.LowPart = sechdrs[i].PointerToRawData;
	status = NtReadFile(file,0,NULL,NULL,&ioblock,(char*)module + sechdrs[i].VirtualAddress,size,&off,NULL);
	if (status) {
	    FIXME("read of PE section %s failed?\n",sechdrs[i].Name);
	    return status;
	}
    }

    /* relocate it */
    PE_do_relocations( module, &nthdr.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC], (DWORD)module - nthdr.OptionalHeader.ImageBase, size);

    /* create the MODREF */

    if (!(wm = alloc_module( module, name ))) return STATUS_NO_MEMORY;

    /* fixup imports */

    if (!(flags & DONT_RESOLVE_DLL_REFERENCES))
    {
        if ((status = fixup_imports( wm, load_path )) != STATUS_SUCCESS)
        {
            /* the module has only be inserted in the load & memory order lists */
            RemoveEntryList(&wm->ldr.InLoadOrderModuleList);
            RemoveEntryList(&wm->ldr.InMemoryOrderModuleList);

            /* FIXME: there are several more dangling references
             * left. Including dlls loaded by this dll before the
             * failed one. Unrolling is rather difficult with the
             * current structure and we can leave them lying
             * around with no problems, so we don't care.
             * As these might reference our wm, we don't free it.
             */
            return status;
        }
    }
    else wm->ldr.Flags |= LDR_DONT_RESOLVE_REFS;

    /* send DLL load event */

    nt = RtlImageNtHeader( module );

    SERVER_START_REQ( load_dll )
    {
        req->handle     = file;
        req->base       = module;
        req->size       = nt->OptionalHeader.SizeOfImage;
        req->dbg_offset = nt->FileHeader.PointerToSymbolTable;
        req->dbg_size   = nt->FileHeader.NumberOfSymbols;
        req->name       = &wm->ldr.FullDllName.Buffer;
        wine_server_add_data( req, wm->ldr.FullDllName.Buffer, wm->ldr.FullDllName.Length );
        wine_server_call( req );
    }
    SERVER_END_REQ;

    if (TRACE_ON(snoop)) SNOOP_SetupDLL( module );

    *pwm = wm;
    return STATUS_SUCCESS;
}


/***********************************************************************
 *           load_builtin_dll
 */
static NTSTATUS load_builtin_dll( LPCWSTR load_path, LPCWSTR path, DWORD flags, WINE_MODREF** pwm )
{
    char error[256], dllname[MAX_PATH];
    int file_exists;
    const WCHAR *name, *p;
    DWORD len, i;
    void *handle;
    struct builtin_load_info info, *prev_info;

    /* Fix the name in case we have a full path and extension */
    name = path;
    if ((p = strrchrW( name, '\\' ))) name = p + 1;
    if ((p = strrchrW( name, '/' ))) name = p + 1;

    /* we don't want to depend on the current codepage here */
    len = strlenW( name ) + 1;
    if (len >= sizeof(dllname)) return STATUS_NAME_TOO_LONG;
    for (i = 0; i < len; i++)
    {
        if (name[i] > 127) return STATUS_DLL_NOT_FOUND;
        dllname[i] = (char)name[i];
        if (dllname[i] >= 'A' && dllname[i] <= 'Z') dllname[i] += 'a' - 'A';
    }

    /* load_library will modify info.status. Note also that load_library can be
     * called several times, if the .so file we're loading has dependencies.
     * info.status will gather all the errors we may get while loading all these
     * libraries
     */
    info.load_path = load_path;
    info.status    = STATUS_SUCCESS;
    info.wm        = NULL;
    prev_info = builtin_load_info;
    builtin_load_info = &info;
    handle = wine_dll_load( dllname, error, sizeof(error), &file_exists );
    builtin_load_info = prev_info;

    if (!handle)
    {
        if (!file_exists)
        {
            /* The file does not exist -> WARN() */
            WARN("cannot open .so lib for builtin %s: %s\n", debugstr_w(name), error);
            return STATUS_DLL_NOT_FOUND;
        }
        /* ERR() for all other errors (missing functions, ...) */
        ERR("failed to load .so lib for builtin %s: %s\n", debugstr_w(name), error );
        return STATUS_PROCEDURE_NOT_FOUND;
    }
    if (info.status != STATUS_SUCCESS) return info.status;

    if (!info.wm)
    {
        /* The constructor wasn't called, this means the .so is already
         * loaded under a different name. We can't support multiple names
         * for the same module, so return an error. */
        return STATUS_INVALID_IMAGE_FORMAT;
    }

    info.wm->ldr.SectionHandle = handle;
    if (strcmpiW( info.wm->ldr.BaseDllName.Buffer, name ))
    {
        ERR( "loaded .so for %s but got %s instead - probably 16-bit dll\n",
             debugstr_w(name), debugstr_w(info.wm->ldr.BaseDllName.Buffer) );
        /* wine_dll_unload( handle );*/
        return STATUS_INVALID_IMAGE_FORMAT;
    }
    *pwm = info.wm;
    return STATUS_SUCCESS;
}


/***********************************************************************
 *	find_dll_file
 *
 * Find the file (or already loaded module) for a given dll name.
 */
static NTSTATUS find_dll_file( const WCHAR *load_path, const WCHAR *libname,
                               WCHAR *filename, ULONG *size, WINE_MODREF **pwm, HANDLE *handle )
{
    WCHAR *file_part, *ext;
    ULONG len;

    if (RtlDetermineDosPathNameType_U( libname ) == RELATIVE_PATH)
    {
        /* we need to search for it */
        /* but first append .dll because RtlDosSearchPath extension handling is broken */
        if (!(ext = strrchrW( libname, '.')) || strchrW( ext, '/' ) || strchrW( ext, '\\'))
        {
            WCHAR *dllname;

            if (!(dllname = RtlAllocateHeap( GetProcessHeap(), 0,
                                             (strlenW(libname) * sizeof(WCHAR)) + sizeof(dllW) )))
                return STATUS_NO_MEMORY;
            strcpyW( dllname, libname );
            strcatW( dllname, dllW );
            len = RtlDosSearchPath_U( load_path, dllname, NULL, *size, filename, &file_part );
            RtlFreeHeap( GetProcessHeap(), 0, dllname );
        }
        else len = RtlDosSearchPath_U( load_path, libname, NULL, *size, filename, &file_part );

        if (len)
        {
            if (len >= *size)
            {
                *size = len + sizeof(WCHAR);
                return STATUS_BUFFER_TOO_SMALL;
            }
            if ((*pwm = find_fullname_module( filename )) != NULL) return STATUS_SUCCESS;

            /* check for already loaded module in a different path */
            if (!contains_path( libname ))
            {
                if ((*pwm = find_basename_module( file_part )) != NULL) return STATUS_SUCCESS;
            }
            *handle = pCreateFileW( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 );
            return STATUS_SUCCESS;
        }

        /* not found */

        if (!contains_path( libname ))
        {
            /* if libname doesn't contain a path at all, we simply return the name as is,
             * to be loaded as builtin */
            len = strlenW(libname) * sizeof(WCHAR);
            if (len >= *size) goto overflow;
            strcpyW( filename, libname );
            if (!strchrW( filename, '.' ))
            {
                len += sizeof(dllW) - sizeof(WCHAR);
                if (len >= *size) goto overflow;
                strcatW( filename, dllW );
            }
            *pwm = find_basename_module( filename );
            return STATUS_SUCCESS;
        }
    }

    /* absolute path name, or relative path name but not found above */

    len = RtlGetFullPathName_U( libname, *size, filename, &file_part );
    if (len >= *size) goto overflow;
    if (file_part && !strchrW( file_part, '.' ))
    {
        len += sizeof(dllW) - sizeof(WCHAR);
        if (len >= *size) goto overflow;
        strcatW( file_part, dllW );
    }
    if ((*pwm = find_fullname_module( filename )) != NULL) return STATUS_SUCCESS;
    *handle = pCreateFileW( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 );
    return STATUS_SUCCESS;

overflow:
    *size = len + sizeof(WCHAR);
    return STATUS_BUFFER_TOO_SMALL;
}


/***********************************************************************
 *	load_dll  (internal)
 *
 * Load a PE style module according to the load order.
 * The loader_section must be locked while calling this function.
 */
static NTSTATUS load_dll( LPCWSTR load_path, LPCWSTR libname, DWORD flags, WINE_MODREF** pwm )
{
    int i;
    enum loadorder_type loadorder[LOADORDER_NTYPES];
    WCHAR buffer[32];
    WCHAR *filename;
    ULONG size;
    const char *filetype = "";
    WINE_MODREF *main_exe;
    HANDLE handle = INVALID_HANDLE_VALUE;
    NTSTATUS nts;

    TRACE( "looking for %s in %s\n", debugstr_w(libname), debugstr_w(load_path) );

    filename = buffer;
    size = sizeof(buffer);
    for (;;)
    {
        nts = find_dll_file( load_path, libname, filename, &size, pwm, &handle );
        if (nts == STATUS_SUCCESS) break;
        if (filename != buffer) RtlFreeHeap( GetProcessHeap(), 0, filename );
        if (nts != STATUS_BUFFER_TOO_SMALL) return nts;
        /* grow the buffer and retry */
        if (!(filename = RtlAllocateHeap( GetProcessHeap(), 0, size ))) return STATUS_NO_MEMORY;
    }

    if (*pwm)  /* found already loaded module */
    {
        if ((*pwm)->ldr.LoadCount != -1) (*pwm)->ldr.LoadCount++;

        TRACE("Found loaded module %s for %s at %p, count=%d\n",
              debugstr_w((*pwm)->ldr.FullDllName.Buffer), debugstr_w(libname),
              (*pwm)->ldr.BaseAddress, (*pwm)->ldr.LoadCount);
        if (filename != buffer) RtlFreeHeap( GetProcessHeap(), 0, filename );
        return STATUS_SUCCESS;
    }

    main_exe = PE_get_modref( NtCurrentTeb()->Peb->ImageBaseAddress );
    MODULE_GetLoadOrderW( loadorder, main_exe->ldr.BaseDllName.Buffer, filename );

    nts = STATUS_DLL_NOT_FOUND;
    for (i = 0; i < LOADORDER_NTYPES; i++)
    {
        if (loadorder[i] == LOADORDER_INVALID) break;

        switch (loadorder[i])
        {
        case LOADORDER_DLL:
            TRACE("Trying native dll %s\n", debugstr_w(filename));
            if (handle == INVALID_HANDLE_VALUE) continue;  /* it cannot possibly be loaded */
            nts = load_native_dll( load_path, filename, handle, flags, pwm );
            filetype = "native";
            break;
        case LOADORDER_BI:
            TRACE("Trying built-in %s\n", debugstr_w(filename));
            nts = load_builtin_dll( load_path, filename, flags, pwm );
            filetype = "builtin";
            break;
        default:
            nts = STATUS_INTERNAL_ERROR;
            break;
        }

        if (nts == STATUS_SUCCESS)
        {
            /* Initialize DLL just loaded */
            TRACE("Loaded module %s (%s) at %p\n",
                  debugstr_w(filename), filetype, (*pwm)->ldr.BaseAddress);
            if (!TRACE_ON(module))
                TRACE_(loaddll)("Loaded module %s : %s\n", debugstr_w(filename), filetype);
            /* Set the ldr.LoadCount here so that an attach failure will */
            /* decrement the dependencies through the MODULE_FreeLibrary call. */
            (*pwm)->ldr.LoadCount = 1;
            if (handle != INVALID_HANDLE_VALUE) NtClose( handle );
            if (filename != buffer) RtlFreeHeap( GetProcessHeap(), 0, filename );
            return nts;
        }
        if (nts != STATUS_DLL_NOT_FOUND) break;
    }

    WARN("Failed to load module %s; status=%lx\n", debugstr_w(libname), nts);
    if (handle != INVALID_HANDLE_VALUE) NtClose( handle );
    if (filename != buffer) RtlFreeHeap( GetProcessHeap(), 0, filename );
    return nts;
}

/******************************************************************
 *		NtLoadDriver (NTDLL.@)
 *
 * Not correct yet.
 */
NTSTATUS WINAPI NtLoadDriver(const UNICODE_STRING *libname)
{
    WINE_MODREF *wm;
    NTSTATUS nts;
    LPCWSTR path_name = NULL;
    DWORD flags = 0;
    static const WCHAR emptyW[1] = { 0 };
    int i;
    PIMAGE_DOS_HEADER dosh;
    PIMAGE_NT_HEADERS nth;
    HANDLE hModule;
    driverfunc func;
    char foo[1000];

    for (i=0;i<nrofloadeddrivers;i++) {
	if (RtlEqualUnicodeString(&loadeddrivers[i].ustr,libname,1)) {
	    FIXME("%s already loaded.\n", debugstr_w(libname->Buffer));
	    return STATUS_SUCCESS;
	}
    }
    WDM_Init();

    RtlEnterCriticalSection( &loader_section );
    if (!path_name) path_name = NtCurrentTeb()->Peb->ProcessParameters->DllPath.Buffer;
    if (!path_name) path_name = emptyW;
    nts = load_dll( path_name, libname->Buffer, flags, &wm );
    hModule = (wm) ? wm->ldr.BaseAddress : NULL;
    RtlLeaveCriticalSection( &loader_section );

    if (!hModule) return 0xc0000001;
    if (nts != STATUS_SUCCESS) return nts;

    dosh = (PIMAGE_DOS_HEADER)hModule;
    if (dosh->e_magic != IMAGE_DOS_SIGNATURE) {
	ERR("Binary has no dos signature!\n");
	return 0xc0000001;
    }
    nth = (PIMAGE_NT_HEADERS)((char*)hModule + dosh->e_lfanew);
    if (memcmp(&nth->Signature,"PE\0\0",4)) {
	ERR("Binary has no PE signature!\n");
	return 0xc0000001;
    }
    if (!loadeddrivers)
        loadeddrivers = RtlAllocateHeap(GetProcessHeap(),0,sizeof(loadeddrivers[0]));
    else
        loadeddrivers = RtlReAllocateHeap(GetProcessHeap(),0,loadeddrivers,sizeof(loadeddrivers[0])*(nrofloadeddrivers+1));
    nrofloadeddrivers++;
    loadeddrivers[nrofloadeddrivers-1].id = driverid++;
    RtlDuplicateUnicodeString(1,libname, &loadeddrivers[nrofloadeddrivers-1].ustr);
    func=(driverfunc)(((char*)hModule)+nth->OptionalHeader.AddressOfEntryPoint);
    memset(&loadeddrivers[nrofloadeddrivers-1].drvob,0,sizeof(DRIVER_OBJECT));
    loadeddrivers[nrofloadeddrivers-1].drvob.drvext = RtlAllocateHeap(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(DRIVER_EXTENSION));
    nts = func(&loadeddrivers[nrofloadeddrivers-1].drvob,foo);
    if (nts != STATUS_SUCCESS) {
	ERR("Init of driver failed?\n");
	return nts;
    }
    return STATUS_SUCCESS;
}

/******************************************************************
 *		_irp_dispatch
 * 
 * Standalone so we can do the evil ebx hack inside.
 */
static DWORD _irp_dispatch(PDRIVER_DISPATCH func, DEVICE_OBJECT *dob,IRP *irp) {
    DWORD ret;

    ret = func(dob,irp);
    /* The function might clobber ebx, even the though Linux ABI does not
     * allow it. So we need to tell the compiler that ebx has been clobbered.
     * So use an empty assembler instruction.
     */
    __asm__ __volatile__("":::"ebx");
    return ret;
}

/******************************************************************
 *		WDM_DeviceIoControl
 *
 * Not correct yet.
 */
NTSTATUS WDM_DeviceIoControl(DWORD clientID, HANDLE hDevice, 
                             HANDLE hEvent, PIO_APC_ROUTINE UserApcRoutine,
                             PVOID UserApcContext, 
                             PIO_STATUS_BLOCK piosb, 
                             ULONG dwIoControlCode,
                             LPVOID lpInBuffer, DWORD nInBufferSize,
                             LPVOID lpOutBuffer, DWORD nOutBufferSize)
{
    int i;
    IRP	xirp;
    IO_STACK_LOCATION iostackentry;
    NTSTATUS ret;
    DEVICE_OBJECT devob;

    for (i=0;i<nrofloadeddrivers;i++)
	if ((clientID & 0xffff) == loadeddrivers[i].id)
	    break;
    if (i == nrofloadeddrivers)
	return STATUS_NO_SUCH_DEVICE;

    if (hEvent) {
	FIXME("hEvents unhandled!\n");
    }

    memset(&xirp,0,sizeof(xirp));
    xirp.iostack = &iostackentry;

    /*
    iostackentry.majorfunc = IRP_MJ_CREATE;
    ret = _irp_dispatch(xdrvobject.drvdsp[IRP_MJ_CREATE],&xdevobject,&xirp);
    */

    iostackentry.majorfunc = IRP_MJ_DEVICE_CONTROL;
    iostackentry.u.devioctrl.ioctrlcode = dwIoControlCode;
    iostackentry.u.devioctrl.obuflen = nOutBufferSize;
    iostackentry.u.devioctrl.ibuflen = nInBufferSize;
    iostackentry.u.devioctrl.type3input = lpInBuffer;
    xirp.userbuffer = lpOutBuffer;
    memset(&devob,0,sizeof(devob));
    ret = _irp_dispatch(loadeddrivers[i].drvob.drvdsp[IRP_MJ_DEVICE_CONTROL],&devob,&xirp);

/*
    MESSAGE("resbuf:\n");
    for (i=0;i<nOutBufferSize;i++) {
	    MESSAGE("%02x ",((BYTE*)lpOutBuffer)[i]);
	    if ((i&15) == 15)
		    MESSAGE("\n");
    }
    MESSAGE("\n");
    MESSAGE("WDM_DeviceIoControl ret = %lx\n",ret);
*/
    if (piosb) memcpy(piosb,&xirp.iostatusblock,sizeof(IO_STATUS_BLOCK));
    return STATUS_SUCCESS;
}


More information about the wine-patches mailing list