2 * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_OSREFERENCE_HEADER_START@
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
10 * License may not be used to create, or enable the creation or
11 * redistribution of, unlawful or unlicensed copies of an Apple operating
12 * system, or to circumvent, violate, or enable the circumvention or
13 * violation of, any terms of an Apple operating system software license
16 * Please obtain a copy of the License at
17 * http://www.opensource.apple.com/apsl/ and read it before using this
20 * The Original Code and all software distributed under the License are
21 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
22 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
23 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
25 * Please see the License for the specific language governing rights and
26 * limitations under the License.
28 * @APPLE_LICENSE_OSREFERENCE_HEADER_END@
32 #include <libsa/vers_rsrc.h>
42 #include "vers_rsrc.h"
49 static void __dgraph_entry_free(dgraph_entry_t
* entry
);
52 /*******************************************************************************
54 *******************************************************************************/
55 char * strdup(const char * string
)
60 length
= strlen(string
);
61 dup
= (char *)malloc((1+length
) * sizeof(char));
71 /*******************************************************************************
73 *******************************************************************************/
74 dgraph_error_t
dgraph_init(dgraph_t
* dgraph
)
76 bzero(dgraph
, sizeof(dgraph_t
));
78 dgraph
->capacity
= (5); // pulled from a hat
80 /* Make sure list is big enough & graph has a good start size.
82 dgraph
->graph
= (dgraph_entry_t
**)malloc(
83 dgraph
->capacity
* sizeof(dgraph_entry_t
*));
93 /*******************************************************************************
95 *******************************************************************************/
96 dgraph_error_t
dgraph_init_with_arglist(
99 const char * dependency_delimiter
,
100 const char * kernel_dependency_delimiter
,
104 dgraph_error_t result
= dgraph_valid
;
106 int found_zero_load_address
= 0;
107 int found_nonzero_load_address
= 0;
108 dgraph_entry_t
* current_dependent
= NULL
;
109 char kernel_dependencies
= 0;
111 result
= dgraph_init(dgraph
);
112 if (result
!= dgraph_valid
) {
116 for (i
= 0; i
< argc
; i
++) {
117 vm_address_t load_address
= 0;
119 if (0 == strcmp(argv
[i
], dependency_delimiter
)) {
120 kernel_dependencies
= 0;
121 current_dependent
= NULL
;
123 } else if (0 == strcmp(argv
[i
], kernel_dependency_delimiter
)) {
124 kernel_dependencies
= 1;
125 current_dependent
= NULL
;
129 if (expect_addresses
) {
130 char * address
= rindex(argv
[i
], '@');
132 *address
++ = 0; // snip the address from the filename
133 load_address
= strtoul(address
, NULL
, 0);
137 if (!current_dependent
) {
138 current_dependent
= dgraph_add_dependent(dgraph
, argv
[i
],
139 /* expected kmod name */ NULL
, /* expected vers */ 0,
141 if (!current_dependent
) {
145 if (!dgraph_add_dependency(dgraph
, current_dependent
, argv
[i
],
146 /* expected kmod name */ NULL
, /* expected vers */ 0,
147 load_address
, kernel_dependencies
)) {
154 dgraph
->root
= dgraph_find_root(dgraph
);
155 dgraph_establish_load_order(dgraph
);
158 kload_log_error("dependency graph has no root" KNL
);
159 return dgraph_invalid
;
162 if (dgraph
->root
->is_kernel_component
&& !dgraph
->root
->is_symbol_set
) {
163 kload_log_error("dependency graph root is a kernel component" KNL
);
164 return dgraph_invalid
;
167 for (i
= 0; i
< dgraph
->length
; i
++) {
168 if (dgraph
->graph
[i
]->loaded_address
== 0) {
169 found_zero_load_address
= 1;
171 found_nonzero_load_address
= 1;
174 (found_zero_load_address
&& found_nonzero_load_address
)) {
177 "load addresses must be specified for all module files" KNL
);
178 return dgraph_invalid
;
184 #endif /* not KERNEL */
186 /*******************************************************************************
188 *******************************************************************************/
189 static void __dgraph_entry_free(dgraph_entry_t
* entry
)
195 if (entry
->expected_kmod_name
) {
196 free(entry
->expected_kmod_name
);
197 entry
->expected_kmod_name
= NULL
;
199 if (entry
->expected_kmod_vers
) {
200 free(entry
->expected_kmod_vers
);
201 entry
->expected_kmod_vers
= NULL
;
203 if (entry
->dependencies
) {
204 free(entry
->dependencies
);
205 entry
->dependencies
= NULL
;
207 if (entry
->symbols_malloc
) {
208 free((void *) entry
->symbols_malloc
);
209 entry
->symbols_malloc
= NULL
;
215 /*******************************************************************************
217 *******************************************************************************/
222 unsigned int entry_index
;
228 for (entry_index
= 0; entry_index
< dgraph
->length
; entry_index
++) {
229 dgraph_entry_t
* current
= dgraph
->graph
[entry_index
];
230 __dgraph_entry_free(current
);
235 dgraph
->graph
= NULL
;
238 if (dgraph
->load_order
) {
239 free(dgraph
->load_order
);
240 dgraph
->load_order
= NULL
;
243 if (free_graph
&& dgraph
) {
251 /*******************************************************************************
253 *******************************************************************************/
254 dgraph_entry_t
* dgraph_find_root(dgraph_t
* dgraph
) {
255 dgraph_entry_t
* root
= NULL
;
256 dgraph_entry_t
* candidate
= NULL
;
257 unsigned int candidate_index
;
258 unsigned int scan_index
;
259 unsigned int dep_index
;
262 /* Scan each entry in the graph for one that isn't in any other entry's
265 for (candidate_index
= 0; candidate_index
< dgraph
->length
;
268 candidate
= dgraph
->graph
[candidate_index
];
270 for (scan_index
= 0; scan_index
< dgraph
->length
; scan_index
++) {
272 dgraph_entry_t
* scan_entry
= dgraph
->graph
[scan_index
];
273 if (candidate
== scan_entry
) {
274 // don't check yourself
277 for (dep_index
= 0; dep_index
< scan_entry
->num_dependencies
;
280 /* If the dependency being checked is the candidate,
281 * then the candidate can't be the root.
283 dgraph_entry_t
* check
= scan_entry
->dependencies
[dep_index
];
285 if (check
== candidate
) {
291 /* If the candidate was rejected, then hop out of this loop.
298 /* If we got here, the candidate is a valid one. However, if we already
299 * found another, that means we have two possible roots (or more), which
304 kload_log_error("dependency graph has multiple roots "
305 "(%s and %s)" KNL
, root
->name
, candidate
->name
);
306 return NULL
; // two valid roots, illegal
314 kload_log_error("dependency graph has no root node" KNL
);
320 /*******************************************************************************
322 *******************************************************************************/
323 dgraph_entry_t
** fill_backward_load_order(
324 dgraph_entry_t
** backward_load_order
,
325 unsigned int * list_length
,
326 dgraph_entry_t
* first_entry
,
327 unsigned int * last_index
/* out param */)
330 unsigned int scan_index
= 0;
331 unsigned int add_index
= 0;
332 dgraph_entry_t
* scan_entry
;
334 if (*list_length
== 0) {
335 if (backward_load_order
) {
336 free(backward_load_order
);
337 backward_load_order
= NULL
;
342 backward_load_order
[add_index
++] = first_entry
;
344 while (scan_index
< add_index
) {
346 if (add_index
> 255) {
348 "dependency list for %s ridiculously long; probably a loop" KNL
,
350 if (backward_load_order
) {
351 free(backward_load_order
);
352 backward_load_order
= NULL
;
357 scan_entry
= backward_load_order
[scan_index
++];
359 /* Increase the load order list if needed.
361 if (add_index
+ scan_entry
->num_dependencies
> (*list_length
)) {
363 backward_load_order
= (dgraph_entry_t
**)realloc(
365 (*list_length
) * sizeof(dgraph_entry_t
*));
366 if (!backward_load_order
) {
371 /* Put the dependencies of the scanning entry into the list.
373 for (i
= 0; i
< scan_entry
->num_dependencies
; i
++) {
374 backward_load_order
[add_index
++] =
375 scan_entry
->dependencies
[i
];
382 *last_index
= add_index
;
384 return backward_load_order
;
387 /*******************************************************************************
389 *******************************************************************************/
390 int dgraph_establish_load_order(dgraph_t
* dgraph
) {
391 unsigned int total_dependencies
;
392 unsigned int entry_index
;
393 unsigned int list_index
;
394 unsigned int backward_index
;
395 unsigned int forward_index
;
396 size_t load_order_size
;
397 size_t backward_load_order_size
;
398 dgraph_entry_t
** backward_load_order
;
400 /* Lose the old load_order list. Size can change, so it's easier to just
401 * recreate from scratch.
403 if (dgraph
->load_order
) {
404 free(dgraph
->load_order
);
405 dgraph
->load_order
= NULL
;
408 /* Figure how long the list needs to be to accommodate the max possible
409 * entries from the graph. Duplicates get weeded out, but the list
410 * initially has to accommodate them all.
412 total_dependencies
= dgraph
->length
;
414 for (entry_index
= 0; entry_index
< dgraph
->length
; entry_index
++) {
415 dgraph_entry_t
* curdep
= dgraph
->graph
[entry_index
];
416 total_dependencies
+= curdep
->num_dependencies
;
419 /* Hmm, nothing to do!
421 if (!total_dependencies
) {
425 backward_load_order_size
= total_dependencies
* sizeof(dgraph_entry_t
*);
427 backward_load_order
= (dgraph_entry_t
**)malloc(backward_load_order_size
);
428 if (!backward_load_order
) {
429 kload_log_error("malloc failure" KNL
);
432 bzero(backward_load_order
, backward_load_order_size
);
434 backward_load_order
= fill_backward_load_order(backward_load_order
,
435 &total_dependencies
, dgraph
->root
, &list_index
);
436 if (!backward_load_order
) {
437 kload_log_error("error establishing load order" KNL
);
441 load_order_size
= dgraph
->length
* sizeof(dgraph_entry_t
*);
442 dgraph
->load_order
= (dgraph_entry_t
**)malloc(load_order_size
);
443 if (!dgraph
->load_order
) {
444 kload_log_error("malloc failure" KNL
);
447 bzero(dgraph
->load_order
, load_order_size
);
450 /* Reverse the list into the dgraph's load_order list,
451 * removing any duplicates.
453 backward_index
= list_index
;
455 // the required 1 is taken off in loop below!
459 dgraph_entry_t
* current_entry
;
460 unsigned int already_got_it
= 0;
464 /* Get the entry to check.
466 current_entry
= backward_load_order
[backward_index
];
468 /* Did we already get it?
470 for (list_index
= 0; list_index
< forward_index
; list_index
++) {
471 if (current_entry
== dgraph
->load_order
[list_index
]) {
477 if (already_got_it
) {
481 /* Haven't seen it before; tack it onto the load-order list.
483 dgraph
->load_order
[forward_index
++] = current_entry
;
485 } while (backward_index
> 0);
487 free(backward_load_order
);
492 /*******************************************************************************
494 *******************************************************************************/
495 void dgraph_log(dgraph_t
* depgraph
)
499 kload_log_message("flattened dependency list: " KNL
);
500 for (i
= 0; i
< depgraph
->length
; i
++) {
501 dgraph_entry_t
* current
= depgraph
->graph
[i
];
503 kload_log_message(" %s" KNL
, current
->name
);
504 kload_log_message(" is kernel component: %s" KNL
,
505 current
->is_kernel_component
? "yes" : "no");
506 kload_log_message(" expected kmod name: [%s]" KNL
,
507 current
->expected_kmod_name
);
508 kload_log_message(" expected kmod vers: [%s]" KNL
,
509 current
->expected_kmod_vers
);
511 kload_log_message("" KNL
);
513 kload_log_message("load order dependency list: " KNL
);
514 for (i
= 0; i
< depgraph
->length
; i
++) {
515 dgraph_entry_t
* current
= depgraph
->load_order
[i
];
516 kload_log_message(" %s" KNL
, current
->name
);
518 kload_log_message("" KNL
);
520 kload_log_message("dependency graph: " KNL
);
521 for (i
= 0; i
< depgraph
->length
; i
++) {
522 dgraph_entry_t
* current
= depgraph
->graph
[i
];
523 for (j
= 0; j
< current
->num_dependencies
; j
++) {
524 dgraph_entry_t
* cdep
= current
->dependencies
[j
];
525 kload_log_message(" %s -> %s" KNL
, current
->name
, cdep
->name
);
528 kload_log_message("" KNL
);
533 /*******************************************************************************
535 *******************************************************************************/
536 dgraph_entry_t
* dgraph_find_dependent(dgraph_t
* dgraph
, const char * name
)
540 for (i
= 0; i
< dgraph
->length
; i
++) {
541 dgraph_entry_t
* current_entry
= dgraph
->graph
[i
];
542 if (0 == strcmp(name
, current_entry
->name
)) {
543 return current_entry
;
550 /*******************************************************************************
552 *******************************************************************************/
553 dgraph_entry_t
* dgraph_add_dependent(
558 size_t object_length
,
561 const char * expected_kmod_name
,
562 const char * expected_kmod_vers
,
563 vm_address_t load_address
,
564 char is_kernel_component
)
567 dgraph_entry_t
* found_entry
= NULL
;
568 dgraph_entry_t
* new_entry
= NULL
; // free on error
569 dgraph_entry_t
* the_entry
= NULL
; // returned
571 /* Already got it? Great!
573 found_entry
= dgraph_find_dependent(dgraph
, name
);
575 if (found_entry
->is_kernel_component
!= is_kernel_component
) {
577 "%s is already defined as a %skernel component" KNL
,
578 name
, found_entry
->is_kernel_component
? "" : "non-");
583 if (load_address
!= 0) {
584 if (found_entry
->loaded_address
== 0) {
585 found_entry
->do_load
= 0;
586 found_entry
->loaded_address
= load_address
;
587 } else if (found_entry
->loaded_address
!= load_address
) {
589 "%s has been assigned two different addresses (0x%x, 0x%x) KNL",
591 found_entry
->loaded_address
,
597 the_entry
= found_entry
;
601 /* If the graph is full, make it bigger.
603 if (dgraph
->length
== dgraph
->capacity
) {
604 unsigned int old_capacity
= dgraph
->capacity
;
605 dgraph_entry_t
** newgraph
;
607 dgraph
->capacity
*= 2;
608 newgraph
= (dgraph_entry_t
**)malloc(dgraph
->capacity
*
609 sizeof(dgraph_entry_t
*));
613 memcpy(newgraph
, dgraph
->graph
, old_capacity
* sizeof(dgraph_entry_t
*));
615 dgraph
->graph
= newgraph
;
618 if (strlen(expected_kmod_name
) > KMOD_MAX_NAME
- 1) {
619 kload_log_error("expected kmod name \"%s\" is too long" KNL
,
627 new_entry
= (dgraph_entry_t
*)malloc(sizeof(dgraph_entry_t
));
632 bzero(new_entry
, sizeof(dgraph_entry_t
));
633 new_entry
->expected_kmod_name
= strdup(expected_kmod_name
);
634 if (!new_entry
->expected_kmod_name
) {
638 new_entry
->expected_kmod_vers
= strdup(expected_kmod_vers
);
639 if (!new_entry
->expected_kmod_vers
) {
643 new_entry
->is_kernel_component
= is_kernel_component
;
646 new_entry
->is_symbol_set
= (2 & is_kernel_component
);
648 new_entry
->opaques
= 0;
649 if (!strncmp(new_entry
->expected_kmod_name
,
650 "com.apple.kpi", strlen("com.apple.kpi")))
651 new_entry
->opaques
|= kOpaqueLink
;
652 if (!strcmp(new_entry
->expected_kmod_name
,
654 new_entry
->opaques
|= kOpaqueLink
| kRawKernelLink
;
657 dgraph
->has_symbol_sets
|= new_entry
->is_symbol_set
;
659 new_entry
->do_load
= !is_kernel_component
;
662 new_entry
->object
= NULL
; // provided elswehere in userland
663 new_entry
->object_length
= 0;
665 new_entry
->object
= object
;
666 new_entry
->object_length
= object_length
;
667 new_entry
->object_is_kmem
= object_is_kmem
;
669 new_entry
->name
= strdup(name
);
670 if (!new_entry
->name
) {
674 dgraph
->graph
[dgraph
->length
++] = new_entry
;
677 /* Create a dependency list for the entry. Start with 5 slots.
679 new_entry
->dependencies_capacity
= 5;
680 new_entry
->num_dependencies
= 0;
681 new_entry
->dependencies
= (dgraph_entry_t
**)malloc(
682 new_entry
->dependencies_capacity
* sizeof(dgraph_entry_t
*));
683 if (!new_entry
->dependencies
) {
688 if (new_entry
->loaded_address
== 0) {
689 new_entry
->loaded_address
= load_address
;
690 if (load_address
!= 0) {
691 new_entry
->do_load
= 0;
695 the_entry
= new_entry
;
699 if (new_entry
) __dgraph_entry_free(new_entry
);
700 the_entry
= new_entry
= NULL
;
705 /*******************************************************************************
707 *******************************************************************************/
708 dgraph_entry_t
* dgraph_add_dependency(
710 dgraph_entry_t
* current_dependent
,
714 size_t object_length
,
717 const char * expected_kmod_name
,
718 const char * expected_kmod_vers
,
719 vm_address_t load_address
,
720 char is_kernel_component
)
722 dgraph_entry_t
* dependency
= NULL
;
725 /* If the dependent's dependency list is full, make it bigger.
727 if (current_dependent
->num_dependencies
==
728 current_dependent
->dependencies_capacity
) {
730 unsigned int old_capacity
= current_dependent
->dependencies_capacity
;
731 dgraph_entry_t
** newlist
;
733 current_dependent
->dependencies_capacity
*= 2;
734 newlist
= (dgraph_entry_t
**)malloc(
735 (current_dependent
->dependencies_capacity
*
736 sizeof(dgraph_entry_t
*)) );
741 memcpy(newlist
, current_dependent
->dependencies
,
742 old_capacity
* sizeof(dgraph_entry_t
*));
743 free(current_dependent
->dependencies
);
744 current_dependent
->dependencies
= newlist
;
748 /* Find or add the entry for the new dependency.
750 dependency
= dgraph_add_dependent(dgraph
, name
,
752 object
, object_length
, object_is_kmem
,
754 expected_kmod_name
, expected_kmod_vers
, load_address
,
755 is_kernel_component
);
760 if (dependency
== current_dependent
) {
761 kload_log_error("attempt to set dependency on itself: %s" KNL
,
762 current_dependent
->name
);
766 for (i
= 0; i
< current_dependent
->num_dependencies
; i
++) {
767 dgraph_entry_t
* this_dependency
= current_dependent
->dependencies
[i
];
768 if (this_dependency
== dependency
) {
773 /* Fill in the dependency.
775 current_dependent
->dependencies
[current_dependent
->num_dependencies
] =
777 current_dependent
->num_dependencies
++;
779 current_dependent
->opaque_link
|= dependency
->opaques
;
780 dgraph
->has_opaque_links
|= current_dependent
->opaque_link
;