1/* 2 * Copyright (c) 2000,2008-2009 Apple Inc. All rights reserved. 3 * 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. The rights granted to you under the License 10 * may not be used to create, or enable the creation or redistribution of, 11 * unlawful or unlicensed copies of an Apple operating system, or to 12 * circumvent, violate, or enable the circumvention or violation of, any 13 * terms of an Apple operating system software license agreement. 14 * 15 * Please obtain a copy of the License at 16 * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 * 18 * The Original Code and all software distributed under the License are 19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23 * Please see the License for the specific language governing rights and 24 * limitations under the License. 25 * 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 */ 28/* 29 * Copyright (c) 1997 Apple Inc. 30 * 31 */ 32#include <libkern/c++/OSMetaClass.h> 33#include <libkern/c++/OSKext.h> 34#include <libkern/c++/OSLib.h> 35#include <libkern/c++/OSSymbol.h> 36 37#include <sys/cdefs.h> 38 39__BEGIN_DECLS 40 41#include <string.h> 42#include <mach/mach_types.h> 43#include <libkern/kernel_mach_header.h> 44#include <stdarg.h> 45 46#if PRAGMA_MARK 47#pragma mark Constants &c. 48#endif /* PRAGMA_MARK */ 49OSKextLogSpec kOSRuntimeLogSpec = 50 kOSKextLogErrorLevel | 51 kOSKextLogLoadFlag | 52 kOSKextLogKextBookkeepingFlag; 53 54#if PRAGMA_MARK 55#pragma mark Logging Bootstrap 56#endif /* PRAGMA_MARK */ 57/********************************************************************* 58* kern_os Logging Bootstrap 59* 60* We can't call in to OSKext until the kernel's C++ environment is up 61* and running, so let's mask those references with a check variable. 62* We print unconditionally if C++ isn't up, but if that's the case 63* we've generally hit a serious error in kernel init! 64*********************************************************************/ 65static bool gKernelCPPInitialized = false; 66 67#define OSRuntimeLog(kext, flags, format, args...) \ 68 do { \ 69 if (gKernelCPPInitialized) { \ 70 OSKextLog((kext), (flags), (format), ## args); \ 71 } else { \ 72 printf((format), ## args); \ 73 } \ 74 } while (0) 75 76 77#if PRAGMA_MARK 78#pragma mark kern_os Allocator Package 79#endif /* PRAGMA_MARK */ 80/********************************************************************* 81* kern_os Allocator Package 82*********************************************************************/ 83 84/********************************************************************* 85*********************************************************************/ 86#if OSALLOCDEBUG 87extern int debug_iomalloc_size; 88#endif 89 90struct _mhead { 91 size_t mlen; 92 char dat[0]; 93}; 94 95/********************************************************************* 96*********************************************************************/ 97void * 98kern_os_malloc(size_t size) 99{ 100 struct _mhead * mem; 101 size_t memsize = sizeof (*mem) + size ; 102 103 if (size == 0) { 104 return (0); 105 } 106 107 mem = (struct _mhead *)kalloc(memsize); 108 if (!mem) { 109 return (0); 110 } 111 112#if OSALLOCDEBUG 113 debug_iomalloc_size += memsize; 114#endif 115 116 mem->mlen = memsize; 117 bzero(mem->dat, size); 118 119 return mem->dat; 120} 121 122/********************************************************************* 123*********************************************************************/ 124void 125kern_os_free(void * addr) 126{ 127 struct _mhead * hdr; 128 129 if (!addr) { 130 return; 131 } 132 133 hdr = (struct _mhead *)addr; 134 hdr--; 135 136#if OSALLOCDEBUG 137 debug_iomalloc_size -= hdr->mlen; 138#endif 139 140#if 0 141 memset((vm_offset_t)hdr, 0xbb, hdr->mlen); 142#else 143 kfree(hdr, hdr->mlen); 144#endif 145} 146 147/********************************************************************* 148*********************************************************************/ 149void * 150kern_os_realloc( 151 void * addr, 152 size_t nsize) 153{ 154 struct _mhead * ohdr; 155 struct _mhead * nmem; 156 size_t nmemsize, osize; 157 158 if (!addr) { 159 return (kern_os_malloc(nsize)); 160 } 161 162 ohdr = (struct _mhead *)addr; 163 ohdr--; 164 osize = ohdr->mlen - sizeof(*ohdr); 165 if (nsize == osize) { 166 return (addr); 167 } 168 169 if (nsize == 0) { 170 kern_os_free(addr); 171 return (0); 172 } 173 174 nmemsize = sizeof (*nmem) + nsize ; 175 nmem = (struct _mhead *) kalloc(nmemsize); 176 if (!nmem){ 177 kern_os_free(addr); 178 return (0); 179 } 180 181#if OSALLOCDEBUG 182 debug_iomalloc_size += (nmemsize - ohdr->mlen); 183#endif 184 185 nmem->mlen = nmemsize; 186 if (nsize > osize) { 187 (void) memset(&nmem->dat[osize], 0, nsize - osize); 188 } 189 (void)memcpy(nmem->dat, ohdr->dat, (nsize > osize) ? osize : nsize); 190 kfree(ohdr, ohdr->mlen); 191 192 return (nmem->dat); 193} 194 195/********************************************************************* 196*********************************************************************/ 197size_t 198kern_os_malloc_size(void * addr) 199{ 200 struct _mhead * hdr; 201 202 if (!addr) { 203 return(0); 204 } 205 206 hdr = (struct _mhead *) addr; hdr--; 207 return hdr->mlen - sizeof (struct _mhead); 208} 209 210#if PRAGMA_MARK 211#pragma mark C++ Runtime Load/Unload 212#endif /* PRAGMA_MARK */ 213/********************************************************************* 214* kern_os C++ Runtime Load/Unload 215*********************************************************************/ 216 217/********************************************************************* 218*********************************************************************/ 219#if __GNUC__ >= 3 220void __cxa_pure_virtual( void ) { panic("%s", __FUNCTION__); } 221#else 222void __pure_virtual( void ) { panic("%s", __FUNCTION__); } 223#endif 224 225typedef void (*structor_t)(void); 226 227/********************************************************************* 228*********************************************************************/ 229static boolean_t 230sectionIsDestructor(kernel_section_t * section) 231{ 232 boolean_t result; 233 234 result = !strncmp(section->sectname, SECT_MODTERMFUNC, 235 sizeof(SECT_MODTERMFUNC) - 1); 236#if !__LP64__ 237 result = result || !strncmp(section->sectname, SECT_DESTRUCTOR, 238 sizeof(SECT_DESTRUCTOR) - 1); 239#endif 240 241 return result; 242} 243 244/********************************************************************* 245*********************************************************************/ 246static boolean_t 247sectionIsConstructor(kernel_section_t * section) 248{ 249 boolean_t result; 250 251 result = !strncmp(section->sectname, SECT_MODINITFUNC, 252 sizeof(SECT_MODINITFUNC) - 1); 253#if !__LP64__ 254 result = result || !strncmp(section->sectname, SECT_CONSTRUCTOR, 255 sizeof(SECT_CONSTRUCTOR) - 1); 256#endif 257 258 return result; 259} 260 261 262/********************************************************************* 263* OSRuntimeUnloadCPPForSegment() 264* 265* Given a pointer to a mach object segment, iterate the segment to 266* obtain a destructor section for C++ objects, and call each of the 267* destructors there. 268*********************************************************************/ 269 270void 271OSRuntimeUnloadCPPForSegmentInKmod( 272 kernel_segment_command_t * segment, 273 kmod_info_t * kmodInfo) 274{ 275 276 kernel_section_t * section = NULL; // do not free 277 OSKext * theKext = NULL; // must release 278 279 if (gKernelCPPInitialized && kmodInfo) { 280 theKext = OSKext::lookupKextWithIdentifier(kmodInfo->name); 281 } 282 283 for (section = firstsect(segment); 284 section != 0; 285 section = nextsect(segment, section)) { 286 287 if (sectionIsDestructor(section)) { 288 structor_t * destructors = (structor_t *)section->addr; 289 290 if (destructors) { 291 int num_destructors = section->size / sizeof(structor_t); 292 int hit_null_destructor = 0; 293 294 for (int i = 0; i < num_destructors; i++) { 295 if (destructors[i]) { 296 (*destructors[i])(); 297 } else if (!hit_null_destructor) { 298 hit_null_destructor = 1; 299 OSRuntimeLog(theKext, kOSRuntimeLogSpec, 300 "Null destructor in kext %s segment %s!", 301 kmodInfo ? kmodInfo->name : "(unknown)", 302 section->segname); 303 } 304 } 305 } /* if (destructors) */ 306 } /* if (strncmp...) */ 307 } /* for (section...) */ 308 309 OSSafeRelease(theKext); 310 return; 311} 312 313void 314OSRuntimeUnloadCPPForSegment(kernel_segment_command_t * segment) { 315 OSRuntimeUnloadCPPForSegmentInKmod(segment, NULL); 316} 317 318/********************************************************************* 319*********************************************************************/ 320void 321OSRuntimeUnloadCPP( 322 kmod_info_t * kmodInfo, 323 void * data __unused) 324{ 325 if (kmodInfo && kmodInfo->address) { 326 327 kernel_segment_command_t * segment; 328 kernel_mach_header_t * header; 329 330 OSSymbol::checkForPageUnload((void *)kmodInfo->address, 331 (void *)(kmodInfo->address + kmodInfo->size)); 332 333 header = (kernel_mach_header_t *)kmodInfo->address; 334 segment = firstsegfromheader(header); 335 336 for (segment = firstsegfromheader(header); 337 segment != 0; 338 segment = nextsegfromheader(header, segment)) { 339 340 OSRuntimeUnloadCPPForSegmentInKmod(segment, kmodInfo); 341 } 342 } 343 344 return; 345} 346 347/********************************************************************* 348*********************************************************************/ 349kern_return_t 350OSRuntimeFinalizeCPP( 351 kmod_info_t * kmodInfo, 352 void * data __unused) 353{ 354 kern_return_t result = KMOD_RETURN_FAILURE; 355 void * metaHandle = NULL; // do not free 356 OSKext * theKext = NULL; // must release 357 358 if (gKernelCPPInitialized) { 359 theKext = OSKext::lookupKextWithIdentifier(kmodInfo->name); 360 } 361 362 if (theKext && !theKext->isCPPInitialized()) { 363 result = KMOD_RETURN_SUCCESS; 364 goto finish; 365 } 366 367 /* OSKext checks for this condition now, but somebody might call 368 * this function directly (the symbol is exported....). 369 */ 370 if (OSMetaClass::modHasInstance(kmodInfo->name)) { 371 // xxx - Don't log under errors? this is more of an info thing 372 OSRuntimeLog(theKext, kOSRuntimeLogSpec, 373 "Can't tear down kext %s C++; classes have instances:", 374 kmodInfo->name); 375 OSKext::reportOSMetaClassInstances(kmodInfo->name, kOSRuntimeLogSpec); 376 result = kOSMetaClassHasInstances; 377 goto finish; 378 } 379 380 /* Tell the meta class system that we are starting to unload. 381 * metaHandle isn't actually needed on the finalize path, 382 * so we don't check it here, even though OSMetaClass::postModLoad() will 383 * return a failure (it only does actual work on the init path anyhow). 384 */ 385 metaHandle = OSMetaClass::preModLoad(kmodInfo->name); 386 OSRuntimeUnloadCPP(kmodInfo, 0); 387 (void)OSMetaClass::postModLoad(metaHandle); 388 389 if (theKext) { 390 theKext->setCPPInitialized(false); 391 } 392 result = KMOD_RETURN_SUCCESS; 393finish: 394 OSSafeRelease(theKext); 395 return result; 396} 397 398// Functions used by the extenTools/kmod library project 399 400/********************************************************************* 401*********************************************************************/ 402kern_return_t 403OSRuntimeInitializeCPP( 404 kmod_info_t * kmodInfo, 405 void * data __unused) 406{ 407 kern_return_t result = KMOD_RETURN_FAILURE; 408 OSKext * theKext = NULL; // must release 409 kernel_mach_header_t * header = NULL; 410 void * metaHandle = NULL; // do not free 411 bool load_success = true; 412 kernel_segment_command_t * segment = NULL; // do not free 413 kernel_segment_command_t * failure_segment = NULL; // do not free 414 415 if (!kmodInfo || !kmodInfo->address || !kmodInfo->name) { 416 result = kOSKextReturnInvalidArgument; 417 goto finish; 418 } 419 420 if (gKernelCPPInitialized) { 421 theKext = OSKext::lookupKextWithIdentifier(kmodInfo->name); 422 } 423 424 if (theKext && theKext->isCPPInitialized()) { 425 result = KMOD_RETURN_SUCCESS; 426 goto finish; 427 } 428 429 header = (kernel_mach_header_t *)kmodInfo->address; 430 431 /* Tell the meta class system that we are starting the load 432 */ 433 metaHandle = OSMetaClass::preModLoad(kmodInfo->name); 434 assert(metaHandle); 435 if (!metaHandle) { 436 goto finish; 437 } 438 439 /* NO GOTO PAST HERE. */ 440 441 /* Scan the header for all constructor sections, in any 442 * segment, and invoke the constructors within those sections. 443 */ 444 for (segment = firstsegfromheader(header); 445 segment != NULL && load_success; 446 segment = nextsegfromheader(header, segment)) { 447 448 kernel_section_t * section; 449 450 /* Record the current segment in the event of a failure. 451 */ 452 failure_segment = segment; 453 454 for (section = firstsect(segment); 455 section != NULL; 456 section = nextsect(segment, section)) { 457 458 if (sectionIsConstructor(section)) { 459 structor_t * constructors = (structor_t *)section->addr; 460 461 if (constructors) { 462 int num_constructors = section->size / sizeof(structor_t); 463 int hit_null_constructor = 0; 464 465 for (int i = 0; 466 i < num_constructors && 467 OSMetaClass::checkModLoad(metaHandle); 468 i++) { 469 470 if (constructors[i]) { 471 (*constructors[i])(); 472 } else if (!hit_null_constructor) { 473 hit_null_constructor = 1; 474 OSRuntimeLog(theKext, kOSRuntimeLogSpec, 475 "Null constructor in kext %s segment %s!", 476 kmodInfo->name, section->segname); 477 } 478 } 479 load_success = OSMetaClass::checkModLoad(metaHandle); 480 481 break; 482 } /* if (constructors) */ 483 } /* if (strncmp...) */ 484 } /* for (section...) */ 485 } /* for (segment...) */ 486 487 /* We failed so call all of the destructors. We must do this before 488 * calling OSMetaClass::postModLoad() as the OSMetaClass destructors 489 * will alter state (in the metaHandle) used by that function. 490 */ 491 if (!load_success) { 492 493 /* Scan the header for all destructor sections, in any 494 * segment, and invoke the constructors within those sections. 495 */ 496 for (segment = firstsegfromheader(header); 497 segment != failure_segment && segment != 0; 498 segment = nextsegfromheader(header, segment)) { 499 500 OSRuntimeUnloadCPPForSegment(segment); 501 502 } /* for (segment...) */ 503 } 504 505 /* Now, regardless of success so far, do the post-init registration 506 * and cleanup. If we had to call the unloadCPP function, static 507 * destructors have removed classes from the stalled list so no 508 * metaclasses will actually be registered. 509 */ 510 result = OSMetaClass::postModLoad(metaHandle); 511 512 /* If we've otherwise been fine up to now, but OSMetaClass::postModLoad() 513 * fails (typically due to a duplicate class), tear down all the C++ 514 * stuff from the kext. This isn't necessary for libkern/OSMetaClass stuff, 515 * but may be necessary for other C++ code. We ignore the return value 516 * because it's only a fail when there are existing instances of libkern 517 * classes, and there had better not be any created on the C++ init path. 518 */ 519 if (load_success && result != KMOD_RETURN_SUCCESS) { 520 (void)OSRuntimeFinalizeCPP(kmodInfo, NULL); 521 } 522 523 if (theKext && load_success && result == KMOD_RETURN_SUCCESS) { 524 theKext->setCPPInitialized(true); 525 } 526finish: 527 OSSafeRelease(theKext); 528 return result; 529} 530 531#if PRAGMA_MARK 532#pragma mark Libkern Init 533#endif /* PRAGMA_MARK */ 534/********************************************************************* 535* Libkern Init 536*********************************************************************/ 537 538/********************************************************************* 539*********************************************************************/ 540extern lck_spin_t gOSObjectTrackLock; 541extern lck_grp_t * IOLockGroup; 542extern kmod_info_t g_kernel_kmod_info; 543 544void OSlibkernInit(void) 545{ 546 lck_spin_init(&gOSObjectTrackLock, IOLockGroup, LCK_ATTR_NULL); 547 548 // This must be called before calling OSRuntimeInitializeCPP. 549 OSMetaClassBase::initialize(); 550 551 g_kernel_kmod_info.address = (vm_address_t) &_mh_execute_header; 552 if (kOSReturnSuccess != OSRuntimeInitializeCPP(&g_kernel_kmod_info, 0)) { 553 panic("OSRuntime: C++ runtime failed to initialize."); 554 } 555 556 gKernelCPPInitialized = true; 557 558 return; 559} 560 561__END_DECLS 562 563#if PRAGMA_MARK 564#pragma mark C++ Allocators & Deallocators 565#endif /* PRAGMA_MARK */ 566/********************************************************************* 567* C++ Allocators & Deallocators 568*********************************************************************/ 569void * 570operator new(size_t size) 571{ 572 void * result; 573 574 result = (void *) kern_os_malloc(size); 575 return result; 576} 577 578void 579operator delete(void * addr) 580{ 581 kern_os_free(addr); 582 return; 583} 584 585void * 586operator new[](unsigned long sz) 587{ 588 if (sz == 0) sz = 1; 589 return kern_os_malloc(sz); 590} 591 592void 593operator delete[](void * ptr) 594{ 595 if (ptr) { 596 kern_os_free(ptr); 597 } 598 return; 599} 600 601/* PR-6481964 - The compiler is going to check for size overflows in calls to 602 * new[], and if there is an overflow, it will call __throw_length_error. 603 * This is an unrecoverable error by the C++ standard, so we must panic here. 604 * 605 * We have to put the function inside the std namespace because of how the 606 * compiler expects the name to be mangled. 607 */ 608namespace std { 609 610void 611__throw_length_error(const char *msg __unused) 612{ 613 panic("Size of array created by new[] has overflowed"); 614} 615 616}; 617 618