]>
Commit | Line | Data |
---|---|---|
916eb79e A |
1 | /* |
2 | * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights | |
7 | * Reserved. This file contains Original Code and/or Modifications of | |
8 | * Original Code as defined in and that are subject to the Apple Public | |
9 | * Source License Version 1.0 (the 'License'). You may not use this file | |
10 | * except in compliance with the License. Please obtain a copy of the | |
11 | * License at http://www.apple.com/publicsource and read it before using | |
12 | * this file. | |
13 | * | |
14 | * The Original Code and all software distributed under the License are | |
15 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
16 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
17 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
18 | * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the | |
19 | * License for the specific language governing rights and limitations | |
20 | * under the License." | |
21 | * | |
22 | * @APPLE_LICENSE_HEADER_END@ | |
23 | */ | |
24 | /*- | |
25 | * Copyright (c) 1997 Doug Rabson | |
26 | * All rights reserved. | |
27 | * | |
28 | * Redistribution and use in source and binary forms, with or without | |
29 | * modification, are permitted provided that the following conditions | |
30 | * are met: | |
31 | * 1. Redistributions of source code must retain the above copyright | |
32 | * notice, this list of conditions and the following disclaimer. | |
33 | * 2. Redistributions in binary form must reproduce the above copyright | |
34 | * notice, this list of conditions and the following disclaimer in the | |
35 | * documentation and/or other materials provided with the distribution. | |
36 | * | |
37 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |
38 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
39 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
40 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |
41 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
42 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
43 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
44 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
45 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
46 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
47 | * SUCH DAMAGE. | |
48 | * | |
49 | * Original code from: | |
50 | * "kldload.c,v 1.5 1998/07/06 06:58:32 charnier Exp" | |
51 | */ | |
52 | #ifndef lint | |
53 | static const char rcsid[] = | |
54 | "$Id: kmodload.c,v 1.14 2002/04/15 20:28:30 lindak Exp $"; | |
55 | #endif /* not lint */ | |
56 | ||
57 | #include <stdlib.h> | |
58 | #include <err.h> | |
59 | #include <sys/file.h> | |
60 | #include <nlist.h> | |
61 | #include <stdio.h> | |
62 | #include <unistd.h> | |
63 | #include <sys/param.h> | |
64 | #include <sys/stat.h> | |
65 | #include <sys/mman.h> | |
66 | ||
67 | #include <paths.h> | |
68 | ||
69 | #include <mach/mach.h> | |
70 | #include <mach/mach_error.h> | |
71 | #include <mach/mach_host.h> | |
72 | #include <mach-o/kld.h> | |
73 | #include <mach-o/fat.h> | |
74 | ||
75 | #include <CoreFoundation/CoreFoundation.h> | |
76 | ||
77 | #include "kld_patch.h" | |
78 | #include "vers_rsrc.h" | |
79 | ||
80 | #define KMOD_ERROR_USAGE 1 | |
81 | #define KMOD_ERROR_PERMS 2 | |
82 | #define KMOD_ERROR_LOADING 3 | |
83 | #define KMOD_ERROR_INTERNAL 4 | |
84 | #define KMOD_ERROR_ALREADY 5 | |
85 | ||
86 | #define kKMOD_INFO_SYMBOLNAME "_kmod_info" | |
87 | #define kKmodsymsName "kmodsyms" | |
88 | ||
89 | static mach_port_t kernel_port; | |
90 | static mach_port_t kernel_priv_port; | |
91 | ||
92 | static kmod_info_t *module_dependencies = 0; | |
93 | static vm_address_t kernel_alloc_address = 0; | |
94 | static unsigned long kernel_alloc_size = 0; | |
95 | static vm_address_t kernel_load_address = 0; | |
96 | static unsigned long kernel_load_size = 0; | |
97 | static unsigned long kernel_hdr_size = 0; | |
98 | static unsigned long kernel_hdr_pad = 0; | |
99 | static unsigned long faked_kernel_load_address = 0; | |
100 | ||
101 | static kmod_info_t *loaded_modules = 0; | |
102 | static int loaded_count = 0; | |
103 | ||
104 | static char *progname = "program name?"; | |
105 | static int kmodsyms = 0; | |
106 | static int link_addrs_set = 0; | |
107 | static int verbose = 0; | |
108 | ||
109 | static char *debugdumpfile = NULL; | |
110 | ||
111 | // must not be static; kld library calls | |
112 | extern void kld_error_vprintf(const char *format, va_list ap); | |
113 | static void e_printf(const char *fmt, ...); | |
114 | static void v_printf(const char *fmt, ...); | |
115 | ||
116 | static void machwarn(int error, const char *message); | |
117 | static void macherr(int error, const char *message); | |
118 | ||
119 | static unsigned long linkedit_address(unsigned long size, | |
120 | unsigned long headers_size); | |
121 | static void abort_load(int exitcode, const char *fmt, ...); | |
122 | static void map_and_patch(const char *base, | |
123 | const char **library_paths, | |
124 | const char *module); | |
125 | static void link_base(const char *base, | |
126 | const char **dependency_paths, | |
127 | const vm_address_t *dependency_addrs); | |
128 | static void clear_globals(void); | |
129 | static kmod_info_t *map_module(const char *filename); | |
130 | static struct mach_header *link_module(const char *filename, | |
131 | const char *output); | |
132 | static vm_address_t update_kmod_info(struct mach_header *mach_header); | |
133 | static kmod_t load_module(struct mach_header *mach_header, | |
134 | vm_address_t info); | |
135 | static void set_module_dependencies(kmod_t id); | |
136 | static void start_module(kmod_t id); | |
137 | ||
138 | static void | |
139 | usage(void) | |
140 | { | |
141 | if (kmodsyms) { | |
142 | fprintf(stderr, "usage: %s [-v] [-k kernelfile] [-d dependencyfile] -o symbolfile modulefile\n", progname); | |
143 | fprintf(stderr, " %s [-v] -k kernelfile [-d dependencyfile@address] -o symbolfile modulefile@address\n", | |
144 | progname); | |
145 | } else { | |
146 | fprintf(stderr, "usage: %s [-v] [-k kernelfile] [-d dependencyfile] [-o symbolfile] modulefile\n", progname); | |
147 | } | |
148 | fflush(stderr); | |
149 | exit(KMOD_ERROR_USAGE); | |
150 | } | |
151 | ||
152 | int | |
153 | main(int argc, char** argv) | |
154 | { | |
155 | int c, r, i; | |
156 | char * kernel = _PATH_UNIX; | |
157 | int kernel_set = 0; | |
158 | char * gdbfile = 0; | |
159 | #define MAX_DEPENDANCIES 128 | |
160 | char * dependencies[MAX_DEPENDANCIES]; | |
161 | vm_address_t loaded_addresses[MAX_DEPENDANCIES]; | |
162 | int dependency_count = 0; | |
163 | struct mach_header *rld_header; | |
164 | ||
165 | char * module_path = ""; | |
166 | vm_address_t module_info = 0; | |
167 | vm_address_t module_faked_address = 0; | |
168 | kmod_t module_id = 0; | |
169 | kmod_info_t *file_kinfo; | |
170 | ||
171 | if ((progname = strrchr(argv[0], '/')) == NULL) | |
172 | progname = argv[0]; | |
173 | else | |
174 | ++progname; | |
175 | ||
176 | kmodsyms = !strcmp(progname, kKmodsymsName); | |
177 | ||
178 | fprintf(stderr, "%s is deprecated; use kextload(8) instead\n", progname); | |
179 | sleep(5); | |
180 | ||
181 | // XXX things to add: | |
182 | // -p data string to send as outofband data on start | |
183 | // -P data file to send as outofband data on start | |
184 | ||
185 | while ((c = getopt(argc, argv, "D:d:o:k:v")) != -1) | |
186 | switch (c) { | |
187 | case 'd': | |
188 | dependencies[dependency_count] = optarg; | |
189 | if (kmodsyms) { | |
190 | char *address; | |
191 | if ((address = strrchr(optarg, '@'))) { | |
192 | *address++ = 0; | |
193 | loaded_addresses[dependency_count] = strtoul(address, NULL, 0); | |
194 | link_addrs_set++; | |
195 | } else { | |
196 | loaded_addresses[dependency_count] = 0; | |
197 | } | |
198 | } | |
199 | if (++dependency_count == MAX_DEPENDANCIES) { | |
200 | abort_load(KMOD_ERROR_INTERNAL, | |
201 | "internal error, dependency count overflow."); | |
202 | } | |
203 | break; | |
204 | case 'o': | |
205 | gdbfile = optarg; | |
206 | break; | |
207 | case 'k': | |
208 | kernel_set++; | |
209 | kernel = optarg; | |
210 | break; | |
211 | case 'v': | |
212 | verbose = 1; | |
213 | break; | |
214 | case 'D': | |
215 | debugdumpfile = optarg; | |
216 | break; | |
217 | default: | |
218 | usage(); | |
219 | } | |
220 | argc -= optind; | |
221 | argv += optind; | |
222 | ||
223 | dependencies[dependency_count] = 0; | |
224 | loaded_addresses[dependency_count] = 0; | |
225 | ||
226 | if (argc != 1) usage(); | |
227 | ||
228 | module_path = argv[0]; | |
229 | ||
230 | if (kmodsyms) { | |
231 | char *address; | |
232 | ||
233 | if (!gdbfile) usage(); | |
234 | ||
235 | // check for @address | |
236 | if ((address = strrchr(module_path, '@'))) { | |
237 | *address++ = 0; | |
238 | module_faked_address = strtoul(address, NULL, 0); | |
239 | link_addrs_set++; | |
240 | } else { | |
241 | module_faked_address = 0; | |
242 | } | |
243 | ||
244 | // if any arg uses @address then they all must be and the kernel must be set | |
245 | if (link_addrs_set) { | |
246 | if (!kernel_set) usage(); | |
247 | if (!module_faked_address) usage(); | |
248 | for (i=0; i < dependency_count; i++) { | |
249 | if (!loaded_addresses[i]) usage(); | |
250 | } | |
251 | } | |
252 | } | |
253 | ||
254 | // map the module if possible, map_module will fail if there is a problem | |
255 | file_kinfo = map_module(module_path); | |
256 | ||
257 | if (!link_addrs_set) { | |
258 | kmod_info_t *k; | |
259 | ||
260 | // we only need the kernel port if we need to lookup loaded kmods | |
261 | r = task_for_pid(mach_task_self(), 0, &kernel_port); | |
262 | machwarn(r, "unable to get kernel task port"); | |
263 | if (KERN_SUCCESS != r) { | |
264 | abort_load(KMOD_ERROR_PERMS, | |
265 | "You must be running as root to load modules in the kernel."); | |
266 | } | |
267 | ||
268 | //get loaded modules | |
269 | r = kmod_get_info(kernel_port, (void *)&loaded_modules, &loaded_count); // never freed | |
270 | macherr(r, "kmod_get_info() failed"); | |
271 | ||
272 | // check to see if the module has been loaded | |
273 | k = loaded_modules; | |
274 | while (k) { | |
275 | if (!strcmp(k->name, file_kinfo->name)) { | |
276 | if (!kmodsyms) { | |
277 | abort_load(KMOD_ERROR_ALREADY, | |
278 | "the module named '%s' is already loaded.", k->name); | |
279 | } else { | |
280 | module_faked_address = k->address; | |
281 | } | |
282 | break; | |
283 | } | |
284 | k = (k->next) ? (k + 1) : 0; | |
285 | } | |
286 | ||
287 | if (kmodsyms && !module_faked_address) { | |
288 | abort_load(KMOD_ERROR_USAGE, | |
289 | "the module named '%s' has not been loaded.", file_kinfo->name); | |
290 | } | |
291 | ||
292 | //XXX it would be nice to be able to verify this is the correct kernel | |
293 | //XXX by comparing the kernel version strings (once we have them) | |
294 | } | |
295 | ||
296 | map_and_patch(kernel, dependencies, module_path); | |
297 | ||
298 | // Tell the kld linker where to get its load address from | |
299 | kld_address_func(linkedit_address); | |
300 | ||
301 | // link the kernel along with any dependencies | |
302 | link_base(kernel, dependencies, loaded_addresses); | |
303 | ||
304 | if (kmodsyms) { | |
305 | faked_kernel_load_address = module_faked_address; | |
306 | ||
307 | if (!faked_kernel_load_address) { | |
308 | abort_load(KMOD_ERROR_INTERNAL, | |
309 | "internal error, fell thru without setting module load address."); | |
310 | } | |
311 | } | |
312 | ||
313 | rld_header = link_module(module_path, gdbfile); | |
314 | module_info = update_kmod_info(rld_header); | |
315 | ||
316 | if (debugdumpfile) | |
317 | kld_file_debug_dump(module_path, debugdumpfile); | |
318 | ||
319 | if (kmodsyms) return 0; | |
320 | ||
321 | // we need the priv port to load modules into the kernel | |
322 | kernel_priv_port = mach_host_self(); /* if we are privileged */ | |
323 | ||
324 | module_id = load_module(rld_header, module_info); | |
325 | set_module_dependencies(module_id); | |
326 | start_module(module_id); | |
327 | ||
328 | return 0; | |
329 | } | |
330 | ||
331 | static void | |
332 | machwarn(int error, const char *message) | |
333 | { | |
334 | if (KERN_SUCCESS != error) | |
335 | e_printf("%s: %s", message, mach_error_string(error)); | |
336 | } | |
337 | ||
338 | static void | |
339 | macherr(int error, const char *message) | |
340 | { | |
341 | if (KERN_SUCCESS != error) | |
342 | abort_load(KMOD_ERROR_INTERNAL, | |
343 | "%s: %s", message, mach_error_string(error)); | |
344 | } | |
345 | ||
346 | static kmod_info_t *map_module(const char *filename) | |
347 | { | |
348 | kmod_info_t *file_kinfo; | |
349 | ||
350 | if (!kld_file_map(filename)) | |
351 | exit(KMOD_ERROR_LOADING); | |
352 | ||
353 | file_kinfo = kld_file_lookupsymbol(filename, kKMOD_INFO_SYMBOLNAME); | |
354 | if (!file_kinfo) { | |
355 | abort_load(KMOD_ERROR_USAGE, | |
356 | "%s is not a valid kernel module.", filename); | |
357 | } | |
358 | ||
359 | return file_kinfo; | |
360 | } | |
361 | ||
362 | static void | |
363 | map_and_patch(const char *base, const char **library_paths, const char *module) | |
364 | { | |
365 | if (!kld_file_map(base)) | |
366 | exit(KMOD_ERROR_INTERNAL); | |
367 | if (!kld_file_merge_OSObjects(base)) | |
368 | abort_load(KMOD_ERROR_LOADING, NULL); | |
369 | ||
370 | if (*library_paths) { | |
371 | char **library; | |
372 | for (library = library_paths; *library; library++) { | |
373 | map_module(*library); | |
374 | if (!kld_file_patch_OSObjects(*library)) | |
375 | abort_load(KMOD_ERROR_LOADING, NULL); | |
376 | } | |
377 | } | |
378 | ||
379 | // Patch the vtables of the object module we are about to load | |
380 | // The module has already been mapped in the main() routine as part | |
381 | // of validation | |
382 | if (!kld_file_patch_OSObjects(module)) | |
383 | abort_load(KMOD_ERROR_LOADING, NULL); | |
384 | ||
385 | // During the patch up process the mapped images were modified | |
386 | // to avoid having to allocate more data than necessary. | |
387 | // Now we have to give the patcher a chance to clean up after itself. | |
388 | if (!kld_file_prepare_for_link()) | |
389 | abort_load(KMOD_ERROR_LOADING, NULL); | |
390 | } | |
391 | ||
392 | static void | |
393 | link_base(const char *base, | |
394 | const char **dependency_paths, | |
395 | const vm_address_t *dependency_addrs) | |
396 | { | |
397 | struct mach_header *rld_header; | |
398 | char *base_addr; | |
399 | long base_size; | |
400 | int ok; | |
401 | ||
402 | // Get the address and size of the base, usually the kernel | |
403 | base_addr = kld_file_getaddr(base, &base_size); | |
404 | if (!base_addr) | |
405 | exit(KMOD_ERROR_INTERNAL); // Error reported by kld library. | |
406 | ||
407 | ok = kld_load_basefile_from_memory(base, base_addr, base_size); | |
408 | fflush(stdout); | |
409 | if (!ok) | |
410 | abort_load(KMOD_ERROR_LOADING, "kld_load_basefile(%s) failed.", base); | |
411 | ||
412 | if (*dependency_paths) { | |
413 | char **dependency = dependency_paths; | |
414 | const vm_address_t *load_addr = dependency_addrs; | |
415 | ||
416 | while (*dependency) { | |
417 | kmod_info_t *file_kinfo; | |
418 | ||
419 | // Find the kmod_info structure in the image. | |
420 | file_kinfo = | |
421 | kld_file_lookupsymbol(*dependency, kKMOD_INFO_SYMBOLNAME); | |
422 | if (!file_kinfo) { | |
423 | abort_load(KMOD_ERROR_USAGE, | |
424 | "%s is not a valid kernel module.", *dependency); | |
425 | } | |
426 | ||
427 | // find the address that this dependency is loaded at | |
428 | if (kmodsyms && *load_addr) { | |
429 | faked_kernel_load_address = *load_addr; | |
430 | } else { | |
431 | kmod_info_t *k; | |
432 | kmod_info_t *tmp; | |
433 | int found_it = 0; | |
434 | ||
435 | // match up file version of kmod_info with kernel version | |
436 | k = loaded_modules; | |
437 | while (k) { | |
438 | if (!strcmp(k->name, file_kinfo->name)) { | |
439 | UInt32 kernel_kmod_version; | |
440 | UInt32 file_kmod_version; | |
441 | ||
442 | if (!VERS_parse_string(k->version, &kernel_kmod_version)) { | |
443 | e_printf("can't parse version string \"%s\" in kernel kmod", | |
444 | k->version); | |
445 | abort_load(KMOD_ERROR_LOADING, | |
446 | "can't parse kernel kmod version string \"%s\"", k->version); | |
447 | } | |
448 | ||
449 | if (!VERS_parse_string(file_kinfo->version, & file_kmod_version)) { | |
450 | e_printf("can't parse version string \"%s\" in kmod file %s", | |
451 | file_kinfo->version, *dependency); | |
452 | abort_load(KMOD_ERROR_LOADING, | |
453 | "can't parse version string \"%s\" in kmod file %s", | |
454 | file_kinfo->version, *dependency); | |
455 | } | |
456 | ||
457 | if (kernel_kmod_version != file_kmod_version) { | |
458 | e_printf("loaded kernel module '%s' version differs.", *dependency); | |
459 | abort_load(KMOD_ERROR_LOADING, | |
460 | "loaded version '%s', file version '%s'.", | |
461 | k->version, file_kinfo->version); | |
462 | } | |
463 | found_it++; | |
464 | break; | |
465 | } | |
466 | k = (k->next) ? (k + 1) : 0; | |
467 | } | |
468 | if (!found_it) { | |
469 | abort_load(KMOD_ERROR_USAGE, | |
470 | "kernel module '%s' is not loaded.", *dependency); | |
471 | } | |
472 | ||
473 | tmp = malloc(sizeof(kmod_info_t)); | |
474 | if (!tmp) | |
475 | abort_load(KMOD_ERROR_LOADING, "no memory."); | |
476 | ||
477 | *tmp = *k; | |
478 | tmp->next = module_dependencies; | |
479 | module_dependencies = tmp; | |
480 | ||
481 | faked_kernel_load_address = k->address; | |
482 | } | |
483 | ||
484 | rld_header = link_module(*dependency, 0); | |
485 | ||
486 | (void) update_kmod_info(rld_header); | |
487 | ||
488 | dependency++; load_addr++; | |
489 | } | |
490 | /* make sure we clear these so clean up does the right thing. */ | |
491 | clear_globals(); | |
492 | } | |
493 | } | |
494 | ||
495 | #if !defined(page_round) | |
496 | #define page_trunc(p) ((int)(p)&~(vm_page_size-1)) | |
497 | #define page_round(p) page_trunc((int)(p)+vm_page_size-1) | |
498 | #endif | |
499 | ||
500 | static unsigned long | |
501 | linkedit_address(unsigned long size, unsigned long headers_size) | |
502 | { | |
503 | int r; | |
504 | unsigned long round_segments_size; | |
505 | unsigned long round_headers_size; | |
506 | unsigned long round_size; | |
507 | ||
508 | kernel_load_size = size; // The actual size allocated by kld_load... | |
509 | ||
510 | round_headers_size = page_round(headers_size); | |
511 | round_segments_size = page_round(size - headers_size); | |
512 | round_size = round_headers_size + round_segments_size; | |
513 | ||
514 | kernel_alloc_size = round_size; | |
515 | kernel_hdr_size = headers_size; // will need to be rounded to page *after* link. | |
516 | kernel_hdr_pad = round_headers_size - headers_size; | |
517 | ||
518 | if (faked_kernel_load_address) { | |
519 | kernel_load_address = faked_kernel_load_address + kernel_hdr_pad; | |
520 | v_printf("Returning fake load address of 0x%8x", kernel_load_address); | |
521 | return kernel_load_address; | |
522 | } | |
523 | if (kmodsyms) { | |
524 | abort_load(KMOD_ERROR_INTERNAL, | |
525 | "internal error, almost tried to alloc kernel memory."); | |
526 | } | |
527 | ||
528 | r = vm_allocate(kernel_port, &kernel_alloc_address, | |
529 | kernel_alloc_size, TRUE); | |
530 | macherr(r, "unable to allocate kernel memory"); | |
531 | ||
532 | v_printf("allocated %ld bytes in kernel space at 0x%8x", | |
533 | kernel_alloc_size, kernel_alloc_address); | |
534 | ||
535 | kernel_load_address = kernel_alloc_address + kernel_hdr_pad; | |
536 | ||
537 | v_printf("Returning load address of 0x%x", kernel_load_address); | |
538 | ||
539 | return kernel_load_address; | |
540 | } | |
541 | ||
542 | static void | |
543 | clear_globals(void) | |
544 | { | |
545 | faked_kernel_load_address = 0; | |
546 | kernel_alloc_address = 0; | |
547 | kernel_alloc_size = 0; | |
548 | kernel_load_address = 0; | |
549 | kernel_load_size = 0; | |
550 | kernel_hdr_size = 0; | |
551 | kernel_hdr_pad = 0; | |
552 | return; | |
553 | } | |
554 | ||
555 | static struct mach_header * | |
556 | link_module(const char *filename, const char *output) | |
557 | { | |
558 | struct mach_header *rld_header; | |
559 | char *object_addr; | |
560 | long object_size; | |
561 | int ok; | |
562 | ||
563 | // Get the address of the thined MachO image. | |
564 | object_addr = kld_file_getaddr(filename, &object_size); | |
565 | if (!object_addr) | |
566 | abort_load(KMOD_ERROR_LOADING, NULL); | |
567 | ||
568 | ok = kld_load_from_memory(&rld_header, filename, | |
569 | object_addr, object_size, output); | |
570 | fflush(stdout); | |
571 | if (!ok) | |
572 | abort_load(KMOD_ERROR_LOADING, "kld_load() failed."); | |
573 | ||
574 | return rld_header; | |
575 | } | |
576 | ||
577 | // Update the kmod_info_t structure in the image to be laoded | |
578 | // Side effect of removing the kKMOD_INFO_SYMBOLNAME from the | |
579 | // loaded symbol name space, otherwise we would have a duplicate | |
580 | // defined symbol failure | |
581 | vm_address_t | |
582 | update_kmod_info(struct mach_header *mach_header) | |
583 | { | |
584 | char * symbol = kKMOD_INFO_SYMBOLNAME; | |
585 | kmod_info_t *info; | |
586 | unsigned long value; | |
587 | int ok; | |
588 | ||
589 | ok = kld_lookup(symbol, &value); fflush(stdout); | |
590 | if (!ok) | |
591 | abort_load(KMOD_ERROR_LOADING, "kld_lookup(%s) failed.", symbol); | |
592 | ||
593 | ok = kld_forget_symbol(symbol); fflush(stdout); | |
594 | if (!ok) | |
595 | abort_load(KMOD_ERROR_LOADING, "kld_forget_symbol(%s) failed.", symbol); | |
596 | ||
597 | /* Get the kmod info by translating from the kernel address at value. | |
598 | */ | |
599 | info = (kmod_info_t *)(value - (unsigned long)kernel_load_address + (unsigned long)mach_header); | |
600 | v_printf("kmod name: %s", info->name); | |
601 | v_printf("kmod start @ 0x%x", (vm_address_t)info->start); | |
602 | v_printf("kmod stop @ 0x%x", (vm_address_t)info->stop); | |
603 | ||
604 | /* Record link info in kmod info struct, rounding the hdr_size to fit | |
605 | * the adjustment that was made. | |
606 | */ | |
607 | info->address = kernel_alloc_address; | |
608 | info->size = kernel_alloc_size; | |
609 | info->hdr_size = page_round(kernel_hdr_size); | |
610 | ||
611 | if (!info->start) | |
612 | abort_load(KMOD_ERROR_LOADING, "invalid start address?"); | |
613 | else if (!info->stop) | |
614 | abort_load(KMOD_ERROR_LOADING, "invalid stop address?"); | |
615 | ||
616 | return (vm_address_t)value; | |
617 | } | |
618 | ||
619 | static kmod_t | |
620 | load_module(struct mach_header *mach_header, vm_address_t info) | |
621 | { | |
622 | int r; | |
623 | kmod_t id; | |
624 | vm_address_t vm_buffer = 0; | |
625 | ||
626 | r = vm_allocate(mach_task_self(), &vm_buffer, | |
627 | kernel_alloc_size, TRUE); | |
628 | macherr(r, "unable to vm_allocate() copy buffer"); | |
629 | ||
630 | /* Copy the linked segment data into the page-aligned buffer. | |
631 | * Do not round the header size here. | |
632 | */ | |
633 | bzero((void *)vm_buffer, kernel_alloc_size); | |
634 | memcpy((void *)vm_buffer, mach_header, kernel_hdr_size); | |
635 | memcpy((void *)vm_buffer + page_round(kernel_hdr_size), | |
636 | (void *)((unsigned long)mach_header + kernel_hdr_size), | |
637 | kernel_load_size - kernel_hdr_size); | |
638 | ||
639 | // copy linked header into kernel address space | |
640 | r = vm_write(kernel_port, kernel_alloc_address, | |
641 | vm_buffer, kernel_alloc_size); | |
642 | macherr(r, "unable to write module into kernel memory"); | |
643 | ||
644 | // let the kernel know about it | |
645 | r = kmod_create(kernel_priv_port, info, &id); | |
646 | macherr(r, "unable to register module with kernel"); | |
647 | ||
648 | v_printf("kmod id %d successfully created at 0x%x size %ld.\n", | |
649 | id, kernel_alloc_address, kernel_alloc_size); | |
650 | ||
651 | // FIXME: make sure this happens even on failure | |
652 | ||
653 | vm_deallocate(mach_task_self(), vm_buffer, kernel_alloc_size); | |
654 | return id; | |
655 | } | |
656 | ||
657 | static void | |
658 | set_module_dependencies(kmod_t id) | |
659 | { | |
660 | int r; | |
661 | void * args = 0; | |
662 | int argsCount= 0; | |
663 | kmod_info_t *module = module_dependencies; | |
664 | ||
665 | while (module) { | |
666 | ||
667 | r = kmod_control(kernel_priv_port, KMOD_PACK_IDS(id, module->id), KMOD_CNTL_RETAIN, &args, &argsCount); | |
668 | machwarn(r, "kmod_control(retain) failed"); | |
669 | if (r) { | |
670 | clear_globals(); | |
671 | r = kmod_destroy(kernel_priv_port, id); | |
672 | macherr(r, "kmod_destroy failed"); | |
673 | } | |
674 | ||
675 | v_printf("kmod id %d reference count was sucessfully incremented.", module->id); | |
676 | ||
677 | module = module->next; | |
678 | } | |
679 | } | |
680 | ||
681 | static void | |
682 | start_module(kmod_t id) | |
683 | { | |
684 | int r; | |
685 | void * args = 0; | |
686 | int argsCount= 0; | |
687 | ||
688 | r = kmod_control(kernel_priv_port, id, KMOD_CNTL_START, &args, &argsCount); | |
689 | machwarn(r, "kmod_control(start) failed"); | |
690 | if (r) { | |
691 | clear_globals(); | |
692 | kmod_destroy(kernel_priv_port, id); | |
693 | macherr(r, "kmod_destroy failed"); | |
694 | } | |
695 | ||
696 | v_printf("kmod id %d successfully started.", id); | |
697 | } | |
698 | ||
699 | static void e_printf(const char *fmt, ...) | |
700 | { | |
701 | va_list ap; | |
702 | char msg[1024]; | |
703 | ||
704 | va_start(ap, fmt); | |
705 | vsnprintf(msg, sizeof(msg), fmt, ap); | |
706 | va_end(ap); | |
707 | ||
708 | fprintf(stderr, "%s: %s\n", progname, msg); | |
709 | } | |
710 | ||
711 | static void v_printf(const char *fmt, ...) | |
712 | { | |
713 | va_list ap; | |
714 | char msg[1024]; | |
715 | ||
716 | if (!verbose) return; | |
717 | ||
718 | va_start(ap, fmt); | |
719 | vsnprintf(msg, sizeof(msg), fmt, ap); | |
720 | va_end(ap); | |
721 | ||
722 | printf("%s: %s\n", progname, msg); | |
723 | } | |
724 | ||
725 | static void abort_load(int exitcode, const char *fmt, ...) | |
726 | { | |
727 | if (fmt) { | |
728 | va_list ap; | |
729 | char msg[1024]; | |
730 | ||
731 | va_start(ap, fmt); | |
732 | vsnprintf(msg, sizeof(msg), fmt, ap); | |
733 | va_end(ap); | |
734 | ||
735 | fprintf(stderr, "%s: %s\n", progname, msg); | |
736 | } | |
737 | ||
738 | if (!faked_kernel_load_address | |
739 | && (kernel_alloc_address || kernel_alloc_size)) { | |
740 | int r; | |
741 | ||
742 | v_printf("freeing %ld bytes in kernel space at 0x%x", | |
743 | kernel_alloc_size, kernel_alloc_address); | |
744 | r = vm_deallocate(kernel_port, kernel_alloc_address, kernel_alloc_size); | |
745 | machwarn(r, "unable to cleanup kernel memory"); | |
746 | } | |
747 | ||
748 | exit(exitcode); | |
749 | } | |
750 | ||
751 | __private_extern__ void | |
752 | kld_error_vprintf(const char *fmt, va_list ap) | |
753 | { | |
754 | vfprintf(stderr, fmt, ap); | |
755 | fflush(stderr); | |
756 | } |