From 6b86dcbae1d67aeedba76d8cee16a589b3493b77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Gardou?= Date: Thu, 17 Jun 2010 00:54:34 +0000 Subject: [PATCH] [ZLIB] [NTOSKRNL] This is the ultimate commit that makes this branch build again svn path=/branches/reactos-yarotows/; revision=47794 --- lib/3rdparty/zlib/gzguts.h | 132 ++ lib/3rdparty/zlib/gzlib.c | 537 ++++++ lib/3rdparty/zlib/gzread.c | 653 ++++++++ lib/3rdparty/zlib/gzwrite.c | 531 ++++++ ntoskrnl/mm/ARM3/mmdbg.c | 326 ++++ ntoskrnl/mm/ARM3/sysldr.c | 3129 +++++++++++++++++++++++++++++++++++ 6 files changed, 5308 insertions(+) create mode 100644 lib/3rdparty/zlib/gzguts.h create mode 100644 lib/3rdparty/zlib/gzlib.c create mode 100644 lib/3rdparty/zlib/gzread.c create mode 100644 lib/3rdparty/zlib/gzwrite.c create mode 100644 ntoskrnl/mm/ARM3/mmdbg.c create mode 100644 ntoskrnl/mm/ARM3/sysldr.c diff --git a/lib/3rdparty/zlib/gzguts.h b/lib/3rdparty/zlib/gzguts.h new file mode 100644 index 00000000000..055455af860 --- /dev/null +++ b/lib/3rdparty/zlib/gzguts.h @@ -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 +#include "zlib.h" +#ifdef STDC +# include +# include +# include +#endif +#include + +#ifdef NO_DEFLATE /* for compatibility with old definition */ +# define NO_GZCOMPRESS +#endif + +#ifdef _MSC_VER +# include +# 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 +# define zstrerror() gz_strwinerror((DWORD)GetLastError()) +#else +# ifdef STDC +# include +# 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 index 00000000000..e9eef783fe1 --- /dev/null +++ b/lib/3rdparty/zlib/gzlib.c @@ -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); /* 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 index 00000000000..548201ab009 --- /dev/null +++ b/lib/3rdparty/zlib/gzread.c @@ -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 index 00000000000..e8defc6887a --- /dev/null +++ b/lib/3rdparty/zlib/gzwrite.c @@ -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 + +/* -- 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 index 00000000000..41260f224c3 --- /dev/null +++ b/ntoskrnl/mm/ARM3/mmdbg.c @@ -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 +#define NDEBUG +#include + +#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 index 00000000000..d8f7157eb21 --- /dev/null +++ b/ntoskrnl/mm/ARM3/sysldr.c @@ -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 +#define NDEBUG +#include + +#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; +} +