xref: /xnu-11215/libkern/c++/OSSymbol.cpp (revision aca3beaa)
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