2 * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_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 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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
30 #include <libsa/vers_rsrc.h>
40 #include "vers_rsrc.h"
47 static void __dgraph_entry_free(dgraph_entry_t
* entry
);
50 /*******************************************************************************
52 *******************************************************************************/
53 char * strdup(const char * string
)
58 length
= strlen(string
);
59 dup
= (char *)malloc((1+length
) * sizeof(char));
69 /*******************************************************************************
71 *******************************************************************************/
72 dgraph_error_t
dgraph_init(dgraph_t
* dgraph
)
74 bzero(dgraph
, sizeof(dgraph_t
));
76 dgraph
->capacity
= (5); // pulled from a hat
78 /* Make sure list is big enough & graph has a good start size.
80 dgraph
->graph
= (dgraph_entry_t
**)malloc(
81 dgraph
->capacity
* sizeof(dgraph_entry_t
*));
91 /*******************************************************************************
93 *******************************************************************************/
94 dgraph_error_t
dgraph_init_with_arglist(
97 const char * dependency_delimiter
,
98 const char * kernel_dependency_delimiter
,
102 dgraph_error_t result
= dgraph_valid
;
104 int found_zero_load_address
= 0;
105 int found_nonzero_load_address
= 0;
106 dgraph_entry_t
* current_dependent
= NULL
;
107 char kernel_dependencies
= 0;
109 result
= dgraph_init(dgraph
);
110 if (result
!= dgraph_valid
) {
114 for (i
= 0; i
< argc
; i
++) {
115 vm_address_t load_address
= 0;
117 if (0 == strcmp(argv
[i
], dependency_delimiter
)) {
118 kernel_dependencies
= 0;
119 current_dependent
= NULL
;
121 } else if (0 == strcmp(argv
[i
], kernel_dependency_delimiter
)) {
122 kernel_dependencies
= 1;
123 current_dependent
= NULL
;
127 if (expect_addresses
) {
128 char * address
= rindex(argv
[i
], '@');
130 *address
++ = 0; // snip the address from the filename
131 load_address
= strtoul(address
, NULL
, 0);
135 if (!current_dependent
) {
136 current_dependent
= dgraph_add_dependent(dgraph
, argv
[i
],
137 /* expected kmod name */ NULL
, /* expected vers */ 0,
139 if (!current_dependent
) {
143 if (!dgraph_add_dependency(dgraph
, current_dependent
, argv
[i
],
144 /* expected kmod name */ NULL
, /* expected vers */ 0,
145 load_address
, kernel_dependencies
)) {
152 dgraph
->root
= dgraph_find_root(dgraph
);
153 dgraph_establish_load_order(dgraph
);
156 kload_log_error("dependency graph has no root" KNL
);
157 return dgraph_invalid
;
160 if (dgraph
->root
->is_kernel_component
&& !dgraph
->root
->is_symbol_set
) {
161 kload_log_error("dependency graph root is a kernel component" KNL
);
162 return dgraph_invalid
;
165 for (i
= 0; i
< dgraph
->length
; i
++) {
166 if (dgraph
->graph
[i
]->loaded_address
== 0) {
167 found_zero_load_address
= 1;
169 found_nonzero_load_address
= 1;
172 (found_zero_load_address
&& found_nonzero_load_address
)) {
175 "load addresses must be specified for all module files" KNL
);
176 return dgraph_invalid
;
182 #endif /* not KERNEL */
184 /*******************************************************************************
186 *******************************************************************************/
187 static void __dgraph_entry_free(dgraph_entry_t
* entry
)
193 if (entry
->expected_kmod_name
) {
194 free(entry
->expected_kmod_name
);
195 entry
->expected_kmod_name
= NULL
;
197 if (entry
->expected_kmod_vers
) {
198 free(entry
->expected_kmod_vers
);
199 entry
->expected_kmod_vers
= NULL
;
201 if (entry
->dependencies
) {
202 free(entry
->dependencies
);
203 entry
->dependencies
= NULL
;
205 if (entry
->symbols_malloc
) {
206 free((void *) entry
->symbols_malloc
);
207 entry
->symbols_malloc
= NULL
;
213 /*******************************************************************************
215 *******************************************************************************/
220 unsigned int entry_index
;
226 for (entry_index
= 0; entry_index
< dgraph
->length
; entry_index
++) {
227 dgraph_entry_t
* current
= dgraph
->graph
[entry_index
];
228 __dgraph_entry_free(current
);
233 dgraph
->graph
= NULL
;
236 if (dgraph
->load_order
) {
237 free(dgraph
->load_order
);
238 dgraph
->load_order
= NULL
;
241 if (free_graph
&& dgraph
) {
249 /*******************************************************************************
251 *******************************************************************************/
252 dgraph_entry_t
* dgraph_find_root(dgraph_t
* dgraph
) {
253 dgraph_entry_t
* root
= NULL
;
254 dgraph_entry_t
* candidate
= NULL
;
255 unsigned int candidate_index
;
256 unsigned int scan_index
;
257 unsigned int dep_index
;
260 /* Scan each entry in the graph for one that isn't in any other entry's
263 for (candidate_index
= 0; candidate_index
< dgraph
->length
;
266 candidate
= dgraph
->graph
[candidate_index
];
268 for (scan_index
= 0; scan_index
< dgraph
->length
; scan_index
++) {
270 dgraph_entry_t
* scan_entry
= dgraph
->graph
[scan_index
];
271 if (candidate
== scan_entry
) {
272 // don't check yourself
275 for (dep_index
= 0; dep_index
< scan_entry
->num_dependencies
;
278 /* If the dependency being checked is the candidate,
279 * then the candidate can't be the root.
281 dgraph_entry_t
* check
= scan_entry
->dependencies
[dep_index
];
283 if (check
== candidate
) {
289 /* If the candidate was rejected, then hop out of this loop.
296 /* If we got here, the candidate is a valid one. However, if we already
297 * found another, that means we have two possible roots (or more), which
302 kload_log_error("dependency graph has multiple roots "
303 "(%s and %s)" KNL
, root
->name
, candidate
->name
);
304 return NULL
; // two valid roots, illegal
312 kload_log_error("dependency graph has no root node" KNL
);
318 /*******************************************************************************
320 *******************************************************************************/
321 dgraph_entry_t
** fill_backward_load_order(
322 dgraph_entry_t
** backward_load_order
,
323 unsigned int * list_length
,
324 dgraph_entry_t
* first_entry
,
325 unsigned int * last_index
/* out param */)
328 unsigned int scan_index
= 0;
329 unsigned int add_index
= 0;
330 dgraph_entry_t
* scan_entry
;
332 if (*list_length
== 0) {
333 if (backward_load_order
) {
334 free(backward_load_order
);
335 backward_load_order
= NULL
;
340 backward_load_order
[add_index
++] = first_entry
;
342 while (scan_index
< add_index
) {
344 if (add_index
> 255) {
346 "dependency list for %s ridiculously long; probably a loop" KNL
,
348 if (backward_load_order
) {
349 free(backward_load_order
);
350 backward_load_order
= NULL
;
355 scan_entry
= backward_load_order
[scan_index
++];
357 /* Increase the load order list if needed.
359 if (add_index
+ scan_entry
->num_dependencies
> (*list_length
)) {
361 backward_load_order
= (dgraph_entry_t
**)realloc(
363 (*list_length
) * sizeof(dgraph_entry_t
*));
364 if (!backward_load_order
) {
369 /* Put the dependencies of the scanning entry into the list.
371 for (i
= 0; i
< scan_entry
->num_dependencies
; i
++) {
372 backward_load_order
[add_index
++] =
373 scan_entry
->dependencies
[i
];
380 *last_index
= add_index
;
382 return backward_load_order
;
385 /*******************************************************************************
387 *******************************************************************************/
388 int dgraph_establish_load_order(dgraph_t
* dgraph
) {
389 unsigned int total_dependencies
;
390 unsigned int entry_index
;
391 unsigned int list_index
;
392 unsigned int backward_index
;
393 unsigned int forward_index
;
394 size_t load_order_size
;
395 size_t backward_load_order_size
;
396 dgraph_entry_t
** backward_load_order
;
398 /* Lose the old load_order list. Size can change, so it's easier to just
399 * recreate from scratch.
401 if (dgraph
->load_order
) {
402 free(dgraph
->load_order
);
403 dgraph
->load_order
= NULL
;
406 /* Figure how long the list needs to be to accommodate the max possible
407 * entries from the graph. Duplicates get weeded out, but the list
408 * initially has to accommodate them all.
410 total_dependencies
= dgraph
->length
;
412 for (entry_index
= 0; entry_index
< dgraph
->length
; entry_index
++) {
413 dgraph_entry_t
* curdep
= dgraph
->graph
[entry_index
];
414 total_dependencies
+= curdep
->num_dependencies
;
417 /* Hmm, nothing to do!
419 if (!total_dependencies
) {
423 backward_load_order_size
= total_dependencies
* sizeof(dgraph_entry_t
*);
425 backward_load_order
= (dgraph_entry_t
**)malloc(backward_load_order_size
);
426 if (!backward_load_order
) {
427 kload_log_error("malloc failure" KNL
);
430 bzero(backward_load_order
, backward_load_order_size
);
432 backward_load_order
= fill_backward_load_order(backward_load_order
,
433 &total_dependencies
, dgraph
->root
, &list_index
);
434 if (!backward_load_order
) {
435 kload_log_error("error establishing load order" KNL
);
439 load_order_size
= dgraph
->length
* sizeof(dgraph_entry_t
*);
440 dgraph
->load_order
= (dgraph_entry_t
**)malloc(load_order_size
);
441 if (!dgraph
->load_order
) {
442 kload_log_error("malloc failure" KNL
);
445 bzero(dgraph
->load_order
, load_order_size
);
448 /* Reverse the list into the dgraph's load_order list,
449 * removing any duplicates.
451 backward_index
= list_index
;
453 // the required 1 is taken off in loop below!
457 dgraph_entry_t
* current_entry
;
458 unsigned int already_got_it
= 0;
462 /* Get the entry to check.
464 current_entry
= backward_load_order
[backward_index
];
466 /* Did we already get it?
468 for (list_index
= 0; list_index
< forward_index
; list_index
++) {
469 if (current_entry
== dgraph
->load_order
[list_index
]) {
475 if (already_got_it
) {
479 /* Haven't seen it before; tack it onto the load-order list.
481 dgraph
->load_order
[forward_index
++] = current_entry
;
483 } while (backward_index
> 0);
485 free(backward_load_order
);
490 /*******************************************************************************
492 *******************************************************************************/
493 void dgraph_log(dgraph_t
* depgraph
)
497 kload_log_message("flattened dependency list: " KNL
);
498 for (i
= 0; i
< depgraph
->length
; i
++) {
499 dgraph_entry_t
* current
= depgraph
->graph
[i
];
501 kload_log_message(" %s" KNL
, current
->name
);
502 kload_log_message(" is kernel component: %s" KNL
,
503 current
->is_kernel_component
? "yes" : "no");
504 kload_log_message(" expected kmod name: [%s]" KNL
,
505 current
->expected_kmod_name
);
506 kload_log_message(" expected kmod vers: [%s]" KNL
,
507 current
->expected_kmod_vers
);
509 kload_log_message("" KNL
);
511 kload_log_message("load order dependency list: " KNL
);
512 for (i
= 0; i
< depgraph
->length
; i
++) {
513 dgraph_entry_t
* current
= depgraph
->load_order
[i
];
514 kload_log_message(" %s" KNL
, current
->name
);
516 kload_log_message("" KNL
);
518 kload_log_message("dependency graph: " KNL
);
519 for (i
= 0; i
< depgraph
->length
; i
++) {
520 dgraph_entry_t
* current
= depgraph
->graph
[i
];
521 for (j
= 0; j
< current
->num_dependencies
; j
++) {
522 dgraph_entry_t
* cdep
= current
->dependencies
[j
];
523 kload_log_message(" %s -> %s" KNL
, current
->name
, cdep
->name
);
526 kload_log_message("" KNL
);
531 /*******************************************************************************
533 *******************************************************************************/
534 dgraph_entry_t
* dgraph_find_dependent(dgraph_t
* dgraph
, const char * name
)
538 for (i
= 0; i
< dgraph
->length
; i
++) {
539 dgraph_entry_t
* current_entry
= dgraph
->graph
[i
];
540 if (0 == strcmp(name
, current_entry
->name
)) {
541 return current_entry
;
548 /*******************************************************************************
550 *******************************************************************************/
551 dgraph_entry_t
* dgraph_add_dependent(
556 size_t object_length
,
559 const char * expected_kmod_name
,
560 const char * expected_kmod_vers
,
561 vm_address_t load_address
,
562 char is_kernel_component
)
565 dgraph_entry_t
* found_entry
= NULL
;
566 dgraph_entry_t
* new_entry
= NULL
; // free on error
567 dgraph_entry_t
* the_entry
= NULL
; // returned
569 /* Already got it? Great!
571 found_entry
= dgraph_find_dependent(dgraph
, name
);
573 if (found_entry
->is_kernel_component
!= is_kernel_component
) {
575 "%s is already defined as a %skernel component" KNL
,
576 name
, found_entry
->is_kernel_component
? "" : "non-");
581 if (load_address
!= 0) {
582 if (found_entry
->loaded_address
== 0) {
583 found_entry
->do_load
= 0;
584 found_entry
->loaded_address
= load_address
;
585 } else if (found_entry
->loaded_address
!= load_address
) {
587 "%s has been assigned two different addresses (0x%x, 0x%x) KNL",
589 found_entry
->loaded_address
,
595 the_entry
= found_entry
;
599 /* If the graph is full, make it bigger.
601 if (dgraph
->length
== dgraph
->capacity
) {
602 unsigned int old_capacity
= dgraph
->capacity
;
603 dgraph_entry_t
** newgraph
;
605 dgraph
->capacity
*= 2;
606 newgraph
= (dgraph_entry_t
**)malloc(dgraph
->capacity
*
607 sizeof(dgraph_entry_t
*));
611 memcpy(newgraph
, dgraph
->graph
, old_capacity
* sizeof(dgraph_entry_t
*));
613 dgraph
->graph
= newgraph
;
616 if (strlen(expected_kmod_name
) > KMOD_MAX_NAME
- 1) {
617 kload_log_error("expected kmod name \"%s\" is too long" KNL
,
625 new_entry
= (dgraph_entry_t
*)malloc(sizeof(dgraph_entry_t
));
630 bzero(new_entry
, sizeof(dgraph_entry_t
));
631 new_entry
->expected_kmod_name
= strdup(expected_kmod_name
);
632 if (!new_entry
->expected_kmod_name
) {
636 new_entry
->expected_kmod_vers
= strdup(expected_kmod_vers
);
637 if (!new_entry
->expected_kmod_vers
) {
641 new_entry
->is_kernel_component
= is_kernel_component
;
644 new_entry
->is_symbol_set
= (2 & is_kernel_component
);
646 new_entry
->opaques
= 0;
647 if (!strncmp(new_entry
->expected_kmod_name
,
648 "com.apple.kpi", strlen("com.apple.kpi")))
649 new_entry
->opaques
|= kOpaqueLink
;
650 if (!strcmp(new_entry
->expected_kmod_name
,
652 new_entry
->opaques
|= kOpaqueLink
| kRawKernelLink
;
655 dgraph
->has_symbol_sets
|= new_entry
->is_symbol_set
;
657 new_entry
->do_load
= !is_kernel_component
;
660 new_entry
->object
= NULL
; // provided elswehere in userland
661 new_entry
->object_length
= 0;
663 new_entry
->object
= object
;
664 new_entry
->object_length
= object_length
;
665 new_entry
->object_is_kmem
= object_is_kmem
;
667 new_entry
->name
= strdup(name
);
668 if (!new_entry
->name
) {
672 dgraph
->graph
[dgraph
->length
++] = new_entry
;
675 /* Create a dependency list for the entry. Start with 5 slots.
677 new_entry
->dependencies_capacity
= 5;
678 new_entry
->num_dependencies
= 0;
679 new_entry
->dependencies
= (dgraph_entry_t
**)malloc(
680 new_entry
->dependencies_capacity
* sizeof(dgraph_entry_t
*));
681 if (!new_entry
->dependencies
) {
686 if (new_entry
->loaded_address
== 0) {
687 new_entry
->loaded_address
= load_address
;
688 if (load_address
!= 0) {
689 new_entry
->do_load
= 0;
693 the_entry
= new_entry
;
697 if (new_entry
) __dgraph_entry_free(new_entry
);
698 the_entry
= new_entry
= NULL
;
703 /*******************************************************************************
705 *******************************************************************************/
706 dgraph_entry_t
* dgraph_add_dependency(
708 dgraph_entry_t
* current_dependent
,
712 size_t object_length
,
715 const char * expected_kmod_name
,
716 const char * expected_kmod_vers
,
717 vm_address_t load_address
,
718 char is_kernel_component
)
720 dgraph_entry_t
* dependency
= NULL
;
723 /* If the dependent's dependency list is full, make it bigger.
725 if (current_dependent
->num_dependencies
==
726 current_dependent
->dependencies_capacity
) {
728 unsigned int old_capacity
= current_dependent
->dependencies_capacity
;
729 dgraph_entry_t
** newlist
;
731 current_dependent
->dependencies_capacity
*= 2;
732 newlist
= (dgraph_entry_t
**)malloc(
733 (current_dependent
->dependencies_capacity
*
734 sizeof(dgraph_entry_t
*)) );
739 memcpy(newlist
, current_dependent
->dependencies
,
740 old_capacity
* sizeof(dgraph_entry_t
*));
741 free(current_dependent
->dependencies
);
742 current_dependent
->dependencies
= newlist
;
746 /* Find or add the entry for the new dependency.
748 dependency
= dgraph_add_dependent(dgraph
, name
,
750 object
, object_length
, object_is_kmem
,
752 expected_kmod_name
, expected_kmod_vers
, load_address
,
753 is_kernel_component
);
758 if (dependency
== current_dependent
) {
759 kload_log_error("attempt to set dependency on itself: %s" KNL
,
760 current_dependent
->name
);
764 for (i
= 0; i
< current_dependent
->num_dependencies
; i
++) {
765 dgraph_entry_t
* this_dependency
= current_dependent
->dependencies
[i
];
766 if (this_dependency
== dependency
) {
771 /* Fill in the dependency.
773 current_dependent
->dependencies
[current_dependent
->num_dependencies
] =
775 current_dependent
->num_dependencies
++;
777 current_dependent
->opaque_link
|= dependency
->opaques
;
778 dgraph
->has_opaque_links
|= current_dependent
->opaque_link
;