"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
Has anyone got STL working in the kernel? Your code was gives us containers
(e.g. vector, map...) and some other stuff, but not strings or streams.
> extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT, PUNICODE_STRING)
> {
> CpprtStartup();
>
> _set_se_translator(translator);
Does this call need to be added to each dispatch routine, callback, etc.?
(BTW, thanks for your posting. I was about to ask about getting better C++
support in drivers.)
Demyn
Rtl generic table is OK for container (for map namely).
Strings are present too :-)
Max
CpprtStartup just needs to be called as early as possible after the driver is loaded (e.g. be the
first routine that DriverEntry calls). CpprtRundown just needs to be called as late possible before
the driver is unloaded (e.g. be the last routine called by the driver unload routine or be the last
routine called by DriverEntry before it returns STATUS_UNSUCCESSFUL).
Gary
In my installation, I saw crt\src without any further subdir.
Is it on the Visual studio CD ?
Jack
Gary Nebbett wrote in message <3976...@guardhouse.chbs>...
Sorry about not being clear. I was wondering if
_set_se_translator(translator) needs to be called whenever we are in an
arbitrary thread?
Demyn.
(i.e., do a 'custom' installation of VC++)
--
remove 'z' from my email address
"Jack Cheng" <uch...@lexis-nexis.com> wrote in message
news:8li194$csj$1...@mailgate2.lexis-nexis.com...
Thanks,
Jack
dave porter wrote in message <8li3jd$jfv$1...@bob.news.rcn.net>...
I need one more help. At end of "build -cef", it gave me infamous unresolved
external symbol in regard to _set_se_translator.
Am I missing any library ?
Also where should I put the the mentioned compiler flag /GR & /EHa. I asked
this because in the sources file, I have xxxx.rc file. The rc compiler seems
doesn't like these two options.
Thanx,
Jack
Jack Cheng wrote in message <8lk4ah$evl$1...@mailgate2.lexis-nexis.com>...