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 <libkern/prelink.h> |
46 | #include <stdarg.h> |
47 | |
48 | #if PRAGMA_MARK |
49 | #pragma mark Constants &c. |
50 | #endif /* PRAGMA_MARK */ |
51 | OSKextLogSpec kOSRuntimeLogSpec = |
52 | kOSKextLogErrorLevel | |
53 | kOSKextLogLoadFlag | |
54 | kOSKextLogKextBookkeepingFlag; |
55 | |
56 | #if PRAGMA_MARK |
57 | #pragma mark Logging Bootstrap |
58 | #endif /* PRAGMA_MARK */ |
59 | /********************************************************************* |
60 | * kern_os Logging Bootstrap |
61 | * |
62 | * We can't call in to OSKext until the kernel's C++ environment is up |
63 | * and running, so let's mask those references with a check variable. |
64 | * We print unconditionally if C++ isn't up, but if that's the case |
65 | * we've generally hit a serious error in kernel init! |
66 | *********************************************************************/ |
67 | static bool gKernelCPPInitialized = false; |
68 | |
69 | #define OSRuntimeLog(kext, flags, format, args...) \ |
70 | do { \ |
71 | if (gKernelCPPInitialized) { \ |
72 | OSKextLog((kext), (flags), (format), ## args); \ |
73 | } else { \ |
74 | printf((format), ## args); \ |
75 | } \ |
76 | } while (0) |
77 | |
78 | #if PRAGMA_MARK |
79 | #pragma mark kern_os Allocator Package |
80 | #endif /* PRAGMA_MARK */ |
81 | /********************************************************************* |
82 | * kern_os Allocator Package |
83 | *********************************************************************/ |
84 | |
85 | /********************************************************************* |
86 | *********************************************************************/ |
87 | #if OSALLOCDEBUG |
88 | extern int debug_iomalloc_size; |
89 | #endif |
90 | |
91 | /********************************************************************* |
92 | *********************************************************************/ |
93 | void * |
94 | kern_os_malloc(size_t size) |
95 | { |
96 | void *mem; |
97 | if (size == 0) { |
98 | return (0); |
99 | } |
100 | |
101 | mem = kallocp_tag_bt((vm_size_t *)&size, VM_KERN_MEMORY_LIBKERN); |
102 | if (!mem) { |
103 | return (0); |
104 | } |
105 | |
106 | #if OSALLOCDEBUG |
107 | OSAddAtomic(size, &debug_iomalloc_size); |
108 | #endif |
109 | |
110 | bzero(mem, size); |
111 | |
112 | return mem; |
113 | } |
114 | |
115 | /********************************************************************* |
116 | *********************************************************************/ |
117 | void |
118 | kern_os_free(void * addr) |
119 | { |
120 | size_t size; |
121 | size = kalloc_size(addr); |
122 | #if OSALLOCDEBUG |
123 | OSAddAtomic(-size, &debug_iomalloc_size); |
124 | #endif |
125 | |
126 | kfree_addr(addr); |
127 | } |
128 | |
129 | /********************************************************************* |
130 | *********************************************************************/ |
131 | void * |
132 | kern_os_realloc( |
133 | void * addr, |
134 | size_t nsize) |
135 | { |
136 | void *nmem; |
137 | size_t osize; |
138 | |
139 | if (!addr) { |
140 | return (kern_os_malloc(nsize)); |
141 | } |
142 | |
143 | osize = kalloc_size(addr); |
144 | if (nsize == osize) { |
145 | return (addr); |
146 | } |
147 | |
148 | if (nsize == 0) { |
149 | kfree_addr(addr); |
150 | return (0); |
151 | } |
152 | |
153 | nmem = kallocp_tag_bt((vm_size_t *)&nsize, VM_KERN_MEMORY_LIBKERN); |
154 | if (!nmem){ |
155 | kfree_addr(addr); |
156 | return (0); |
157 | } |
158 | |
159 | #if OSALLOCDEBUG |
160 | OSAddAtomic((nsize - osize), &debug_iomalloc_size); |
161 | #endif |
162 | |
163 | if (nsize > osize) { |
164 | (void)memset((char *)nmem + osize, 0, nsize - osize); |
165 | } |
166 | (void)memcpy(nmem, addr, (nsize > osize) ? osize : nsize); |
167 | kfree_addr(addr); |
168 | |
169 | return (nmem); |
170 | } |
171 | |
172 | #if PRAGMA_MARK |
173 | #pragma mark Libkern Init |
174 | #endif /* PRAGMA_MARK */ |
175 | /********************************************************************* |
176 | * Libkern Init |
177 | *********************************************************************/ |
178 | |
179 | #if __GNUC__ >= 3 |
180 | void __cxa_pure_virtual( void ) { panic("%s" , __FUNCTION__); } |
181 | #else |
182 | void __pure_virtual( void ) { panic("%s" , __FUNCTION__); } |
183 | #endif |
184 | |
185 | extern lck_grp_t * IOLockGroup; |
186 | extern kmod_info_t g_kernel_kmod_info; |
187 | |
188 | enum { |
189 | kOSSectionNamesDefault = 0, |
190 | kOSSectionNamesBuiltinKext = 1, |
191 | kOSSectionNamesCount = 2, |
192 | }; |
193 | enum { |
194 | kOSSectionNameInitializer = 0, |
195 | kOSSectionNameFinalizer = 1, |
196 | kOSSectionNameCount = 2 |
197 | }; |
198 | |
199 | static const char * |
200 | gOSStructorSectionNames[kOSSectionNamesCount][kOSSectionNameCount] = { |
201 | { SECT_MODINITFUNC, SECT_MODTERMFUNC }, |
202 | { kBuiltinInitSection, kBuiltinTermSection } |
203 | }; |
204 | |
205 | void OSlibkernInit(void) |
206 | { |
207 | // This must be called before calling OSRuntimeInitializeCPP. |
208 | OSMetaClassBase::initialize(); |
209 | |
210 | g_kernel_kmod_info.address = (vm_address_t) &_mh_execute_header; |
211 | if (kOSReturnSuccess != OSRuntimeInitializeCPP(NULL)) { |
212 | // &g_kernel_kmod_info, gOSSectionNamesStandard, 0, 0)) { |
213 | panic("OSRuntime: C++ runtime failed to initialize." ); |
214 | } |
215 | |
216 | gKernelCPPInitialized = true; |
217 | |
218 | return; |
219 | } |
220 | |
221 | __END_DECLS |
222 | |
223 | #if PRAGMA_MARK |
224 | #pragma mark C++ Runtime Load/Unload |
225 | #endif /* PRAGMA_MARK */ |
226 | /********************************************************************* |
227 | * kern_os C++ Runtime Load/Unload |
228 | *********************************************************************/ |
229 | |
230 | |
231 | typedef void (*structor_t)(void); |
232 | |
233 | static bool |
234 | OSRuntimeCallStructorsInSection( |
235 | OSKext * theKext, |
236 | kmod_info_t * kmodInfo, |
237 | void * metaHandle, |
238 | kernel_segment_command_t * segment, |
239 | const char * sectionName, |
240 | uintptr_t textStart, |
241 | uintptr_t textEnd) |
242 | { |
243 | kernel_section_t * section; |
244 | bool result = TRUE; |
245 | |
246 | for (section = firstsect(segment); |
247 | section != NULL; |
248 | section = nextsect(segment, section)) |
249 | { |
250 | if (strncmp(section->sectname, sectionName, sizeof(section->sectname) - 1)) continue; |
251 | |
252 | structor_t * structors = (structor_t *)section->addr; |
253 | if (!structors) continue; |
254 | |
255 | structor_t structor; |
256 | unsigned int num_structors = section->size / sizeof(structor_t); |
257 | unsigned int hit_null_structor = 0; |
258 | unsigned int firstIndex = 0; |
259 | |
260 | if (textStart) |
261 | { |
262 | // bsearch for any in range |
263 | unsigned int baseIdx; |
264 | unsigned int lim; |
265 | uintptr_t value; |
266 | firstIndex = num_structors; |
267 | for (lim = num_structors, baseIdx = 0; lim; lim >>= 1) |
268 | { |
269 | value = (uintptr_t) structors[baseIdx + (lim >> 1)]; |
270 | if (!value) panic("%s: null structor" , kmodInfo->name); |
271 | if ((value >= textStart) && (value < textEnd)) |
272 | { |
273 | firstIndex = (baseIdx + (lim >> 1)); |
274 | // scan back for the first in range |
275 | for (; firstIndex; firstIndex--) |
276 | { |
277 | value = (uintptr_t) structors[firstIndex - 1]; |
278 | if ((value < textStart) || (value >= textEnd)) break; |
279 | } |
280 | break; |
281 | } |
282 | if (textStart > value) |
283 | { |
284 | // move right |
285 | baseIdx += (lim >> 1) + 1; |
286 | lim--; |
287 | } |
288 | // else move left |
289 | } |
290 | baseIdx = (baseIdx + (lim >> 1)); |
291 | } |
292 | for (; |
293 | (firstIndex < num_structors) |
294 | && (!metaHandle || OSMetaClass::checkModLoad(metaHandle)); |
295 | firstIndex++) |
296 | { |
297 | if ((structor = structors[firstIndex])) |
298 | { |
299 | if ((textStart && ((uintptr_t) structor < textStart)) |
300 | || (textEnd && ((uintptr_t) structor >= textEnd))) break; |
301 | |
302 | (*structor)(); |
303 | } |
304 | else if (!hit_null_structor) |
305 | { |
306 | hit_null_structor = 1; |
307 | OSRuntimeLog(theKext, kOSRuntimeLogSpec, |
308 | "Null structor in kext %s segment %s!" , |
309 | kmodInfo->name, section->segname); |
310 | } |
311 | } |
312 | if (metaHandle) result = OSMetaClass::checkModLoad(metaHandle); |
313 | break; |
314 | } /* for (section...) */ |
315 | return (result); |
316 | } |
317 | |
318 | /********************************************************************* |
319 | *********************************************************************/ |
320 | kern_return_t |
321 | OSRuntimeFinalizeCPP( |
322 | OSKext * theKext) |
323 | { |
324 | kern_return_t result = KMOD_RETURN_FAILURE; |
325 | void * metaHandle = NULL; // do not free |
326 | kernel_mach_header_t * ; |
327 | kernel_segment_command_t * segment; |
328 | kmod_info_t * kmodInfo; |
329 | const char ** sectionNames; |
330 | uintptr_t textStart; |
331 | uintptr_t textEnd; |
332 | |
333 | textStart = 0; |
334 | textEnd = 0; |
335 | sectionNames = gOSStructorSectionNames[kOSSectionNamesDefault]; |
336 | if (theKext) { |
337 | if (!theKext->isCPPInitialized()) { |
338 | result = KMOD_RETURN_SUCCESS; |
339 | goto finish; |
340 | } |
341 | kmodInfo = theKext->kmod_info; |
342 | if (!kmodInfo || !kmodInfo->address) { |
343 | result = kOSKextReturnInvalidArgument; |
344 | goto finish; |
345 | } |
346 | header = (kernel_mach_header_t *)kmodInfo->address; |
347 | if (theKext->flags.builtin) { |
348 | header = (kernel_mach_header_t *)g_kernel_kmod_info.address; |
349 | textStart = kmodInfo->address; |
350 | textEnd = textStart + kmodInfo->size; |
351 | sectionNames = gOSStructorSectionNames[kOSSectionNamesBuiltinKext]; |
352 | } |
353 | } else { |
354 | kmodInfo = &g_kernel_kmod_info; |
355 | header = (kernel_mach_header_t *)kmodInfo->address; |
356 | } |
357 | |
358 | /* OSKext checks for this condition now, but somebody might call |
359 | * this function directly (the symbol is exported....). |
360 | */ |
361 | if (OSMetaClass::modHasInstance(kmodInfo->name)) { |
362 | // xxx - Don't log under errors? this is more of an info thing |
363 | OSRuntimeLog(theKext, kOSRuntimeLogSpec, |
364 | "Can't tear down kext %s C++; classes have instances:" , |
365 | kmodInfo->name); |
366 | OSKext::reportOSMetaClassInstances(kmodInfo->name, kOSRuntimeLogSpec); |
367 | result = kOSMetaClassHasInstances; |
368 | goto finish; |
369 | } |
370 | |
371 | /* Tell the meta class system that we are starting to unload. |
372 | * metaHandle isn't actually needed on the finalize path, |
373 | * so we don't check it here, even though OSMetaClass::postModLoad() will |
374 | * return a failure (it only does actual work on the init path anyhow). |
375 | */ |
376 | metaHandle = OSMetaClass::preModLoad(kmodInfo->name); |
377 | |
378 | OSSymbol::checkForPageUnload((void *)kmodInfo->address, |
379 | (void *)(kmodInfo->address + kmodInfo->size)); |
380 | |
381 | header = (kernel_mach_header_t *)kmodInfo->address; |
382 | segment = firstsegfromheader(header); |
383 | |
384 | for (segment = firstsegfromheader(header); |
385 | segment != 0; |
386 | segment = nextsegfromheader(header, segment)) { |
387 | |
388 | OSRuntimeCallStructorsInSection(theKext, kmodInfo, NULL, segment, |
389 | sectionNames[kOSSectionNameFinalizer], textStart, textEnd); |
390 | } |
391 | |
392 | (void)OSMetaClass::postModLoad(metaHandle); |
393 | |
394 | if (theKext) { |
395 | theKext->setCPPInitialized(false); |
396 | } |
397 | result = KMOD_RETURN_SUCCESS; |
398 | finish: |
399 | return result; |
400 | } |
401 | |
402 | /********************************************************************* |
403 | *********************************************************************/ |
404 | kern_return_t |
405 | OSRuntimeInitializeCPP( |
406 | OSKext * theKext) |
407 | { |
408 | kern_return_t result = KMOD_RETURN_FAILURE; |
409 | kernel_mach_header_t * = 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 | kmod_info_t * kmodInfo; |
415 | const char ** sectionNames; |
416 | uintptr_t textStart; |
417 | uintptr_t textEnd; |
418 | |
419 | textStart = 0; |
420 | textEnd = 0; |
421 | sectionNames = gOSStructorSectionNames[kOSSectionNamesDefault]; |
422 | if (theKext) { |
423 | if (theKext->isCPPInitialized()) { |
424 | result = KMOD_RETURN_SUCCESS; |
425 | goto finish; |
426 | } |
427 | |
428 | kmodInfo = theKext->kmod_info; |
429 | if (!kmodInfo || !kmodInfo->address) { |
430 | result = kOSKextReturnInvalidArgument; |
431 | goto finish; |
432 | } |
433 | header = (kernel_mach_header_t *)kmodInfo->address; |
434 | |
435 | if (theKext->flags.builtin) { |
436 | header = (kernel_mach_header_t *)g_kernel_kmod_info.address; |
437 | textStart = kmodInfo->address; |
438 | textEnd = textStart + kmodInfo->size; |
439 | sectionNames = gOSStructorSectionNames[kOSSectionNamesBuiltinKext]; |
440 | } |
441 | } else { |
442 | kmodInfo = &g_kernel_kmod_info; |
443 | header = (kernel_mach_header_t *)kmodInfo->address; |
444 | } |
445 | |
446 | /* Tell the meta class system that we are starting the load |
447 | */ |
448 | metaHandle = OSMetaClass::preModLoad(kmodInfo->name); |
449 | assert(metaHandle); |
450 | if (!metaHandle) { |
451 | goto finish; |
452 | } |
453 | |
454 | /* NO GOTO PAST HERE. */ |
455 | |
456 | /* Scan the header for all constructor sections, in any |
457 | * segment, and invoke the constructors within those sections. |
458 | */ |
459 | for (segment = firstsegfromheader(header); |
460 | segment != NULL && load_success; |
461 | segment = nextsegfromheader(header, segment)) |
462 | { |
463 | /* Record the current segment in the event of a failure. |
464 | */ |
465 | failure_segment = segment; |
466 | load_success = OSRuntimeCallStructorsInSection( |
467 | theKext, kmodInfo, metaHandle, segment, |
468 | sectionNames[kOSSectionNameInitializer], |
469 | textStart, textEnd); |
470 | } /* for (segment...) */ |
471 | |
472 | /* We failed so call all of the destructors. We must do this before |
473 | * calling OSMetaClass::postModLoad() as the OSMetaClass destructors |
474 | * will alter state (in the metaHandle) used by that function. |
475 | */ |
476 | if (!load_success) { |
477 | |
478 | /* Scan the header for all destructor sections, in any |
479 | * segment, and invoke the constructors within those sections. |
480 | */ |
481 | for (segment = firstsegfromheader(header); |
482 | segment != failure_segment && segment != 0; |
483 | segment = nextsegfromheader(header, segment)) { |
484 | |
485 | OSRuntimeCallStructorsInSection(theKext, kmodInfo, NULL, segment, |
486 | sectionNames[kOSSectionNameFinalizer], textStart, textEnd); |
487 | |
488 | } /* for (segment...) */ |
489 | } |
490 | |
491 | /* Now, regardless of success so far, do the post-init registration |
492 | * and cleanup. If we had to call the unloadCPP function, static |
493 | * destructors have removed classes from the stalled list so no |
494 | * metaclasses will actually be registered. |
495 | */ |
496 | result = OSMetaClass::postModLoad(metaHandle); |
497 | |
498 | /* If we've otherwise been fine up to now, but OSMetaClass::postModLoad() |
499 | * fails (typically due to a duplicate class), tear down all the C++ |
500 | * stuff from the kext. This isn't necessary for libkern/OSMetaClass stuff, |
501 | * but may be necessary for other C++ code. We ignore the return value |
502 | * because it's only a fail when there are existing instances of libkern |
503 | * classes, and there had better not be any created on the C++ init path. |
504 | */ |
505 | if (load_success && result != KMOD_RETURN_SUCCESS) { |
506 | (void)OSRuntimeFinalizeCPP(theKext); //kmodInfo, sectionNames, textStart, textEnd); |
507 | } |
508 | |
509 | if (theKext && load_success && result == KMOD_RETURN_SUCCESS) { |
510 | theKext->setCPPInitialized(true); |
511 | } |
512 | finish: |
513 | return result; |
514 | } |
515 | |
516 | /********************************************************************* |
517 | Unload a kernel segment. |
518 | *********************************************************************/ |
519 | |
520 | void |
521 | OSRuntimeUnloadCPPForSegment( |
522 | kernel_segment_command_t * segment) |
523 | { |
524 | OSRuntimeCallStructorsInSection(NULL, &g_kernel_kmod_info, NULL, segment, |
525 | gOSStructorSectionNames[kOSSectionNamesDefault][kOSSectionNameFinalizer], 0, 0); |
526 | } |
527 | |
528 | #if PRAGMA_MARK |
529 | #pragma mark C++ Allocators & Deallocators |
530 | #endif /* PRAGMA_MARK */ |
531 | /********************************************************************* |
532 | * C++ Allocators & Deallocators |
533 | *********************************************************************/ |
534 | void * |
535 | operator new(size_t size) |
536 | { |
537 | void * result; |
538 | |
539 | result = (void *) kern_os_malloc(size); |
540 | return result; |
541 | } |
542 | |
543 | void |
544 | operator delete(void * addr) |
545 | #if __cplusplus >= 201103L |
546 | noexcept |
547 | #endif |
548 | { |
549 | kern_os_free(addr); |
550 | return; |
551 | } |
552 | |
553 | void * |
554 | operator new[](unsigned long sz) |
555 | { |
556 | if (sz == 0) sz = 1; |
557 | return kern_os_malloc(sz); |
558 | } |
559 | |
560 | void |
561 | operator delete[](void * ptr) |
562 | #if __cplusplus >= 201103L |
563 | noexcept |
564 | #endif |
565 | { |
566 | if (ptr) { |
567 | kern_os_free(ptr); |
568 | } |
569 | return; |
570 | } |
571 | |
572 | /* PR-6481964 - The compiler is going to check for size overflows in calls to |
573 | * new[], and if there is an overflow, it will call __throw_length_error. |
574 | * This is an unrecoverable error by the C++ standard, so we must panic here. |
575 | * |
576 | * We have to put the function inside the std namespace because of how the |
577 | * compiler expects the name to be mangled. |
578 | */ |
579 | namespace std { |
580 | |
581 | void |
582 | __throw_length_error(const char *msg __unused) |
583 | { |
584 | panic("Size of array created by new[] has overflowed" ); |
585 | } |
586 | |
587 | }; |
588 | |