[ZLIB]
authorJérôme Gardou <jerome.gardou@reactos.org>
Thu, 17 Jun 2010 00:54:34 +0000 (00:54 +0000)
committerJérôme Gardou <jerome.gardou@reactos.org>
Thu, 17 Jun 2010 00:54:34 +0000 (00:54 +0000)
[NTOSKRNL]
This is the ultimate commit that makes this branch build again

svn path=/branches/reactos-yarotows/; revision=47794

lib/3rdparty/zlib/gzguts.h [new file with mode: 0644]
lib/3rdparty/zlib/gzlib.c [new file with mode: 0644]
lib/3rdparty/zlib/gzread.c [new file with mode: 0644]
lib/3rdparty/zlib/gzwrite.c [new file with mode: 0644]
ntoskrnl/mm/ARM3/mmdbg.c [new file with mode: 0644]
ntoskrnl/mm/ARM3/sysldr.c [new file with mode: 0644]

diff --git a/lib/3rdparty/zlib/gzguts.h b/lib/3rdparty/zlib/gzguts.h
new file mode 100644 (file)
index 0000000..055455a
--- /dev/null
@@ -0,0 +1,132 @@
+/* gzguts.h -- zlib internal header definitions for gz* operations
+ * Copyright (C) 2004, 2005, 2010 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+#ifdef _LARGEFILE64_SOURCE
+#  ifndef _LARGEFILE_SOURCE
+#    define _LARGEFILE_SOURCE 1
+#  endif
+#  ifdef _FILE_OFFSET_BITS
+#    undef _FILE_OFFSET_BITS
+#  endif
+#endif
+
+#if ((__GNUC__-0) * 10 + __GNUC_MINOR__-0 >= 33) && !defined(NO_VIZ)
+#  define ZLIB_INTERNAL __attribute__((visibility ("hidden")))
+#else
+#  define ZLIB_INTERNAL
+#endif
+
+#include <stdio.h>
+#include "zlib.h"
+#ifdef STDC
+#  include <string.h>
+#  include <stdlib.h>
+#  include <limits.h>
+#endif
+#include <fcntl.h>
+
+#ifdef NO_DEFLATE       /* for compatibility with old definition */
+#  define NO_GZCOMPRESS
+#endif
+
+#ifdef _MSC_VER
+#  include <io.h>
+#  define vsnprintf _vsnprintf
+#endif
+
+#ifndef local
+#  define local static
+#endif
+/* compile with -Dlocal if your debugger can't find static symbols */
+
+/* gz* functions always use library allocation functions */
+#ifndef STDC
+  extern voidp  malloc OF((uInt size));
+  extern void   free   OF((voidpf ptr));
+#endif
+
+/* get errno and strerror definition */
+#if defined UNDER_CE
+#  include <windows.h>
+#  define zstrerror() gz_strwinerror((DWORD)GetLastError())
+#else
+#  ifdef STDC
+#    include <errno.h>
+#    define zstrerror() strerror(errno)
+#  else
+#    define zstrerror() "stdio error (consult errno)"
+#  endif
+#endif
+
+/* provide prototypes for these when building zlib without LFS */
+#if !defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0
+    ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *));
+    ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int));
+    ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile));
+    ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile));
+#endif
+
+/* default i/o buffer size -- double this for output when reading */
+#define GZBUFSIZE 8192
+
+/* gzip modes, also provide a little integrity check on the passed structure */
+#define GZ_NONE 0
+#define GZ_READ 7247
+#define GZ_WRITE 31153
+#define GZ_APPEND 1     /* mode set to GZ_WRITE after the file is opened */
+
+/* values for gz_state how */
+#define LOOK 0      /* look for a gzip header */
+#define COPY 1      /* copy input directly */
+#define GZIP 2      /* decompress a gzip stream */
+
+/* internal gzip file state data structure */
+typedef struct {
+        /* used for both reading and writing */
+    int mode;               /* see gzip modes above */
+    int fd;                 /* file descriptor */
+    char *path;             /* path or fd for error messages */
+    z_off64_t pos;          /* current position in uncompressed data */
+    unsigned size;          /* buffer size, zero if not allocated yet */
+    unsigned want;          /* requested buffer size, default is GZBUFSIZE */
+    unsigned char *in;      /* input buffer */
+    unsigned char *out;     /* output buffer (double-sized when reading) */
+    unsigned char *next;    /* next output data to deliver or write */
+        /* just for reading */
+    unsigned have;          /* amount of output data unused at next */
+    int eof;                /* true if end of input file reached */
+    z_off64_t start;        /* where the gzip data started, for rewinding */
+    z_off64_t raw;          /* where the raw data started, for seeking */
+    int how;                /* 0: get header, 1: copy, 2: decompress */
+    int direct;             /* true if last read direct, false if gzip */
+        /* just for writing */
+    int level;              /* compression level */
+    int strategy;           /* compression strategy */
+        /* seek request */
+    z_off64_t skip;         /* amount to skip (already rewound if backwards) */
+    int seek;               /* true if seek request pending */
+        /* error information */
+    int err;                /* error code */
+    char *msg;              /* error message */
+        /* zlib inflate or deflate stream */
+    z_stream strm;          /* stream structure in-place (not a pointer) */
+} gz_state;
+typedef gz_state FAR *gz_statep;
+
+/* shared functions */
+void gz_error OF((gz_statep, int, const char *));
+#if defined UNDER_CE
+char *gz_strwinerror OF((DWORD error));
+#endif
+
+/* GT_OFF(x), where x is an unsigned value, is true if x > maximum z_off64_t
+   value -- needed when comparing unsigned to z_off64_t, which is signed
+   (possible z_off64_t types off_t, off64_t, and long are all signed) */
+#ifdef INT_MAX
+#  define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > INT_MAX)
+#else
+unsigned gz_intmax OF((void));
+#  define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > gz_intmax())
+#endif
diff --git a/lib/3rdparty/zlib/gzlib.c b/lib/3rdparty/zlib/gzlib.c
new file mode 100644 (file)
index 0000000..e9eef78
--- /dev/null
@@ -0,0 +1,537 @@
+/* gzlib.c -- zlib functions common to reading and writing gzip files
+ * Copyright (C) 2004, 2010 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+#include "gzguts.h"
+
+#if defined(_LARGEFILE64_SOURCE) && _LFS64_LARGEFILE-0
+#  define LSEEK lseek64
+#else
+#  define LSEEK lseek
+#endif
+
+/* Local functions */
+local void gz_reset OF((gz_statep));
+local gzFile gz_open OF((const char *, int, const char *));
+
+#if defined UNDER_CE
+
+/* Map the Windows error number in ERROR to a locale-dependent error message
+   string and return a pointer to it.  Typically, the values for ERROR come
+   from GetLastError.
+
+   The string pointed to shall not be modified by the application, but may be
+   overwritten by a subsequent call to gz_strwinerror
+
+   The gz_strwinerror function does not change the current setting of
+   GetLastError. */
+char *gz_strwinerror (error)
+     DWORD error;
+{
+    static char buf[1024];
+
+    wchar_t *msgbuf;
+    DWORD lasterr = GetLastError();
+    DWORD chars = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM
+        | FORMAT_MESSAGE_ALLOCATE_BUFFER,
+        NULL,
+        error,
+        0, /* Default language */
+        (LPVOID)&msgbuf,
+        0,
+        NULL);
+    if (chars != 0) {
+        /* If there is an \r\n appended, zap it.  */
+        if (chars >= 2
+            && msgbuf[chars - 2] == '\r' && msgbuf[chars - 1] == '\n') {
+            chars -= 2;
+            msgbuf[chars] = 0;
+        }
+
+        if (chars > sizeof (buf) - 1) {
+            chars = sizeof (buf) - 1;
+            msgbuf[chars] = 0;
+        }
+
+        wcstombs(buf, msgbuf, chars + 1);
+        LocalFree(msgbuf);
+    }
+    else {
+        sprintf(buf, "unknown win32 error (%ld)", error);
+    }
+
+    SetLastError(lasterr);
+    return buf;
+}
+
+#endif /* UNDER_CE */
+
+/* Reset gzip file state */
+local void gz_reset(state)
+    gz_statep state;
+{
+    if (state->mode == GZ_READ) {   /* for reading ... */
+        state->have = 0;            /* no output data available */
+        state->eof = 0;             /* not at end of file */
+        state->how = LOOK;          /* look for gzip header */
+        state->direct = 1;          /* default for empty file */
+    }
+    state->seek = 0;                /* no seek request pending */
+    gz_error(state, Z_OK, NULL);    /* clear error */
+    state->pos = 0;                 /* no uncompressed data yet */
+    state->strm.avail_in = 0;       /* no input data yet */
+}
+
+/* Open a gzip file either by name or file descriptor. */
+local gzFile gz_open(path, fd, mode)
+    const char *path;
+    int fd;
+    const char *mode;
+{
+    gz_statep state;
+
+    /* allocate gzFile structure to return */
+    state = malloc(sizeof(gz_state));
+    if (state == NULL)
+        return NULL;
+    state->size = 0;            /* no buffers allocated yet */
+    state->want = GZBUFSIZE;    /* requested buffer size */
+    state->msg = NULL;          /* no error message yet */
+
+    /* interpret mode */
+    state->mode = GZ_NONE;
+    state->level = Z_DEFAULT_COMPRESSION;
+    state->strategy = Z_DEFAULT_STRATEGY;
+    while (*mode) {
+        if (*mode >= '0' && *mode <= '9')
+            state->level = *mode - '0';
+        else
+            switch (*mode) {
+            case 'r':
+                state->mode = GZ_READ;
+                break;
+#ifndef NO_GZCOMPRESS
+            case 'w':
+                state->mode = GZ_WRITE;
+                break;
+            case 'a':
+                state->mode = GZ_APPEND;
+                break;
+#endif
+            case '+':       /* can't read and write at the same time */
+                free(state);
+                return NULL;
+            case 'b':       /* ignore -- will request binary anyway */
+                break;
+            case 'f':
+                state->strategy = Z_FILTERED;
+                break;
+            case 'h':
+                state->strategy = Z_HUFFMAN_ONLY;
+                break;
+            case 'R':
+                state->strategy = Z_RLE;
+                break;
+            case 'F':
+                state->strategy = Z_FIXED;
+            default:        /* could consider as an error, but just ignore */
+                ;
+            }
+        mode++;
+    }
+
+    /* must provide an "r", "w", or "a" */
+    if (state->mode == GZ_NONE) {
+        free(state);
+        return NULL;
+    }
+
+    /* save the path name for error messages */
+    state->path = malloc(strlen(path) + 1);
+    if (state->path == NULL) {
+        free(state);
+        return NULL;
+    }
+    strcpy(state->path, path);
+
+    /* open the file with the appropriate mode (or just use fd) */
+    state->fd = fd != -1 ? fd :
+        open(path,
+#ifdef O_LARGEFILE
+            O_LARGEFILE |
+#endif
+#ifdef O_BINARY
+            O_BINARY |
+#endif
+            (state->mode == GZ_READ ?
+                O_RDONLY :
+                (O_WRONLY | O_CREAT | (
+                    state->mode == GZ_WRITE ?
+                        O_TRUNC :
+                        O_APPEND))),
+            0666);
+    if (state->fd == -1) {
+        free(state->path);
+        free(state);
+        return NULL;
+    }
+    if (state->mode == GZ_APPEND)
+        state->mode = GZ_WRITE;         /* simplify later checks */
+
+    /* save the current position for rewinding (only if reading) */
+    if (state->mode == GZ_READ) {
+        state->start = LSEEK(state->fd, 0, SEEK_CUR);
+        if (state->start == -1) state->start = 0;
+    }
+
+    /* initialize stream */
+    gz_reset(state);
+
+    /* return stream */
+    return (gzFile)state;
+}
+
+/* -- see zlib.h -- */
+gzFile ZEXPORT gzopen(path, mode)
+    const char *path;
+    const char *mode;
+{
+    return gz_open(path, -1, mode);
+}
+
+/* -- see zlib.h -- */
+gzFile ZEXPORT gzopen64(path, mode)
+    const char *path;
+    const char *mode;
+{
+    return gz_open(path, -1, mode);
+}
+
+/* -- see zlib.h -- */
+gzFile ZEXPORT gzdopen(fd, mode)
+    int fd;
+    const char *mode;
+{
+    char *path;         /* identifier for error messages */
+    gzFile gz;
+
+    if (fd == -1 || (path = malloc(7 + 3 * sizeof(int))) == NULL)
+        return NULL;
+    sprintf(path, "<fd:%d>", fd);   /* for debugging */
+    gz = gz_open(path, fd, mode);
+    free(path);
+    return gz;
+}
+
+/* -- see zlib.h -- */
+int ZEXPORT gzbuffer(file, size)
+    gzFile file;
+    unsigned size;
+{
+    gz_statep state;
+
+    /* get internal structure and check integrity */
+    if (file == NULL)
+        return -1;
+    state = (gz_statep)file;
+    if (state->mode != GZ_READ && state->mode != GZ_WRITE)
+        return -1;
+
+    /* make sure we haven't already allocated memory */
+    if (state->size != 0)
+        return -1;
+
+    /* check and set requested size */
+    if (size == 0)
+        return -1;
+    state->want = size;
+    return 0;
+}
+
+/* -- see zlib.h -- */
+int ZEXPORT gzrewind(file)
+    gzFile file;
+{
+    gz_statep state;
+
+    /* get internal structure */
+    if (file == NULL)
+        return -1;
+    state = (gz_statep)file;
+
+    /* check that we're reading and that there's no error */
+    if (state->mode != GZ_READ || state->err != Z_OK)
+        return -1;
+
+    /* back up and start over */
+    if (LSEEK(state->fd, state->start, SEEK_SET) == -1)
+        return -1;
+    gz_reset(state);
+    return 0;
+}
+
+/* -- see zlib.h -- */
+z_off64_t ZEXPORT gzseek64(file, offset, whence)
+    gzFile file;
+    z_off64_t offset;
+    int whence;
+{
+    unsigned n;
+    z_off64_t ret;
+    gz_statep state;
+
+    /* get internal structure and check integrity */
+    if (file == NULL)
+        return -1;
+    state = (gz_statep)file;
+    if (state->mode != GZ_READ && state->mode != GZ_WRITE)
+        return -1;
+
+    /* check that there's no error */
+    if (state->err != Z_OK)
+        return -1;
+
+    /* can only seek from start or relative to current position */
+    if (whence != SEEK_SET && whence != SEEK_CUR)
+        return -1;
+
+    /* normalize offset to a SEEK_CUR specification */
+    if (whence == SEEK_SET)
+        offset -= state->pos;
+    else if (state->seek)
+        offset += state->skip;
+    state->seek = 0;
+
+    /* if within raw area while reading, just go there */
+    if (state->mode == GZ_READ && state->how == COPY &&
+        state->pos + offset >= state->raw) {
+        ret = LSEEK(state->fd, offset - state->have, SEEK_CUR);
+        if (ret == -1)
+            return -1;
+        state->have = 0;
+        state->eof = 0;
+        state->seek = 0;
+        gz_error(state, Z_OK, NULL);
+        state->strm.avail_in = 0;
+        state->pos += offset;
+        return state->pos;
+    }
+
+    /* calculate skip amount, rewinding if needed for back seek when reading */
+    if (offset < 0) {
+        if (state->mode != GZ_READ)         /* writing -- can't go backwards */
+            return -1;
+        offset += state->pos;
+        if (offset < 0)                     /* before start of file! */
+            return -1;
+        if (gzrewind(file) == -1)           /* rewind, then skip to offset */
+            return -1;
+    }
+
+    /* if reading, skip what's in output buffer (one less gzgetc() check) */
+    if (state->mode == GZ_READ) {
+        n = GT_OFF(state->have) || (z_off64_t)state->have > offset ?
+            (unsigned)offset : state->have;
+        state->have -= n;
+        state->next += n;
+        state->pos += n;
+        offset -= n;
+    }
+
+    /* request skip (if not zero) */
+    if (offset) {
+        state->seek = 1;
+        state->skip = offset;
+    }
+    return state->pos + offset;
+}
+
+/* -- see zlib.h -- */
+z_off_t ZEXPORT gzseek(file, offset, whence)
+    gzFile file;
+    z_off_t offset;
+    int whence;
+{
+    z_off64_t ret;
+
+    ret = gzseek64(file, (z_off64_t)offset, whence);
+    return ret == (z_off_t)ret ? (z_off_t)ret : -1;
+}
+
+/* -- see zlib.h -- */
+z_off64_t ZEXPORT gztell64(file)
+    gzFile file;
+{
+    gz_statep state;
+
+    /* get internal structure and check integrity */
+    if (file == NULL)
+        return -1;
+    state = (gz_statep)file;
+    if (state->mode != GZ_READ && state->mode != GZ_WRITE)
+        return -1;
+
+    /* return position */
+    return state->pos + (state->seek ? state->skip : 0);
+}
+
+/* -- see zlib.h -- */
+z_off_t ZEXPORT gztell(file)
+    gzFile file;
+{
+    z_off64_t ret;
+
+    ret = gztell64(file);
+    return ret == (z_off_t)ret ? (z_off_t)ret : -1;
+}
+
+/* -- see zlib.h -- */
+z_off64_t ZEXPORT gzoffset64(file)
+    gzFile file;
+{
+    z_off64_t offset;
+    gz_statep state;
+
+    /* get internal structure and check integrity */
+    if (file == NULL)
+        return -1;
+    state = (gz_statep)file;
+    if (state->mode != GZ_READ && state->mode != GZ_WRITE)
+        return -1;
+
+    /* compute and return effective offset in file */
+    offset = LSEEK(state->fd, 0, SEEK_CUR);
+    if (offset == -1)
+        return -1;
+    if (state->mode == GZ_READ)             /* reading */
+        offset -= state->strm.avail_in;     /* don't count buffered input */
+    return offset;
+}
+
+/* -- see zlib.h -- */
+z_off_t ZEXPORT gzoffset(file)
+    gzFile file;
+{
+    z_off64_t ret;
+
+    ret = gzoffset64(file);
+    return ret == (z_off_t)ret ? (z_off_t)ret : -1;
+}
+
+/* -- see zlib.h -- */
+int ZEXPORT gzeof(file)
+    gzFile file;
+{
+    gz_statep state;
+
+    /* get internal structure and check integrity */
+    if (file == NULL)
+        return 0;
+    state = (gz_statep)file;
+    if (state->mode != GZ_READ && state->mode != GZ_WRITE)
+        return 0;
+
+    /* return end-of-file state */
+    return state->mode == GZ_READ ?
+        (state->eof && state->strm.avail_in == 0 && state->have == 0) : 0;
+}
+
+/* -- see zlib.h -- */
+const char * ZEXPORT gzerror(file, errnum)
+    gzFile file;
+    int *errnum;
+{
+    gz_statep state;
+
+    /* get internal structure and check integrity */
+    if (file == NULL)
+        return NULL;
+    state = (gz_statep)file;
+    if (state->mode != GZ_READ && state->mode != GZ_WRITE)
+        return NULL;
+
+    /* return error information */
+    if (errnum != NULL)
+        *errnum = state->err;
+    return state->msg == NULL ? "" : state->msg;
+}
+
+/* -- see zlib.h -- */
+void ZEXPORT gzclearerr(file)
+    gzFile file;
+{
+    gz_statep state;
+
+    /* get internal structure and check integrity */
+    if (file == NULL)
+        return;
+    state = (gz_statep)file;
+    if (state->mode != GZ_READ && state->mode != GZ_WRITE)
+        return;
+
+    /* clear error and end-of-file */
+    if (state->mode == GZ_READ)
+        state->eof = 0;
+    gz_error(state, Z_OK, NULL);
+}
+
+/* Create an error message in allocated memory and set state->err and
+   state->msg accordingly.  Free any previous error message already there.  Do
+   not try to free or allocate space if the error is Z_MEM_ERROR (out of
+   memory).  Simply save the error message as a static string.  If there is an
+   allocation failure constructing the error message, then convert the error to
+   out of memory. */
+void gz_error(state, err, msg)
+    gz_statep state;
+    int err;
+    const char *msg;
+{
+    /* free previously allocated message and clear */
+    if (state->msg != NULL) {
+        if (state->err != Z_MEM_ERROR)
+            free(state->msg);
+        state->msg = NULL;
+    }
+
+    /* set error code, and if no message, then done */
+    state->err = err;
+    if (msg == NULL)
+        return;
+
+    /* for an out of memory error, save as static string */
+    if (err == Z_MEM_ERROR) {
+        state->msg = (char *)msg;
+        return;
+    }
+
+    /* construct error message with path */
+    if ((state->msg = malloc(strlen(state->path) + strlen(msg) + 3)) == NULL) {
+        state->err = Z_MEM_ERROR;
+        state->msg = (char *)"out of memory";
+        return;
+    }
+    strcpy(state->msg, state->path);
+    strcat(state->msg, ": ");
+    strcat(state->msg, msg);
+    return;
+}
+
+#ifndef INT_MAX
+/* portably return maximum value for an int (when limits.h presumed not
+   available) -- we need to do this to cover cases where 2's complement not
+   used, since C standard permits 1's complement and sign-bit representations,
+   otherwise we could just use ((unsigned)-1) >> 1 */
+unsigned gz_intmax()
+{
+    unsigned p, q;
+
+    p = 1;
+    do {
+        q = p;
+        p <<= 1;
+        p++;
+    } while (p > q);
+    return q >> 1;
+}
+#endif
diff --git a/lib/3rdparty/zlib/gzread.c b/lib/3rdparty/zlib/gzread.c
new file mode 100644 (file)
index 0000000..548201a
--- /dev/null
@@ -0,0 +1,653 @@
+/* gzread.c -- zlib functions for reading gzip files
+ * Copyright (C) 2004, 2005, 2010 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+#include "gzguts.h"
+
+/* Local functions */
+local int gz_load OF((gz_statep, unsigned char *, unsigned, unsigned *));
+local int gz_avail OF((gz_statep));
+local int gz_next4 OF((gz_statep, unsigned long *));
+local int gz_head OF((gz_statep));
+local int gz_decomp OF((gz_statep));
+local int gz_make OF((gz_statep));
+local int gz_skip OF((gz_statep, z_off64_t));
+
+/* Use read() to load a buffer -- return -1 on error, otherwise 0.  Read from
+   state->fd, and update state->eof, state->err, and state->msg as appropriate.
+   This function needs to loop on read(), since read() is not guaranteed to
+   read the number of bytes requested, depending on the type of descriptor. */
+local int gz_load(state, buf, len, have)
+    gz_statep state;
+    unsigned char *buf;
+    unsigned len;
+    unsigned *have;
+{
+    int ret;
+
+    *have = 0;
+    do {
+        ret = read(state->fd, buf + *have, len - *have);
+        if (ret <= 0)
+            break;
+        *have += ret;
+    } while (*have < len);
+    if (ret < 0) {
+        gz_error(state, Z_ERRNO, zstrerror());
+        return -1;
+    }
+    if (ret == 0)
+        state->eof = 1;
+    return 0;
+}
+
+/* Load up input buffer and set eof flag if last data loaded -- return -1 on
+   error, 0 otherwise.  Note that the eof flag is set when the end of the input
+   file is reached, even though there may be unused data in the buffer.  Once
+   that data has been used, no more attempts will be made to read the file.
+   gz_avail() assumes that strm->avail_in == 0. */
+local int gz_avail(state)
+    gz_statep state;
+{
+    z_streamp strm = &(state->strm);
+
+    if (state->err != Z_OK)
+        return -1;
+    if (state->eof == 0) {
+        if (gz_load(state, state->in, state->size,
+                (unsigned *)&(strm->avail_in)) == -1)
+            return -1;
+        strm->next_in = state->in;
+    }
+    return 0;
+}
+
+/* Get next byte from input, or -1 if end or error. */
+#define NEXT() ((strm->avail_in == 0 && gz_avail(state) == -1) ? -1 : \
+                (strm->avail_in == 0 ? -1 : \
+                 (strm->avail_in--, *(strm->next_in)++)))
+
+/* Get a four-byte little-endian integer and return 0 on success and the value
+   in *ret.  Otherwise -1 is returned and *ret is not modified. */
+local int gz_next4(state, ret)
+    gz_statep state;
+    unsigned long *ret;
+{
+    int ch;
+    unsigned long val;
+    z_streamp strm = &(state->strm);
+
+    val = NEXT();
+    val += (unsigned)NEXT() << 8;
+    val += (unsigned long)NEXT() << 16;
+    ch = NEXT();
+    if (ch == -1)
+        return -1;
+    val += (unsigned long)ch << 24;
+    *ret = val;
+    return 0;
+}
+
+/* Look for gzip header, set up for inflate or copy.  state->have must be zero.
+   If this is the first time in, allocate required memory.  state->how will be
+   left unchanged if there is no more input data available, will be set to COPY
+   if there is no gzip header and direct copying will be performed, or it will
+   be set to GZIP for decompression, and the gzip header will be skipped so
+   that the next available input data is the raw deflate stream.  If direct
+   copying, then leftover input data from the input buffer will be copied to
+   the output buffer.  In that case, all further file reads will be directly to
+   either the output buffer or a user buffer.  If decompressing, the inflate
+   state and the check value will be initialized.  gz_head() will return 0 on
+   success or -1 on failure.  Failures may include read errors or gzip header
+   errors.  */
+local int gz_head(state)
+    gz_statep state;
+{
+    z_streamp strm = &(state->strm);
+    int flags;
+    unsigned len;
+
+    /* allocate read buffers and inflate memory */
+    if (state->size == 0) {
+        /* allocate buffers */
+        state->in = malloc(state->want);
+        state->out = malloc(state->want << 1);
+        if (state->in == NULL || state->out == NULL) {
+            if (state->out != NULL)
+                free(state->out);
+            if (state->in != NULL)
+                free(state->in);
+            gz_error(state, Z_MEM_ERROR, "out of memory");
+            return -1;
+        }
+        state->size = state->want;
+
+        /* allocate inflate memory */
+        state->strm.zalloc = Z_NULL;
+        state->strm.zfree = Z_NULL;
+        state->strm.opaque = Z_NULL;
+        state->strm.avail_in = 0;
+        state->strm.next_in = Z_NULL;
+        if (inflateInit2(&(state->strm), -15) != Z_OK) {    /* raw inflate */
+            free(state->out);
+            free(state->in);
+            state->size = 0;
+            gz_error(state, Z_MEM_ERROR, "out of memory");
+            return -1;
+        }
+    }
+
+    /* get some data in the input buffer */
+    if (strm->avail_in == 0) {
+        if (gz_avail(state) == -1)
+            return -1;
+        if (strm->avail_in == 0)
+            return 0;
+    }
+
+    /* look for the gzip magic header bytes 31 and 139 */
+    if (strm->next_in[0] == 31) {
+        strm->avail_in--;
+        strm->next_in++;
+        if (strm->avail_in == 0 && gz_avail(state) == -1)
+            return -1;
+        if (strm->avail_in && strm->next_in[0] == 139) {
+            /* we have a gzip header, woo hoo! */
+            strm->avail_in--;
+            strm->next_in++;
+
+            /* skip rest of header */
+            if (NEXT() != 8) {      /* compression method */
+                gz_error(state, Z_DATA_ERROR, "unknown compression method");
+                return -1;
+            }
+            flags = NEXT();
+            if (flags & 0xe0) {     /* reserved flag bits */
+                gz_error(state, Z_DATA_ERROR, "unknown header flags set");
+                return -1;
+            }
+            NEXT();                 /* modification time */
+            NEXT();
+            NEXT();
+            NEXT();
+            NEXT();                 /* extra flags */
+            NEXT();                 /* operating system */
+            if (flags & 4) {        /* extra field */
+                len = (unsigned)NEXT();
+                len += (unsigned)NEXT() << 8;
+                while (len--)
+                    if (NEXT() < 0)
+                        break;
+            }
+            if (flags & 8)          /* file name */
+                while (NEXT() > 0)
+                    ;
+            if (flags & 16)         /* comment */
+                while (NEXT() > 0)
+                    ;
+            if (flags & 2) {        /* header crc */
+                NEXT();
+                NEXT();
+            }
+            /* an unexpected end of file is not checked for here -- it will be
+               noticed on the first request for uncompressed data */
+
+            /* set up for decompression */
+            inflateReset(strm);
+            strm->adler = crc32(0L, Z_NULL, 0);
+            state->how = GZIP;
+            state->direct = 0;
+            return 0;
+        }
+        else {
+            /* not a gzip file -- save first byte (31) and fall to raw i/o */
+            state->out[0] = 31;
+            state->have = 1;
+        }
+    }
+
+    /* doing raw i/o, save start of raw data for seeking, copy any leftover
+       input to output -- this assumes that the output buffer is larger than
+       the input buffer, which also assures space for gzungetc() */
+    state->raw = state->pos;
+    state->next = state->out;
+    if (strm->avail_in) {
+        memcpy(state->next + state->have, strm->next_in, strm->avail_in);
+        state->have += strm->avail_in;
+        strm->avail_in = 0;
+    }
+    state->how = COPY;
+    state->direct = 1;
+    return 0;
+}
+
+/* Decompress from input to the provided next_out and avail_out in the state.
+   If the end of the compressed data is reached, then verify the gzip trailer
+   check value and length (modulo 2^32).  state->have and state->next are set
+   to point to the just decompressed data, and the crc is updated.  If the
+   trailer is verified, state->how is reset to LOOK to look for the next gzip
+   stream or raw data, once state->have is depleted.  Returns 0 on success, -1
+   on failure.  Failures may include invalid compressed data or a failed gzip
+   trailer verification. */
+local int gz_decomp(state)
+    gz_statep state;
+{
+    int ret;
+    unsigned had;
+    unsigned long crc, len;
+    z_streamp strm = &(state->strm);
+
+    /* fill output buffer up to end of deflate stream */
+    had = strm->avail_out;
+    do {
+        /* get more input for inflate() */
+        if (strm->avail_in == 0 && gz_avail(state) == -1)
+            return -1;
+        if (strm->avail_in == 0) {
+            gz_error(state, Z_DATA_ERROR, "unexpected end of file");
+            return -1;
+        }
+
+        /* decompress and handle errors */
+        ret = inflate(strm, Z_NO_FLUSH);
+        if (ret == Z_STREAM_ERROR || ret == Z_NEED_DICT) {
+            gz_error(state, Z_STREAM_ERROR,
+                      "internal error: inflate stream corrupt");
+            return -1;
+        }
+        if (ret == Z_MEM_ERROR) {
+            gz_error(state, Z_MEM_ERROR, "out of memory");
+            return -1;
+        }
+        if (ret == Z_DATA_ERROR) {              /* deflate stream invalid */
+            gz_error(state, Z_DATA_ERROR,
+                      strm->msg == NULL ? "compressed data error" : strm->msg);
+            return -1;
+        }
+    } while (strm->avail_out && ret != Z_STREAM_END);
+
+    /* update available output and crc check value */
+    state->have = had - strm->avail_out;
+    state->next = strm->next_out - state->have;
+    strm->adler = crc32(strm->adler, state->next, state->have);
+
+    /* check gzip trailer if at end of deflate stream */
+    if (ret == Z_STREAM_END) {
+        if (gz_next4(state, &crc) == -1 || gz_next4(state, &len) == -1) {
+            gz_error(state, Z_DATA_ERROR, "unexpected end of file");
+            return -1;
+        }
+        if (crc != strm->adler) {
+            gz_error(state, Z_DATA_ERROR, "incorrect data check");
+            return -1;
+        }
+        if (len != (strm->total_out & 0xffffffffL)) {
+            gz_error(state, Z_DATA_ERROR, "incorrect length check");
+            return -1;
+        }
+        state->how = LOOK;      /* ready for next stream, once have is 0 (leave
+                                   state->direct unchanged to remember how) */
+    }
+
+    /* good decompression */
+    return 0;
+}
+
+/* Make data and put in the output buffer.  Assumes that state->have == 0.
+   Data is either copied from the input file or decompressed from the input
+   file depending on state->how.  If state->how is LOOK, then a gzip header is
+   looked for (and skipped if found) to determine wither to copy or decompress.
+   Returns -1 on error, otherwise 0.  gz_make() will leave state->have as COPY
+   or GZIP unless the end of the input file has been reached and all data has
+   been processed.  */
+local int gz_make(state)
+    gz_statep state;
+{
+    z_streamp strm = &(state->strm);
+
+    if (state->how == LOOK) {           /* look for gzip header */
+        if (gz_head(state) == -1)
+            return -1;
+        if (state->have)                /* got some data from gz_head() */
+            return 0;
+    }
+    if (state->how == COPY) {           /* straight copy */
+        if (gz_load(state, state->out, state->size << 1, &(state->have)) == -1)
+            return -1;
+        state->next = state->out;
+    }
+    else if (state->how == GZIP) {      /* decompress */
+        strm->avail_out = state->size << 1;
+        strm->next_out = state->out;
+        if (gz_decomp(state) == -1)
+            return -1;
+    }
+    return 0;
+}
+
+/* Skip len uncompressed bytes of output.  Return -1 on error, 0 on success. */
+local int gz_skip(state, len)
+    gz_statep state;
+    z_off64_t len;
+{
+    unsigned n;
+
+    /* skip over len bytes or reach end-of-file, whichever comes first */
+    while (len)
+        /* skip over whatever is in output buffer */
+        if (state->have) {
+            n = GT_OFF(state->have) || (z_off64_t)state->have > len ?
+                (unsigned)len : state->have;
+            state->have -= n;
+            state->next += n;
+            state->pos += n;
+            len -= n;
+        }
+
+        /* output buffer empty -- return if we're at the end of the input */
+        else if (state->eof && state->strm.avail_in == 0)
+            break;
+
+        /* need more data to skip -- load up output buffer */
+        else {
+            /* get more output, looking for header if required */
+            if (gz_make(state) == -1)
+                return -1;
+        }
+    return 0;
+}
+
+/* -- see zlib.h -- */
+int ZEXPORT gzread(file, buf, len)
+    gzFile file;
+    voidp buf;
+    unsigned len;
+{
+    unsigned got, n;
+    gz_statep state;
+    z_streamp strm;
+
+    /* get internal structure */
+    if (file == NULL)
+        return -1;
+    state = (gz_statep)file;
+    strm = &(state->strm);
+
+    /* check that we're reading and that there's no error */
+    if (state->mode != GZ_READ || state->err != Z_OK)
+        return -1;
+
+    /* since an int is returned, make sure len fits in one, otherwise return
+       with an error (this avoids the flaw in the interface) */
+    if ((int)len < 0) {
+        gz_error(state, Z_BUF_ERROR, "requested length does not fit in int");
+        return -1;
+    }
+
+    /* if len is zero, avoid unnecessary operations */
+    if (len == 0)
+        return 0;
+
+    /* process a skip request */
+    if (state->seek) {
+        state->seek = 0;
+        if (gz_skip(state, state->skip) == -1)
+            return -1;
+    }
+
+    /* get len bytes to buf, or less than len if at the end */
+    got = 0;
+    do {
+        /* first just try copying data from the output buffer */
+        if (state->have) {
+            n = state->have > len ? len : state->have;
+            memcpy(buf, state->next, n);
+            state->next += n;
+            state->have -= n;
+        }
+
+        /* output buffer empty -- return if we're at the end of the input */
+        else if (state->eof && strm->avail_in == 0)
+            break;
+
+        /* need output data -- for small len or new stream load up our output
+           buffer */
+        else if (state->how == LOOK || len < (state->size << 1)) {
+            /* get more output, looking for header if required */
+            if (gz_make(state) == -1)
+                return -1;
+            continue;       /* no progress yet -- go back to memcpy() above */
+            /* the copy above assures that we will leave with space in the
+               output buffer, allowing at least one gzungetc() to succeed */
+        }
+
+        /* large len -- read directly into user buffer */
+        else if (state->how == COPY) {      /* read directly */
+            if (gz_load(state, buf, len, &n) == -1)
+                return -1;
+        }
+
+        /* large len -- decompress directly into user buffer */
+        else {  /* state->how == GZIP */
+            strm->avail_out = len;
+            strm->next_out = buf;
+            if (gz_decomp(state) == -1)
+                return -1;
+            n = state->have;
+            state->have = 0;
+        }
+
+        /* update progress */
+        len -= n;
+        buf = (char *)buf + n;
+        got += n;
+        state->pos += n;
+    } while (len);
+
+    /* return number of bytes read into user buffer (will fit in int) */
+    return (int)got;
+}
+
+/* -- see zlib.h -- */
+int ZEXPORT gzgetc(file)
+    gzFile file;
+{
+    int ret;
+    unsigned char buf[1];
+    gz_statep state;
+
+    /* get internal structure */
+    if (file == NULL)
+        return -1;
+    state = (gz_statep)file;
+
+    /* check that we're reading and that there's no error */
+    if (state->mode != GZ_READ || state->err != Z_OK)
+        return -1;
+
+    /* try output buffer (no need to check for skip request) */
+    if (state->have) {
+        state->have--;
+        state->pos++;
+        return *(state->next)++;
+    }
+
+    /* nothing there -- try gzread() */
+    ret = gzread(file, buf, 1);
+    return ret < 1 ? -1 : buf[0];
+}
+
+/* -- see zlib.h -- */
+int ZEXPORT gzungetc(c, file)
+    int c;
+    gzFile file;
+{
+    gz_statep state;
+
+    /* get internal structure */
+    if (file == NULL)
+        return -1;
+    state = (gz_statep)file;
+
+    /* check that we're reading and that there's no error */
+    if (state->mode != GZ_READ || state->err != Z_OK)
+        return -1;
+
+    /* process a skip request */
+    if (state->seek) {
+        state->seek = 0;
+        if (gz_skip(state, state->skip) == -1)
+            return -1;
+    }
+
+    /* can't push EOF */
+    if (c < 0)
+        return -1;
+
+    /* if output buffer empty, put byte at end (allows more pushing) */
+    if (state->have == 0) {
+        state->have = 1;
+        state->next = state->out + (state->size << 1) - 1;
+        state->next[0] = c;
+        state->pos--;
+        return c;
+    }
+
+    /* if no room, give up (must have already done a gzungetc()) */
+    if (state->have == (state->size << 1)) {
+        gz_error(state, Z_BUF_ERROR, "out of room to push characters");
+        return -1;
+    }
+
+    /* slide output data if needed and insert byte before existing data */
+    if (state->next == state->out) {
+        unsigned char *src = state->out + state->have;
+        unsigned char *dest = state->out + (state->size << 1);
+        while (src > state->out)
+            *--dest = *--src;
+        state->next = dest;
+    }
+    state->have++;
+    state->next--;
+    state->next[0] = c;
+    state->pos--;
+    return c;
+}
+
+/* -- see zlib.h -- */
+char * ZEXPORT gzgets(file, buf, len)
+    gzFile file;
+    char *buf;
+    int len;
+{
+    unsigned left, n;
+    char *str;
+    unsigned char *eol;
+    gz_statep state;
+
+    /* check parameters and get internal structure */
+    if (file == NULL || buf == NULL || len < 1)
+        return NULL;
+    state = (gz_statep)file;
+
+    /* check that we're reading and that there's no error */
+    if (state->mode != GZ_READ || state->err != Z_OK)
+        return NULL;
+
+    /* process a skip request */
+    if (state->seek) {
+        state->seek = 0;
+        if (gz_skip(state, state->skip) == -1)
+            return NULL;
+    }
+
+    /* copy output bytes up to new line or len - 1, whichever comes first --
+       append a terminating zero to the string (we don't check for a zero in
+       the contents, let the user worry about that) */
+    str = buf;
+    left = (unsigned)len - 1;
+    if (left) do {
+        /* assure that something is in the output buffer */
+        if (state->have == 0) {
+            if (gz_make(state) == -1)
+                return NULL;            /* error */
+            if (state->have == 0) {     /* end of file */
+                if (buf == str)         /* got bupkus */
+                    return NULL;
+                break;                  /* got something -- return it */
+            }
+        }
+
+        /* look for end-of-line in current output buffer */
+        n = state->have > left ? left : state->have;
+        eol = memchr(state->next, '\n', n);
+        if (eol != NULL)
+            n = (unsigned)(eol - state->next) + 1;
+
+        /* copy through end-of-line, or remainder if not found */
+        memcpy(buf, state->next, n);
+        state->have -= n;
+        state->next += n;
+        state->pos += n;
+        left -= n;
+        buf += n;
+    } while (left && eol == NULL);
+
+    /* found end-of-line or out of space -- terminate string and return it */
+    buf[0] = 0;
+    return str;
+}
+
+/* -- see zlib.h -- */
+int ZEXPORT gzdirect(file)
+    gzFile file;
+{
+    gz_statep state;
+
+    /* get internal structure */
+    if (file == NULL)
+        return 0;
+    state = (gz_statep)file;
+
+    /* check that we're reading */
+    if (state->mode != GZ_READ)
+        return 0;
+
+    /* if the state is not known, but we can find out, then do so (this is
+       mainly for right after a gzopen() or gzdopen()) */
+    if (state->how == LOOK && state->have == 0)
+        (void)gz_head(state);
+
+    /* return 1 if reading direct, 0 if decompressing a gzip stream */
+    return state->direct;
+}
+
+/* -- see zlib.h -- */
+int ZEXPORT gzclose_r(file)
+    gzFile file;
+{
+    int ret;
+    gz_statep state;
+
+    /* get internal structure */
+    if (file == NULL)
+        return Z_STREAM_ERROR;
+    state = (gz_statep)file;
+
+    /* check that we're reading */
+    if (state->mode != GZ_READ)
+        return Z_STREAM_ERROR;
+
+    /* free memory and close file */
+    if (state->size) {
+        inflateEnd(&(state->strm));
+        free(state->out);
+        free(state->in);
+    }
+    gz_error(state, Z_OK, NULL);
+    free(state->path);
+    ret = close(state->fd);
+    free(state);
+    return ret ? Z_ERRNO : Z_OK;
+}
diff --git a/lib/3rdparty/zlib/gzwrite.c b/lib/3rdparty/zlib/gzwrite.c
new file mode 100644 (file)
index 0000000..e8defc6
--- /dev/null
@@ -0,0 +1,531 @@
+/* gzwrite.c -- zlib functions for writing gzip files
+ * Copyright (C) 2004, 2005, 2010 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+#include "gzguts.h"
+
+/* Local functions */
+local int gz_init OF((gz_statep));
+local int gz_comp OF((gz_statep, int));
+local int gz_zero OF((gz_statep, z_off64_t));
+
+/* Initialize state for writing a gzip file.  Mark initialization by setting
+   state->size to non-zero.  Return -1 on failure or 0 on success. */
+local int gz_init(state)
+    gz_statep state;
+{
+    int ret;
+    z_streamp strm = &(state->strm);
+
+    /* allocate input and output buffers */
+    state->in = malloc(state->want);
+    state->out = malloc(state->want);
+    if (state->in == NULL || state->out == NULL) {
+        if (state->out != NULL)
+            free(state->out);
+        if (state->in != NULL)
+            free(state->in);
+        gz_error(state, Z_MEM_ERROR, "out of memory");
+        return -1;
+    }
+
+    /* allocate deflate memory, set up for gzip compression */
+    strm->zalloc = Z_NULL;
+    strm->zfree = Z_NULL;
+    strm->opaque = Z_NULL;
+    ret = deflateInit2(strm, state->level, Z_DEFLATED,
+                       15 + 16, 8, state->strategy);
+    if (ret != Z_OK) {
+        free(state->in);
+        gz_error(state, Z_MEM_ERROR, "out of memory");
+        return -1;
+    }
+
+    /* mark state as initialized */
+    state->size = state->want;
+
+    /* initialize write buffer */
+    strm->avail_out = state->size;
+    strm->next_out = state->out;
+    state->next = strm->next_out;
+    return 0;
+}
+
+/* Compress whatever is at avail_in and next_in and write to the output file.
+   Return -1 if there is an error writing to the output file, otherwise 0.
+   flush is assumed to be a valid deflate() flush value.  If flush is Z_FINISH,
+   then the deflate() state is reset to start a new gzip stream. */
+local int gz_comp(state, flush)
+    gz_statep state;
+    int flush;
+{
+    int ret, got;
+    unsigned have;
+    z_streamp strm = &(state->strm);
+
+    /* allocate memory if this is the first time through */
+    if (state->size == 0 && gz_init(state) == -1)
+        return -1;
+
+    /* run deflate() on provided input until it produces no more output */
+    ret = Z_OK;
+    do {
+        /* write out current buffer contents if full, or if flushing, but if
+           doing Z_FINISH then don't write until we get to Z_STREAM_END */
+        if (strm->avail_out == 0 || (flush != Z_NO_FLUSH &&
+            (flush != Z_FINISH || ret == Z_STREAM_END))) {
+            have = (unsigned)(strm->next_out - state->next);
+            if (have && ((got = write(state->fd, state->next, have)) < 0 ||
+                         (unsigned)got != have)) {
+                gz_error(state, Z_ERRNO, zstrerror());
+                return -1;
+            }
+            if (strm->avail_out == 0) {
+                strm->avail_out = state->size;
+                strm->next_out = state->out;
+            }
+            state->next = strm->next_out;
+        }
+
+        /* compress */
+        have = strm->avail_out;
+        ret = deflate(strm, flush);
+        if (ret == Z_STREAM_ERROR) {
+            gz_error(state, Z_STREAM_ERROR,
+                      "internal error: deflate stream corrupt");
+            return -1;
+        }
+        have -= strm->avail_out;
+    } while (have);
+
+    /* if that completed a deflate stream, allow another to start */
+    if (flush == Z_FINISH)
+        deflateReset(strm);
+
+    /* all done, no errors */
+    return 0;
+}
+
+/* Compress len zeros to output.  Return -1 on error, 0 on success. */
+local int gz_zero(state, len)
+    gz_statep state;
+    z_off64_t len;
+{
+    int first;
+    unsigned n;
+    z_streamp strm = &(state->strm);
+
+    /* consume whatever's left in the input buffer */
+    if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1)
+        return -1;
+
+    /* compress len zeros (len guaranteed > 0) */
+    first = 1;
+    while (len) {
+        n = GT_OFF(state->size) || (z_off64_t)state->size > len ?
+            (unsigned)len : state->size;
+        if (first) {
+            memset(state->in, 0, n);
+            first = 0;
+        }
+        strm->avail_in = n;
+        strm->next_in = state->in;
+        state->pos += n;
+        if (gz_comp(state, Z_NO_FLUSH) == -1)
+            return -1;
+        len -= n;
+    }
+    return 0;
+}
+
+/* -- see zlib.h -- */
+int ZEXPORT gzwrite(file, buf, len)
+    gzFile file;
+    voidpc buf;
+    unsigned len;
+{
+    unsigned put = len;
+    unsigned n;
+    gz_statep state;
+    z_streamp strm;
+
+    /* get internal structure */
+    if (file == NULL)
+        return 0;
+    state = (gz_statep)file;
+    strm = &(state->strm);
+
+    /* check that we're writing and that there's no error */
+    if (state->mode != GZ_WRITE || state->err != Z_OK)
+        return 0;
+
+    /* since an int is returned, make sure len fits in one, otherwise return
+       with an error (this avoids the flaw in the interface) */
+    if ((int)len < 0) {
+        gz_error(state, Z_BUF_ERROR, "requested length does not fit in int");
+        return 0;
+    }
+
+    /* if len is zero, avoid unnecessary operations */
+    if (len == 0)
+        return 0;
+
+    /* allocate memory if this is the first time through */
+    if (state->size == 0 && gz_init(state) == -1)
+        return 0;
+
+    /* check for seek request */
+    if (state->seek) {
+        state->seek = 0;
+        if (gz_zero(state, state->skip) == -1)
+            return 0;
+    }
+
+    /* for small len, copy to input buffer, otherwise compress directly */
+    if (len < state->size) {
+        /* copy to input buffer, compress when full */
+        do {
+            if (strm->avail_in == 0)
+                strm->next_in = state->in;
+            n = state->size - strm->avail_in;
+            if (n > len)
+                n = len;
+            memcpy(strm->next_in + strm->avail_in, buf, n);
+            strm->avail_in += n;
+            state->pos += n;
+            buf = (char *)buf + n;
+            len -= n;
+            if (len && gz_comp(state, Z_NO_FLUSH) == -1)
+                return 0;
+        } while (len);
+    }
+    else {
+        /* consume whatever's left in the input buffer */
+        if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1)
+            return 0;
+
+        /* directly compress user buffer to file */
+        strm->avail_in = len;
+        strm->next_in = (voidp)buf;
+        state->pos += len;
+        if (gz_comp(state, Z_NO_FLUSH) == -1)
+            return 0;
+    }
+
+    /* input was all buffered or compressed (put will fit in int) */
+    return (int)put;
+}
+
+/* -- see zlib.h -- */
+int ZEXPORT gzputc(file, c)
+    gzFile file;
+    int c;
+{
+    unsigned char buf[1];
+    gz_statep state;
+    z_streamp strm;
+
+    /* get internal structure */
+    if (file == NULL)
+        return -1;
+    state = (gz_statep)file;
+    strm = &(state->strm);
+
+    /* check that we're writing and that there's no error */
+    if (state->mode != GZ_WRITE || state->err != Z_OK)
+        return -1;
+
+    /* check for seek request */
+    if (state->seek) {
+        state->seek = 0;
+        if (gz_zero(state, state->skip) == -1)
+            return -1;
+    }
+
+    /* try writing to input buffer for speed (state->size == 0 if buffer not
+       initialized) */
+    if (strm->avail_in < state->size) {
+        if (strm->avail_in == 0)
+            strm->next_in = state->in;
+        strm->next_in[strm->avail_in++] = c;
+        state->pos++;
+        return c;
+    }
+
+    /* no room in buffer or not initialized, use gz_write() */
+    buf[0] = c;
+    if (gzwrite(file, buf, 1) != 1)
+        return -1;
+    return c;
+}
+
+/* -- see zlib.h -- */
+int ZEXPORT gzputs(file, str)
+    gzFile file;
+    const char *str;
+{
+    int ret;
+    unsigned len;
+
+    /* write string */
+    len = (unsigned)strlen(str);
+    ret = gzwrite(file, str, len);
+    return ret == 0 && len != 0 ? -1 : ret;
+}
+
+#ifdef STDC
+#include <stdarg.h>
+
+/* -- see zlib.h -- */
+int ZEXPORTVA gzprintf (gzFile file, const char *format, ...)
+{
+    int size, len;
+    gz_statep state;
+    z_streamp strm;
+    va_list va;
+
+    /* get internal structure */
+    if (file == NULL)
+        return -1;
+    state = (gz_statep)file;
+    strm = &(state->strm);
+
+    /* check that we're writing and that there's no error */
+    if (state->mode != GZ_WRITE || state->err != Z_OK)
+        return 0;
+
+    /* make sure we have some buffer space */
+    if (state->size == 0 && gz_init(state) == -1)
+        return 0;
+
+    /* check for seek request */
+    if (state->seek) {
+        state->seek = 0;
+        if (gz_zero(state, state->skip) == -1)
+            return 0;
+    }
+
+    /* consume whatever's left in the input buffer */
+    if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1)
+        return 0;
+
+    /* do the printf() into the input buffer, put length in len */
+    size = (int)(state->size);
+    state->in[size - 1] = 0;
+    va_start(va, format);
+#ifdef NO_vsnprintf
+#  ifdef HAS_vsprintf_void
+    (void)vsprintf(state->in, format, va);
+    va_end(va);
+    for (len = 0; len < size; len++)
+        if (state->in[len] == 0) break;
+#  else
+    len = vsprintf(state->in, format, va);
+    va_end(va);
+#  endif
+#else
+#  ifdef HAS_vsnprintf_void
+    (void)vsnprintf(state->in, size, format, va);
+    va_end(va);
+    len = strlen(state->in);
+#  else
+    len = vsnprintf((char *)(state->in), size, format, va);
+    va_end(va);
+#  endif
+#endif
+
+    /* check that printf() results fit in buffer */
+    if (len <= 0 || len >= (int)size || state->in[size - 1] != 0)
+        return 0;
+
+    /* update buffer and position, defer compression until needed */
+    strm->avail_in = (unsigned)len;
+    strm->next_in = state->in;
+    state->pos += len;
+    return len;
+}
+
+#else /* !STDC */
+
+/* -- see zlib.h -- */
+int ZEXPORTVA gzprintf (file, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10,
+                       a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
+    gzFile file;
+    const char *format;
+    int a1, a2, a3, a4, a5, a6, a7, a8, a9, a10,
+        a11, a12, a13, a14, a15, a16, a17, a18, a19, a20;
+{
+    int size, len;
+    gz_statep state;
+    z_streamp strm;
+
+    /* get internal structure */
+    if (file == NULL)
+        return -1;
+    state = (gz_statep)file;
+    strm = &(state->strm);
+
+    /* check that we're writing and that there's no error */
+    if (state->mode != GZ_WRITE || state->err != Z_OK)
+        return 0;
+
+    /* make sure we have some buffer space */
+    if (state->size == 0 && gz_init(state) == -1)
+        return 0;
+
+    /* check for seek request */
+    if (state->seek) {
+        state->seek = 0;
+        if (gz_zero(state, state->skip) == -1)
+            return 0;
+    }
+
+    /* consume whatever's left in the input buffer */
+    if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1)
+        return 0;
+
+    /* do the printf() into the input buffer, put length in len */
+    size = (int)(state->size);
+    state->in[size - 1] = 0;
+#ifdef NO_snprintf
+#  ifdef HAS_sprintf_void
+    sprintf(state->in, format, a1, a2, a3, a4, a5, a6, a7, a8,
+            a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20);
+    for (len = 0; len < size; len++)
+        if (state->in[len] == 0) break;
+#  else
+    len = sprintf(state->in, format, a1, a2, a3, a4, a5, a6, a7, a8,
+                a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20);
+#  endif
+#else
+#  ifdef HAS_snprintf_void
+    snprintf(state->in, size, format, a1, a2, a3, a4, a5, a6, a7, a8,
+             a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20);
+    len = strlen(state->in);
+#  else
+    len = snprintf(state->in, size, format, a1, a2, a3, a4, a5, a6, a7, a8,
+                 a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20);
+#  endif
+#endif
+
+    /* check that printf() results fit in buffer */
+    if (len <= 0 || len >= (int)size || state->in[size - 1] != 0)
+        return 0;
+
+    /* update buffer and position, defer compression until needed */
+    strm->avail_in = (unsigned)len;
+    strm->next_in = state->in;
+    state->pos += len;
+    return len;
+}
+
+#endif
+
+/* -- see zlib.h -- */
+int ZEXPORT gzflush(file, flush)
+    gzFile file;
+    int flush;
+{
+    gz_statep state;
+
+    /* get internal structure */
+    if (file == NULL)
+        return -1;
+    state = (gz_statep)file;
+
+    /* check that we're writing and that there's no error */
+    if (state->mode != GZ_WRITE || state->err != Z_OK)
+        return Z_STREAM_ERROR;
+
+    /* check flush parameter */
+    if (flush < 0 || flush > Z_FINISH)
+        return Z_STREAM_ERROR;
+
+    /* check for seek request */
+    if (state->seek) {
+        state->seek = 0;
+        if (gz_zero(state, state->skip) == -1)
+            return -1;
+    }
+
+    /* compress remaining data with requested flush */
+    gz_comp(state, flush);
+    return state->err;
+}
+
+/* -- see zlib.h -- */
+int ZEXPORT gzsetparams(file, level, strategy)
+    gzFile file;
+    int level;
+    int strategy;
+{
+    gz_statep state;
+    z_streamp strm;
+
+    /* get internal structure */
+    if (file == NULL)
+        return Z_STREAM_ERROR;
+    state = (gz_statep)file;
+    strm = &(state->strm);
+
+    /* check that we're writing and that there's no error */
+    if (state->mode != GZ_WRITE || state->err != Z_OK)
+        return Z_STREAM_ERROR;
+
+    /* if no change is requested, then do nothing */
+    if (level == state->level && strategy == state->strategy)
+        return Z_OK;
+
+    /* check for seek request */
+    if (state->seek) {
+        state->seek = 0;
+        if (gz_zero(state, state->skip) == -1)
+            return -1;
+    }
+
+    /* change compression parameters for subsequent input */
+    if (state->size) {
+        /* flush previous input with previous parameters before changing */
+        if (strm->avail_in && gz_comp(state, Z_PARTIAL_FLUSH) == -1)
+            return state->err;
+        deflateParams(strm, level, strategy);
+    }
+    state->level = level;
+    state->strategy = strategy;
+    return Z_OK;
+}
+
+/* -- see zlib.h -- */
+int ZEXPORT gzclose_w(file)
+    gzFile file;
+{
+    int ret = 0;
+    gz_statep state;
+
+    /* get internal structure */
+    if (file == NULL)
+        return Z_STREAM_ERROR;
+    state = (gz_statep)file;
+
+    /* check that we're writing */
+    if (state->mode != GZ_WRITE)
+        return Z_STREAM_ERROR;
+
+    /* check for seek request */
+    if (state->seek) {
+        state->seek = 0;
+        ret += gz_zero(state, state->skip);
+    }
+
+    /* flush, free memory, and close file */
+    ret += gz_comp(state, Z_FINISH);
+    (void)deflateEnd(&(state->strm));
+    free(state->out);
+    free(state->in);
+    gz_error(state, Z_OK, NULL);
+    free(state->path);
+    ret += close(state->fd);
+    free(state);
+    return ret ? Z_ERRNO : Z_OK;
+}
diff --git a/ntoskrnl/mm/ARM3/mmdbg.c b/ntoskrnl/mm/ARM3/mmdbg.c
new file mode 100644 (file)
index 0000000..41260f2
--- /dev/null
@@ -0,0 +1,326 @@
+/*
+ * PROJECT:         ReactOS Kernel
+ * LICENSE:         BSD - See COPYING.ARM in the top level directory
+ * FILE:            ntoskrnl/mm/ARM3/mmdbg.c
+ * PURPOSE:         Memory Manager support routines for the Kernel Debugger
+ * PROGRAMMERS:     Stefan Ginsberg (stefan.ginsberg@reactos.org)
+ */
+
+/* INCLUDES *******************************************************************/
+
+#include <ntoskrnl.h>
+#define NDEBUG
+#include <debug.h>
+
+#line 16 "ARM³::DEBUGSUP"
+#define MODULE_INVOLVED_IN_ARM3
+#include "../ARM3/miarm.h"
+
+#ifndef _WINKD_
+#define KdpDprintf DPRINT
+#endif
+
+/* GLOBALS ********************************************************************/
+
+PMMPTE MmDebugPte = MiAddressToPte(MI_DEBUG_MAPPING);
+BOOLEAN MiDbgReadyForPhysical = FALSE;
+
+/* FUNCTIONS ******************************************************************/
+
+BOOLEAN
+NTAPI
+MmIsSessionAddress(IN PVOID Address)
+{
+    //
+    // No session space support yet
+    //
+    return FALSE;
+}
+
+PVOID
+NTAPI
+MiDbgTranslatePhysicalAddress(IN ULONG64 PhysicalAddress,
+                              IN ULONG Flags)
+{
+    PFN_NUMBER Pfn;
+    MMPTE TempPte;
+    PVOID MappingBaseAddress;
+
+    //
+    // Check if we are called too early 
+    //
+    if (MiDbgReadyForPhysical == FALSE)
+    {
+        //
+        // The structures we require aren't initialized yet, fail
+        //
+        KdpDprintf("MiDbgTranslatePhysicalAddress called too early! "
+                   "Address: 0x%I64x\n", PhysicalAddress);
+        return NULL;
+    }
+
+    //
+    // FIXME: No support for cache flags yet
+    //
+    if ((Flags & (MMDBG_COPY_CACHED |
+                  MMDBG_COPY_UNCACHED |
+                  MMDBG_COPY_WRITE_COMBINED)) != 0)
+    {
+        //
+        // Fail
+        //
+        KdpDprintf("MiDbgTranslatePhysicalAddress: Cache Flags not yet supported. "
+                   "Flags: 0x%lx\n", Flags & (MMDBG_COPY_CACHED |
+                                              MMDBG_COPY_UNCACHED |
+                                              MMDBG_COPY_WRITE_COMBINED));
+        return NULL;
+    }
+
+    //
+    // Save the base address of our mapping page
+    //
+    MappingBaseAddress = MiPteToAddress(MmDebugPte);
+
+    //
+    // Get the template
+    //
+    TempPte = ValidKernelPte;
+
+    //
+    // Convert physical address to PFN
+    //
+    Pfn = (PFN_NUMBER)(PhysicalAddress >> PAGE_SHIFT);
+
+    /* Check if this could be an I/O mapping */
+    if (!MiGetPfnEntry(Pfn))
+    {
+        //
+        // FIXME: We don't support this yet
+        //
+        KdpDprintf("MiDbgTranslatePhysicalAddress: I/O Space not yet supported. "
+                   "PFN: 0x%I64x\n", (ULONG64)Pfn);
+        return NULL;
+    }
+    else
+    {
+        //
+        // Set the PFN in the PTE
+        //
+        TempPte.u.Hard.PageFrameNumber = Pfn;
+    }
+
+    //
+    // Map the PTE and invalidate its TLB entry
+    //
+    *MmDebugPte = TempPte;
+    KeInvalidateTlbEntry(MappingBaseAddress);
+
+    //
+    // Calculate and return the virtual offset into our mapping page
+    //
+    return (PVOID)((ULONG_PTR)MappingBaseAddress +
+                    BYTE_OFFSET(PhysicalAddress));
+}
+
+VOID
+NTAPI
+MiDbgUnTranslatePhysicalAddress(VOID)
+{
+    PVOID MappingBaseAddress = MiPteToAddress(MmDebugPte);
+
+    //
+    // The address must still be valid at this point
+    //
+    ASSERT(MmIsAddressValid(MappingBaseAddress));
+
+    // 
+    // Clear the mapping PTE and invalidate its TLB entry
+    //
+    MmDebugPte->u.Long = 0;
+    KeInvalidateTlbEntry(MappingBaseAddress);
+}
+
+NTSTATUS
+NTAPI
+MmDbgCopyMemory(IN ULONG64 Address,
+                IN PVOID Buffer,
+                IN ULONG Size,
+                IN ULONG Flags)
+{
+    NTSTATUS Status;
+    PVOID TargetAddress;
+
+    //
+    // No local kernel debugging support yet, so don't worry about locking
+    //
+    ASSERT(Flags & MMDBG_COPY_UNSAFE);
+
+    //
+    // We only handle 1, 2, 4 and 8 byte requests
+    //
+    if ((Size != 1) &&
+        (Size != 2) &&
+        (Size != 4) &&
+        (Size != MMDBG_COPY_MAX_SIZE))
+    {
+        //
+        // Invalid size, fail
+        //
+        KdpDprintf("MmDbgCopyMemory: Received Illegal Size 0x%lx\n", Size);
+        return STATUS_INVALID_PARAMETER_3;
+    }
+
+    //
+    // The copy must be aligned
+    //
+    if ((Address & (Size - 1)) != 0)
+    {
+        //
+        // Fail
+        //
+        KdpDprintf("MmDbgCopyMemory: Received Unaligned Address 0x%I64x Size %lx\n",
+                  Address, Size);
+        return STATUS_INVALID_PARAMETER_3;
+    }
+
+    //
+    // Check if this is physical or virtual copy
+    //
+    if (Flags & MMDBG_COPY_PHYSICAL)
+    {
+        //
+        // Physical: translate and map it to our mapping space
+        //
+        TargetAddress = MiDbgTranslatePhysicalAddress(Address, Flags);
+
+        //
+        // Check if translation failed
+        //
+        if (!TargetAddress)
+        {
+            //
+            // Fail
+            //
+            KdpDprintf("MmDbgCopyMemory: Failed to Translate Physical Address "
+                       "%I64x\n", Address);
+            return STATUS_UNSUCCESSFUL;
+        }
+
+        //
+        // The address we received must be valid!
+        //
+        ASSERT(MmIsAddressValid(TargetAddress));
+    }
+    else
+    {
+        //
+        // Virtual; truncate it to avoid casts later down
+        //
+        TargetAddress = (PVOID)(ULONG_PTR)Address;
+
+        //
+        // Check if the address is invalid
+        //
+        if (!MmIsAddressValid(TargetAddress))
+        {
+            //
+            // Fail
+            //
+            KdpDprintf("MmDbgCopyMemory: Failing %s for invalid "
+                       "Virtual Address 0x%p\n",
+                       Flags & MMDBG_COPY_WRITE ? "write" : "read",
+                       TargetAddress);
+            return STATUS_UNSUCCESSFUL;
+        }
+
+        //
+        // No session space support yet
+        //
+        ASSERT(MmIsSessionAddress(TargetAddress) == FALSE);
+    }
+
+    //
+    // If we are going to write to the address then make sure it is writeable too
+    //
+    if ((Flags & MMDBG_COPY_WRITE) &&
+        (!MI_IS_PAGE_WRITEABLE(MiAddressToPte(TargetAddress))))
+    {
+        //
+        // Check if we mapped anything
+        //
+        if (Flags & MMDBG_COPY_PHYSICAL)
+        {
+            //
+            // Get rid of the mapping
+            //
+            MiDbgUnTranslatePhysicalAddress();
+        }
+
+        //
+        // Fail
+        //
+        // FIXME: We should attempt to override the write protection instead of
+        // failing here
+        //
+        KdpDprintf("MmDbgCopyMemory: Failing Write for Protected Address 0x%p\n",
+                   TargetAddress);
+        return STATUS_UNSUCCESSFUL;
+    }
+
+    //
+    // Use SEH to try to catch anything else somewhat cleanly
+    //
+    _SEH2_TRY
+    {
+        //
+        // Check if this is read or write
+        //
+        if (Flags & MMDBG_COPY_WRITE)
+        {
+            //
+            // Do the write
+            //
+            RtlCopyMemory(TargetAddress,
+                          Buffer,
+                          Size);
+        }
+        else
+        {
+            //
+            // Do the read
+            //
+            RtlCopyMemory(Buffer,
+                          TargetAddress,
+                          Size);
+        }
+
+        //
+        // Copy succeeded
+        //
+        Status = STATUS_SUCCESS;
+    }
+    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+    {
+        //
+        // Get the exception code
+        //
+        Status = _SEH2_GetExceptionCode();
+    }
+    _SEH2_END;
+
+    //
+    // Get rid of the mapping if this was a physical copy
+    //
+    if (Flags & MMDBG_COPY_PHYSICAL)
+    {
+        //
+        // Unmap and flush it
+        //
+        MiDbgUnTranslatePhysicalAddress();
+    }
+
+    //
+    // Return status to caller
+    //
+    return Status;
+}
diff --git a/ntoskrnl/mm/ARM3/sysldr.c b/ntoskrnl/mm/ARM3/sysldr.c
new file mode 100644 (file)
index 0000000..d8f7157
--- /dev/null
@@ -0,0 +1,3129 @@
+/*
+* PROJECT:         ReactOS Kernel
+* LICENSE:         BSD - See COPYING.ARM in the top level directory
+* FILE:            ntoskrnl/mm/ARM3/sysldr.c
+* PURPOSE:         Contains the Kernel Loader (SYSLDR) for loading PE files.
+* PROGRAMMERS:     Alex Ionescu (alex.ionescu@reactos.org)
+*                  ReactOS Portable Systems Group
+*/
+
+/* INCLUDES *******************************************************************/
+
+#include <ntoskrnl.h>
+#define NDEBUG
+#include <debug.h>
+
+#line 16 "ARM³::LOADER"
+#define MODULE_INVOLVED_IN_ARM3
+#include "../ARM3/miarm.h"
+
+/* GCC's incompetence strikes again */
+__inline
+VOID
+sprintf_nt(IN PCHAR Buffer,
+           IN PCHAR Format,
+           IN ...)
+{
+    va_list ap;
+    va_start(ap, Format);
+    vsprintf(Buffer, Format, ap);
+    va_end(ap);
+}
+
+/* GLOBALS ********************************************************************/
+
+LIST_ENTRY PsLoadedModuleList;
+LIST_ENTRY MmLoadedUserImageList;
+KSPIN_LOCK PsLoadedModuleSpinLock;
+ERESOURCE PsLoadedModuleResource;
+ULONG_PTR PsNtosImageBase;
+KMUTANT MmSystemLoadLock;
+
+PFN_NUMBER MmTotalSystemDriverPages;
+
+PVOID MmUnloadedDrivers;
+PVOID MmLastUnloadedDrivers;
+
+BOOLEAN MmMakeLowMemory;
+BOOLEAN MmEnforceWriteProtection = TRUE;
+
+PMMPTE MiKernelResourceStartPte, MiKernelResourceEndPte;
+ULONG_PTR ExPoolCodeStart, ExPoolCodeEnd, MmPoolCodeStart, MmPoolCodeEnd;
+ULONG_PTR MmPteCodeStart, MmPteCodeEnd;
+
+/* FUNCTIONS ******************************************************************/
+
+PVOID
+NTAPI
+MiCacheImageSymbols(IN PVOID BaseAddress)
+{
+    ULONG DebugSize;
+    PVOID DebugDirectory = NULL;
+    PAGED_CODE();
+
+    /* Make sure it's safe to access the image */
+    _SEH2_TRY
+    {
+        /* Get the debug directory */
+        DebugDirectory = RtlImageDirectoryEntryToData(BaseAddress,
+                                                      TRUE,
+                                                      IMAGE_DIRECTORY_ENTRY_DEBUG,
+                                                      &DebugSize);
+    }
+    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+    {
+        /* Nothing */
+    }
+    _SEH2_END;
+
+    /* Return the directory */
+    return DebugDirectory;
+}
+
+NTSTATUS
+NTAPI
+MiLoadImageSection(IN OUT PVOID *SectionPtr,
+                   OUT PVOID *ImageBase,
+                   IN PUNICODE_STRING FileName,
+                   IN BOOLEAN SessionLoad,
+                   IN PLDR_DATA_TABLE_ENTRY LdrEntry)
+{
+    PROS_SECTION_OBJECT Section = *SectionPtr;
+    NTSTATUS Status;
+    PEPROCESS Process;
+    PVOID Base = NULL;
+    SIZE_T ViewSize = 0;
+    KAPC_STATE ApcState;
+    LARGE_INTEGER SectionOffset = {{0, 0}};
+    BOOLEAN LoadSymbols = FALSE;
+    PFN_NUMBER PteCount;
+    PMMPTE PointerPte, LastPte;
+    PVOID DriverBase;
+    MMPTE TempPte;
+    PAGED_CODE();
+
+    /* Detect session load */
+    if (SessionLoad)
+    {
+        /* Fail */
+        DPRINT1("Session loading not yet supported!\n");
+        while (TRUE);
+    }
+
+    /* Not session load, shouldn't have an entry */
+    ASSERT(LdrEntry == NULL);
+    
+    /* Attach to the system process */
+    KeStackAttachProcess(&PsInitialSystemProcess->Pcb, &ApcState);
+
+    /* Check if we need to load symbols */
+    if (NtGlobalFlag & FLG_ENABLE_KDEBUG_SYMBOL_LOAD)
+    {
+        /* Yes we do */
+        LoadSymbols = TRUE;
+        NtGlobalFlag &= ~FLG_ENABLE_KDEBUG_SYMBOL_LOAD;
+    }
+
+    /* Map the driver */
+    Process = PsGetCurrentProcess();
+    Status = MmMapViewOfSection(Section,
+                                Process,
+                                &Base,
+                                0,
+                                0,
+                                &SectionOffset,
+                                &ViewSize,
+                                ViewUnmap,
+                                0,
+                                PAGE_EXECUTE);
+
+    /* Re-enable the flag */
+    if (LoadSymbols) NtGlobalFlag |= FLG_ENABLE_KDEBUG_SYMBOL_LOAD;
+
+    /* Check if we failed with distinguished status code */
+    if (Status == STATUS_IMAGE_MACHINE_TYPE_MISMATCH)
+    {
+        /* Change it to something more generic */
+        Status = STATUS_INVALID_IMAGE_FORMAT;
+    }
+
+    /* Now check if we failed */
+    if (!NT_SUCCESS(Status))
+    {
+        /* Detach and return */
+        KeUnstackDetachProcess(&ApcState);
+        return Status;
+    }
+    
+    /* Reserve system PTEs needed */
+    PteCount = ROUND_TO_PAGES(Section->ImageSection->ImageSize) >> PAGE_SHIFT;
+    PointerPte = MiReserveSystemPtes(PteCount, SystemPteSpace);
+    if (!PointerPte) return STATUS_INSUFFICIENT_RESOURCES;
+    
+    /* New driver base */
+    LastPte = PointerPte + PteCount;
+    DriverBase = MiPteToAddress(PointerPte);
+
+    /* The driver is here */
+    *ImageBase = DriverBase;
+
+    /* Loop the new driver PTEs */
+    TempPte = ValidKernelPte;
+    while (PointerPte < LastPte)
+    {
+        /* Allocate a page */
+        TempPte.u.Hard.PageFrameNumber = MiAllocatePfn(PointerPte, MM_EXECUTE);
+
+        /* Write it */
+        MI_WRITE_VALID_PTE(PointerPte, TempPte);
+
+        /* Move on */
+        PointerPte++;
+    }
+        
+    /* Copy the image */
+    RtlCopyMemory(DriverBase, Base, PteCount << PAGE_SHIFT);
+
+    /* Now unmap the view */
+    Status = MmUnmapViewOfSection(Process, Base);
+    ASSERT(NT_SUCCESS(Status));
+
+    /* Detach and return status */
+    KeUnstackDetachProcess(&ApcState);
+    return Status;
+}
+
+PVOID
+NTAPI
+MiLocateExportName(IN PVOID DllBase,
+                   IN PCHAR ExportName)
+{
+    PULONG NameTable;
+    PUSHORT OrdinalTable;
+    PIMAGE_EXPORT_DIRECTORY ExportDirectory;
+    LONG Low = 0, Mid = 0, High, Ret;
+    USHORT Ordinal;
+    PVOID Function;
+    ULONG ExportSize;
+    PULONG ExportTable;
+    PAGED_CODE();
+
+    /* Get the export directory */
+    ExportDirectory = RtlImageDirectoryEntryToData(DllBase,
+                                                   TRUE,
+                                                   IMAGE_DIRECTORY_ENTRY_EXPORT,
+                                                   &ExportSize);
+    if (!ExportDirectory) return NULL;
+
+    /* Setup name tables */
+    NameTable = (PULONG)((ULONG_PTR)DllBase +
+                         ExportDirectory->AddressOfNames);
+    OrdinalTable = (PUSHORT)((ULONG_PTR)DllBase +
+                             ExportDirectory->AddressOfNameOrdinals);
+
+    /* Do a binary search */
+    High = ExportDirectory->NumberOfNames - 1;
+    while (High >= Low)
+    {
+        /* Get new middle value */
+        Mid = (Low + High) >> 1;
+
+        /* Compare name */
+        Ret = strcmp(ExportName, (PCHAR)DllBase + NameTable[Mid]);
+        if (Ret < 0)
+        {
+            /* Update high */
+            High = Mid - 1;
+        }
+        else if (Ret > 0)
+        {
+            /* Update low */
+            Low = Mid + 1;
+        }
+        else
+        {
+            /* We got it */
+            break;
+        }
+    }
+
+    /* Check if we couldn't find it */
+    if (High < Low) return NULL;
+
+    /* Otherwise, this is the ordinal */
+    Ordinal = OrdinalTable[Mid];
+
+    /* Resolve the address and write it */
+    ExportTable = (PULONG)((ULONG_PTR)DllBase +
+                           ExportDirectory->AddressOfFunctions);
+    Function = (PVOID)((ULONG_PTR)DllBase + ExportTable[Ordinal]);
+
+    /* Check if the function is actually a forwarder */
+    if (((ULONG_PTR)Function > (ULONG_PTR)ExportDirectory) &&
+        ((ULONG_PTR)Function < ((ULONG_PTR)ExportDirectory + ExportSize)))
+    {
+        /* It is, fail */
+        return NULL;
+    }
+
+    /* We found it */
+    return Function;
+}
+
+NTSTATUS
+NTAPI
+MmCallDllInitialize(IN PLDR_DATA_TABLE_ENTRY LdrEntry,
+                    IN PLIST_ENTRY ListHead)
+{
+    UNICODE_STRING ServicesKeyName = RTL_CONSTANT_STRING(
+        L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\");
+    PMM_DLL_INITIALIZE DllInit;
+    UNICODE_STRING RegPath, ImportName;
+    NTSTATUS Status;
+
+    /* Try to see if the image exports a DllInitialize routine */
+    DllInit = (PMM_DLL_INITIALIZE)MiLocateExportName(LdrEntry->DllBase,
+                                                     "DllInitialize");
+    if (!DllInit) return STATUS_SUCCESS;
+
+    /* Do a temporary copy of BaseDllName called ImportName
+     * because we'll alter the length of the string
+     */
+    ImportName.Length = LdrEntry->BaseDllName.Length;
+    ImportName.MaximumLength = LdrEntry->BaseDllName.MaximumLength;
+    ImportName.Buffer = LdrEntry->BaseDllName.Buffer;
+
+    /* Obtain the path to this dll's service in the registry */
+    RegPath.MaximumLength = ServicesKeyName.Length +
+        ImportName.Length + sizeof(UNICODE_NULL);
+    RegPath.Buffer = ExAllocatePoolWithTag(NonPagedPool,
+                                           RegPath.MaximumLength,
+                                           TAG_LDR_WSTR);
+
+    /* Check if this allocation was unsuccessful */
+    if (!RegPath.Buffer) return STATUS_INSUFFICIENT_RESOURCES;
+
+    /* Build and append the service name itself */
+    RegPath.Length = ServicesKeyName.Length;
+    RtlCopyMemory(RegPath.Buffer,
+                  ServicesKeyName.Buffer,
+                  ServicesKeyName.Length);
+
+    /* Check if there is a dot in the filename */
+    if (wcschr(ImportName.Buffer, L'.'))
+    {
+        /* Remove the extension */
+        ImportName.Length = (wcschr(ImportName.Buffer, L'.') -
+            ImportName.Buffer) * sizeof(WCHAR);
+    }
+
+    /* Append service name (the basename without extension) */
+    RtlAppendUnicodeStringToString(&RegPath, &ImportName);
+
+    /* Now call the DllInit func */
+    DPRINT("Calling DllInit(%wZ)\n", &RegPath);
+    Status = DllInit(&RegPath);
+
+    /* Clean up */
+    ExFreePool(RegPath.Buffer);
+
+    /* Return status value which DllInitialize returned */
+    return Status;
+}
+
+BOOLEAN
+NTAPI
+MiCallDllUnloadAndUnloadDll(IN PLDR_DATA_TABLE_ENTRY LdrEntry)
+{
+    NTSTATUS Status;
+    PMM_DLL_UNLOAD Func;
+    PAGED_CODE();
+
+    /* Get the unload routine */
+    Func = (PMM_DLL_UNLOAD)MiLocateExportName(LdrEntry->DllBase, "DllUnload");
+    if (!Func) return FALSE;
+
+    /* Call it and check for success */
+    Status = Func();
+    if (!NT_SUCCESS(Status)) return FALSE;
+
+    /* Lie about the load count so we can unload the image */
+    ASSERT(LdrEntry->LoadCount == 0);
+    LdrEntry->LoadCount = 1;
+
+    /* Unload it and return true */
+    MmUnloadSystemImage(LdrEntry);
+    return TRUE;
+}
+
+NTSTATUS
+NTAPI
+MiDereferenceImports(IN PLOAD_IMPORTS ImportList)
+{
+    SIZE_T i;
+    LOAD_IMPORTS SingleEntry;
+    PLDR_DATA_TABLE_ENTRY LdrEntry;
+    PVOID CurrentImports;
+    PAGED_CODE();
+
+    /* Check if there's no imports or if we're a boot driver */
+    if ((ImportList == MM_SYSLDR_NO_IMPORTS) ||
+        (ImportList == MM_SYSLDR_BOOT_LOADED) ||
+        (ImportList->Count == 0))
+    {
+        /* Then there's nothing to do */
+        return STATUS_SUCCESS;
+    }
+    
+    /* Check for single-entry */
+    if ((ULONG_PTR)ImportList & MM_SYSLDR_SINGLE_ENTRY)
+    {
+        /* Set it up */
+        SingleEntry.Count = 1;
+        SingleEntry.Entry[0] = (PVOID)((ULONG_PTR)ImportList &~ MM_SYSLDR_SINGLE_ENTRY);
+        
+        /* Use this as the import list */
+        ImportList = &SingleEntry;
+    }
+
+    /* Loop the import list */
+    for (i = 0; (i < ImportList->Count) && (ImportList->Entry[i]); i++)
+    {
+        /* Get the entry */
+        LdrEntry = ImportList->Entry[i];
+        DPRINT1("%wZ <%wZ>\n", &LdrEntry->FullDllName, &LdrEntry->BaseDllName);
+        
+        /* Skip boot loaded images */
+        if (LdrEntry->LoadedImports == MM_SYSLDR_BOOT_LOADED) continue;
+        
+        /* Dereference the entry */
+        ASSERT(LdrEntry->LoadCount >= 1);
+        if (!--LdrEntry->LoadCount)
+        {
+            /* Save the import data in case unload fails */
+            CurrentImports = LdrEntry->LoadedImports;
+            
+            /* This is the last entry */
+            LdrEntry->LoadedImports = MM_SYSLDR_NO_IMPORTS;
+            if (MiCallDllUnloadAndUnloadDll(LdrEntry))
+            {
+                /* Unloading worked, parse this DLL's imports too */
+                MiDereferenceImports(CurrentImports);
+                
+                /* Check if we had valid imports */
+                if ((CurrentImports != MM_SYSLDR_BOOT_LOADED) ||
+                    (CurrentImports != MM_SYSLDR_NO_IMPORTS) ||
+                    !((ULONG_PTR)LdrEntry->LoadedImports & MM_SYSLDR_SINGLE_ENTRY))
+                {
+                    /* Free them */
+                    ExFreePool(CurrentImports);
+                }
+            }
+            else
+            {
+                /* Unload failed, restore imports */
+                LdrEntry->LoadedImports = CurrentImports;
+            }
+        }
+    }
+    
+    /* Done */
+    return STATUS_SUCCESS;
+}
+
+VOID
+NTAPI
+MiClearImports(IN PLDR_DATA_TABLE_ENTRY LdrEntry)
+{
+    PAGED_CODE();
+
+    /* Check if there's no imports or we're a boot driver or only one entry */
+    if ((LdrEntry->LoadedImports == MM_SYSLDR_BOOT_LOADED) ||
+        (LdrEntry->LoadedImports == MM_SYSLDR_NO_IMPORTS) ||
+        ((ULONG_PTR)LdrEntry->LoadedImports & MM_SYSLDR_SINGLE_ENTRY))
+    {
+        /* Nothing to do */
+        return;
+    }
+
+    /* Otherwise, free the import list */
+    ExFreePool(LdrEntry->LoadedImports);
+    LdrEntry->LoadedImports = MM_SYSLDR_BOOT_LOADED;
+}
+
+PVOID
+NTAPI
+MiFindExportedRoutineByName(IN PVOID DllBase,
+                            IN PANSI_STRING ExportName)
+{
+    PULONG NameTable;
+    PUSHORT OrdinalTable;
+    PIMAGE_EXPORT_DIRECTORY ExportDirectory;
+    LONG Low = 0, Mid = 0, High, Ret;
+    USHORT Ordinal;
+    PVOID Function;
+    ULONG ExportSize;
+    PULONG ExportTable;
+    PAGED_CODE();
+
+    /* Get the export directory */
+    ExportDirectory = RtlImageDirectoryEntryToData(DllBase,
+                                                   TRUE,
+                                                   IMAGE_DIRECTORY_ENTRY_EXPORT,
+                                                   &ExportSize);
+    if (!ExportDirectory) return NULL;
+
+    /* Setup name tables */
+    NameTable = (PULONG)((ULONG_PTR)DllBase +
+                         ExportDirectory->AddressOfNames);
+    OrdinalTable = (PUSHORT)((ULONG_PTR)DllBase +
+                             ExportDirectory->AddressOfNameOrdinals);
+
+    /* Do a binary search */
+    High = ExportDirectory->NumberOfNames - 1;
+    while (High >= Low)
+    {
+        /* Get new middle value */
+        Mid = (Low + High) >> 1;
+
+        /* Compare name */
+        Ret = strcmp(ExportName->Buffer, (PCHAR)DllBase + NameTable[Mid]);
+        if (Ret < 0)
+        {
+            /* Update high */
+            High = Mid - 1;
+        }
+        else if (Ret > 0)
+        {
+            /* Update low */
+            Low = Mid + 1;
+        }
+        else
+        {
+            /* We got it */
+            break;
+        }
+    }
+
+    /* Check if we couldn't find it */
+    if (High < Low) return NULL;
+
+    /* Otherwise, this is the ordinal */
+    Ordinal = OrdinalTable[Mid];
+
+    /* Validate the ordinal */
+    if (Ordinal >= ExportDirectory->NumberOfFunctions) return NULL;
+
+    /* Resolve the address and write it */
+    ExportTable = (PULONG)((ULONG_PTR)DllBase +
+                           ExportDirectory->AddressOfFunctions);
+    Function = (PVOID)((ULONG_PTR)DllBase + ExportTable[Ordinal]);
+
+    /* We found it! */
+    ASSERT(!(Function > (PVOID)ExportDirectory) &&
+           (Function < (PVOID)((ULONG_PTR)ExportDirectory + ExportSize)));
+    return Function;
+}
+
+VOID
+NTAPI
+MiProcessLoaderEntry(IN PLDR_DATA_TABLE_ENTRY LdrEntry,
+                     IN BOOLEAN Insert)
+{
+    KIRQL OldIrql;
+
+    /* Acquire module list lock */
+    KeEnterCriticalRegion();
+    ExAcquireResourceExclusiveLite(&PsLoadedModuleResource, TRUE);
+
+    /* Acquire the spinlock too as we will insert or remove the entry */
+    OldIrql = KeAcquireSpinLockRaiseToSynch(&PsLoadedModuleSpinLock);
+
+    /* Insert or remove from the list */
+    Insert ? InsertTailList(&PsLoadedModuleList, &LdrEntry->InLoadOrderLinks) :
+             RemoveEntryList(&LdrEntry->InLoadOrderLinks);
+
+    /* Release locks */
+    KeReleaseSpinLock(&PsLoadedModuleSpinLock, OldIrql);
+    ExReleaseResourceLite(&PsLoadedModuleResource);
+    KeLeaveCriticalRegion();
+}
+
+VOID
+NTAPI
+MiUpdateThunks(IN PLOADER_PARAMETER_BLOCK LoaderBlock,
+               IN PVOID OldBase,
+               IN PVOID NewBase,
+               IN ULONG Size)
+{
+    ULONG_PTR OldBaseTop, Delta;
+    PLDR_DATA_TABLE_ENTRY LdrEntry;
+    PLIST_ENTRY NextEntry;
+    ULONG ImportSize, i;
+    PULONG_PTR ImageThunk;
+    PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor;
+
+    /* Calculate the top and delta */
+    OldBaseTop = (ULONG_PTR)OldBase + Size - 1;
+    Delta = (ULONG_PTR)NewBase - (ULONG_PTR)OldBase;
+
+    /* Loop the loader block */
+    for (NextEntry = LoaderBlock->LoadOrderListHead.Flink;
+         NextEntry != &LoaderBlock->LoadOrderListHead;
+         NextEntry = NextEntry->Flink)
+    {
+        /* Get the loader entry */
+        LdrEntry = CONTAINING_RECORD(NextEntry,
+                                     LDR_DATA_TABLE_ENTRY,
+                                     InLoadOrderLinks);
+#ifdef _WORKING_LINKER_
+        /* Get the IAT */
+        ImageThunk = RtlImageDirectoryEntryToData(LdrEntry->DllBase,
+                                                  TRUE,
+                                                  IMAGE_DIRECTORY_ENTRY_IAT,
+                                                  &ImportSize);
+        if (!ImageThunk) continue;
+
+        /* Make sure we have an IAT */
+        DPRINT("[Mm0]: Updating thunks in: %wZ\n", &LdrEntry->BaseDllName);
+        for (i = 0; i < ImportSize; i++, ImageThunk++)
+        {
+            /* Check if it's within this module */
+            if ((*ImageThunk >= (ULONG_PTR)OldBase) && (*ImageThunk <= OldBaseTop))
+            {
+                /* Relocate it */
+                DPRINT("[Mm0]: Updating IAT at: %p. Old Entry: %p. New Entry: %p.\n",
+                        ImageThunk, *ImageThunk, *ImageThunk + Delta);
+                *ImageThunk += Delta;
+            }
+        }
+#else
+        /* Get the import table */
+        i = ImportSize;
+        ImportDescriptor = RtlImageDirectoryEntryToData(LdrEntry->DllBase,
+                                                        TRUE,
+                                                        IMAGE_DIRECTORY_ENTRY_IMPORT,
+                                                        &ImportSize);
+        if (!ImportDescriptor) continue;
+
+        /* Make sure we have an IAT */
+        DPRINT("[Mm0]: Updating thunks in: %wZ\n", &LdrEntry->BaseDllName);
+        while ((ImportDescriptor->Name) &&
+               (ImportDescriptor->OriginalFirstThunk))
+        {
+            /* Get the image thunk */
+            ImageThunk = (PVOID)((ULONG_PTR)LdrEntry->DllBase +
+                                 ImportDescriptor->FirstThunk);
+            while (*ImageThunk)
+            {
+                /* Check if it's within this module */
+                if ((*ImageThunk >= (ULONG_PTR)OldBase) && (*ImageThunk <= OldBaseTop))
+                {
+                    /* Relocate it */
+                    DPRINT("[Mm0]: Updating IAT at: %p. Old Entry: %p. New Entry: %p.\n",
+                            ImageThunk, *ImageThunk, *ImageThunk + Delta);
+                    *ImageThunk += Delta;
+                }
+
+                /* Go to the next thunk */
+                ImageThunk++;
+            }
+
+            /* Go to the next import */
+            ImportDescriptor++;
+        }
+#endif
+    }
+}
+
+NTSTATUS
+NTAPI
+MiSnapThunk(IN PVOID DllBase,
+            IN PVOID ImageBase,
+            IN PIMAGE_THUNK_DATA Name,
+            IN PIMAGE_THUNK_DATA Address,
+            IN PIMAGE_EXPORT_DIRECTORY ExportDirectory,
+            IN ULONG ExportSize,
+            IN BOOLEAN SnapForwarder,
+            OUT PCHAR *MissingApi)
+{
+    BOOLEAN IsOrdinal;
+    USHORT Ordinal;
+    PULONG NameTable;
+    PUSHORT OrdinalTable;
+    PIMAGE_IMPORT_BY_NAME NameImport;
+    USHORT Hint;
+    ULONG Low = 0, Mid = 0, High;
+    LONG Ret;
+    NTSTATUS Status;
+    PCHAR MissingForwarder;
+    CHAR NameBuffer[MAXIMUM_FILENAME_LENGTH];
+    PULONG ExportTable;
+    ANSI_STRING DllName;
+    UNICODE_STRING ForwarderName;
+    PLIST_ENTRY NextEntry;
+    PLDR_DATA_TABLE_ENTRY LdrEntry;
+    ULONG ForwardExportSize;
+    PIMAGE_EXPORT_DIRECTORY ForwardExportDirectory;
+    PIMAGE_IMPORT_BY_NAME ForwardName;
+    ULONG ForwardLength;
+    IMAGE_THUNK_DATA ForwardThunk;
+    PAGED_CODE();
+
+    /* Check if this is an ordinal */
+    IsOrdinal = IMAGE_SNAP_BY_ORDINAL(Name->u1.Ordinal);
+    if ((IsOrdinal) && !(SnapForwarder))
+    {
+        /* Get the ordinal number and set it as missing */
+        Ordinal = (USHORT)(IMAGE_ORDINAL(Name->u1.Ordinal) -
+                           ExportDirectory->Base);
+        *MissingApi = (PCHAR)(ULONG_PTR)Ordinal;
+    }
+    else
+    {
+        /* Get the VA if we don't have to snap */
+        if (!SnapForwarder) Name->u1.AddressOfData += (ULONG_PTR)ImageBase;
+        NameImport = (PIMAGE_IMPORT_BY_NAME)Name->u1.AddressOfData;
+
+        /* Copy the procedure name */
+        strncpy(*MissingApi,
+                (PCHAR)&NameImport->Name[0],
+                MAXIMUM_FILENAME_LENGTH - 1);
+
+        /* Setup name tables */
+        DPRINT("Import name: %s\n", NameImport->Name);
+        NameTable = (PULONG)((ULONG_PTR)DllBase +
+                             ExportDirectory->AddressOfNames);
+        OrdinalTable = (PUSHORT)((ULONG_PTR)DllBase +
+                                 ExportDirectory->AddressOfNameOrdinals);
+
+        /* Get the hint and check if it's valid */
+        Hint = NameImport->Hint;
+        if ((Hint < ExportDirectory->NumberOfNames) &&
+            !(strcmp((PCHAR)NameImport->Name, (PCHAR)DllBase + NameTable[Hint])))
+        {
+            /* We have a match, get the ordinal number from here */
+            Ordinal = OrdinalTable[Hint];
+        }
+        else
+        {
+            /* Do a binary search */
+            High = ExportDirectory->NumberOfNames - 1;
+            while (High >= Low)
+            {
+                /* Get new middle value */
+                Mid = (Low + High) >> 1;
+
+                /* Compare name */
+                Ret = strcmp((PCHAR)NameImport->Name, (PCHAR)DllBase + NameTable[Mid]);
+                if (Ret < 0)
+                {
+                    /* Update high */
+                    High = Mid - 1;
+                }
+                else if (Ret > 0)
+                {
+                    /* Update low */
+                    Low = Mid + 1;
+                }
+                else
+                {
+                    /* We got it */
+                    break;
+                }
+            }
+
+            /* Check if we couldn't find it */
+            if (High < Low) return STATUS_DRIVER_ENTRYPOINT_NOT_FOUND;
+
+            /* Otherwise, this is the ordinal */
+            Ordinal = OrdinalTable[Mid];
+        }
+    }
+
+    /* Check if the ordinal is invalid */
+    if (Ordinal >= ExportDirectory->NumberOfFunctions)
+    {
+        /* Fail */
+        Status = STATUS_DRIVER_ORDINAL_NOT_FOUND;
+    }
+    else
+    {
+        /* In case the forwarder is missing */
+        MissingForwarder = NameBuffer;
+
+        /* Resolve the address and write it */
+        ExportTable = (PULONG)((ULONG_PTR)DllBase +
+                               ExportDirectory->AddressOfFunctions);
+        Address->u1.Function = (ULONG_PTR)DllBase + ExportTable[Ordinal];
+
+        /* Assume success from now on */
+        Status = STATUS_SUCCESS;
+
+        /* Check if the function is actually a forwarder */
+        if ((Address->u1.Function > (ULONG_PTR)ExportDirectory) &&
+            (Address->u1.Function < ((ULONG_PTR)ExportDirectory + ExportSize)))
+        {
+            /* Now assume failure in case the forwarder doesn't exist */
+            Status = STATUS_DRIVER_ENTRYPOINT_NOT_FOUND;
+
+            /* Build the forwarder name */
+            DllName.Buffer = (PCHAR)Address->u1.Function;
+            DllName.Length = strchr(DllName.Buffer, '.') -
+                             DllName.Buffer +
+                             sizeof(ANSI_NULL);
+            DllName.MaximumLength = DllName.Length;
+
+            /* Convert it */
+            if (!NT_SUCCESS(RtlAnsiStringToUnicodeString(&ForwarderName,
+                                                         &DllName,
+                                                         TRUE)))
+            {
+                /* We failed, just return an error */
+                return Status;
+            }
+
+            /* Loop the module list */
+            NextEntry = PsLoadedModuleList.Flink;
+            while (NextEntry != &PsLoadedModuleList)
+            {
+                /* Get the loader entry */
+                LdrEntry = CONTAINING_RECORD(NextEntry,
+                                             LDR_DATA_TABLE_ENTRY,
+                                             InLoadOrderLinks);
+
+                /* Check if it matches */
+                if (RtlPrefixString((PSTRING)&ForwarderName,
+                                    (PSTRING)&LdrEntry->BaseDllName,
+                                    TRUE))
+                {
+                    /* Get the forwarder export directory */
+                    ForwardExportDirectory =
+                        RtlImageDirectoryEntryToData(LdrEntry->DllBase,
+                                                     TRUE,
+                                                     IMAGE_DIRECTORY_ENTRY_EXPORT,
+                                                     &ForwardExportSize);
+                    if (!ForwardExportDirectory) break;
+
+                    /* Allocate a name entry */
+                    ForwardLength = strlen(DllName.Buffer + DllName.Length) +
+                                    sizeof(ANSI_NULL);
+                    ForwardName = ExAllocatePoolWithTag(PagedPool,
+                                                        sizeof(*ForwardName) +
+                                                        ForwardLength,
+                                                        TAG_LDR_WSTR);
+                    if (!ForwardName) break;
+
+                    /* Copy the data */
+                    RtlCopyMemory(&ForwardName->Name[0],
+                                  DllName.Buffer + DllName.Length,
+                                  ForwardLength);
+                    ForwardName->Hint = 0;
+
+                    /* Set the new address */
+                    ForwardThunk.u1.AddressOfData = (ULONG_PTR)ForwardName;
+
+                    /* Snap the forwarder */
+                    Status = MiSnapThunk(LdrEntry->DllBase,
+                                         ImageBase,
+                                         &ForwardThunk,
+                                         &ForwardThunk,
+                                         ForwardExportDirectory,
+                                         ForwardExportSize,
+                                         TRUE,
+                                         &MissingForwarder);
+
+                    /* Free the forwarder name and set the thunk */
+                    ExFreePoolWithTag(ForwardName, TAG_LDR_WSTR);
+                    Address->u1 = ForwardThunk.u1;
+                    break;
+                }
+
+                /* Go to the next entry */
+                NextEntry = NextEntry->Flink;
+            }
+
+            /* Free the name */
+            RtlFreeUnicodeString(&ForwarderName);
+        }
+    }
+
+    /* Return status */
+    return Status;
+}
+
+NTSTATUS
+NTAPI
+MmUnloadSystemImage(IN PVOID ImageHandle)
+{
+    PLDR_DATA_TABLE_ENTRY LdrEntry = ImageHandle;
+    PVOID BaseAddress = LdrEntry->DllBase;
+    NTSTATUS Status;
+    STRING TempName;
+    BOOLEAN HadEntry = FALSE;
+
+    /* Acquire the loader lock */
+    KeEnterCriticalRegion();
+    KeWaitForSingleObject(&MmSystemLoadLock,
+                          WrVirtualMemory,
+                          KernelMode,
+                          FALSE,
+                          NULL);
+
+    /* Check if this driver was loaded at boot and didn't get imports parsed */
+    if (LdrEntry->LoadedImports == MM_SYSLDR_BOOT_LOADED) goto Done;
+
+    /* We should still be alive */
+    ASSERT(LdrEntry->LoadCount != 0);
+    LdrEntry->LoadCount--;
+
+    /* Check if we're still loaded */
+    if (LdrEntry->LoadCount) goto Done;
+
+    /* We should cleanup... are symbols loaded */
+    if (LdrEntry->Flags & LDRP_DEBUG_SYMBOLS_LOADED)
+    {
+        /* Create the ANSI name */
+        Status = RtlUnicodeStringToAnsiString(&TempName,
+                                              &LdrEntry->BaseDllName,
+                                              TRUE);
+        if (NT_SUCCESS(Status))
+        {
+            /* Unload the symbols */
+            DbgUnLoadImageSymbols(&TempName,
+                                  BaseAddress,
+                                  (ULONG_PTR)ZwCurrentProcess());
+            RtlFreeAnsiString(&TempName);
+        }
+    }
+
+    /* FIXME: Free the driver */
+    DPRINT1("Leaking driver: %wZ\n", &LdrEntry->BaseDllName);
+    //MmFreeSection(LdrEntry->DllBase);
+
+    /* Check if we're linked in */
+    if (LdrEntry->InLoadOrderLinks.Flink)
+    {
+        /* Remove us */
+        MiProcessLoaderEntry(LdrEntry, FALSE);
+        HadEntry = TRUE;
+    }
+
+    /* Dereference and clear the imports */
+    MiDereferenceImports(LdrEntry->LoadedImports);
+    MiClearImports(LdrEntry);
+
+    /* Check if the entry needs to go away */
+    if (HadEntry)
+    {
+        /* Check if it had a name */
+        if (LdrEntry->FullDllName.Buffer)
+        {
+            /* Free it */
+            ExFreePool(LdrEntry->FullDllName.Buffer);
+        }
+
+        /* Check if we had a section */
+        if (LdrEntry->SectionPointer)
+        {
+            /* Dereference it */
+            ObDereferenceObject(LdrEntry->SectionPointer);
+        }
+
+        /* Free the entry */
+        ExFreePool(LdrEntry);
+    }
+
+    /* Release the system lock and return */
+Done:
+    KeReleaseMutant(&MmSystemLoadLock, 1, FALSE, FALSE);
+    KeLeaveCriticalRegion();
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS
+NTAPI
+MiResolveImageReferences(IN PVOID ImageBase,
+                         IN PUNICODE_STRING ImageFileDirectory,
+                         IN PUNICODE_STRING NamePrefix OPTIONAL,
+                         OUT PCHAR *MissingApi,
+                         OUT PWCHAR *MissingDriver,
+                         OUT PLOAD_IMPORTS *LoadImports)
+{
+    PCHAR MissingApiBuffer = *MissingApi, ImportName;
+    PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor, CurrentImport;
+    ULONG ImportSize, ImportCount = 0, LoadedImportsSize, ExportSize;
+    PLOAD_IMPORTS LoadedImports, NewImports;
+    ULONG GdiLink, NormalLink, i;
+    BOOLEAN ReferenceNeeded, Loaded;
+    ANSI_STRING TempString;
+    UNICODE_STRING NameString, DllName;
+    PLDR_DATA_TABLE_ENTRY LdrEntry = NULL, DllEntry, ImportEntry = NULL;
+    PVOID ImportBase, DllBase;
+    PLIST_ENTRY NextEntry;
+    PIMAGE_EXPORT_DIRECTORY ExportDirectory;
+    NTSTATUS Status;
+    PIMAGE_THUNK_DATA OrigThunk, FirstThunk;
+    PAGED_CODE();
+    DPRINT("%s - ImageBase: %p. ImageFileDirectory: %wZ\n",
+           __FUNCTION__, ImageBase, ImageFileDirectory);
+
+    /* Assume no imports */
+    *LoadImports = MM_SYSLDR_NO_IMPORTS;
+
+    /* Get the import descriptor */
+    ImportDescriptor = RtlImageDirectoryEntryToData(ImageBase,
+                                                    TRUE,
+                                                    IMAGE_DIRECTORY_ENTRY_IMPORT,
+                                                    &ImportSize);
+    if (!ImportDescriptor) return STATUS_SUCCESS;
+
+    /* Loop all imports to count them */
+    for (CurrentImport = ImportDescriptor;
+         (CurrentImport->Name) && (CurrentImport->OriginalFirstThunk);
+         CurrentImport++)
+    {
+        /* One more */
+        ImportCount++;
+    }
+
+    /* Make sure we have non-zero imports */
+    if (ImportCount)
+    {
+        /* Calculate and allocate the list we'll need */
+        LoadedImportsSize = ImportCount * sizeof(PVOID) + sizeof(SIZE_T);
+        LoadedImports = ExAllocatePoolWithTag(PagedPool,
+                                              LoadedImportsSize,
+                                              'TDmM');
+        if (LoadedImports)
+        {
+            /* Zero it */
+            RtlZeroMemory(LoadedImports, LoadedImportsSize);
+            LoadedImports->Count = ImportCount;
+        }
+    }
+    else
+    {
+        /* No table */
+        LoadedImports = NULL;
+    }
+
+    /* Reset the import count and loop descriptors again */
+    ImportCount = GdiLink = NormalLink = 0;
+    while ((ImportDescriptor->Name) && (ImportDescriptor->OriginalFirstThunk))
+    {
+        /* Get the name */
+        ImportName = (PCHAR)((ULONG_PTR)ImageBase + ImportDescriptor->Name);
+
+        /* Check if this is a GDI driver */
+        GdiLink = GdiLink |
+                  !(_strnicmp(ImportName, "win32k", sizeof("win32k") - 1));
+
+        /* We can also allow dxapi (for Windows compat, allow IRT and coverage )*/
+        NormalLink = NormalLink |
+                     ((_strnicmp(ImportName, "win32k", sizeof("win32k") - 1)) &&
+                      (_strnicmp(ImportName, "dxapi", sizeof("dxapi") - 1)) &&
+                      (_strnicmp(ImportName, "coverage", sizeof("coverage") - 1)) &&
+                      (_strnicmp(ImportName, "irt", sizeof("irt") - 1)));
+
+        /* Check if this is a valid GDI driver */
+        if ((GdiLink) && (NormalLink))
+        {
+            /* It's not, it's importing stuff it shouldn't be! */
+            MiDereferenceImports(LoadedImports);
+            if (LoadedImports) ExFreePool(LoadedImports);
+            return STATUS_PROCEDURE_NOT_FOUND;
+        }
+
+        /* Check for user-mode printer or video card drivers, which don't belong */
+        if (!(_strnicmp(ImportName, "ntdll", sizeof("ntdll") - 1)) ||
+            !(_strnicmp(ImportName, "winsrv", sizeof("winsrv") - 1)) ||
+            !(_strnicmp(ImportName, "advapi32", sizeof("advapi32") - 1)) ||
+            !(_strnicmp(ImportName, "kernel32", sizeof("kernel32") - 1)) ||
+            !(_strnicmp(ImportName, "user32", sizeof("user32") - 1)) ||
+            !(_strnicmp(ImportName, "gdi32", sizeof("gdi32") - 1)))
+        {
+            /* This is not kernel code */
+            MiDereferenceImports(LoadedImports);
+            if (LoadedImports) ExFreePool(LoadedImports);
+            return STATUS_PROCEDURE_NOT_FOUND;
+        }
+
+        /* Check if this is a "core" import, which doesn't get referenced */
+        if (!(_strnicmp(ImportName, "ntoskrnl", sizeof("ntoskrnl") - 1)) ||
+            !(_strnicmp(ImportName, "win32k", sizeof("win32k") - 1)) ||
+            !(_strnicmp(ImportName, "hal", sizeof("hal") - 1)))
+        {
+            /* Don't reference this */
+            ReferenceNeeded = FALSE;
+        }
+        else
+        {
+            /* Reference these modules */
+            ReferenceNeeded = TRUE;
+        }
+
+        /* Now setup a unicode string for the import */
+        RtlInitAnsiString(&TempString, ImportName);
+        Status = RtlAnsiStringToUnicodeString(&NameString, &TempString, TRUE);
+        if (!NT_SUCCESS(Status))
+        {
+            /* Failed */
+            MiDereferenceImports(LoadedImports);
+            if (LoadedImports) ExFreePoolWithTag(LoadedImports, TAG_LDR_WSTR);
+            return Status;
+        }
+
+        /* We don't support name prefixes yet */
+        if (NamePrefix) DPRINT1("Name Prefix not yet supported!\n");
+
+        /* Remember that we haven't loaded the import at this point */
+CheckDllState:
+        Loaded = FALSE;
+        ImportBase = NULL;
+
+        /* Loop the driver list */
+        NextEntry = PsLoadedModuleList.Flink;
+        while (NextEntry != &PsLoadedModuleList)
+        {
+            /* Get the loader entry and compare the name */
+            LdrEntry = CONTAINING_RECORD(NextEntry,
+                                         LDR_DATA_TABLE_ENTRY,
+                                         InLoadOrderLinks);
+            if (RtlEqualUnicodeString(&NameString,
+                                      &LdrEntry->BaseDllName,
+                                      TRUE))
+            {
+                /* Get the base address */
+                ImportBase = LdrEntry->DllBase;
+
+                /* Check if we haven't loaded yet, and we need references */
+                if (!(Loaded) && (ReferenceNeeded))
+                {
+                    /* Make sure we're not already loading */
+                    if (!(LdrEntry->Flags & LDRP_LOAD_IN_PROGRESS))
+                    {
+                        /* Increase the load count */
+                        LdrEntry->LoadCount++;
+                    }
+                }
+
+                /* Done, break out */
+                break;
+            }
+
+            /* Go to the next entry */
+            NextEntry = NextEntry->Flink;
+        }
+
+        /* Check if we haven't loaded the import yet */
+        if (!ImportBase)
+        {
+            /* Setup the import DLL name */
+            DllName.MaximumLength = NameString.Length +
+                                    ImageFileDirectory->Length +
+                                    sizeof(UNICODE_NULL);
+            DllName.Buffer = ExAllocatePoolWithTag(NonPagedPool,
+                                                   DllName.MaximumLength,
+                                                   'TDmM');
+            if (DllName.Buffer)
+            {
+                /* Setup the base length and copy it */
+                DllName.Length = ImageFileDirectory->Length;
+                RtlCopyMemory(DllName.Buffer,
+                              ImageFileDirectory->Buffer,
+                              ImageFileDirectory->Length);
+
+                /* Now add the import name and null-terminate it */
+                RtlAppendStringToString((PSTRING)&DllName,
+                                        (PSTRING)&NameString);
+                DllName.Buffer[(DllName.MaximumLength - 1) / sizeof(WCHAR)] = UNICODE_NULL;
+
+                /* Load the image */
+                Status = MmLoadSystemImage(&DllName,
+                                           NamePrefix,
+                                           NULL,
+                                           FALSE,
+                                           (PVOID)&DllEntry,
+                                           &DllBase);
+                if (NT_SUCCESS(Status))
+                {
+                    /* We can free the DLL Name */
+                    ExFreePool(DllName.Buffer);
+                }
+                else
+                {
+                    /* Fill out the information for the error */
+                    *MissingDriver = DllName.Buffer;
+                    *(PULONG)MissingDriver |= 1;
+                    *MissingApi = NULL;
+                }
+            }
+            else
+            {
+                /* We're out of resources */
+                Status = STATUS_INSUFFICIENT_RESOURCES;
+            }
+
+            /* Check if we're OK until now */
+            if (NT_SUCCESS(Status))
+            {
+                /* We're now loaded */
+                Loaded = TRUE;
+
+                /* Sanity check */
+                ASSERT(DllBase = DllEntry->DllBase);
+
+                /* Call the initialization routines */
+                Status = MmCallDllInitialize(DllEntry, &PsLoadedModuleList);
+                if (!NT_SUCCESS(Status))
+                {
+                    /* We failed, unload the image */
+                    MmUnloadSystemImage(DllEntry);
+                    while (TRUE);
+                    Loaded = FALSE;
+                }
+            }
+
+            /* Check if we failed by here */
+            if (!NT_SUCCESS(Status))
+            {
+                /* Cleanup and return */
+                RtlFreeUnicodeString(&NameString);
+                MiDereferenceImports(LoadedImports);
+                if (LoadedImports) ExFreePoolWithTag(LoadedImports, TAG_LDR_WSTR);
+                return Status;
+            }
+
+            /* Loop again to make sure that everything is OK */
+            goto CheckDllState;
+        }
+
+        /* Check if we're support to reference this import */
+        if ((ReferenceNeeded) && (LoadedImports))
+        {
+            /* Make sure we're not already loading */
+            if (!(LdrEntry->Flags & LDRP_LOAD_IN_PROGRESS))
+            {
+                /* Add the entry */
+                LoadedImports->Entry[ImportCount] = LdrEntry;
+                ImportCount++;
+            }
+        }
+
+        /* Free the import name */
+        RtlFreeUnicodeString(&NameString);
+
+        /* Set the missing driver name and get the export directory */
+        *MissingDriver = LdrEntry->BaseDllName.Buffer;
+        ExportDirectory = RtlImageDirectoryEntryToData(ImportBase,
+                                                       TRUE,
+                                                       IMAGE_DIRECTORY_ENTRY_EXPORT,
+                                                       &ExportSize);
+        if (!ExportDirectory)
+        {
+            /* Cleanup and return */
+            MiDereferenceImports(LoadedImports);
+            if (LoadedImports) ExFreePoolWithTag(LoadedImports, TAG_LDR_WSTR);
+            return STATUS_DRIVER_ENTRYPOINT_NOT_FOUND;
+        }
+
+        /* Make sure we have an IAT */
+        if (ImportDescriptor->OriginalFirstThunk)
+        {
+            /* Get the first thunks */
+            OrigThunk = (PVOID)((ULONG_PTR)ImageBase +
+                                ImportDescriptor->OriginalFirstThunk);
+            FirstThunk = (PVOID)((ULONG_PTR)ImageBase +
+                                 ImportDescriptor->FirstThunk);
+
+            /* Loop the IAT */
+            while (OrigThunk->u1.AddressOfData)
+            {
+                /* Snap thunk */
+                Status = MiSnapThunk(ImportBase,
+                                     ImageBase,
+                                     OrigThunk++,
+                                     FirstThunk++,
+                                     ExportDirectory,
+                                     ExportSize,
+                                     FALSE,
+                                     MissingApi);
+                if (!NT_SUCCESS(Status))
+                {
+                    /* Cleanup and return */
+                    MiDereferenceImports(LoadedImports);
+                    if (LoadedImports) ExFreePoolWithTag(LoadedImports, TAG_LDR_WSTR);
+                    return Status;
+                }
+
+                /* Reset the buffer */
+                *MissingApi = MissingApiBuffer;
+            }
+        }
+
+        /* Go to the next import */
+        ImportDescriptor++;
+    }
+
+    /* Check if we have an import list */
+    if (LoadedImports)
+    {
+        /* Reset the count again, and loop entries*/
+        ImportCount = 0;
+        for (i = 0; i < LoadedImports->Count; i++)
+        {
+            if (LoadedImports->Entry[i])
+            {
+                /* Got an entry, OR it with 1 in case it's the single entry */
+                ImportEntry = (PVOID)((ULONG_PTR)LoadedImports->Entry[i] |
+                                      MM_SYSLDR_SINGLE_ENTRY);
+                ImportCount++;
+            }
+        }
+
+        /* Check if we had no imports */
+        if (!ImportCount)
+        {
+            /* Free the list and set it to no imports */
+            ExFreePoolWithTag(LoadedImports, TAG_LDR_WSTR);
+            LoadedImports = MM_SYSLDR_NO_IMPORTS;
+        }
+        else if (ImportCount == 1)
+        {
+            /* Just one entry, we can free the table and only use our entry */
+            ExFreePoolWithTag(LoadedImports, TAG_LDR_WSTR);
+            LoadedImports = (PLOAD_IMPORTS)ImportEntry;
+        }
+        else if (ImportCount != LoadedImports->Count)
+        {
+            /* Allocate a new list */
+            LoadedImportsSize = ImportCount * sizeof(PVOID) + sizeof(SIZE_T);
+            NewImports = ExAllocatePoolWithTag(PagedPool,
+                                               LoadedImportsSize,
+                                               'TDmM');
+            if (NewImports)
+            {
+                /* Set count */
+                NewImports->Count = 0;
+
+                /* Loop all the imports */
+                for (i = 0; i < LoadedImports->Count; i++)
+                {
+                    /* Make sure it's valid */
+                    if (LoadedImports->Entry[i])
+                    {
+                        /* Copy it */
+                        NewImports->Entry[NewImports->Count] = LoadedImports->Entry[i];
+                        NewImports->Count++;
+                    }
+                }
+
+                /* Free the old copy */
+                ExFreePoolWithTag(LoadedImports, TAG_LDR_WSTR);
+                LoadedImports = NewImports;
+            }
+        }
+
+        /* Return the list */
+        *LoadImports = LoadedImports;
+    }
+
+    /* Return success */
+    return STATUS_SUCCESS;
+}
+
+VOID
+NTAPI
+MiReloadBootLoadedDrivers(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
+{
+    PLIST_ENTRY NextEntry;
+    ULONG i = 0;
+    PIMAGE_NT_HEADERS NtHeader;
+    PLDR_DATA_TABLE_ENTRY LdrEntry;
+    PIMAGE_FILE_HEADER FileHeader;
+    BOOLEAN ValidRelocs;
+    PIMAGE_DATA_DIRECTORY DataDirectory;
+    PVOID DllBase, NewImageAddress;
+    NTSTATUS Status;
+    PMMPTE PointerPte, StartPte, LastPte;
+    PFN_NUMBER PteCount;
+    PMMPFN Pfn1;
+    MMPTE TempPte, OldPte;
+
+    /* Loop driver list */
+    for (NextEntry = LoaderBlock->LoadOrderListHead.Flink;
+         NextEntry != &LoaderBlock->LoadOrderListHead;
+         NextEntry = NextEntry->Flink)
+    {
+        /* Get the loader entry and NT header */
+        LdrEntry = CONTAINING_RECORD(NextEntry,
+                                     LDR_DATA_TABLE_ENTRY,
+                                     InLoadOrderLinks);
+        NtHeader = RtlImageNtHeader(LdrEntry->DllBase);
+
+        /* Debug info */
+        DPRINT("[Mm0]: Driver at: %p ending at: %p for module: %wZ\n",
+                LdrEntry->DllBase,
+                (ULONG_PTR)LdrEntry->DllBase + LdrEntry->SizeOfImage,
+                &LdrEntry->FullDllName);
+
+        /* Skip kernel and HAL */
+        /* ROS HACK: Skip BOOTVID/KDCOM too */
+        i++;
+        if (i <= 4) continue;
+
+        /* Skip non-drivers */
+        if (!NtHeader) continue;
+
+        /* Get the file header and make sure we can relocate */
+        FileHeader = &NtHeader->FileHeader;
+        if (FileHeader->Characteristics & IMAGE_FILE_RELOCS_STRIPPED) continue;
+        if (NtHeader->OptionalHeader.NumberOfRvaAndSizes <
+            IMAGE_DIRECTORY_ENTRY_BASERELOC) continue;
+
+        /* Everything made sense until now, check the relocation section too */
+        DataDirectory = &NtHeader->OptionalHeader.
+                        DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
+        if (!DataDirectory->VirtualAddress)
+        {
+            /* We don't really have relocations */
+            ValidRelocs = FALSE;
+        }
+        else
+        {
+            /* Make sure the size is valid */
+            if ((DataDirectory->VirtualAddress + DataDirectory->Size) >
+                LdrEntry->SizeOfImage)
+            {
+                /* They're not, skip */
+                continue;
+            }
+
+            /* We have relocations */
+            ValidRelocs = TRUE;
+        }
+
+        /* Remember the original address */
+        DllBase = LdrEntry->DllBase;
+        
+        /* Get the first PTE and the number of PTEs we'll need */
+        PointerPte = StartPte = MiAddressToPte(LdrEntry->DllBase);
+        PteCount = ROUND_TO_PAGES(LdrEntry->SizeOfImage) >> PAGE_SHIFT;
+        LastPte = StartPte + PteCount;
+        
+        /* Loop the PTEs */
+        while (PointerPte < LastPte)
+        {
+            /* Mark the page modified in the PFN database */
+            ASSERT(PointerPte->u.Hard.Valid == 1);
+            Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(PointerPte));
+            ASSERT(Pfn1->u3.e1.Rom == 0);
+            Pfn1->u3.e1.Modified = TRUE;
+            
+            /* Next */
+            PointerPte++;
+        }
+        
+        /* Now reserve system PTEs for the image */
+        PointerPte = MiReserveSystemPtes(PteCount, SystemPteSpace);
+        if (!PointerPte)
+        {
+            /* Shouldn't happen */
+            DPRINT1("[Mm0]: Couldn't allocate driver section!\n");
+            while (TRUE);
+        }
+        
+        /* This is the new virtual address for the module */
+        LastPte = PointerPte + PteCount;
+        NewImageAddress = MiPteToAddress(PointerPte);
+
+        /* Sanity check */
+        DPRINT("[Mm0]: Copying from: %p to: %p\n", DllBase, NewImageAddress);
+        ASSERT(ExpInitializationPhase == 0);
+        
+        /* Loop the new driver PTEs */
+        TempPte = ValidKernelPte;
+        while (PointerPte < LastPte)
+        {
+            /* Copy the old data */
+            OldPte = *StartPte;
+            ASSERT(OldPte.u.Hard.Valid == 1);
+
+            /* Set page number from the loader's memory */
+            TempPte.u.Hard.PageFrameNumber = OldPte.u.Hard.PageFrameNumber;
+
+            /* Write it */
+            MI_WRITE_VALID_PTE(PointerPte, TempPte);
+
+            /* Move on */
+            PointerPte++;
+            StartPte++;
+        }
+        
+        /* Update position */
+        PointerPte -= PteCount;
+
+        /* Sanity check */
+        ASSERT(*(PULONG)NewImageAddress == *(PULONG)DllBase);
+
+        /* Set the image base to the address where the loader put it */
+        NtHeader->OptionalHeader.ImageBase = (ULONG_PTR)DllBase;
+
+        /* Check if we had relocations */
+        if (ValidRelocs)
+        {
+            /* Relocate the image */
+            Status = LdrRelocateImageWithBias(NewImageAddress,
+                                              0,
+                                              "SYSLDR",
+                                              STATUS_SUCCESS,
+                                              STATUS_CONFLICTING_ADDRESSES,
+                                              STATUS_INVALID_IMAGE_FORMAT);
+            if (!NT_SUCCESS(Status))
+            {
+                /* This shouldn't happen */
+                DPRINT1("Relocations failed!\n");
+                while (TRUE);
+            }
+        }
+
+        /* Update the loader entry */
+        LdrEntry->DllBase = NewImageAddress;
+
+        /* Update the thunks */
+        DPRINT("[Mm0]: Updating thunks to: %wZ\n", &LdrEntry->BaseDllName);
+        MiUpdateThunks(LoaderBlock,
+                       DllBase,
+                       NewImageAddress,
+                       LdrEntry->SizeOfImage);
+
+        /* Update the loader entry */
+        LdrEntry->Flags |= LDRP_SYSTEM_MAPPED;
+        LdrEntry->EntryPoint = (PVOID)((ULONG_PTR)NewImageAddress +
+                                NtHeader->OptionalHeader.AddressOfEntryPoint);
+        LdrEntry->SizeOfImage = PteCount << PAGE_SHIFT;
+        
+        /* FIXME: We'll need to fixup the PFN linkage when switching to ARM3 */
+    }
+}
+
+NTSTATUS
+NTAPI
+MiBuildImportsForBootDrivers(VOID)
+{
+    PLIST_ENTRY NextEntry, NextEntry2;
+    PLDR_DATA_TABLE_ENTRY LdrEntry, KernelEntry, HalEntry, LdrEntry2, LastEntry;
+    PLDR_DATA_TABLE_ENTRY* EntryArray;
+    UNICODE_STRING KernelName = RTL_CONSTANT_STRING(L"ntoskrnl.exe");
+    UNICODE_STRING HalName = RTL_CONSTANT_STRING(L"hal.dll");
+    PLOAD_IMPORTS LoadedImports;
+    ULONG LoadedImportsSize, ImportSize;
+    PULONG_PTR ImageThunk;
+    ULONG_PTR DllBase, DllEnd;
+    ULONG Modules = 0, i, j = 0;
+    PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor;
+    
+    /* Initialize variables */
+    KernelEntry = HalEntry = LastEntry = NULL;
+
+    /* Loop the loaded module list... we are early enough that no lock is needed */
+    NextEntry = PsLoadedModuleList.Flink;
+    while (NextEntry != &PsLoadedModuleList)
+    {
+        /* Get the entry */
+        LdrEntry = CONTAINING_RECORD(NextEntry,
+                                     LDR_DATA_TABLE_ENTRY,
+                                     InLoadOrderLinks);
+
+        /* Check if it's the kernel or HAL */
+        if (RtlEqualUnicodeString(&KernelName, &LdrEntry->BaseDllName, TRUE))
+        {
+            /* Found it */
+            KernelEntry = LdrEntry;
+        }
+        else if (RtlEqualUnicodeString(&HalName, &LdrEntry->BaseDllName, TRUE))
+        {
+            /* Found it */
+            HalEntry = LdrEntry;
+        }
+        
+        /* Check if this is a driver DLL */
+        if (LdrEntry->Flags & LDRP_DRIVER_DEPENDENT_DLL)
+        {
+            /* Check if this is the HAL or kernel */
+            if ((LdrEntry == HalEntry) || (LdrEntry == KernelEntry))
+            {
+                /* Add a reference */
+                LdrEntry->LoadCount = 1;
+            }
+            else
+            {
+                /* No referencing needed */
+                LdrEntry->LoadCount = 0;
+            }
+        }
+        else
+        {
+            /* No referencing needed */
+            LdrEntry->LoadCount = 0;    
+        }
+        
+        /* Remember this came from the loader */
+        LdrEntry->LoadedImports = MM_SYSLDR_BOOT_LOADED;
+
+        /* Keep looping */
+        NextEntry = NextEntry->Flink;
+        Modules++;
+    }
+    
+    /* We must have at least found the kernel and HAL */
+    if (!(HalEntry) || (!KernelEntry)) return STATUS_NOT_FOUND;
+    
+    /* Allocate the list */
+    EntryArray = ExAllocatePoolWithTag(PagedPool, Modules * sizeof(PVOID), 'TDmM');
+    if (!EntryArray) return STATUS_INSUFFICIENT_RESOURCES;
+
+    /* Loop the loaded module list again */
+    NextEntry = PsLoadedModuleList.Flink;
+    while (NextEntry != &PsLoadedModuleList)
+    {
+        /* Get the entry */
+        LdrEntry = CONTAINING_RECORD(NextEntry,
+                                     LDR_DATA_TABLE_ENTRY,
+                                     InLoadOrderLinks);
+#ifdef _WORKING_LOADER_
+        /* Get its imports */
+        ImageThunk = RtlImageDirectoryEntryToData(LdrEntry->DllBase,
+                                                  TRUE,
+                                                  IMAGE_DIRECTORY_ENTRY_IAT,
+                                                  &ImportSize);
+        if (!ImageThunk)                                                
+#else
+        /* Get its imports */
+        ImportDescriptor = RtlImageDirectoryEntryToData(LdrEntry->DllBase,
+                                                        TRUE,
+                                                        IMAGE_DIRECTORY_ENTRY_IMPORT,
+                                                        &ImportSize);
+        if (!ImportDescriptor)
+#endif
+        {
+            /* None present */
+            LdrEntry->LoadedImports = MM_SYSLDR_NO_IMPORTS;
+            NextEntry = NextEntry->Flink;
+            continue;
+        }
+        
+        /* Clear the list and count the number of IAT thunks */
+        RtlZeroMemory(EntryArray, Modules * sizeof(PVOID));
+#ifdef _WORKING_LOADER_
+        ImportSize /= sizeof(ULONG_PTR);
+        
+        /* Scan the thunks */
+        for (i = 0, DllBase = 0, DllEnd = 0; i < ImportSize; i++, ImageThunk++)
+#else
+        i = DllBase = DllEnd = 0;
+        while ((ImportDescriptor->Name) &&
+               (ImportDescriptor->OriginalFirstThunk))
+        {
+            /* Get the image thunk */
+            ImageThunk = (PVOID)((ULONG_PTR)LdrEntry->DllBase +
+                                 ImportDescriptor->FirstThunk);
+            while (*ImageThunk)
+#endif
+            {
+            /* Do we already have an address? */
+            if (DllBase)
+            {
+                /* Is the thunk in the same address? */
+                if ((*ImageThunk >= DllBase) && (*ImageThunk < DllEnd))
+                {
+                    /* Skip it, we already have a reference for it */
+                    ASSERT(EntryArray[j]);
+                    ImageThunk++;
+                    continue;
+                }
+            }
+            
+            /* Loop the loaded module list to locate this address owner */
+            j = 0;
+            NextEntry2 = PsLoadedModuleList.Flink;
+            while (NextEntry2 != &PsLoadedModuleList)
+            {
+                /* Get the entry */
+                LdrEntry2 = CONTAINING_RECORD(NextEntry2,
+                                              LDR_DATA_TABLE_ENTRY,
+                                              InLoadOrderLinks);
+                                              
+                /* Get the address range for this module */
+                DllBase = (ULONG_PTR)LdrEntry2->DllBase;
+                DllEnd = DllBase + LdrEntry2->SizeOfImage;
+                
+                /* Check if this IAT entry matches it */
+                if ((*ImageThunk >= DllBase) && (*ImageThunk < DllEnd))
+                {
+                    /* Save it */
+                    //DPRINT1("Found imported dll: %wZ\n", &LdrEntry2->BaseDllName);
+                    EntryArray[j] = LdrEntry2;
+                    break;
+                }
+                
+                /* Keep searching */
+                NextEntry2 = NextEntry2->Flink;
+                j++;
+            }
+            
+            /* Do we have a thunk outside the range? */
+            if ((*ImageThunk < DllBase) || (*ImageThunk >= DllEnd))
+            {
+                /* Could be 0... */
+                if (*ImageThunk)
+                {
+                    /* Should not be happening */
+                    DPRINT1("Broken IAT entry for %p at %p (%lx)\n",
+                            LdrEntry, ImageThunk, *ImageThunk);
+                    ASSERT(FALSE);
+                }
+                
+                /* Reset if we hit this */
+                DllBase = 0;
+            }
+#ifndef _WORKING_LOADER_
+            ImageThunk++;
+            }
+        
+            i++;
+            ImportDescriptor++;
+#endif
+        }
+        
+        /* Now scan how many imports we really have */
+        for (i = 0, ImportSize = 0; i < Modules; i++)
+        {
+            /* Skip HAL and kernel */
+            if ((EntryArray[i]) &&
+                (EntryArray[i] != HalEntry) &&
+                (EntryArray[i] != KernelEntry))
+            {
+                /* A valid reference */
+                LastEntry = EntryArray[i];
+                ImportSize++;
+            }
+        }
+        
+        /* Do we have any imports after all? */
+        if (!ImportSize)
+        {
+            /* No */
+            LdrEntry->LoadedImports = MM_SYSLDR_NO_IMPORTS;
+        }
+        else if (ImportSize == 1)
+        {
+            /* A single entry import */
+            LdrEntry->LoadedImports = (PVOID)((ULONG_PTR)LastEntry | MM_SYSLDR_SINGLE_ENTRY);
+            LastEntry->LoadCount++;
+        }
+        else
+        {
+            /* We need an import table */
+            LoadedImportsSize = ImportSize * sizeof(PVOID) + sizeof(SIZE_T);
+            LoadedImports = ExAllocatePoolWithTag(PagedPool,
+                                                  LoadedImportsSize,
+                                                  'TDmM');
+            ASSERT(LoadedImports);
+            
+            /* Save the count */
+            LoadedImports->Count = ImportSize;
+            
+            /* Now copy all imports */
+            for (i = 0, j = 0; i < Modules; i++)
+            {
+                /* Skip HAL and kernel */
+                if ((EntryArray[i]) &&
+                    (EntryArray[i] != HalEntry) &&
+                    (EntryArray[i] != KernelEntry))
+                {
+                    /* A valid reference */
+                    //DPRINT1("Found valid entry: %p\n", EntryArray[i]);
+                    LoadedImports->Entry[j] = EntryArray[i];
+                    EntryArray[i]->LoadCount++;
+                    j++;
+                }
+            }
+            
+            /* Should had as many entries as we expected */
+            ASSERT(j == ImportSize);
+            LdrEntry->LoadedImports = LoadedImports;
+        }
+        
+        /* Next */
+        NextEntry = NextEntry->Flink;
+    }
+    
+    /* Free the initial array */
+    ExFreePool(EntryArray);
+    
+    /* FIXME: Might not need to keep the HAL/Kernel imports around */
+    
+    /* Kernel and HAL are loaded at boot */
+    KernelEntry->LoadedImports = MM_SYSLDR_BOOT_LOADED;
+    HalEntry->LoadedImports = MM_SYSLDR_BOOT_LOADED;
+    
+    /* All worked well */
+    return STATUS_SUCCESS;
+}
+
+VOID
+NTAPI
+MiLocateKernelSections(IN PLDR_DATA_TABLE_ENTRY LdrEntry)
+{
+    ULONG_PTR DllBase;
+    PIMAGE_NT_HEADERS NtHeaders;
+    PIMAGE_SECTION_HEADER SectionHeader;
+    ULONG Sections, Size;
+    
+    /* Get the kernel section header */
+    DllBase = (ULONG_PTR)LdrEntry->DllBase;
+    NtHeaders = RtlImageNtHeader((PVOID)DllBase);
+    SectionHeader = IMAGE_FIRST_SECTION(NtHeaders);
+
+    /* Loop all the sections */
+    Sections = NtHeaders->FileHeader.NumberOfSections;
+    while (Sections)
+    {
+        /* Grab the size of the section */
+        Size = max(SectionHeader->SizeOfRawData, SectionHeader->Misc.VirtualSize);
+        
+        /* Check for .RSRC section */
+        if (*(PULONG)SectionHeader->Name == 'rsr.')
+        {
+            /* Remember the PTEs so we can modify them later */
+            MiKernelResourceStartPte = MiAddressToPte(DllBase +
+                                                      SectionHeader->VirtualAddress);
+            MiKernelResourceEndPte = MiKernelResourceStartPte +
+                                     BYTES_TO_PAGES(SectionHeader->VirtualAddress + Size);
+        }
+        else if (*(PULONG)SectionHeader->Name == 'LOOP')
+        {
+            /* POOLCODE vs. POOLMI */
+            if (*(PULONG)&SectionHeader->Name[4] == 'EDOC')
+            {
+                /* Found Ex* Pool code */
+                ExPoolCodeStart = DllBase + SectionHeader->VirtualAddress;
+                ExPoolCodeEnd = ExPoolCodeStart + Size;
+            }
+            else if (*(PUSHORT)&SectionHeader->Name[4] == 'MI')
+            {
+                /* Found Mm* Pool code */
+                MmPoolCodeStart = DllBase + SectionHeader->VirtualAddress;
+                MmPoolCodeEnd = ExPoolCodeStart + Size;                
+            }
+        }
+        else if ((*(PULONG)SectionHeader->Name == 'YSIM') &&
+                 (*(PULONG)&SectionHeader->Name[4] == 'ETPS'))
+        {
+            /* Found MISYSPTE (Mm System PTE code)*/
+            MmPteCodeStart = DllBase + SectionHeader->VirtualAddress;
+            MmPteCodeEnd = ExPoolCodeStart + Size;
+        }
+        
+        /* Keep going */
+        Sections--;
+        SectionHeader++;
+    }
+}
+
+BOOLEAN
+NTAPI
+MiInitializeLoadedModuleList(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
+{
+    PLDR_DATA_TABLE_ENTRY LdrEntry, NewEntry;
+    PLIST_ENTRY ListHead, NextEntry;
+    ULONG EntrySize;
+
+    /* Setup the loaded module list and locks */
+    ExInitializeResourceLite(&PsLoadedModuleResource);
+    KeInitializeSpinLock(&PsLoadedModuleSpinLock);
+    InitializeListHead(&PsLoadedModuleList);
+
+    /* Get loop variables and the kernel entry */
+    ListHead = &LoaderBlock->LoadOrderListHead;
+    NextEntry = ListHead->Flink;
+    LdrEntry = CONTAINING_RECORD(NextEntry,
+                                 LDR_DATA_TABLE_ENTRY,
+                                 InLoadOrderLinks);
+    PsNtosImageBase = (ULONG_PTR)LdrEntry->DllBase;
+    
+    /* Locate resource section, pool code, and system pte code */
+    MiLocateKernelSections(LdrEntry);
+
+    /* Loop the loader block */
+    while (NextEntry != ListHead)
+    {
+        /* Get the loader entry */
+        LdrEntry = CONTAINING_RECORD(NextEntry,
+                                     LDR_DATA_TABLE_ENTRY,
+                                     InLoadOrderLinks);
+
+        /* FIXME: ROS HACK. Make sure this is a driver */
+        if (!RtlImageNtHeader(LdrEntry->DllBase))
+        {
+            /* Skip this entry */
+            NextEntry = NextEntry->Flink;
+            continue;
+        }
+
+        /* Calculate the size we'll need and allocate a copy */
+        EntrySize = sizeof(LDR_DATA_TABLE_ENTRY) +
+                    LdrEntry->BaseDllName.MaximumLength +
+                    sizeof(UNICODE_NULL);
+        NewEntry = ExAllocatePoolWithTag(NonPagedPool, EntrySize, TAG_LDR_WSTR);
+        if (!NewEntry) return FALSE;
+
+        /* Copy the entry over */
+        *NewEntry = *LdrEntry;
+
+        /* Allocate the name */
+        NewEntry->FullDllName.Buffer =
+            ExAllocatePoolWithTag(PagedPool,
+                                  LdrEntry->FullDllName.MaximumLength +
+                                  sizeof(UNICODE_NULL),
+                                  TAG_LDR_WSTR);
+        if (!NewEntry->FullDllName.Buffer) return FALSE;
+
+        /* Set the base name */
+        NewEntry->BaseDllName.Buffer = (PVOID)(NewEntry + 1);
+
+        /* Copy the full and base name */
+        RtlCopyMemory(NewEntry->FullDllName.Buffer,
+                      LdrEntry->FullDllName.Buffer,
+                      LdrEntry->FullDllName.MaximumLength);
+        RtlCopyMemory(NewEntry->BaseDllName.Buffer,
+                      LdrEntry->BaseDllName.Buffer,
+                      LdrEntry->BaseDllName.MaximumLength);
+
+        /* Null-terminate the base name */
+        NewEntry->BaseDllName.Buffer[NewEntry->BaseDllName.Length /
+                                     sizeof(WCHAR)] = UNICODE_NULL;
+
+        /* Insert the entry into the list */
+        InsertTailList(&PsLoadedModuleList, &NewEntry->InLoadOrderLinks);
+        NextEntry = NextEntry->Flink;
+    }
+
+    /* Build the import lists for the boot drivers */
+    MiBuildImportsForBootDrivers();
+
+    /* We're done */
+    return TRUE;
+}
+
+LOGICAL
+NTAPI
+MiUseLargeDriverPage(IN ULONG NumberOfPtes,
+                     IN OUT PVOID *ImageBaseAddress,
+                     IN PUNICODE_STRING BaseImageName,
+                     IN BOOLEAN BootDriver)
+{
+    PLIST_ENTRY NextEntry;
+    BOOLEAN DriverFound = FALSE;
+    PMI_LARGE_PAGE_DRIVER_ENTRY LargePageDriverEntry;
+    ASSERT(KeGetCurrentIrql () <= APC_LEVEL);
+    ASSERT(*ImageBaseAddress >= MmSystemRangeStart);
+
+#ifdef _X86_
+    if (!(KeFeatureBits & KF_LARGE_PAGE)) return FALSE;
+    if (!(__readcr4() & CR4_PSE)) return FALSE;
+#endif
+
+    /* Make sure there's enough system PTEs for a large page driver */
+    if (MmTotalFreeSystemPtes[SystemPteSpace] < (16 * (PDE_MAPPED_VA >> PAGE_SHIFT)))
+    {
+        return FALSE;
+    }
+
+    /* This happens if the registry key had a "*" (wildcard) in it */
+    if (MiLargePageAllDrivers == 0)
+    {
+        /* It didn't, so scan the list */
+        NextEntry = MiLargePageDriverList.Flink;
+        while (NextEntry != &MiLargePageDriverList)
+        {
+            /* Check if the driver name matches */
+            LargePageDriverEntry = CONTAINING_RECORD(NextEntry,
+                                                     MI_LARGE_PAGE_DRIVER_ENTRY,
+                                                     Links);
+            if (RtlEqualUnicodeString(BaseImageName,
+                                      &LargePageDriverEntry->BaseName,
+                                      TRUE))
+            {
+                /* Enable large pages for this driver */
+                DriverFound = TRUE;
+                break;
+            }
+
+            /* Keep trying */
+            NextEntry = NextEntry->Flink;
+        }
+        
+        /* If we didn't find the driver, it doesn't need large pages */
+        if (DriverFound == FALSE) return FALSE;
+    }
+    /* Nothing to do yet */
+    DPRINT1("Large pages not supported!\n");
+    return FALSE;
+}
+
+ULONG
+NTAPI
+MiComputeDriverProtection(IN BOOLEAN SessionSpace,
+                          IN ULONG SectionProtection)
+{
+    ULONG Protection = MM_ZERO_ACCESS;
+    
+    /* Check if the caller gave anything */
+    if (SectionProtection)
+    {
+        /* Always turn on execute access */
+        SectionProtection |= IMAGE_SCN_MEM_EXECUTE;
+        
+        /* Check if the registry setting is on or not */
+        if (!MmEnforceWriteProtection)
+        {
+            /* Turn on write access too */
+            SectionProtection |= (IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_EXECUTE);
+        }
+    }
+    
+    /* Convert to internal PTE flags */
+    if (SectionProtection & IMAGE_SCN_MEM_EXECUTE) Protection |= MM_EXECUTE;
+    if (SectionProtection & IMAGE_SCN_MEM_READ) Protection |= MM_READONLY;
+    
+    /* Check for write access */
+    if (SectionProtection & IMAGE_SCN_MEM_WRITE)
+    {
+        /* Session space is not supported */
+        if (SessionSpace)
+        {
+            DPRINT1("Session drivers not supported\n");
+            ASSERT(SessionSpace == FALSE);
+        }
+        else
+        {
+            /* Convert to internal PTE flag */
+            Protection = (Protection & MM_EXECUTE) ? MM_EXECUTE_READWRITE : MM_READWRITE;
+        }
+    }
+    
+    /* If there's no access at all by now, convert to internal no access flag */
+    if (Protection == MM_ZERO_ACCESS) Protection = MM_NOACCESS;
+    
+    /* Return the computed PTE protection */
+    return Protection;
+}
+
+VOID
+NTAPI
+MiSetSystemCodeProtection(IN PMMPTE FirstPte,
+                          IN PMMPTE LastPte,
+                          IN ULONG ProtectionMask)
+{
+    /* I'm afraid to introduce regressions at the moment... */
+    return;
+}
+
+VOID
+NTAPI
+MiWriteProtectSystemImage(IN PVOID ImageBase)
+{
+    PIMAGE_NT_HEADERS NtHeaders;
+    PIMAGE_SECTION_HEADER Section;
+    PFN_NUMBER DriverPages;
+    ULONG CurrentProtection, SectionProtection, CombinedProtection, ProtectionMask;
+    ULONG Sections, Size;
+    ULONG_PTR BaseAddress, CurrentAddress;
+    PMMPTE PointerPte, StartPte, LastPte, CurrentPte, ComboPte = NULL;
+    ULONG CurrentMask, CombinedMask = 0;
+    PAGED_CODE();
+    
+    /* No need to write protect physical memory-backed drivers (large pages) */
+    if (MI_IS_PHYSICAL_ADDRESS(ImageBase)) return;
+    
+    /* Get the image headers */
+    NtHeaders = RtlImageNtHeader(ImageBase);
+    if (!NtHeaders) return;
+    
+    /* Check if this is a session driver or not */
+    if (!MI_IS_SESSION_ADDRESS(ImageBase))
+    {
+        /* Don't touch NT4 drivers */
+        if (NtHeaders->OptionalHeader.MajorOperatingSystemVersion < 5) return;
+        if (NtHeaders->OptionalHeader.MajorImageVersion < 5) return;
+    }
+    else
+    {
+        /* Not supported */
+        DPRINT1("Session drivers not supported\n");
+        ASSERT(FALSE);
+    }
+    
+    /* These are the only protection masks we care about */
+    ProtectionMask = IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_EXECUTE;
+    
+    /* Calculate the number of pages this driver is occupying */
+    DriverPages = BYTES_TO_PAGES(NtHeaders->OptionalHeader.SizeOfImage);
+    
+    /* Get the number of sections and the first section header */
+    Sections = NtHeaders->FileHeader.NumberOfSections;
+    ASSERT(Sections != 0);
+    Section = IMAGE_FIRST_SECTION(NtHeaders);
+
+    /* Loop all the sections */
+    CurrentAddress = (ULONG_PTR)ImageBase;
+    while (Sections)
+    {
+        /* Get the section size */
+        Size = max(Section->SizeOfRawData, Section->Misc.VirtualSize);
+        
+        /* Get its virtual address */
+        BaseAddress = (ULONG_PTR)ImageBase + Section->VirtualAddress;
+        if (BaseAddress < CurrentAddress)
+        {
+            /* Windows doesn't like these */
+            DPRINT1("Badly linked image!\n");
+            return;
+        }
+        
+        /* Remember the current address */
+        CurrentAddress = BaseAddress + Size - 1;
+        
+        /* Next */
+        Sections--;
+        Section++;
+    }
+    
+    /* Get the number of sections and the first section header */
+    Sections = NtHeaders->FileHeader.NumberOfSections;
+    ASSERT(Sections != 0);
+    Section = IMAGE_FIRST_SECTION(NtHeaders);
+    
+    /* Set the address at the end to initialize the loop */
+    CurrentAddress = (ULONG_PTR)Section + Sections - 1;
+    CurrentProtection = IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_READ;
+    
+    /* Set the PTE points for the image, and loop its sections */
+    StartPte = MiAddressToPte(ImageBase);
+    LastPte = StartPte + DriverPages;
+    while (Sections)
+    {
+        /* Get the section size */
+        Size = max(Section->SizeOfRawData, Section->Misc.VirtualSize);
+        
+        /* Get its virtual address and PTE */
+        BaseAddress = (ULONG_PTR)ImageBase + Section->VirtualAddress;
+        PointerPte = MiAddressToPte(BaseAddress);
+        
+        /* Check if we were already protecting a run, and found a new run */
+        if ((ComboPte) && (PointerPte > ComboPte))
+        {
+            /* Compute protection */
+            CombinedMask = MiComputeDriverProtection(FALSE, CombinedProtection);
+            
+            /* Set it */
+            MiSetSystemCodeProtection(ComboPte, ComboPte, CombinedMask);
+            
+            /* Check for overlap */
+            if (ComboPte == StartPte) StartPte++;
+            
+            /* One done, reset variables */
+            ComboPte = NULL;
+            CombinedProtection = 0;
+        }
+        
+        /* Break out when needed */
+        if (PointerPte >= LastPte) break;
+        
+        /* Get the requested protection from the image header */
+        SectionProtection = Section->Characteristics & ProtectionMask;
+        if (SectionProtection == CurrentProtection)
+        {
+            /* Same protection, so merge the request */
+            CurrentAddress = BaseAddress + Size - 1;
+        
+            /* Next */
+            Sections--;
+            Section++;
+            continue;
+        }
+        
+        /* This is now a new section, so close up the old one */
+        CurrentPte = MiAddressToPte(CurrentAddress);
+        
+        /* Check for overlap */
+        if (CurrentPte == PointerPte)
+        {
+            /* Skip the last PTE, since it overlaps with us */
+            CurrentPte--;
+    
+            /* And set the PTE we will merge with */
+            ASSERT((ComboPte == NULL) || (ComboPte == PointerPte));
+            ComboPte = PointerPte;
+            
+            /* Get the most flexible protection by merging both */
+            CombinedMask |= (SectionProtection | CurrentProtection);
+        }
+        
+        /* Loop any PTEs left */
+        if (CurrentPte >= StartPte)
+        {
+            /* Sanity check */
+            ASSERT(StartPte < LastPte);
+            
+            /* Make sure we don't overflow past the last PTE in the driver */
+            if (CurrentPte >= LastPte) CurrentPte = LastPte - 1;
+            ASSERT(CurrentPte >= StartPte);
+            
+            /* Compute the protection and set it */
+            CurrentMask = MiComputeDriverProtection(FALSE, CurrentProtection);
+            MiSetSystemCodeProtection(StartPte, CurrentPte, CurrentMask);
+        }
+        
+        /* Set new state */
+        StartPte = PointerPte;
+        CurrentAddress = BaseAddress + Size - 1;
+        CurrentProtection = SectionProtection;
+        
+        /* Next */
+        Sections--;
+        Section++;
+    }
+    
+    /* Is there a leftover section to merge? */
+    if (ComboPte)
+    {
+        /* Compute and set the protection */
+        CombinedMask = MiComputeDriverProtection(FALSE, CombinedProtection);
+        MiSetSystemCodeProtection(ComboPte, ComboPte, CombinedMask);
+        
+        /* Handle overlap */
+        if (ComboPte == StartPte) StartPte++;
+    }
+    
+    /* Finally, handle the last section */
+    CurrentPte = MiPteToAddress(CurrentAddress);
+    if ((StartPte < LastPte) && (CurrentPte >= StartPte))
+    {
+        /* Handle overlap */
+        if (CurrentPte >= LastPte) CurrentPte = LastPte - 1;
+        ASSERT(CurrentPte >= StartPte);
+        
+        /* Compute and set the protection */
+        CurrentMask = MiComputeDriverProtection(FALSE, CurrentProtection);
+        MiSetSystemCodeProtection(StartPte, CurrentPte, CurrentMask);
+    }
+}
+
+VOID
+NTAPI
+MiSetPagingOfDriver(IN PMMPTE PointerPte,
+                    IN PMMPTE LastPte)
+{
+    PVOID ImageBase;
+    PETHREAD CurrentThread;
+    PFN_NUMBER PageCount = 0, PageFrameIndex;
+    PMMPFN Pfn1;
+    PAGED_CODE();
+    
+    /* Get the driver's base address */
+    ImageBase = MiPteToAddress(PointerPte);
+    ASSERT(MI_IS_SESSION_IMAGE_ADDRESS(ImageBase) == FALSE);
+    
+    /* If this is a large page, it's stuck in physical memory */
+    if (MI_IS_PHYSICAL_ADDRESS(ImageBase)) return;
+
+    /* We should lock the system working set -- we don't have one yet, so just be consistent */
+    CurrentThread = PsGetCurrentThread();
+    KeEnterGuardedRegion();
+    ASSERT((CurrentThread->OwnsSystemWorkingSetExclusive == 0) &&
+           (CurrentThread->OwnsSystemWorkingSetShared == 0));
+    CurrentThread->OwnsSystemWorkingSetExclusive = 1;
+    
+    /* Loop the PTEs */
+    while (PointerPte <= LastPte)
+    {
+        /* Check for valid PTE */
+        if (PointerPte->u.Hard.Valid == 1)
+        {
+            PageFrameIndex = PFN_FROM_PTE(PointerPte);
+            Pfn1 = MiGetPfnEntry(PageFrameIndex);
+            ASSERT(Pfn1->u2.ShareCount == 1);
+            
+            /* No working sets in ReactOS yet */
+            PageCount++;
+        }
+        
+        ImageBase = (PVOID)((ULONG_PTR)ImageBase + PAGE_SIZE);
+        PointerPte++;
+    }
+    
+    /* Release the working set "lock" */
+    ASSERT(KeAreAllApcsDisabled() == TRUE);
+    CurrentThread->OwnsSystemWorkingSetExclusive = 0;
+    KeLeaveGuardedRegion();
+    
+    /* Do we have any driver pages? */
+    if (PageCount)
+    {
+        /* Update counters */
+        InterlockedExchangeAdd((PLONG)&MmTotalSystemDriverPages, PageCount);
+    }
+}
+
+VOID
+NTAPI
+MiEnablePagingOfDriver(IN PLDR_DATA_TABLE_ENTRY LdrEntry)
+{
+    ULONG_PTR ImageBase;
+    PIMAGE_NT_HEADERS NtHeaders;
+    ULONG Sections, Alignment, Size;
+    PIMAGE_SECTION_HEADER Section;
+    PMMPTE PointerPte = NULL, LastPte = NULL;
+    if (MmDisablePagingExecutive) return;
+    
+    /* Get the driver base address and its NT header */
+    ImageBase = (ULONG_PTR)LdrEntry->DllBase;
+    NtHeaders = RtlImageNtHeader((PVOID)ImageBase);
+    if (!NtHeaders) return;
+    
+    /* Get the sections and their alignment */
+    Sections = NtHeaders->FileHeader.NumberOfSections;
+    Alignment = NtHeaders->OptionalHeader.SectionAlignment - 1;
+    
+    /* Loop each section */
+    Section = IMAGE_FIRST_SECTION(NtHeaders);
+    while (Sections)
+    {
+        /* Find PAGE or .edata */
+        if ((*(PULONG)Section->Name == 'EGAP') ||
+            (*(PULONG)Section->Name == 'ade.'))
+        {
+            /* Had we already done some work? */
+            if (!PointerPte)
+            {
+                /* Nope, setup the first PTE address */
+                PointerPte = MiAddressToPte(ROUND_TO_PAGES(ImageBase +
+                                                           Section->
+                                                           VirtualAddress));
+            }
+            
+            /* Compute the size */
+            Size = max(Section->SizeOfRawData, Section->Misc.VirtualSize);
+            
+            /* Find the last PTE that maps this section */
+            LastPte = MiAddressToPte(ImageBase +
+                                     Section->VirtualAddress +
+                                     Alignment +
+                                     Size -
+                                     PAGE_SIZE);
+        }
+        else
+        {
+            /* Had we found a section before? */
+            if (PointerPte)
+            {
+                /* Mark it as pageable */
+                MiSetPagingOfDriver(PointerPte, LastPte);
+                PointerPte = NULL;
+            }
+        }
+        
+        /* Keep searching */
+        Sections--;
+        Section++;
+    }
+    
+    /* Handle the straggler */
+    if (PointerPte) MiSetPagingOfDriver(PointerPte, LastPte);
+}
+
+BOOLEAN
+NTAPI
+MmVerifyImageIsOkForMpUse(IN PVOID BaseAddress)
+{
+    PIMAGE_NT_HEADERS NtHeader;
+    PAGED_CODE();
+
+    /* Get NT Headers */
+    NtHeader = RtlImageNtHeader(BaseAddress);
+    if (NtHeader)
+    {
+        /* Check if this image is only safe for UP while we have 2+ CPUs */
+        if ((KeNumberProcessors > 1) &&
+            (NtHeader->FileHeader.Characteristics & IMAGE_FILE_UP_SYSTEM_ONLY))
+        {
+            /* Fail */
+            return FALSE;
+        }
+    }
+
+    /* Otherwise, it's safe */
+    return TRUE;
+}
+
+NTSTATUS
+NTAPI
+MmCheckSystemImage(IN HANDLE ImageHandle,
+                   IN BOOLEAN PurgeSection)
+{
+    NTSTATUS Status;
+    HANDLE SectionHandle;
+    PVOID ViewBase = NULL;
+    SIZE_T ViewSize = 0;
+    IO_STATUS_BLOCK IoStatusBlock;
+    FILE_STANDARD_INFORMATION FileStandardInfo;
+    KAPC_STATE ApcState;
+    PIMAGE_NT_HEADERS NtHeaders;
+    OBJECT_ATTRIBUTES ObjectAttributes;
+    PAGED_CODE();
+    
+    /* Setup the object attributes */
+    InitializeObjectAttributes(&ObjectAttributes,
+                               NULL,
+                               OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+                               NULL,
+                               NULL);
+
+    /* Create a section for the DLL */
+    Status = ZwCreateSection(&SectionHandle,
+                             SECTION_MAP_EXECUTE,
+                             &ObjectAttributes,
+                             NULL,
+                             PAGE_EXECUTE,
+                             SEC_IMAGE,
+                             ImageHandle);
+    if (!NT_SUCCESS(Status)) return Status;
+
+    /* Make sure we're in the system process */
+    KeStackAttachProcess(&PsInitialSystemProcess->Pcb, &ApcState);
+
+    /* Map it */
+    Status = ZwMapViewOfSection(SectionHandle,
+                                NtCurrentProcess(),
+                                &ViewBase,
+                                0,
+                                0,
+                                NULL,
+                                &ViewSize,
+                                ViewShare,
+                                0,
+                                PAGE_EXECUTE);
+    if (!NT_SUCCESS(Status))
+    {
+        /* We failed, close the handle and return */
+        KeUnstackDetachProcess(&ApcState);
+        ZwClose(SectionHandle);
+        return Status;
+    }
+
+    /* Now query image information */
+    Status = ZwQueryInformationFile(ImageHandle,
+                                    &IoStatusBlock,
+                                    &FileStandardInfo,
+                                    sizeof(FileStandardInfo),
+                                    FileStandardInformation);
+    if (NT_SUCCESS(Status))
+    {
+        /* First, verify the checksum */
+        if (!LdrVerifyMappedImageMatchesChecksum(ViewBase,
+                                                 ViewSize,
+                                                 FileStandardInfo.
+                                                 EndOfFile.LowPart))
+        {
+            /* Set checksum failure */
+            Status = STATUS_IMAGE_CHECKSUM_MISMATCH;
+            goto Fail;
+        }
+        
+        /* Make sure it's a real image */
+        NtHeaders = RtlImageNtHeader(ViewBase);
+        if (!NtHeaders)
+        {
+            /* Set checksum failure */
+            Status = STATUS_IMAGE_CHECKSUM_MISMATCH;
+            goto Fail;
+        }
+        
+        /* Make sure it's for the correct architecture */
+        if ((NtHeaders->FileHeader.Machine != IMAGE_FILE_MACHINE_NATIVE) ||
+            (NtHeaders->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC))
+        {
+            /* Set protection failure */
+            Status = STATUS_INVALID_IMAGE_PROTECT;
+            goto Fail;
+        }
+
+        /* Check that it's a valid SMP image if we have more then one CPU */
+        if (!MmVerifyImageIsOkForMpUse(ViewBase))
+        {
+            /* Otherwise it's not the right image */
+            Status = STATUS_IMAGE_MP_UP_MISMATCH;
+        }
+    }
+
+    /* Unmap the section, close the handle, and return status */
+Fail:
+    ZwUnmapViewOfSection(NtCurrentProcess(), ViewBase);
+    KeUnstackDetachProcess(&ApcState);
+    ZwClose(SectionHandle);
+    return Status;
+}
+
+NTSTATUS
+NTAPI
+MmLoadSystemImage(IN PUNICODE_STRING FileName,
+                  IN PUNICODE_STRING NamePrefix OPTIONAL,
+                  IN PUNICODE_STRING LoadedName OPTIONAL,
+                  IN ULONG Flags,
+                  OUT PVOID *ModuleObject,
+                  OUT PVOID *ImageBaseAddress)
+{
+    PVOID ModuleLoadBase = NULL;
+    NTSTATUS Status;
+    HANDLE FileHandle = NULL;
+    OBJECT_ATTRIBUTES ObjectAttributes;
+    IO_STATUS_BLOCK IoStatusBlock;
+    PIMAGE_NT_HEADERS NtHeader;
+    UNICODE_STRING BaseName, BaseDirectory, PrefixName, UnicodeTemp;
+    PLDR_DATA_TABLE_ENTRY LdrEntry = NULL;
+    ULONG EntrySize, DriverSize;
+    PLOAD_IMPORTS LoadedImports = MM_SYSLDR_NO_IMPORTS;
+    PCHAR MissingApiName, Buffer;
+    PWCHAR MissingDriverName;
+    HANDLE SectionHandle;
+    ACCESS_MASK DesiredAccess;
+    PVOID Section = NULL;
+    BOOLEAN LockOwned = FALSE;
+    PLIST_ENTRY NextEntry;
+    IMAGE_INFO ImageInfo;
+    STRING AnsiTemp;
+    PAGED_CODE();
+
+    /* Detect session-load */
+    if (Flags)
+    {
+        /* Sanity checks */
+        ASSERT(NamePrefix == NULL);
+        ASSERT(LoadedName == NULL);
+
+        /* Make sure the process is in session too */
+        if (!PsGetCurrentProcess()->ProcessInSession) return STATUS_NO_MEMORY;
+    }
+
+    /* Allocate a buffer we'll use for names */
+    Buffer = ExAllocatePoolWithTag(NonPagedPool, MAX_PATH, 'nLmM');
+    if (!Buffer) return STATUS_INSUFFICIENT_RESOURCES;
+
+    /* Check for a separator */
+    if (FileName->Buffer[0] == OBJ_NAME_PATH_SEPARATOR)
+    {
+        PWCHAR p;
+        ULONG BaseLength;
+
+        /* Loop the path until we get to the base name */
+        p = &FileName->Buffer[FileName->Length / sizeof(WCHAR)];
+        while (*(p - 1) != OBJ_NAME_PATH_SEPARATOR) p--;
+
+        /* Get the length */
+        BaseLength = (ULONG)(&FileName->Buffer[FileName->Length / sizeof(WCHAR)] - p);
+        BaseLength *= sizeof(WCHAR);
+
+        /* Setup the string */
+        BaseName.Length = (USHORT)BaseLength;
+        BaseName.Buffer = p;
+    }
+    else
+    {
+        /* Otherwise, we already have a base name */
+        BaseName.Length = FileName->Length;
+        BaseName.Buffer = FileName->Buffer;
+    }
+
+    /* Setup the maximum length */
+    BaseName.MaximumLength = BaseName.Length;
+
+    /* Now compute the base directory */
+    BaseDirectory = *FileName;
+    BaseDirectory.Length -= BaseName.Length;
+    BaseDirectory.MaximumLength = BaseDirectory.Length;
+
+    /* And the prefix, which for now is just the name itself */
+    PrefixName = *FileName;
+
+    /* Check if we have a prefix */
+    if (NamePrefix) DPRINT1("Prefixed images are not yet supported!\n");
+
+    /* Check if we already have a name, use it instead */
+    if (LoadedName) BaseName = *LoadedName;
+    
+    /* Check for loader snap debugging */
+    if (NtGlobalFlag & FLG_SHOW_LDR_SNAPS)
+    {
+        /* Print out standard string */
+        DPRINT1("MM:SYSLDR Loading %wZ (%wZ) %s\n",
+                &PrefixName, &BaseName, Flags ? "in session space" : "");
+    }
+
+    /* Acquire the load lock */
+LoaderScan:
+    ASSERT(LockOwned == FALSE);
+    LockOwned = TRUE;
+    KeEnterCriticalRegion();
+    KeWaitForSingleObject(&MmSystemLoadLock,
+                          WrVirtualMemory,
+                          KernelMode,
+                          FALSE,
+                          NULL);
+
+    /* Scan the module list */
+    NextEntry = PsLoadedModuleList.Flink;
+    while (NextEntry != &PsLoadedModuleList)
+    {
+        /* Get the entry and compare the names */
+        LdrEntry = CONTAINING_RECORD(NextEntry,
+                                     LDR_DATA_TABLE_ENTRY,
+                                     InLoadOrderLinks);
+        if (RtlEqualUnicodeString(&PrefixName, &LdrEntry->FullDllName, TRUE))
+        {
+            /* Found it, break out */
+            break;
+        }
+
+        /* Keep scanning */
+        NextEntry = NextEntry->Flink;
+    }
+
+    /* Check if we found the image */
+    if (NextEntry != &PsLoadedModuleList)
+    {
+        /* Check if we had already mapped a section */
+        if (Section)
+        {
+            /* Dereference and clear */
+            ObDereferenceObject(Section);
+            Section = NULL;
+        }
+
+        /* Check if this was supposed to be a session load */
+        if (!Flags)
+        {
+            /* It wasn't, so just return the data */
+            *ModuleObject = LdrEntry;
+            *ImageBaseAddress = LdrEntry->DllBase;
+            Status = STATUS_IMAGE_ALREADY_LOADED;
+        }
+        else
+        {
+            /* We don't support session loading yet */
+            DPRINT1("Unsupported Session-Load!\n");
+            while (TRUE);
+        }
+
+        /* Do cleanup */
+        goto Quickie;
+    }
+    else if (!Section)
+    {
+        /* It wasn't loaded, and we didn't have a previous attempt */
+        KeReleaseMutant(&MmSystemLoadLock, 1, FALSE, FALSE);
+        KeLeaveCriticalRegion();
+        LockOwned = FALSE;
+
+        /* Check if KD is enabled */
+        if ((KdDebuggerEnabled) && !(KdDebuggerNotPresent))
+        {
+            /* FIXME: Attempt to get image from KD */
+        }
+
+        /* We don't have a valid entry */
+        LdrEntry = NULL;
+
+        /* Setup image attributes */
+        InitializeObjectAttributes(&ObjectAttributes,
+                                   FileName,
+                                   OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+                                   NULL,
+                                   NULL);
+
+        /* Open the image */
+        Status = ZwOpenFile(&FileHandle,
+                            FILE_EXECUTE,
+                            &ObjectAttributes,
+                            &IoStatusBlock,
+                            FILE_SHARE_READ | FILE_SHARE_DELETE,
+                            0);
+        if (!NT_SUCCESS(Status)) goto Quickie;
+
+        /* Validate it */
+        Status = MmCheckSystemImage(FileHandle, FALSE);
+        if ((Status == STATUS_IMAGE_CHECKSUM_MISMATCH) ||
+            (Status == STATUS_IMAGE_MP_UP_MISMATCH) ||
+            (Status == STATUS_INVALID_IMAGE_PROTECT))
+        {
+            /* Fail loading */
+            goto Quickie;
+        }
+
+        /* Check if this is a session-load */
+        if (Flags)
+        {
+            /* Then we only need read and execute */
+            DesiredAccess = SECTION_MAP_READ | SECTION_MAP_EXECUTE;
+        }
+        else
+        {
+            /* Otherwise, we can allow write access */
+            DesiredAccess = SECTION_ALL_ACCESS;
+        }
+
+        /* Initialize the attributes for the section */
+        InitializeObjectAttributes(&ObjectAttributes,
+                                   NULL,
+                                   OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+                                   NULL,
+                                   NULL);
+
+        /* Create the section */
+        Status = ZwCreateSection(&SectionHandle,
+                                 DesiredAccess,
+                                 &ObjectAttributes,
+                                 NULL,
+                                 PAGE_EXECUTE,
+                                 SEC_IMAGE,
+                                 FileHandle);
+        if (!NT_SUCCESS(Status)) goto Quickie;
+
+        /* Now get the section pointer */
+        Status = ObReferenceObjectByHandle(SectionHandle,
+                                           SECTION_MAP_EXECUTE,
+                                           MmSectionObjectType,
+                                           KernelMode,
+                                           &Section,
+                                           NULL);
+        ZwClose(SectionHandle);
+        if (!NT_SUCCESS(Status)) goto Quickie;
+
+        /* Check if this was supposed to be a session-load */
+        if (Flags)
+        {
+            /* We don't support session loading yet */
+            DPRINT1("Unsupported Session-Load!\n");
+            while (TRUE);
+        }
+
+        /* Check the loader list again, we should end up in the path below */
+        goto LoaderScan;
+    }
+    else
+    {
+        /* We don't have a valid entry */
+        LdrEntry = NULL;
+    }
+
+    /* Load the image */
+    Status = MiLoadImageSection(&Section,
+                                &ModuleLoadBase,
+                                FileName,
+                                FALSE,
+                                NULL);
+    ASSERT(Status != STATUS_ALREADY_COMMITTED);
+
+    /* Get the size of the driver */
+    DriverSize = ((PROS_SECTION_OBJECT)Section)->ImageSection->ImageSize;
+
+    /* Make sure we're not being loaded into session space */
+    if (!Flags)
+    {
+        /* Check for success */
+        if (NT_SUCCESS(Status))
+        {
+            /* Support large pages for drivers */
+            MiUseLargeDriverPage(DriverSize / PAGE_SIZE,
+                                 &ModuleLoadBase,
+                                 &BaseName,
+                                 TRUE);
+        }
+
+        /* Dereference the section */
+        ObDereferenceObject(Section);
+        Section = NULL;
+    }
+    
+    /* Check for failure of the load earlier */
+    if (!NT_SUCCESS(Status)) goto Quickie;
+
+    /* Relocate the driver */
+    Status = LdrRelocateImageWithBias(ModuleLoadBase,
+                                      0,
+                                      "SYSLDR",
+                                      STATUS_SUCCESS,
+                                      STATUS_CONFLICTING_ADDRESSES,
+                                      STATUS_INVALID_IMAGE_FORMAT);
+    if (!NT_SUCCESS(Status)) goto Quickie;
+
+    
+    /* Get the NT Header */
+    NtHeader = RtlImageNtHeader(ModuleLoadBase);
+
+    /* Calculate the size we'll need for the entry and allocate it */
+    EntrySize = sizeof(LDR_DATA_TABLE_ENTRY) +
+                BaseName.Length +
+                sizeof(UNICODE_NULL);
+
+    /* Allocate the entry */
+    LdrEntry = ExAllocatePoolWithTag(NonPagedPool, EntrySize, TAG_MODULE_OBJECT);
+    if (!LdrEntry)
+    {
+        /* Fail */
+        Status = STATUS_INSUFFICIENT_RESOURCES;
+        goto Quickie;
+    }
+
+    /* Setup the entry */
+    LdrEntry->Flags = LDRP_LOAD_IN_PROGRESS;
+    LdrEntry->LoadCount = 1;
+    LdrEntry->LoadedImports = LoadedImports;
+    LdrEntry->PatchInformation = NULL;
+
+    /* Check the version */
+    if ((NtHeader->OptionalHeader.MajorOperatingSystemVersion >= 5) &&
+        (NtHeader->OptionalHeader.MajorImageVersion >= 5))
+    {
+        /* Mark this image as a native image */
+        LdrEntry->Flags |= LDRP_ENTRY_NATIVE;
+    }
+
+    /* Setup the rest of the entry */
+    LdrEntry->DllBase = ModuleLoadBase;
+    LdrEntry->EntryPoint = (PVOID)((ULONG_PTR)ModuleLoadBase +
+                                   NtHeader->OptionalHeader.AddressOfEntryPoint);
+    LdrEntry->SizeOfImage = DriverSize;
+    LdrEntry->CheckSum = NtHeader->OptionalHeader.CheckSum;
+    LdrEntry->SectionPointer = Section;
+
+    /* Now write the DLL name */
+    LdrEntry->BaseDllName.Buffer = (PVOID)(LdrEntry + 1);
+    LdrEntry->BaseDllName.Length = BaseName.Length;
+    LdrEntry->BaseDllName.MaximumLength = BaseName.Length;
+
+    /* Copy and null-terminate it */
+    RtlCopyMemory(LdrEntry->BaseDllName.Buffer,
+                  BaseName.Buffer,
+                  BaseName.Length);
+    LdrEntry->BaseDllName.Buffer[BaseName.Length / sizeof(WCHAR)] = UNICODE_NULL;
+
+    /* Now allocate the full name */
+    LdrEntry->FullDllName.Buffer = ExAllocatePoolWithTag(PagedPool,
+                                                         PrefixName.Length +
+                                                         sizeof(UNICODE_NULL),
+                                                         TAG_LDR_WSTR);
+    if (!LdrEntry->FullDllName.Buffer)
+    {
+        /* Don't fail, just set it to zero */
+        LdrEntry->FullDllName.Length = 0;
+        LdrEntry->FullDllName.MaximumLength = 0;
+    }
+    else
+    {
+        /* Set it up */
+        LdrEntry->FullDllName.Length = PrefixName.Length;
+        LdrEntry->FullDllName.MaximumLength = PrefixName.Length;
+
+        /* Copy and null-terminate */
+        RtlCopyMemory(LdrEntry->FullDllName.Buffer,
+                      PrefixName.Buffer,
+                      PrefixName.Length);
+        LdrEntry->FullDllName.Buffer[PrefixName.Length / sizeof(WCHAR)] = UNICODE_NULL;
+    }
+
+    /* Add the entry */
+    MiProcessLoaderEntry(LdrEntry, TRUE);
+
+    /* Resolve imports */
+    MissingApiName = Buffer;
+    Status = MiResolveImageReferences(ModuleLoadBase,
+                                      &BaseDirectory,
+                                      NULL,
+                                      &MissingApiName,
+                                      &MissingDriverName,
+                                      &LoadedImports);
+    if (!NT_SUCCESS(Status))
+    {
+        /* Fail */
+        MiProcessLoaderEntry(LdrEntry, FALSE);
+
+        /* Check if we need to free the name */
+        if (LdrEntry->FullDllName.Buffer)
+        {
+            /* Free it */
+            ExFreePool(LdrEntry->FullDllName.Buffer);
+        }
+
+        /* Free the entry itself */
+        ExFreePoolWithTag(LdrEntry, TAG_MODULE_OBJECT);
+        LdrEntry = NULL;
+        goto Quickie;
+    }
+
+    /* Update the loader entry */
+    LdrEntry->Flags |= (LDRP_SYSTEM_MAPPED |
+                        LDRP_ENTRY_PROCESSED |
+                        LDRP_MM_LOADED);
+    LdrEntry->Flags &= ~LDRP_LOAD_IN_PROGRESS;
+    LdrEntry->LoadedImports = LoadedImports;
+
+    /* FIXME: Call driver verifier's loader function */
+
+    /* Write-protect the system image */
+    MiWriteProtectSystemImage(LdrEntry->DllBase);
+
+    /* Check if notifications are enabled */
+    if (PsImageNotifyEnabled)
+    {
+        /* Fill out the notification data */
+        ImageInfo.Properties = 0;
+        ImageInfo.ImageAddressingMode = IMAGE_ADDRESSING_MODE_32BIT;
+        ImageInfo.SystemModeImage = TRUE;
+        ImageInfo.ImageSize = LdrEntry->SizeOfImage;
+        ImageInfo.ImageBase = LdrEntry->DllBase;
+        ImageInfo.ImageSectionNumber = ImageInfo.ImageSelector = 0;
+
+        /* Send the notification */
+        PspRunLoadImageNotifyRoutines(FileName, NULL, &ImageInfo);
+    }
+
+#if defined(KDBG) || defined(_WINKD_)
+    /* MiCacheImageSymbols doesn't detect rossym */
+    if (TRUE)
+#else
+    /* Check if there's symbols */
+    if (MiCacheImageSymbols(LdrEntry->DllBase))
+#endif
+    {
+        /* Check if the system root is present */
+        if ((PrefixName.Length > (11 * sizeof(WCHAR))) &&
+            !(_wcsnicmp(PrefixName.Buffer, L"\\SystemRoot", 11)))
+        {
+            /* Add the system root */
+            UnicodeTemp = PrefixName;
+            UnicodeTemp.Buffer += 11;
+            UnicodeTemp.Length -= (11 * sizeof(WCHAR));
+            sprintf_nt(Buffer,
+                       "%ws%wZ",
+                       &SharedUserData->NtSystemRoot[2],
+                       &UnicodeTemp);
+        }
+        else
+        {
+            /* Build the name */
+            sprintf_nt(Buffer, "%wZ", &BaseName);
+        }
+
+        /* Setup the ansi string */
+        RtlInitString(&AnsiTemp, Buffer);
+
+        /* Notify the debugger */
+        DbgLoadImageSymbols(&AnsiTemp,
+                            LdrEntry->DllBase,
+                            (ULONG_PTR)ZwCurrentProcess());
+        LdrEntry->Flags |= LDRP_DEBUG_SYMBOLS_LOADED;
+    }
+
+    /* Page the driver */
+    ASSERT(Section == NULL);
+    MiEnablePagingOfDriver(LdrEntry);
+
+    /* Return pointers */
+    *ModuleObject = LdrEntry;
+    *ImageBaseAddress = LdrEntry->DllBase;
+
+Quickie:
+    /* Check if we have the lock acquired */
+    if (LockOwned)
+    {
+        /* Release the lock */
+        KeReleaseMutant(&MmSystemLoadLock, 1, FALSE, FALSE);
+        KeLeaveCriticalRegion();
+        LockOwned = FALSE;
+    }
+
+    /* If we have a file handle, close it */
+    if (FileHandle) ZwClose(FileHandle);
+
+    /* Check if we had a prefix */
+    if (NamePrefix) ExFreePool(PrefixName.Buffer);
+
+    /* Free the name buffer and return status */
+    ExFreePoolWithTag(Buffer, TAG_LDR_WSTR);
+    return Status;
+}
+
+PLDR_DATA_TABLE_ENTRY
+NTAPI
+MiLookupDataTableEntry(IN PVOID Address)
+{
+    PLDR_DATA_TABLE_ENTRY LdrEntry, FoundEntry = NULL;
+    PLIST_ENTRY NextEntry;
+    PAGED_CODE();
+    
+    /* Loop entries */
+    NextEntry = PsLoadedModuleList.Flink;
+    do
+    {
+        /* Get the loader entry */
+        LdrEntry =  CONTAINING_RECORD(NextEntry,
+                                      LDR_DATA_TABLE_ENTRY,
+                                      InLoadOrderLinks);
+        
+        /* Check if the address matches */
+        if ((Address >= LdrEntry->DllBase) &&
+            (Address < (PVOID)((ULONG_PTR)LdrEntry->DllBase +
+                               LdrEntry->SizeOfImage)))
+        {
+            /* Found a match */
+            FoundEntry = LdrEntry;
+            break;
+        }
+        
+        /* Move on */
+        NextEntry = NextEntry->Flink;
+    } while(NextEntry != &PsLoadedModuleList);
+    
+    /* Return the entry */
+    return FoundEntry;
+}
+
+/* PUBLIC FUNCTIONS ***********************************************************/
+
+/*
+ * @implemented
+ */
+PVOID
+NTAPI
+MmPageEntireDriver(IN PVOID AddressWithinSection)
+{
+    PMMPTE StartPte, EndPte;
+    PLDR_DATA_TABLE_ENTRY LdrEntry;
+    PAGED_CODE();
+
+    /* Get the loader entry */
+    LdrEntry = MiLookupDataTableEntry(AddressWithinSection);
+    if (!LdrEntry) return NULL;
+
+    /* Check if paging of kernel mode is disabled or if the driver is mapped as an image */
+    if ((MmDisablePagingExecutive) || (LdrEntry->SectionPointer))
+    {
+        /* Don't do anything, just return the base address */
+        return LdrEntry->DllBase;
+    }
+
+    /* Wait for active DPCs to finish before we page out the driver */
+    KeFlushQueuedDpcs();
+
+    /* Get the PTE range for the whole driver image */
+    StartPte = MiAddressToPte((ULONG_PTR)LdrEntry->DllBase);
+    EndPte = MiAddressToPte((ULONG_PTR)LdrEntry->DllBase + LdrEntry->SizeOfImage);
+
+    /* Enable paging for the PTE range */
+    ASSERT(MI_IS_SESSION_IMAGE_ADDRESS(AddressWithinSection) == FALSE);
+    MiSetPagingOfDriver(StartPte, EndPte);
+
+    /* Return the base address */
+    return LdrEntry->DllBase;
+}
+
+/*
+ * @unimplemented
+ */
+VOID
+NTAPI
+MmResetDriverPaging(IN PVOID AddressWithinSection)
+{
+    UNIMPLEMENTED;
+}
+
+/*
+ * @implemented
+ */
+PVOID
+NTAPI
+MmGetSystemRoutineAddress(IN PUNICODE_STRING SystemRoutineName)
+{
+    PVOID ProcAddress = NULL;
+    ANSI_STRING AnsiRoutineName;
+    NTSTATUS Status;
+    PLIST_ENTRY NextEntry;
+    PLDR_DATA_TABLE_ENTRY LdrEntry;
+    BOOLEAN Found = FALSE;
+    UNICODE_STRING KernelName = RTL_CONSTANT_STRING(L"ntoskrnl.exe");
+    UNICODE_STRING HalName = RTL_CONSTANT_STRING(L"hal.dll");
+    ULONG Modules = 0;
+
+    /* Convert routine to ansi name */
+    Status = RtlUnicodeStringToAnsiString(&AnsiRoutineName,
+                                          SystemRoutineName,
+                                          TRUE);
+    if (!NT_SUCCESS(Status)) return NULL;
+
+    /* Lock the list */
+    KeEnterCriticalRegion();
+    ExAcquireResourceSharedLite(&PsLoadedModuleResource, TRUE);
+
+    /* Loop the loaded module list */
+    NextEntry = PsLoadedModuleList.Flink;
+    while (NextEntry != &PsLoadedModuleList)
+    {
+        /* Get the entry */
+        LdrEntry = CONTAINING_RECORD(NextEntry,
+                                     LDR_DATA_TABLE_ENTRY,
+                                     InLoadOrderLinks);
+
+        /* Check if it's the kernel or HAL */
+        if (RtlEqualUnicodeString(&KernelName, &LdrEntry->BaseDllName, TRUE))
+        {
+            /* Found it */
+            Found = TRUE;
+            Modules++;
+        }
+        else if (RtlEqualUnicodeString(&HalName, &LdrEntry->BaseDllName, TRUE))
+        {
+            /* Found it */
+            Found = TRUE;
+            Modules++;
+        }
+
+        /* Check if we found a valid binary */
+        if (Found)
+        {
+            /* Find the procedure name */
+            ProcAddress = MiFindExportedRoutineByName(LdrEntry->DllBase,
+                                                      &AnsiRoutineName);
+
+            /* Break out if we found it or if we already tried both modules */
+            if (ProcAddress) break;
+            if (Modules == 2) break;
+        }
+
+        /* Keep looping */
+        NextEntry = NextEntry->Flink;
+    }
+
+    /* Release the lock */
+    ExReleaseResourceLite(&PsLoadedModuleResource);
+    KeLeaveCriticalRegion();
+
+    /* Free the string and return */
+    RtlFreeAnsiString(&AnsiRoutineName);
+    return ProcAddress;
+}
+