1 /* 2 * Copyright (c) 2000-2016 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 /* IOSymbol.cpp created by gvdl on Fri 1998-11-17 */ 29 30 #define IOKIT_ENABLE_SHARED_PTR 31 32 #include <string.h> 33 #include <sys/cdefs.h> 34 35 #include <kern/bits.h> 36 #include <kern/locks.h> 37 #include <kern/smr_hash.h> 38 #include <kern/thread_call.h> 39 40 #if defined(__arm64__) 41 #include <arm64/amcc_rorgn.h> /* rorgn_contains */ 42 #endif 43 #include <libkern/c++/OSSymbol.h> 44 #include <libkern/c++/OSSharedPtr.h> 45 #include <libkern/c++/OSLib.h> 46 #include <os/cpp_util.h> 47 #include <os/hash.h> 48 #include <string.h> 49 50 static ZONE_DEFINE(OSSymbol_zone, "iokit.OSSymbol", sizeof(OSSymbol), ZC_NONE); 51 static SMR_DEFINE(OSSymbol_smr); 52 static LCK_GRP_DECLARE(lock_group, "OSSymbolPool"); 53 54 #pragma clang diagnostic push 55 #pragma clang diagnostic ignored "-Winvalid-offsetof" 56 57 /* 58 * This implements a relativistic hash table, using <kern/smr.h> as underlying 59 * safe memory reclamation scheme. 60 * 61 * (https://www.usenix.org/legacy/event/atc11/tech/final_files/Triplett.pdf) 62 * 63 * One twist is that the OSSymbol_smr_free() callback must be 64 * preemption-disabled safe, which means the `kfree_data()` it calls _MUST_ be 65 * smaller than KALLOC_SAFE_ALLOC_SIZE. To deal with that, if a Symbol is made 66 * with a string that is much larger (should be rare), these go on a lock-based 67 * "huge" queue. 68 */ 69 class OSSymbolPool 70 { 71 /* empirically most devices have at least 10+k symbols */ 72 static constexpr uint32_t MIN_SIZE = 4096; 73 74 static inline smrh_key_t 75 OSSymbol_get_key(const OSSymbol *sym) 76 { 77 return { 78 .smrk_string = sym->string, 79 .smrk_len = (size_t)(sym->length - 1) 80 }; 81 } 82 83 static uint32_t 84 OSSymbol_obj_hash(const struct smrq_slink *link, uint32_t seed) 85 { 86 OSSymbol *sym = __container_of(link, OSSymbol, hashlink); 87 88 return smrh_key_hash_str(OSSymbol_get_key(sym), seed); 89 } 90 91 static bool 92 OSSymbol_obj_equ(const struct smrq_slink *link, smrh_key_t key) 93 { 94 OSSymbol *sym = __container_of(link, OSSymbol, hashlink); 95 96 return smrh_key_equ_str(OSSymbol_get_key(sym), key); 97 } 98 99 static bool 100 OSSymbol_obj_try_get(void *obj) 101 { 102 OSSymbol *sym = (OSSymbol *)obj; 103 104 return (sym->flags & kOSSSymbolPermanent) || 105 sym->taggedTryRetain(nullptr); 106 } 107 108 SMRH_TRAITS_DEFINE_STR(hash_traits, OSSymbol, hashlink, 109 .domain = &OSSymbol_smr, 110 .obj_hash = OSSymbol_obj_hash, 111 .obj_equ = OSSymbol_obj_equ, 112 .obj_try_get = OSSymbol_obj_try_get, 113 ); 114 115 mutable lck_mtx_t _mutex; 116 struct smr_hash _hash; 117 smrq_slist_head _huge_head; 118 thread_call_t _tcall; 119 uint32_t _hugeCount = 0; 120 bool _tcallScheduled; 121 122 private: 123 124 inline void 125 lock() const 126 { 127 lck_mtx_lock(&_mutex); 128 } 129 130 inline void 131 unlock() const 132 { 133 lck_mtx_unlock(&_mutex); 134 } 135 136 inline bool 137 shouldShrink() const 138 { 139 /* shrink if there are more than 2 buckets per 1 symbol */ 140 return smr_hash_serialized_should_shrink(&_hash, MIN_SIZE, 2, 1); 141 } 142 143 inline bool 144 shouldGrow() const 145 { 146 /* shrink if there less more than 1 bucket per 4 symbol */ 147 return smr_hash_serialized_should_grow(&_hash, 1, 4); 148 } 149 150 public: 151 152 static void rehash(thread_call_param_t, thread_call_param_t); 153 inline static OSSymbolPool &instance() __pure2; 154 155 OSSymbolPool() 156 { 157 lck_mtx_init(&_mutex, &lock_group, LCK_ATTR_NULL); 158 159 smr_hash_init(&_hash, MIN_SIZE); 160 smrq_init(&_huge_head); 161 162 _tcall = thread_call_allocate_with_options(rehash, this, 163 THREAD_CALL_PRIORITY_KERNEL, THREAD_CALL_OPTIONS_ONCE); 164 } 165 OSSymbolPool(const OSSymbolPool &) = delete; 166 OSSymbolPool(OSSymbolPool &&) = delete; 167 OSSymbolPool &operator=(const OSSymbolPool &) = delete; 168 OSSymbolPool &operator=(OSSymbolPool &&) = delete; 169 170 ~OSSymbolPool() = delete; 171 172 OSSharedPtr<const OSSymbol> findSymbol(smrh_key_t key) const; 173 174 void insertSymbol( 175 OSSharedPtr<OSSymbol> &sym, 176 smrh_key_t key, 177 bool makePermanent = false); 178 179 void removeSymbol(OSSymbol *sym); 180 181 void rehash(); 182 183 void checkForPageUnload(void *startAddr, void *endAddr); 184 }; 185 186 static _Alignas(OSSymbolPool) uint8_t OSSymbolPoolStorage[sizeof(OSSymbolPool)]; 187 188 OSSymbolPool & 189 OSSymbolPool::instance() 190 { 191 return reinterpret_cast<OSSymbolPool &>(OSSymbolPoolStorage); 192 } 193 194 static inline bool 195 OSSymbol_is_huge(size_t size) 196 { 197 return size > KALLOC_SAFE_ALLOC_SIZE; 198 } 199 200 OSSharedPtr<const OSSymbol> 201 OSSymbolPool::findSymbol(smrh_key_t key) const 202 { 203 OSSymbol *sym; 204 OSSharedPtr<const OSSymbol> ret; 205 206 if (!OSSymbol_is_huge(key.smrk_len)) { 207 char tmp_buf[128]; /* empirically all keys are < 110 bytes */ 208 char *copy_s = NULL; 209 210 /* 211 * rdar://105075708: the key might be in pageable memory, 212 * and smr_hash_get() disable preemption which prevents 213 * faulting the memory. 214 */ 215 if (key.smrk_len <= sizeof(tmp_buf)) { 216 memcpy(tmp_buf, key.smrk_opaque, key.smrk_len); 217 key.smrk_string = tmp_buf; 218 } else { 219 copy_s = (char *)kalloc_data(key.smrk_len, 220 Z_WAITOK_ZERO_NOFAIL); 221 memcpy(copy_s, key.smrk_opaque, key.smrk_len); 222 key.smrk_string = copy_s; 223 } 224 sym = smr_hash_get(&_hash, key, &hash_traits); 225 if (copy_s) { 226 kfree_data(copy_s, key.smrk_len); 227 } 228 } else { 229 lock(); 230 sym = (OSSymbol *)__smr_hash_serialized_find(&_huge_head, key, 231 &hash_traits.smrht); 232 if (sym && !OSSymbol_obj_try_get(sym)) { 233 sym = NULL; 234 } 235 unlock(); 236 } 237 238 if (sym) { 239 ret.reset(sym, OSNoRetain); 240 } 241 242 return ret; 243 } 244 245 void 246 OSSymbolPool::insertSymbol( 247 OSSharedPtr<OSSymbol> &symToInsert, 248 smrh_key_t key, 249 bool make_permanent) 250 { 251 OSSymbol *sym; 252 253 /* make sure no one ever subclassed OSSymbols */ 254 zone_require(OSSymbol_zone, symToInsert.get()); 255 256 symToInsert->flags |= kOSSSymbolHashed; 257 if (make_permanent) { 258 symToInsert->flags |= kOSSSymbolPermanent; 259 } 260 261 lock(); 262 263 if (!OSSymbol_is_huge(key.smrk_len)) { 264 sym = smr_hash_serialized_get_or_insert(&_hash, key, 265 &symToInsert->hashlink, &hash_traits); 266 267 if (shouldGrow() && !_tcallScheduled && 268 startup_phase >= STARTUP_SUB_THREAD_CALL) { 269 _tcallScheduled = true; 270 thread_call_enter(_tcall); 271 } 272 } else { 273 sym = (OSSymbol *)__smr_hash_serialized_find(&_huge_head, key, 274 &hash_traits.smrht); 275 if (!sym || !OSSymbol_obj_try_get(sym)) { 276 smrq_serialized_insert_head(&_huge_head, 277 &symToInsert->hashlink); 278 _hugeCount++; 279 sym = NULL; 280 } 281 } 282 283 unlock(); 284 285 if (sym) { 286 symToInsert->flags &= ~(kOSSSymbolHashed | kOSSSymbolPermanent); 287 symToInsert.reset(sym, OSNoRetain); 288 } 289 } 290 291 void 292 OSSymbolPool::removeSymbol(OSSymbol *sym) 293 { 294 lock(); 295 296 assert(sym->flags & kOSSSymbolHashed); 297 sym->flags &= ~kOSSSymbolHashed; 298 299 if (!OSSymbol_is_huge(sym->length)) { 300 smr_hash_serialized_remove(&_hash, &sym->hashlink, &hash_traits); 301 302 if (shouldShrink() && !_tcallScheduled && 303 startup_phase >= STARTUP_SUB_THREAD_CALL) { 304 _tcallScheduled = true; 305 thread_call_enter(_tcall); 306 } 307 } else { 308 smrq_serialized_remove(&_huge_head, &sym->hashlink); 309 _hugeCount--; 310 } 311 312 unlock(); 313 } 314 315 void 316 OSSymbolPool::rehash(thread_call_param_t arg0, thread_call_param_t arg1 __unused) 317 { 318 reinterpret_cast<OSSymbolPool *>(arg0)->rehash(); 319 } 320 321 void 322 OSSymbolPool::rehash() 323 { 324 lock(); 325 _tcallScheduled = false; 326 327 if (shouldShrink()) { 328 smr_hash_shrink_and_unlock(&_hash, &_mutex, &hash_traits); 329 } else if (shouldGrow()) { 330 smr_hash_grow_and_unlock(&_hash, &_mutex, &hash_traits); 331 } else { 332 unlock(); 333 } 334 } 335 336 void 337 OSSymbolPool::checkForPageUnload(void *startAddr, void *endAddr) 338 { 339 OSSymbol *sym; 340 char *s; 341 bool mustSync = false; 342 343 lock(); 344 smr_hash_foreach(sym, &_hash, &hash_traits) { 345 if (sym->string >= startAddr && sym->string < endAddr) { 346 assert(sym->flags & kOSStringNoCopy); 347 348 s = (char *)kalloc_data(sym->length, 349 Z_WAITOK_ZERO); 350 if (s) { 351 memcpy(s, sym->string, sym->length); 352 /* 353 * make sure the memcpy is visible for readers 354 * who dereference `string` below. 355 * 356 * We can't use os_atomic_store(&..., release) 357 * because OSSymbol::string is PACed 358 */ 359 os_atomic_thread_fence(release); 360 } 361 sym->string = s; 362 sym->flags &= ~kOSStringNoCopy; 363 mustSync = true; 364 } 365 } 366 367 unlock(); 368 369 /* Make sure no readers can see stale pointers that we rewrote */ 370 if (mustSync) { 371 smr_synchronize(&OSSymbol_smr); 372 } 373 } 374 375 #pragma clang diagnostic pop /* -Winvalid-offsetof */ 376 377 /* 378 ********************************************************************* 379 * From here on we are actually implementing the OSSymbol class 380 ********************************************************************* 381 */ 382 #define super OSString 383 384 OSDefineMetaClassWithInit(OSSymbol, OSString, OSSymbol::initialize()); 385 OSMetaClassConstructorInit(OSSymbol, OSString, OSSymbol::initialize()); 386 OSDefineBasicStructors(OSSymbol, OSString) 387 OSMetaClassDefineReservedUnused(OSSymbol, 0); 388 OSMetaClassDefineReservedUnused(OSSymbol, 1); 389 OSMetaClassDefineReservedUnused(OSSymbol, 2); 390 OSMetaClassDefineReservedUnused(OSSymbol, 3); 391 OSMetaClassDefineReservedUnused(OSSymbol, 4); 392 OSMetaClassDefineReservedUnused(OSSymbol, 5); 393 OSMetaClassDefineReservedUnused(OSSymbol, 6); 394 OSMetaClassDefineReservedUnused(OSSymbol, 7); 395 396 static void 397 OSSymbol_smr_free(void *sym, vm_size_t size __unused) 398 { 399 reinterpret_cast<OSSymbol *>(sym)->smr_free(); 400 } 401 402 void 403 OSSymbol::initialize() 404 { 405 zone_enable_smr(OSSymbol_zone, &OSSymbol_smr, &OSSymbol_smr_free); 406 new (OSSymbolPoolStorage) OSSymbolPool(); 407 } 408 409 bool 410 OSSymbol::initWithCStringNoCopy(const char *) 411 { 412 return false; 413 } 414 bool 415 OSSymbol::initWithCString(const char *) 416 { 417 return false; 418 } 419 bool 420 OSSymbol::initWithString(const OSString *) 421 { 422 return false; 423 } 424 425 OSSharedPtr<const OSSymbol> 426 OSSymbol::withString(const OSString *aString) 427 { 428 // This string may be a OSSymbol already, cheap check. 429 if (OSDynamicCast(OSSymbol, aString)) { 430 OSSharedPtr<const OSSymbol> aStringNew((const OSSymbol *)aString, OSRetain); 431 return aStringNew; 432 } else if (((const OSSymbol *) aString)->flags & kOSStringNoCopy) { 433 return OSSymbol::withCStringNoCopy(aString->getCStringNoCopy()); 434 } else { 435 return OSSymbol::withCString(aString->getCStringNoCopy()); 436 } 437 } 438 439 OSSharedPtr<const OSSymbol> 440 OSSymbol::withCString(const char *cString) 441 { 442 auto &pool = OSSymbolPool::instance(); 443 smrh_key_t key = { 444 .smrk_string = cString, 445 .smrk_len = strnlen(cString, kMaxStringLength), 446 }; 447 bool permanent = false; 448 449 if (key.smrk_len >= kMaxStringLength) { 450 return nullptr; 451 } 452 453 auto symbol = pool.findSymbol(key); 454 if (__probable(symbol)) { 455 return symbol; 456 } 457 458 #if defined(KERNEL_INTEGRITY_KTRR) || defined(KERNEL_INTEGRITY_CTRR) 459 /* 460 * Empirically, symbols which string is from the rorgn part of the 461 * kernel are asked about all the time. 462 * 463 * Making them noCopy + permanent avoids a significant amount of 464 * useless refcounting traffic. 465 * 466 * On embedded, this policy causes about 200 extra symbols to be made 467 * from baseline (~6k), but avoiding the string copies saves about 60k. 468 */ 469 permanent = rorgn_contains((vm_offset_t)cString, key.smrk_len + 1, false); 470 #endif /* defined(KERNEL_INTEGRITY_KTRR) || defined(KERNEL_INTEGRITY_CTRR) */ 471 472 /* 473 * can't use OSString::initWithCString* because it calls 474 * OSObject::init() which tries to enroll in IOTracking if it's on. 475 */ 476 477 auto newSymb = OSMakeShared<OSSymbol>(); 478 479 if (permanent) { 480 newSymb->flags = kOSStringNoCopy; 481 newSymb->length = (uint32_t)(key.smrk_len + 1); 482 newSymb->string = const_cast<char *>(cString); 483 pool.insertSymbol(/* inout */ newSymb, key, permanent); 484 } else if (char *s = (char *)kalloc_data(key.smrk_len + 1, Z_WAITOK_ZERO)) { 485 memcpy(s, cString, key.smrk_len); 486 newSymb->flags = 0; 487 newSymb->length = (uint32_t)(key.smrk_len + 1); 488 newSymb->string = s; 489 pool.insertSymbol(/* inout */ newSymb, key, permanent); 490 } else { 491 newSymb.reset(); 492 } 493 494 return os::move(newSymb); // return the newly created & inserted symbol. 495 } 496 497 OSSharedPtr<const OSSymbol> 498 OSSymbol::withCStringNoCopy(const char *cString) 499 { 500 auto &pool = OSSymbolPool::instance(); 501 smrh_key_t key = { 502 .smrk_string = cString, 503 .smrk_len = strnlen(cString, kMaxStringLength), 504 }; 505 bool permanent = false; 506 507 if (key.smrk_len >= kMaxStringLength) { 508 return nullptr; 509 } 510 511 auto symbol = pool.findSymbol(key); 512 if (__probable(symbol)) { 513 return symbol; 514 } 515 516 #if defined(KERNEL_INTEGRITY_KTRR) || defined(KERNEL_INTEGRITY_CTRR) 517 permanent = rorgn_contains((vm_offset_t)cString, key.smrk_len + 1, false); 518 #endif /* defined(KERNEL_INTEGRITY_KTRR) || defined(KERNEL_INTEGRITY_CTRR) */ 519 520 auto newSymb = OSMakeShared<OSSymbol>(); 521 522 /* 523 * can't use OSString::initWithCStringNoCopy because it calls 524 * OSObject::init() which tries to enrol in IOTracking if it's on. 525 */ 526 newSymb->flags = kOSStringNoCopy; 527 newSymb->length = (uint32_t)(key.smrk_len + 1); 528 newSymb->string = const_cast<char *>(cString); 529 pool.insertSymbol(/* inout */ newSymb, key, permanent); 530 531 return os::move(newSymb); // return the newly created & inserted symbol. 532 } 533 534 OSSharedPtr<const OSSymbol> 535 OSSymbol::existingSymbolForString(const OSString *aString) 536 { 537 if (!aString) { 538 return NULL; 539 } 540 if (OSDynamicCast(OSSymbol, aString)) { 541 OSSharedPtr<const OSSymbol> aStringNew((const OSSymbol *)aString, OSRetain); 542 return aStringNew; 543 } 544 545 smrh_key_t key = { 546 .smrk_string = aString->getCStringNoCopy(), 547 .smrk_len = aString->getLength(), 548 }; 549 return OSSymbolPool::instance().findSymbol(key); 550 } 551 552 OSSharedPtr<const OSSymbol> 553 OSSymbol::existingSymbolForCString(const char *cString) 554 { 555 smrh_key_t key = { 556 .smrk_string = cString, 557 .smrk_len = strlen(cString), 558 }; 559 return OSSymbolPool::instance().findSymbol(key); 560 } 561 562 void 563 OSSymbol::checkForPageUnload(void *startAddr, void *endAddr) 564 { 565 OSSymbolPool::instance().checkForPageUnload(startAddr, endAddr); 566 } 567 568 void 569 OSSymbol::taggedRetain(const void *tag) const 570 { 571 if ((flags & kOSSSymbolPermanent) == 0) { 572 super::taggedRetain(tag); 573 } 574 } 575 576 void 577 OSSymbol::taggedRelease(const void *tag) const 578 { 579 if ((flags & kOSSSymbolPermanent) == 0) { 580 super::taggedRelease(tag); 581 } 582 } 583 584 void 585 OSSymbol::taggedRelease(const void *tag, const int when) const 586 { 587 if ((flags & kOSSSymbolPermanent) == 0) { 588 super::taggedRelease(tag, when); 589 } 590 } 591 592 void * 593 OSSymbol::operator new(size_t size __unused) 594 { 595 return zalloc_smr(OSSymbol_zone, Z_WAITOK_ZERO_NOFAIL); 596 } 597 598 void 599 OSSymbol::operator delete(void *mem, size_t size) 600 { 601 /* 602 * OSSymbol dying is this sequence: 603 * 604 * OSSymbol::taggedRelease() hits 0, 605 * which calls OSSymbol::free(), 606 * which calls zfree_smr(). 607 * 608 * At this stage, the memory of the OSSymbol is on a deferred 609 * reclamation queue. 610 * 611 * When the memory is being recycled by zalloc, OSSymbol::smr_free() 612 * is called which terminates with a delete call and only needs 613 * to zero said memory given that the memory has already been 614 * returned to the allocator. 615 */ 616 bzero(mem, size); 617 } 618 619 void 620 OSSymbol::smr_free() 621 { 622 /* 623 * This is called when the object is getting reused 624 */ 625 626 if (!(flags & kOSStringNoCopy) && string) { 627 kfree_data(string, length); 628 } 629 630 /* 631 * Note: we do not call super::free() on purpose because 632 * it would call OSObject::free() which tries to support 633 * iotracking. iotracking is fundamentally incompatible 634 * with SMR, so we on purpose do not call into these. 635 * 636 * to debug OSSymbol leaks etc, the zone logging feature 637 * can be used instead on the iokit.OSSymbol zone. 638 */ 639 OSSymbol::gMetaClass.instanceDestructed(); 640 641 delete this; 642 } 643 644 void 645 OSSymbol::free() 646 { 647 bool freeNow = true; 648 649 if (flags & kOSSSymbolHashed) { 650 OSSymbolPool::instance().removeSymbol(this); 651 freeNow = OSSymbol_is_huge(length); 652 } 653 654 if (freeNow && !(flags & kOSStringNoCopy) && string) { 655 /* 656 * If the element isn't in the hash, it was a failed insertion 657 * racing, and no one will every do a hazardous access, 658 * so we can clean up the string right away. 659 * 660 * If it is huge, then it is not looked up via SMR but under 661 * locks, so we can free right now (actually _must_ because 662 * this free is not preemption disabled safe and can't be done 663 * in smr_free()) 664 */ 665 kfree_data(string, length); 666 assert(string == nullptr); /* kfree_data nils out */ 667 } 668 669 (zfree_smr)(OSSymbol_zone, this); 670 } 671 672 uint32_t 673 OSSymbol::hash() const 674 { 675 assert(!OSSymbol_is_huge(length)); 676 return os_hash_jenkins(string, length - 1); 677 } 678 679 bool 680 OSSymbol::isEqualTo(const char *aCString) const 681 { 682 return super::isEqualTo(aCString); 683 } 684 685 bool 686 OSSymbol::isEqualTo(const OSSymbol *aSymbol) const 687 { 688 return aSymbol == this; 689 } 690 691 bool 692 OSSymbol::isEqualTo(const OSMetaClassBase *obj) const 693 { 694 OSSymbol * sym; 695 OSString * str; 696 697 if ((sym = OSDynamicCast(OSSymbol, obj))) { 698 return isEqualTo(sym); 699 } else if ((str = OSDynamicCast(OSString, obj))) { 700 return super::isEqualTo(str); 701 } else { 702 return false; 703 } 704 } 705 706 unsigned int 707 OSSymbol::bsearch( 708 const void * key, 709 const void * array, 710 unsigned int arrayCount, 711 size_t memberSize) 712 { 713 const void **p; 714 unsigned int baseIdx = 0; 715 unsigned int lim; 716 717 for (lim = arrayCount; lim; lim >>= 1) { 718 p = (typeof(p))(((uintptr_t) array) + (baseIdx + (lim >> 1)) * memberSize); 719 if (key == *p) { 720 return baseIdx + (lim >> 1); 721 } 722 if (key > *p) { 723 // move right 724 baseIdx += (lim >> 1) + 1; 725 lim--; 726 } 727 // else move left 728 } 729 // not found, insertion point here 730 return baseIdx + (lim >> 1); 731 } 732 733 #if DEBUG || DEVELOPMENT 734 static int 735 iokit_symbol_basic_test(int64_t size, int64_t *out) 736 { 737 OSSharedPtr<const OSSymbol> sym1; 738 OSSharedPtr<const OSSymbol> sym2; 739 char *data; 740 741 data = (char *)kalloc_data(size, Z_WAITOK); 742 if (!data) { 743 return ENOMEM; 744 } 745 746 memset(data, 'A', size - 1); 747 data[size - 1] = '\0'; 748 749 sym1 = OSSymbol::withCString(data); 750 if (sym1 == nullptr) { 751 return ENOMEM; 752 } 753 assert(sym1->getLength() == size - 1); 754 755 sym2 = OSSymbol::withCString(data); 756 assert(sym1 == sym2); 757 758 sym2.reset(); 759 sym1.reset(); 760 761 *out = 1; 762 return 0; 763 } 764 SYSCTL_TEST_REGISTER(iokit_symbol_basic, iokit_symbol_basic_test); 765 #endif /* DEBUG || DEVELOPMENT */ 766