xref: /xnu-11215/libkern/c++/OSRuntime.cpp (revision 0f3703ac)
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 #include <IOKit/IOKitDebug.h>
37 
38 #include <sys/cdefs.h>
39 
40 __BEGIN_DECLS
41 
42 #include <string.h>
43 #include <mach/mach_types.h>
44 #include <libkern/kernel_mach_header.h>
45 #include <stdarg.h>
46 
47 #if PRAGMA_MARK
48 #pragma mark Constants &c.
49 #endif /* PRAGMA_MARK */
50 OSKextLogSpec kOSRuntimeLogSpec =
51     kOSKextLogErrorLevel |
52     kOSKextLogLoadFlag |
53     kOSKextLogKextBookkeepingFlag;
54 
55 #if PRAGMA_MARK
56 #pragma mark Logging Bootstrap
57 #endif /* PRAGMA_MARK */
58 /*********************************************************************
59 * kern_os Logging Bootstrap
60 *
61 * We can't call in to OSKext until the kernel's C++ environment is up
62 * and running, so let's mask those references with a check variable.
63 * We print unconditionally if C++ isn't up, but if that's the case
64 * we've generally hit a serious error in kernel init!
65 *********************************************************************/
66 static bool gKernelCPPInitialized = false;
67 
68 #define OSRuntimeLog(kext, flags, format, args...)            \
69     do {                                                      \
70         if (gKernelCPPInitialized) {                          \
71             OSKextLog((kext), (flags), (format), ## args);  \
72         } else {                                              \
73             printf((format), ## args);                        \
74         }                                                     \
75     } while (0)
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
87 extern int debug_iomalloc_size;
88 #endif
89 
90 struct _mhead {
91     size_t  mlen;
92     char    dat[0];
93 };
94 
95 /*********************************************************************
96 *********************************************************************/
97 void *
98 kern_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_tag_bt(memsize, VM_KERN_MEMORY_LIBKERN);
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 *********************************************************************/
124 void
125 kern_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 *********************************************************************/
149 void *
150 kern_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_tag_bt(nmemsize, VM_KERN_MEMORY_LIBKERN);
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 *********************************************************************/
197 size_t
198 kern_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
220 void __cxa_pure_virtual( void )    { panic("%s", __FUNCTION__); }
221 #else
222 void __pure_virtual( void )        { panic("%s", __FUNCTION__); }
223 #endif
224 
225 typedef void (*structor_t)(void);
226 
227 /*********************************************************************
228 *********************************************************************/
229 static boolean_t
230 sectionIsDestructor(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 *********************************************************************/
246 static boolean_t
247 sectionIsConstructor(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 
270 void
271 OSRuntimeUnloadCPPForSegmentInKmod(
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 
313 void
314 OSRuntimeUnloadCPPForSegment(kernel_segment_command_t * segment) {
315     OSRuntimeUnloadCPPForSegmentInKmod(segment, NULL);
316 }
317 
318 /*********************************************************************
319 *********************************************************************/
320 void
321 OSRuntimeUnloadCPP(
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 *********************************************************************/
349 kern_return_t
350 OSRuntimeFinalizeCPP(
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;
393 finish:
394     OSSafeRelease(theKext);
395     return result;
396 }
397 
398 // Functions used by the extenTools/kmod library project
399 
400 /*********************************************************************
401 *********************************************************************/
402 kern_return_t
403 OSRuntimeInitializeCPP(
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) {
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     }
526 finish:
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 *********************************************************************/
540 extern lck_grp_t * IOLockGroup;
541 extern kmod_info_t g_kernel_kmod_info;
542 
543 void OSlibkernInit(void)
544 {
545     // This must be called before calling OSRuntimeInitializeCPP.
546     OSMetaClassBase::initialize();
547 
548     g_kernel_kmod_info.address = (vm_address_t) &_mh_execute_header;
549     if (kOSReturnSuccess != OSRuntimeInitializeCPP(&g_kernel_kmod_info, 0)) {
550         panic("OSRuntime: C++ runtime failed to initialize.");
551     }
552 
553     gKernelCPPInitialized = true;
554 
555     return;
556 }
557 
558 __END_DECLS
559 
560 #if PRAGMA_MARK
561 #pragma mark C++ Allocators & Deallocators
562 #endif /* PRAGMA_MARK */
563 /*********************************************************************
564 * C++ Allocators & Deallocators
565 *********************************************************************/
566 void *
567 operator new(size_t size)
568 #if __cplusplus >= 201103L
569 								noexcept
570 #endif
571 {
572     void * result;
573 
574     result = (void *) kern_os_malloc(size);
575     return result;
576 }
577 
578 void
579 operator delete(void * addr)
580 #if __cplusplus >= 201103L
581 								noexcept
582 #endif
583 {
584     kern_os_free(addr);
585     return;
586 }
587 
588 void *
589 operator new[](unsigned long sz)
590 #if __cplusplus >= 201103L
591 								noexcept
592 #endif
593 {
594     if (sz == 0) sz = 1;
595     return kern_os_malloc(sz);
596 }
597 
598 void
599 operator delete[](void * ptr)
600 #if __cplusplus >= 201103L
601 								noexcept
602 #endif
603 {
604     if (ptr) {
605         kern_os_free(ptr);
606     }
607     return;
608 }
609 
610 /* PR-6481964 - The compiler is going to check for size overflows in calls to
611  * new[], and if there is an overflow, it will call __throw_length_error.
612  * This is an unrecoverable error by the C++ standard, so we must panic here.
613  *
614  * We have to put the function inside the std namespace because of how the
615  * compiler expects the name to be mangled.
616  */
617 namespace std {
618 
619 void
620 __throw_length_error(const char *msg __unused)
621 {
622     panic("Size of array created by new[] has overflowed");
623 }
624 
625 };
626 
627