]> git.saurik.com Git - apple/xnu.git/blob - libsa/dgraph.c
xnu-1228.5.20.tar.gz
[apple/xnu.git] / libsa / dgraph.c
1 /*
2 * Copyright (c) 2004-2007 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 #ifdef KERNEL
30 #include <libsa/vers_rsrc.h>
31 #else
32 #include <libc.h>
33 #include <stdlib.h>
34 #include <stdarg.h>
35 #include <stdio.h>
36 #include <string.h>
37 #include <unistd.h>
38
39 #include "KXKext.h"
40 #include "vers_rsrc.h"
41 #endif /* KERNEL */
42
43 #include "dgraph.h"
44 #include "load.h"
45
46 #ifdef KERNEL
47 #include <sys/malloc.h>
48 #endif
49
50 static void __dgraph_entry_free(dgraph_entry_t * entry);
51
52 #ifdef KERNEL
53 #define dgstrdup(string) STRDUP(string, M_TEMP)
54 #define dgfree(string) FREE(string, M_TEMP)
55 #else
56 #define dgstrdup strdup
57 #define dgfree(string) free(string)
58 #endif /* KERNEL */
59
60 /*******************************************************************************
61 *
62 *******************************************************************************/
63 dgraph_error_t dgraph_init(dgraph_t * dgraph)
64 {
65 bzero(dgraph, sizeof(dgraph_t));
66
67 dgraph->capacity = (5); // pulled from a hat
68
69 /* Make sure list is big enough & graph has a good start size.
70 */
71 dgraph->graph = (dgraph_entry_t **)malloc(
72 dgraph->capacity * sizeof(dgraph_entry_t *));
73
74 if (!dgraph->graph) {
75 return dgraph_error;
76 }
77
78 return dgraph_valid;
79 }
80
81 #ifndef KERNEL
82 /*******************************************************************************
83 *
84 *******************************************************************************/
85 dgraph_error_t dgraph_init_with_arglist(
86 dgraph_t * dgraph,
87 int expect_addresses,
88 const char * dependency_delimiter,
89 const char * kernel_dependency_delimiter,
90 int argc,
91 char * argv[])
92 {
93 dgraph_error_t result = dgraph_valid;
94 unsigned int i;
95 int found_zero_load_address = 0;
96 int found_nonzero_load_address = 0;
97 dgraph_entry_t * current_dependent = NULL;
98 char kernel_dependencies = 0;
99
100 result = dgraph_init(dgraph);
101 if (result != dgraph_valid) {
102 return result;
103 }
104
105 for (i = 0; i < argc; i++) {
106 vm_address_t load_address = 0;
107
108 if (0 == strcmp(argv[i], dependency_delimiter)) {
109 kernel_dependencies = 0;
110 current_dependent = NULL;
111 continue;
112 } else if (0 == strcmp(argv[i], kernel_dependency_delimiter)) {
113 kernel_dependencies = 1;
114 current_dependent = NULL;
115 continue;
116 }
117
118 if (expect_addresses) {
119 char * address = rindex(argv[i], '@');
120 if (address) {
121 *address++ = 0; // snip the address from the filename
122 load_address = strtoul(address, NULL, 0);
123 }
124 }
125
126 if (!current_dependent) {
127 current_dependent = dgraph_add_dependent(dgraph, argv[i],
128 /* expected kmod name */ NULL, /* expected vers */ 0,
129 load_address, 0);
130 if (!current_dependent) {
131 return dgraph_error;
132 }
133 } else {
134 if (!dgraph_add_dependency(dgraph, current_dependent, argv[i],
135 /* expected kmod name */ NULL, /* expected vers */ 0,
136 load_address, kernel_dependencies)) {
137
138 return dgraph_error;
139 }
140 }
141 }
142
143 dgraph->root = dgraph_find_root(dgraph);
144 dgraph_establish_load_order(dgraph);
145
146 if (!dgraph->root) {
147 kload_log_error("dependency graph has no root" KNL);
148 return dgraph_invalid;
149 }
150
151 if (dgraph->root->is_kernel_component && !dgraph->root->is_symbol_set) {
152 kload_log_error("dependency graph root is a kernel component" KNL);
153 return dgraph_invalid;
154 }
155
156 for (i = 0; i < dgraph->length; i++) {
157 if (dgraph->graph[i]->loaded_address == 0) {
158 found_zero_load_address = 1;
159 } else {
160 found_nonzero_load_address = 1;
161 }
162 if ( (i > 0) &&
163 (found_zero_load_address && found_nonzero_load_address)) {
164
165 kload_log_error(
166 "load addresses must be specified for all module files" KNL);
167 return dgraph_invalid;
168 }
169 }
170
171 return dgraph_valid;
172 }
173 #endif /* not KERNEL */
174
175 /*******************************************************************************
176 *
177 *******************************************************************************/
178 static void __dgraph_entry_free(dgraph_entry_t * entry)
179 {
180 if (entry->name) {
181 dgfree(entry->name);
182 entry->name = NULL;
183 }
184 if (entry->expected_kmod_name) {
185 dgfree(entry->expected_kmod_name);
186 entry->expected_kmod_name = NULL;
187 }
188 if (entry->expected_kmod_vers) {
189 dgfree(entry->expected_kmod_vers);
190 entry->expected_kmod_vers = NULL;
191 }
192 if (entry->dependencies) {
193 free(entry->dependencies);
194 entry->dependencies = NULL;
195 }
196 if (entry->symbols_malloc) {
197 free((void *) entry->symbols_malloc);
198 entry->symbols_malloc = 0;
199 }
200 free(entry);
201 return;
202 }
203
204 /*******************************************************************************
205 *
206 *******************************************************************************/
207 void dgraph_free(
208 dgraph_t * dgraph,
209 int free_graph)
210 {
211 unsigned int entry_index;
212
213 if (!dgraph) {
214 return;
215 }
216
217 for (entry_index = 0; entry_index < dgraph->length; entry_index++) {
218 dgraph_entry_t * current = dgraph->graph[entry_index];
219 __dgraph_entry_free(current);
220 }
221
222 if (dgraph->graph) {
223 free(dgraph->graph);
224 dgraph->graph = NULL;
225 }
226
227 if (dgraph->load_order) {
228 free(dgraph->load_order);
229 dgraph->load_order = NULL;
230 }
231
232 if (free_graph && dgraph) {
233 free(dgraph);
234 }
235
236 return;
237 }
238
239
240 /*******************************************************************************
241 *
242 *******************************************************************************/
243 dgraph_entry_t * dgraph_find_root(dgraph_t * dgraph) {
244 dgraph_entry_t * root = NULL;
245 dgraph_entry_t * candidate = NULL;
246 unsigned int candidate_index;
247 unsigned int scan_index;
248 unsigned int dep_index;
249
250
251 /* Scan each entry in the graph for one that isn't in any other entry's
252 * dependencies.
253 */
254 for (candidate_index = 0; candidate_index < dgraph->length;
255 candidate_index++) {
256
257 candidate = dgraph->graph[candidate_index];
258
259 for (scan_index = 0; scan_index < dgraph->length; scan_index++) {
260
261 dgraph_entry_t * scan_entry = dgraph->graph[scan_index];
262 if (candidate == scan_entry) {
263 // don't check yourself
264 continue;
265 }
266 for (dep_index = 0; dep_index < scan_entry->num_dependencies;
267 dep_index++) {
268
269 /* If the dependency being checked is the candidate,
270 * then the candidate can't be the root.
271 */
272 dgraph_entry_t * check = scan_entry->dependencies[dep_index];
273
274 if (check == candidate) {
275 candidate = NULL;
276 break;
277 }
278 }
279
280 /* If the candidate was rejected, then hop out of this loop.
281 */
282 if (!candidate) {
283 break;
284 }
285 }
286
287 /* If we got here, the candidate is a valid one. However, if we already
288 * found another, that means we have two possible roots (or more), which
289 * is NOT ALLOWED.
290 */
291 if (candidate) {
292 if (root) {
293 kload_log_error("dependency graph has multiple roots "
294 "(%s and %s)" KNL, root->name, candidate->name);
295 return NULL; // two valid roots, illegal
296 } else {
297 root = candidate;
298 }
299 }
300 }
301
302 if (!root) {
303 kload_log_error("dependency graph has no root node" KNL);
304 }
305
306 return root;
307 }
308
309 /*******************************************************************************
310 *
311 *******************************************************************************/
312 dgraph_entry_t ** fill_backward_load_order(
313 dgraph_entry_t ** backward_load_order,
314 unsigned int * list_length,
315 dgraph_entry_t * first_entry,
316 unsigned int * last_index /* out param */)
317 {
318 unsigned int i;
319 unsigned int scan_index = 0;
320 unsigned int add_index = 0;
321 dgraph_entry_t * scan_entry;
322
323 if (*list_length == 0) {
324 if (backward_load_order) {
325 free(backward_load_order);
326 backward_load_order = NULL;
327 }
328 goto finish;
329 }
330
331 backward_load_order[add_index++] = first_entry;
332
333 while (scan_index < add_index) {
334
335 if (add_index > 255) {
336 kload_log_error(
337 "dependency list for %s ridiculously long; probably a loop" KNL,
338 first_entry->name);
339 if (backward_load_order) {
340 free(backward_load_order);
341 backward_load_order = NULL;
342 }
343 goto finish;
344 }
345
346 scan_entry = backward_load_order[scan_index++];
347
348 /* Increase the load order list if needed.
349 */
350 if (add_index + scan_entry->num_dependencies > (*list_length)) {
351 (*list_length) *= 2;
352 backward_load_order = (dgraph_entry_t **)realloc(
353 backward_load_order,
354 (*list_length) * sizeof(dgraph_entry_t *));
355 if (!backward_load_order) {
356 goto finish;
357 }
358 }
359
360 /* Put the dependencies of the scanning entry into the list.
361 */
362 for (i = 0; i < scan_entry->num_dependencies; i++) {
363 backward_load_order[add_index++] =
364 scan_entry->dependencies[i];
365 }
366 }
367
368 finish:
369
370 if (last_index) {
371 *last_index = add_index;
372 }
373 return backward_load_order;
374 }
375
376 /*******************************************************************************
377 *
378 *******************************************************************************/
379 int dgraph_establish_load_order(dgraph_t * dgraph) {
380 unsigned int total_dependencies;
381 unsigned int entry_index;
382 unsigned int list_index;
383 unsigned int backward_index;
384 unsigned int forward_index;
385 size_t load_order_size;
386 size_t backward_load_order_size;
387 dgraph_entry_t ** backward_load_order;
388
389 /* Lose the old load_order list. Size can change, so it's easier to just
390 * recreate from scratch.
391 */
392 if (dgraph->load_order) {
393 free(dgraph->load_order);
394 dgraph->load_order = NULL;
395 }
396
397 /* Figure how long the list needs to be to accommodate the max possible
398 * entries from the graph. Duplicates get weeded out, but the list
399 * initially has to accommodate them all.
400 */
401 total_dependencies = dgraph->length;
402
403 for (entry_index = 0; entry_index < dgraph->length; entry_index ++) {
404 dgraph_entry_t * curdep = dgraph->graph[entry_index];
405 total_dependencies += curdep->num_dependencies;
406 }
407
408 /* Hmm, nothing to do!
409 */
410 if (!total_dependencies) {
411 return 1;
412 }
413
414 backward_load_order_size = total_dependencies * sizeof(dgraph_entry_t *);
415
416 backward_load_order = (dgraph_entry_t **)malloc(backward_load_order_size);
417 if (!backward_load_order) {
418 kload_log_error("malloc failure" KNL);
419 return 0;
420 }
421 bzero(backward_load_order, backward_load_order_size);
422
423 backward_load_order = fill_backward_load_order(backward_load_order,
424 &total_dependencies, dgraph->root, &list_index);
425 if (!backward_load_order) {
426 kload_log_error("error establishing load order" KNL);
427 return 0;
428 }
429
430 load_order_size = dgraph->length * sizeof(dgraph_entry_t *);
431 dgraph->load_order = (dgraph_entry_t **)malloc(load_order_size);
432 if (!dgraph->load_order) {
433 kload_log_error("malloc failure" KNL);
434 return 0;
435 }
436 bzero(dgraph->load_order, load_order_size);
437
438
439 /* Reverse the list into the dgraph's load_order list,
440 * removing any duplicates.
441 */
442 backward_index = list_index;
443 //
444 // the required 1 is taken off in loop below!
445
446 forward_index = 0;
447 do {
448 dgraph_entry_t * current_entry;
449 unsigned int already_got_it = 0;
450
451 backward_index--;
452
453 /* Get the entry to check.
454 */
455 current_entry = backward_load_order[backward_index];
456
457 /* Did we already get it?
458 */
459 for (list_index = 0; list_index < forward_index; list_index++) {
460 if (current_entry == dgraph->load_order[list_index]) {
461 already_got_it = 1;
462 break;
463 }
464 }
465
466 if (already_got_it) {
467 continue;
468 }
469
470 /* Haven't seen it before; tack it onto the load-order list.
471 */
472 dgraph->load_order[forward_index++] = current_entry;
473
474 } while (backward_index > 0);
475
476 free(backward_load_order);
477
478 return 1;
479 }
480
481 /*******************************************************************************
482 *
483 *******************************************************************************/
484 void dgraph_log(dgraph_t * depgraph)
485 {
486 unsigned int i, j;
487
488 kload_log_message("flattened dependency list: " KNL);
489 for (i = 0; i < depgraph->length; i++) {
490 dgraph_entry_t * current = depgraph->graph[i];
491
492 kload_log_message(" %s" KNL, current->name);
493 kload_log_message(" is kernel component: %s" KNL,
494 current->is_kernel_component ? "yes" : "no");
495 kload_log_message(" expected kmod name: [%s]" KNL,
496 current->expected_kmod_name);
497 kload_log_message(" expected kmod vers: [%s]" KNL,
498 current->expected_kmod_vers);
499 }
500 kload_log_message("" KNL);
501
502 kload_log_message("load order dependency list: " KNL);
503 for (i = 0; i < depgraph->length; i++) {
504 dgraph_entry_t * current = depgraph->load_order[i];
505 kload_log_message(" %s" KNL, current->name);
506 }
507 kload_log_message("" KNL);
508
509 kload_log_message("dependency graph: " KNL);
510 for (i = 0; i < depgraph->length; i++) {
511 dgraph_entry_t * current = depgraph->graph[i];
512 for (j = 0; j < current->num_dependencies; j++) {
513 dgraph_entry_t * cdep = current->dependencies[j];
514 kload_log_message(" %s -> %s" KNL, current->name, cdep->name);
515 }
516 }
517 kload_log_message("" KNL);
518
519 return;
520 }
521
522 /*******************************************************************************
523 *
524 *******************************************************************************/
525 dgraph_entry_t * dgraph_find_dependent(dgraph_t * dgraph, const char * name)
526 {
527 unsigned int i;
528
529 for (i = 0; i < dgraph->length; i++) {
530 dgraph_entry_t * current_entry = dgraph->graph[i];
531 if (0 == strcmp(name, current_entry->name)) {
532 return current_entry;
533 }
534 }
535
536 return NULL;
537 }
538
539 /*******************************************************************************
540 *
541 *******************************************************************************/
542 dgraph_entry_t * dgraph_add_dependent(
543 dgraph_t * dgraph,
544 const char * name,
545 #ifdef KERNEL
546 void * object,
547 size_t object_length,
548 bool object_is_kmem,
549 #if CONFIG_MACF_KEXT
550 kmod_args_t user_data,
551 mach_msg_type_number_t user_data_length,
552 #endif
553 #endif /* KERNEL */
554 const char * expected_kmod_name,
555 const char * expected_kmod_vers,
556 vm_address_t load_address,
557 char is_kernel_component)
558 {
559 int error = 0;
560 dgraph_entry_t * found_entry = NULL;
561 dgraph_entry_t * new_entry = NULL; // free on error
562 dgraph_entry_t * the_entry = NULL; // returned
563
564 /* Already got it? Great!
565 */
566 found_entry = dgraph_find_dependent(dgraph, name);
567 if (found_entry) {
568 if (found_entry->is_kernel_component != is_kernel_component) {
569 kload_log_error(
570 "%s is already defined as a %skernel component" KNL,
571 name, found_entry->is_kernel_component ? "" : "non-");
572 error = 1;
573 goto finish;
574 }
575
576 if (load_address != 0) {
577 if (found_entry->loaded_address == 0) {
578 found_entry->do_load = 0;
579 found_entry->loaded_address = load_address;
580 } else if (found_entry->loaded_address != load_address) {
581 kload_log_error(
582 "%s has been assigned two different addresses (0x%x, 0x%x) KNL",
583 found_entry->name,
584 found_entry->loaded_address,
585 load_address);
586 error = 1;
587 goto finish;
588 }
589 }
590 the_entry = found_entry;
591 goto finish;
592 }
593
594 /* If the graph is full, make it bigger.
595 */
596 if (dgraph->length == dgraph->capacity) {
597 unsigned int old_capacity = dgraph->capacity;
598 dgraph_entry_t ** newgraph;
599
600 dgraph->capacity *= 2;
601 newgraph = (dgraph_entry_t **)malloc(dgraph->capacity *
602 sizeof(dgraph_entry_t *));
603 if (!newgraph) {
604 return NULL;
605 }
606 memcpy(newgraph, dgraph->graph, old_capacity * sizeof(dgraph_entry_t *));
607 free(dgraph->graph);
608 dgraph->graph = newgraph;
609 }
610
611 if (strlen(expected_kmod_name) > KMOD_MAX_NAME - 1) {
612 kload_log_error("expected kmod name \"%s\" is too long" KNL,
613 expected_kmod_name);
614 error = 1;
615 goto finish;
616 }
617
618 /* Fill it.
619 */
620 new_entry = (dgraph_entry_t *)malloc(sizeof(dgraph_entry_t));
621 if (!new_entry) {
622 error = 1;
623 goto finish;
624 }
625 bzero(new_entry, sizeof(dgraph_entry_t));
626 new_entry->expected_kmod_name = dgstrdup(expected_kmod_name);
627 if (!new_entry->expected_kmod_name) {
628 error = 1;
629 goto finish;
630 }
631 new_entry->expected_kmod_vers = dgstrdup(expected_kmod_vers);
632 if (!new_entry->expected_kmod_vers) {
633 error = 1;
634 goto finish;
635 }
636 new_entry->is_kernel_component = is_kernel_component;
637
638 // /hacks
639 new_entry->is_symbol_set = (2 & is_kernel_component);
640
641 new_entry->opaques = 0;
642 if (!strncmp(new_entry->expected_kmod_name,
643 "com.apple.kpi", strlen("com.apple.kpi")))
644 new_entry->opaques |= kOpaqueLink;
645 if (!strcmp(new_entry->expected_kmod_name,
646 "com.apple.kernel"))
647 new_entry->opaques |= kOpaqueLink | kRawKernelLink;
648 // hacks/
649
650 dgraph->has_symbol_sets |= new_entry->is_symbol_set;
651
652 new_entry->do_load = !is_kernel_component;
653
654 #ifndef KERNEL
655 new_entry->object = NULL; // provided elswehere in userland
656 new_entry->object_length = 0;
657 #else
658 new_entry->object = object;
659 new_entry->object_length = object_length;
660 new_entry->object_is_kmem = object_is_kmem;
661 #if CONFIG_MACF_KEXT
662 new_entry->user_data = user_data;
663 new_entry->user_data_length = user_data_length;
664 #endif
665 #endif /* KERNEL */
666 new_entry->name = dgstrdup(name);
667 if (!new_entry->name) {
668 error = 1;
669 goto finish;
670 }
671 dgraph->graph[dgraph->length++] = new_entry;
672
673
674 /* Create a dependency list for the entry. Start with 5 slots.
675 */
676 new_entry->dependencies_capacity = 5;
677 new_entry->num_dependencies = 0;
678 new_entry->dependencies = (dgraph_entry_t **)malloc(
679 new_entry->dependencies_capacity * sizeof(dgraph_entry_t *));
680 if (!new_entry->dependencies) {
681 error = 1;
682 goto finish;
683 }
684
685 if (new_entry->loaded_address == 0) {
686 new_entry->loaded_address = load_address;
687 if (load_address != 0) {
688 new_entry->do_load = 0;
689 }
690 }
691
692 the_entry = new_entry;
693
694 finish:
695 if (error) {
696 if (new_entry) __dgraph_entry_free(new_entry);
697 the_entry = new_entry = NULL;
698 }
699 return the_entry;
700 }
701
702 /*******************************************************************************
703 *
704 *******************************************************************************/
705 dgraph_entry_t * dgraph_add_dependency(
706 dgraph_t * dgraph,
707 dgraph_entry_t * current_dependent,
708 const char * name,
709 #ifdef KERNEL
710 void * object,
711 size_t object_length,
712 bool object_is_kmem,
713 #if CONFIG_MACF_KEXT
714 kmod_args_t user_data,
715 mach_msg_type_number_t user_data_length,
716 #endif
717 #endif /* KERNEL */
718 const char * expected_kmod_name,
719 const char * expected_kmod_vers,
720 vm_address_t load_address,
721 char is_kernel_component)
722 {
723 dgraph_entry_t * dependency = NULL;
724 unsigned int i = 0;
725
726 /* If the dependent's dependency list is full, make it bigger.
727 */
728 if (current_dependent->num_dependencies ==
729 current_dependent->dependencies_capacity) {
730
731 unsigned int old_capacity = current_dependent->dependencies_capacity;
732 dgraph_entry_t ** newlist;
733
734 current_dependent->dependencies_capacity *= 2;
735 newlist = (dgraph_entry_t **)malloc(
736 (current_dependent->dependencies_capacity *
737 sizeof(dgraph_entry_t *)) );
738
739 if (!newlist) {
740 return NULL;
741 }
742 memcpy(newlist, current_dependent->dependencies,
743 old_capacity * sizeof(dgraph_entry_t *));
744 free(current_dependent->dependencies);
745 current_dependent->dependencies = newlist;
746 }
747
748
749 /* Find or add the entry for the new dependency.
750 */
751 dependency = dgraph_add_dependent(dgraph, name,
752 #ifdef KERNEL
753 object, object_length, object_is_kmem,
754 #if CONFIG_MACF_KEXT
755 user_data, user_data_length,
756 #endif
757 #endif /* KERNEL */
758 expected_kmod_name, expected_kmod_vers, load_address,
759 is_kernel_component);
760 if (!dependency) {
761 return NULL;
762 }
763
764 if (dependency == current_dependent) {
765 kload_log_error("attempt to set dependency on itself: %s" KNL,
766 current_dependent->name);
767 return NULL;
768 }
769
770 for (i = 0; i < current_dependent->num_dependencies; i++) {
771 dgraph_entry_t * this_dependency = current_dependent->dependencies[i];
772 if (this_dependency == dependency) {
773 return dependency;
774 }
775 }
776
777 /* Fill in the dependency.
778 */
779 current_dependent->dependencies[current_dependent->num_dependencies] =
780 dependency;
781 current_dependent->num_dependencies++;
782
783 current_dependent->opaque_link |= dependency->opaques;
784 dgraph->has_opaque_links |= current_dependent->opaque_link;
785
786 return dependency;
787 }