Enabling C++ exceptions and RTTI in kernel-mode code

来源:互联网 发布:追风抢购软件 编辑:程序博客网 时间:2024/05/27 10:43

There are a number of sources of information on using C++ language features in Windows NT/2000
kernel-mode code, amongst which are:

"C++ Run Time Support for NT/WDM Kernel Mode Drivers"
(http://www.numega.com/drivercentral/examples/c++support.shtml)
"C++ Runtime Support for the NT DDK" (http://www.osr.com/ntinsider/1999/global.htm)

However both of these sources stop short of explaining how to enable use of C++ exceptions and Run
Time Type Information. This message presents one technique of enabling these features. The first
step is to compile cpprt.cpp and then to build a library from cpprt.obj and selected runtime support
object modules provided with Visual C++.

The batch commands:

@echo off
if "%1" == "" exit /b
setlocal
set lib=%1/crt/src/intel/st_lib
link/lib /nologo %lib%/ehprolog.obj %lib%/throw.obj %lib%/frame.obj %lib%/trnsctrl.obj
%lib%/unhandld.obj %lib%/validate.obj %lib%/exsup.obj %lib%/lowhelpr.obj %lib%/hooks.obj
%lib%/stdexcpt.obj %lib%/ehvecdtr.obj %lib%/ehvecctr.obj %lib%/ehveccvb.obj %lib%/user.obj
%lib%/typinfo.obj %lib%/typname.obj %lib%/rtti.obj %lib%/undname.obj cpprt.obj /out:cpprt.lib
endlocal

collect the necessary modules into the library (where %1 is the directory containing the Visual C++
installation). The st_lib (single threaded library) modules are used; the only threading issue (of
which I am aware) is the use of two global variables to hold pointers to an exception record and a
context record whilst searching for a handler and calling its catch block. It would be possible to
address this issue by using the multi-threaded (mt_lib) or dll (dll_lib) object modules and
implementing the function _getptd() suitably.

The source of cpprt.cpp is:
------------------------------------------------------------------------------
extern "C" {
#include <ntddk.h>

}

#include "cpprt.h"

#pragma comment(linker, "-ignore:4049")  // locally defined symbol "<symbol>" imported

#pragma data_seg(".CRT$XIA")
void (*_xi_a[])() = {0};
#pragma data_seg(".CRT$XIZ")
void (*_xi_z[])() = {0};
#pragma data_seg(".CRT$XCA")
void (*_xc_a[])() = {0};
#pragma data_seg(".CRT$XCZ")
void (*_xc_z[])() = {0};
#pragma data_seg(".CRT$XPA")
void (*_xp_a[])() = {0};
#pragma data_seg(".CRT$XPZ")
void (*_xp_z[])() = {0};
#pragma data_seg(".CRT$XTA")
void (*_xt_a[])() = {0};
#pragma data_seg(".CRT$XTZ")
void (*_xt_z[])() = {0};
#pragma data_seg()

extern "C"
NTSYSAPI
VOID
NTAPI
RtlRaiseException(
    IN PEXCEPTION_RECORD ExceptionRecord
    );

class AtExitCall {
  public:
    AtExitCall(void (__cdecl *func)()) : func(func), next(list) { list = this; }
    ~AtExitCall() { func(); list = next; }
    static AtExitCall* list;
  private:
    void (__cdecl *func)();
    AtExitCall* next;

};

AtExitCall* AtExitCall::list = 0;

extern "C" int __cdecl atexit(void (__cdecl *func)())
{
    return (new (PagedPool) AtExitCall(func) == 0) ? 1 : 0;

}

extern "C" void * __cdecl malloc(size_t n)
{
    return ExAllocatePool(NonPagedPool, n);

}

extern "C" void __cdecl free(void * p)
{
    if (p) ExFreePool(p);

}

extern "C" void __cdecl abort()
{
    KeBugCheck(0x1E);

}

extern "C" void __stdcall
RaiseException(ULONG Code, ULONG Flags, ULONG NumberParameters, const ULONG * Information)
{
    EXCEPTION_RECORD er = {Code, Flags, 0, RaiseException, NumberParameters};

    for (ULONG i = 0; i < NumberParameters; i++) er.ExceptionInformation[i] = Information[i];

    RtlRaiseException(&er);

}

int probe(void * p, size_t n, LOCK_OPERATION op)
{
    PMDL mdl = IoAllocateMdl(p, n, FALSE, FALSE, 0);

    __try {
        MmProbeAndLockPages(mdl, KernelMode, op);
        MmUnlockPages(mdl);
    }
    __except (EXCEPTION_EXECUTE_HANDLER) {
        IoFreeMdl(mdl);
        return 1;
    }

    IoFreeMdl(mdl);
    return 0;

}

extern "C" int __stdcall IsBadReadPtr(const void * p, size_t n)
{
    return probe(const_cast<void *>(p), n, IoReadAccess);

}

extern "C" int __stdcall IsBadWritePtr(void * p, size_t n)
{
    return probe(p, n, IoWriteAccess);

}

extern "C" int __stdcall IsBadCodePtr(const void * p)
{
    return IsBadReadPtr(p, 1);

}

extern "C" void * SetUnhandledExceptionFilter(void *)
{
    return 0;

}

void CpprtStartup()
{
    void (**p)();

    for (p = _xi_a; p < _xi_z; p++) if (*p) (*p)();

    for (p = _xc_a; p < _xc_z; p++) if (*p) (*p)();

}

void CpprtRundown()
{
    while (AtExitCall::list) delete AtExitCall::list;

    void (**p)();

    for (p = _xp_a; p < _xp_z; p++) if (*p) (*p)();

    for (p = _xt_a; p < _xt_z; p++) if (*p) (*p)();

}

---------------------------------------------------------------------------

---
and the source of cpprt.h is:
------------------------------------------------------------------------------
#ifndef __CPPRT__
#define __CPPRT__

inline void * __cdecl operator new(size_t n)
{
    return n ? ExAllocatePool(NonPagedPool, n) : 0;

}

inline void * __cdecl operator new(size_t n, POOL_TYPE pooltype)
{
    return n ? ExAllocatePool(pooltype, n) : 0;
}

inline void __cdecl operator delete(void * p)
{
    if (p) ExFreePool(p);
}

inline void __cdecl operator delete[](void * p)
{
    if (p) ExFreePool(p);
}

inline void __cdecl operator delete(void * p, POOL_TYPE)
{
    if (p) ExFreePool(p);
}

void CpprtStartup();

void CpprtRundown();

#endif  // __CPPRT__
------------------------------------------------------------------------------

When building the code, it is necessary to specify the appropriate compiler options (/GR, /EHa,
etc.), reference library cpprt.lib and to heed the warnings about code placement in pageable
sections given in the NuMega and OSR documents. Since there is no exception handler at the top of
every kernel-mode stack, the terminate routine is not called if a C++ exception is not caught - the
system BugChecks instead! set_terminate can be called but it has no effect. The structured exception
translation function does work, so C++ handlers can also catch system exceptions.

The following sample code demonstrates some of the features:
------------------------------------------------------------------------------
extern "C" {
#include <ntddk.h>

}

#include "cpprt.h"
#include <typeinfo>

struct OsException {
    OsException(unsigned int code, PEXCEPTION_POINTERS ep = 0) : code(code), ep(ep) {}
    unsigned int code;
    PEXCEPTION_POINTERS ep;

};

void __cdecl translator(unsigned int code, PEXCEPTION_POINTERS ep)
{
    throw OsException(code, ep);
}

struct GlobalTest {
    GlobalTest() { DbgPrint("%s constructor/n", typeid(*this).name()); }
    ~GlobalTest() { DbgPrint("%s destructor/n", typeid(*this).name()); }
} gt[2];

extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT, PUNICODE_STRING)
{
    CpprtStartup();

    _set_se_translator(translator);

    try {
        struct Test {
            Test() { DbgPrint("%s constructor/n", typeid(*this).name()); }
            ~Test() { DbgPrint("%s destructor/n", typeid(*this).name()); }
        } t[2];

        volatile int *x = 0, y = *x;
    }
    catch (OsException e) {
        DbgPrint("Caught test exception %lX/n", e.code);
    }
    catch (...) {
        DbgPrint("Caught exception in catch-all clause/n");
    }

    KIRQL SaveIrql;

    KeRaiseIrql(DISPATCH_LEVEL, &SaveIrql);

    try {
        struct Test {
            Test() { DbgPrint("%s constructor/n", typeid(*this).name()); }
            ~Test() { DbgPrint("%s destructor/n", typeid(*this).name()); }
        } t[2];

        throw OsException(0xdead);
    }
    catch (OsException e) {
        DbgPrint("Caught test exception %lX/n", e.code);
    }
    catch (...) {
        DbgPrint("Caught exception in catch-all clause/n");
    }

    KeLowerIrql(SaveIrql);

    CpprtRundown();

    return STATUS_UNSUCCESSFUL;

}

------------------------------------------------------------------------------

Gary

原创粉丝点击