#include <libkern/c++/OSMetaClass.h>
#include <libkern/c++/OSKext.h>
#include <libkern/c++/OSLib.h>
#include <libkern/c++/OSSymbol.h>
#include <sys/cdefs.h>
__BEGIN_DECLS
#include <string.h>
#include <mach/mach_types.h>
#include <libkern/kernel_mach_header.h>
#include <stdarg.h>
#if PRAGMA_MARK
#pragma mark Constants &c.
#endif
OSKextLogSpec kOSRuntimeLogSpec =
kOSKextLogErrorLevel |
kOSKextLogLoadFlag |
kOSKextLogKextBookkeepingFlag;
#if PRAGMA_MARK
#pragma mark Logging Bootstrap
#endif
static bool gKernelCPPInitialized = false;
#define OSRuntimeLog(kext, flags, format, args...) \
do { \
if (gKernelCPPInitialized) { \
OSKextLog((kext), (flags), (format), ## args); \
} else { \
printf((format), ## args); \
} \
} while (0)
#if PRAGMA_MARK
#pragma mark kern_os Allocator Package
#endif
#if OSALLOCDEBUG
extern int debug_iomalloc_size;
#endif
struct _mhead {
size_t mlen;
char dat[0];
};
void *
kern_os_malloc(size_t size)
{
struct _mhead * mem;
size_t memsize = sizeof (*mem) + size ;
if (size == 0) {
return (0);
}
mem = (struct _mhead *)kalloc(memsize);
if (!mem) {
return (0);
}
#if OSALLOCDEBUG
debug_iomalloc_size += memsize;
#endif
mem->mlen = memsize;
bzero(mem->dat, size);
return mem->dat;
}
void
kern_os_free(void * addr)
{
struct _mhead * hdr;
if (!addr) {
return;
}
hdr = (struct _mhead *)addr;
hdr--;
#if OSALLOCDEBUG
debug_iomalloc_size -= hdr->mlen;
#endif
#if 0
memset((vm_offset_t)hdr, 0xbb, hdr->mlen);
#else
kfree(hdr, hdr->mlen);
#endif
}
void *
kern_os_realloc(
void * addr,
size_t nsize)
{
struct _mhead * ohdr;
struct _mhead * nmem;
size_t nmemsize, osize;
if (!addr) {
return (kern_os_malloc(nsize));
}
ohdr = (struct _mhead *)addr;
ohdr--;
osize = ohdr->mlen - sizeof(*ohdr);
if (nsize == osize) {
return (addr);
}
if (nsize == 0) {
kern_os_free(addr);
return (0);
}
nmemsize = sizeof (*nmem) + nsize ;
nmem = (struct _mhead *) kalloc(nmemsize);
if (!nmem){
kern_os_free(addr);
return (0);
}
#if OSALLOCDEBUG
debug_iomalloc_size += (nmemsize - ohdr->mlen);
#endif
nmem->mlen = nmemsize;
if (nsize > osize) {
(void) memset(&nmem->dat[osize], 0, nsize - osize);
}
(void)memcpy(nmem->dat, ohdr->dat, (nsize > osize) ? osize : nsize);
kfree(ohdr, ohdr->mlen);
return (nmem->dat);
}
size_t
kern_os_malloc_size(void * addr)
{
struct _mhead * hdr;
if (!addr) {
return(0);
}
hdr = (struct _mhead *) addr; hdr--;
return hdr->mlen - sizeof (struct _mhead);
}
#if PRAGMA_MARK
#pragma mark C++ Runtime Load/Unload
#endif
#if __GNUC__ >= 3
void __cxa_pure_virtual( void ) { panic("%s", __FUNCTION__); }
#else
void __pure_virtual( void ) { panic("%s", __FUNCTION__); }
#endif
typedef void (*structor_t)(void);
static boolean_t
sectionIsDestructor(kernel_section_t * section)
{
boolean_t result;
result = !strncmp(section->sectname, SECT_MODTERMFUNC,
sizeof(SECT_MODTERMFUNC) - 1);
#if !__LP64__
result = result || !strncmp(section->sectname, SECT_DESTRUCTOR,
sizeof(SECT_DESTRUCTOR) - 1);
#endif
return result;
}
static boolean_t
sectionIsConstructor(kernel_section_t * section)
{
boolean_t result;
result = !strncmp(section->sectname, SECT_MODINITFUNC,
sizeof(SECT_MODINITFUNC) - 1);
#if !__LP64__
result = result || !strncmp(section->sectname, SECT_CONSTRUCTOR,
sizeof(SECT_CONSTRUCTOR) - 1);
#endif
return result;
}
void
OSRuntimeUnloadCPPForSegmentInKmod(
kernel_segment_command_t * segment,
kmod_info_t * kmodInfo)
{
kernel_section_t * section = NULL; OSKext * theKext = NULL;
if (gKernelCPPInitialized && kmodInfo) {
theKext = OSKext::lookupKextWithIdentifier(kmodInfo->name);
}
for (section = firstsect(segment);
section != 0;
section = nextsect(segment, section)) {
if (sectionIsDestructor(section)) {
structor_t * destructors = (structor_t *)section->addr;
if (destructors) {
int num_destructors = section->size / sizeof(structor_t);
int hit_null_destructor = 0;
for (int i = 0; i < num_destructors; i++) {
if (destructors[i]) {
(*destructors[i])();
} else if (!hit_null_destructor) {
hit_null_destructor = 1;
OSRuntimeLog(theKext, kOSRuntimeLogSpec,
"Null destructor in kext %s segment %s!",
kmodInfo ? kmodInfo->name : "(unknown)",
section->segname);
}
}
}
}
}
OSSafeRelease(theKext);
return;
}
void
OSRuntimeUnloadCPPForSegment(kernel_segment_command_t * segment) {
OSRuntimeUnloadCPPForSegmentInKmod(segment, NULL);
}
void
OSRuntimeUnloadCPP(
kmod_info_t * kmodInfo,
void * data __unused)
{
if (kmodInfo && kmodInfo->address) {
kernel_segment_command_t * segment;
kernel_mach_header_t * header;
OSSymbol::checkForPageUnload((void *)kmodInfo->address,
(void *)(kmodInfo->address + kmodInfo->size));
header = (kernel_mach_header_t *)kmodInfo->address;
segment = firstsegfromheader(header);
for (segment = firstsegfromheader(header);
segment != 0;
segment = nextsegfromheader(header, segment)) {
OSRuntimeUnloadCPPForSegmentInKmod(segment, kmodInfo);
}
}
return;
}
kern_return_t
OSRuntimeFinalizeCPP(
kmod_info_t * kmodInfo,
void * data __unused)
{
kern_return_t result = KMOD_RETURN_FAILURE;
void * metaHandle = NULL; OSKext * theKext = NULL;
if (gKernelCPPInitialized) {
theKext = OSKext::lookupKextWithIdentifier(kmodInfo->name);
}
if (theKext && !theKext->isCPPInitialized()) {
result = KMOD_RETURN_SUCCESS;
goto finish;
}
if (OSMetaClass::modHasInstance(kmodInfo->name)) {
OSRuntimeLog(theKext, kOSRuntimeLogSpec,
"Can't tear down kext %s C++; classes have instances:",
kmodInfo->name);
OSKext::reportOSMetaClassInstances(kmodInfo->name, kOSRuntimeLogSpec);
result = kOSMetaClassHasInstances;
goto finish;
}
metaHandle = OSMetaClass::preModLoad(kmodInfo->name);
OSRuntimeUnloadCPP(kmodInfo, 0);
(void)OSMetaClass::postModLoad(metaHandle);
if (theKext) {
theKext->setCPPInitialized(false);
}
result = KMOD_RETURN_SUCCESS;
finish:
OSSafeRelease(theKext);
return result;
}
kern_return_t
OSRuntimeInitializeCPP(
kmod_info_t * kmodInfo,
void * data __unused)
{
kern_return_t result = KMOD_RETURN_FAILURE;
OSKext * theKext = NULL; kernel_mach_header_t * header = NULL;
void * metaHandle = NULL; bool load_success = true;
kernel_segment_command_t * segment = NULL; kernel_segment_command_t * failure_segment = NULL;
if (!kmodInfo || !kmodInfo->address || !kmodInfo->name) {
result = kOSKextReturnInvalidArgument;
goto finish;
}
if (gKernelCPPInitialized) {
theKext = OSKext::lookupKextWithIdentifier(kmodInfo->name);
}
if (theKext && theKext->isCPPInitialized()) {
result = KMOD_RETURN_SUCCESS;
goto finish;
}
header = (kernel_mach_header_t *)kmodInfo->address;
metaHandle = OSMetaClass::preModLoad(kmodInfo->name);
assert(metaHandle);
if (!metaHandle) {
goto finish;
}
for (segment = firstsegfromheader(header);
segment != NULL && load_success;
segment = nextsegfromheader(header, segment)) {
kernel_section_t * section;
failure_segment = segment;
for (section = firstsect(segment);
section != NULL;
section = nextsect(segment, section)) {
if (sectionIsConstructor(section)) {
structor_t * constructors = (structor_t *)section->addr;
if (constructors) {
int num_constructors = section->size / sizeof(structor_t);
int hit_null_constructor = 0;
for (int i = 0;
i < num_constructors &&
OSMetaClass::checkModLoad(metaHandle);
i++) {
if (constructors[i]) {
(*constructors[i])();
} else if (!hit_null_constructor) {
hit_null_constructor = 1;
OSRuntimeLog(theKext, kOSRuntimeLogSpec,
"Null constructor in kext %s segment %s!",
kmodInfo->name, section->segname);
}
}
load_success = OSMetaClass::checkModLoad(metaHandle);
break;
}
}
}
}
if (!load_success) {
for (segment = firstsegfromheader(header);
segment != failure_segment && segment != 0;
segment = nextsegfromheader(header, segment)) {
OSRuntimeUnloadCPPForSegment(segment);
}
}
result = OSMetaClass::postModLoad(metaHandle);
if (load_success && result != KMOD_RETURN_SUCCESS) {
(void)OSRuntimeFinalizeCPP(kmodInfo, NULL);
}
if (theKext && load_success && result == KMOD_RETURN_SUCCESS) {
theKext->setCPPInitialized(true);
}
finish:
OSSafeRelease(theKext);
return result;
}
#if PRAGMA_MARK
#pragma mark Libkern Init
#endif
extern lck_spin_t gOSObjectTrackLock;
extern lck_grp_t * IOLockGroup;
extern kmod_info_t g_kernel_kmod_info;
void OSlibkernInit(void)
{
lck_spin_init(&gOSObjectTrackLock, IOLockGroup, LCK_ATTR_NULL);
OSMetaClassBase::initialize();
g_kernel_kmod_info.address = (vm_address_t) &_mh_execute_header;
if (kOSReturnSuccess != OSRuntimeInitializeCPP(&g_kernel_kmod_info, 0)) {
panic("OSRuntime: C++ runtime failed to initialize.");
}
gKernelCPPInitialized = true;
return;
}
__END_DECLS
#if PRAGMA_MARK
#pragma mark C++ Allocators & Deallocators
#endif
void *
operator new(size_t size)
{
void * result;
result = (void *) kern_os_malloc(size);
return result;
}
void
operator delete(void * addr)
{
kern_os_free(addr);
return;
}
void *
operator new[](unsigned long sz)
{
if (sz == 0) sz = 1;
return kern_os_malloc(sz);
}
void
operator delete[](void * ptr)
{
if (ptr) {
kern_os_free(ptr);
}
return;
}
namespace std {
void
__throw_length_error(const char *msg __unused)
{
panic("Size of array created by new[] has overflowed");
}
};