I thought I read a few years back that C++ is officially supported building drivers. I've long before time used C++ (no exceptions/implemented new/delete versions using ExAllocatePool / ExFreePool) , however now if I don't include those overrides it says there is no "new" or "delete", if I include <new>
to override it complains about exception handling (even though the header file is in a /km/ sub-directory). What's available and proper way to now implement my own new/delete (the old method has some issues)?
Basically you'd include <new>
then do this:
inline void * __cdecl operator new (size_t size)
{
return malloc(size);
}
inline void * __cdecl operator new [] (size_t size)
{
return malloc(size);
}
inline void __cdecl operator delete(void *p)
{
free(p);
};
inline void __cdecl operator delete [] (void *p)
{
free(p);
};
The malloc/free actually used the ExAllocatePool/ExFreePool.
But now, you can't include <new>
, and it doesn't like the "inline", if you omit both, then a link error that it can't resolve.
C++ is supported. You need to provide your own operator new
and operator delete
that calls ExAllocPool
and friends. The include file <kcom.h>
contains implementations of both.
Thanks for that, in the mean time I had looked in to it more and had come up with this which worked:
//-------------------------------------------------------------------------
_Check_return_ _Ret_maybenull_ _Post_writable_byte_size_(_Count* _Size) _CRTIMP _CRT_JIT_INTRINSIC _CRTNOALIAS _CRTRESTRICT
void* __cdecl calloc(_In_ size_t _Count, _In_ size_t _Size)
{
PAGED_CODE();
void *p=malloc(_Count*_Size);
if (p) {
memset(p,0,_Count*_Size);
}
return p;
}
//-------------------------------------------------------------------------
_CRTIMP _CRTNOALIAS
void __cdecl free(_Pre_maybenull_ _Post_invalid_ void* _Memory)
{
PAGED_CODE();
if (!_Memory) return;
ExFreePool(_Memory);
}
//-------------------------------------------------------------------------
_Check_return_ _Ret_maybenull_ _Post_writable_byte_size_(_Size) _CRTIMP _CRT_JIT_INTRINSIC _CRTNOALIAS _CRTRESTRICT
void* __cdecl malloc(_In_ size_t _Size)
{
PAGED_CODE();
return ExAllocatePagedPool(_Size);
}
//-------------------------------------------------------------------------
_Check_return_ _Ret_maybenull_ _Post_writable_byte_size_(_NewSize) _CRTIMP _CRTNOALIAS _CRTRESTRICT
void* __cdecl realloc(_Pre_maybenull_ _Post_invalid_ void* _Memory, _In_ size_t _NewSize)
{
PAGED_CODE();
if (_NewSize==0 && _Memory) {
free(_Memory);
return NULL;
}
// we don't know how many bytes to copy over from p so we'll just
// fail that and use this only if p is null to emulate malloc
if (!_Memory) {
return malloc(_NewSize);
}
// no point in allocating because we don't know number of bytes to copy
return NULL;
}
//-------------------------------------------------------------------------
void * __cdecl operator new (size_t size)
{
return malloc(size);
}
void * __cdecl operator new [] (size_t size)
{
return malloc(size);
}
void __cdecl operator delete(void *p, size_t )
{
free(p);
};
void __cdecl operator delete [] (void *p)
{
free(p);
};
You should also be aware that no global object constructors are invoked.
I used to provide a full c++ runtime library for the kernel. I might toss it up on github. It is from circa 2000, so possibly outdated.
2 Likes
Excellent point that is often overlooked.
Is it only the global objects? Funny, I was just looking at the new implementation and thought, does this call the constructor, but it had always worked in the past (I could look at the disassembly).
Well, the operator doesn't, but the compiler does. "new" is not involved with global objects.
Global object constructors are a strange little quirk in C++. The compiler puts the objects and the constructor address in a special section of the binary surrounded by special symbol names. The runtime initialization then starts at the "begin" symbol and initializes everything up to the "end" symbol. However, a kernel driver doesn't undergo runtime initialization.
Note also that if you don't want to have a global new and delete or if they exist but you don't want to use them you can add "operator new" and "operator delete" as member functions to each of your classes and they will be called instead of the global ones.
Actually, we can live without global object constructors being invoked.
I would recommend studying GitHub - jxy-s/stlkrn: C++ STL in the Windows Kernel with C++ Exception Support. We can use most of the std like vector, smart pointers so on, with few exceptions, I had to implement my self the std::function and some kind of shared pointer. The library also adds scoped exit objects used to replace ugly cleanup and much more...
Also exceptions are supported, though me personally I made a fork with no exceptions support.