2 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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
22 * @APPLE_LICENSE_HEADER_END@
25 * Copyright (c) 1997 Doug Rabson
26 * All rights reserved.
28 * Redistribution and use in source and binary forms, with or without
29 * modification, are permitted provided that the following conditions
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.
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
50 * "kldload.c,v 1.5 1998/07/06 06:58:32 charnier Exp"
54 static const char rcsid
[] =
55 "$Id: kmodload.c,v 1.7 2001/02/05 19:53:16 lindak Exp $";
64 #include <sys/param.h>
68 #include <mach/mach.h>
69 #include <mach/mach_error.h>
70 #include <mach/mach_host.h>
71 #include <mach-o/kld.h>
72 #include <mach-o/fat.h>
74 #define KMOD_ERROR_USAGE 1
75 #define KMOD_ERROR_PERMS 2
76 #define KMOD_ERROR_LOADING 3
77 #define KMOD_ERROR_INTERNAL 4
78 #define KMOD_ERROR_ALREADY 5
80 static mach_port_t kernel_port
;
81 static mach_port_t kernel_priv_port
;
83 static kmod_info_t
*module_dependencies
= 0;
84 static vm_address_t kernel_alloc_address
= 0;
85 static unsigned long kernel_alloc_size
= 0;
86 static vm_address_t kernel_load_address
= 0;
87 static unsigned long kernel_load_size
= 0;
88 static unsigned long kernel_hdr_size
= 0;
89 static unsigned long kernel_hdr_pad
= 0;
90 static unsigned long faked_kernel_load_address
= 0;
92 static kmod_info_t
*loaded_modules
= 0;
93 static int loaded_count
= 0;
95 static char *progname
= "program name?";
96 static int kmodsyms
= 0;
97 static int link_addrs_set
= 0;
98 static int verbose
= 0;
99 #define v_printf if (verbose) printf
101 // must not be static; kld library calls
102 void kld_error_vprintf(const char *format
, va_list ap
);
104 static void machwarn(int error
, const char *message
);
105 static void macherr(int error
, const char *message
);
107 static unsigned long linkedit_address(unsigned long size
,
108 unsigned long headers_size
);
109 static void cleanup_kernel_memory();
110 static void link_base(const char *base
,
111 const char **dependency_paths
,
112 const vm_address_t
*dependency_addrs
);
113 static void clear_globals(void);
114 static void map_module(char *module_path
, char **object_addr
,
115 long *object_size
, kmod_info_t
**kinfo
);
116 static struct mach_header
*link_module(const char *filename
,
118 static vm_address_t
patch_module(struct mach_header
*mach_header
);
119 static kmod_t
load_module(struct mach_header
*mach_header
,
121 static void set_module_dependencies(kmod_t id
);
122 static void start_module(kmod_t id
);
128 fprintf(stderr
, "usage: kmodsyms [-v] [-k kernelfile] [-d dependencyfile] -o symbolfile modulefile\n");
129 fprintf(stderr
, " kmodsyms [-v] -k kernelfile [-d dependencyfile@address] -o symbolfile modulefile@address\n");
131 fprintf(stderr
, "usage: kmodload [-v] [-k kernelfile] [-d dependencyfile] [-o symbolfile] modulefile\n");
133 exit(KMOD_ERROR_USAGE
);
137 main(int argc
, char** argv
)
140 char * kernel
= _PATH_UNIX
;
143 #define MAX_DEPENDANCIES 128
144 char * dependencies
[MAX_DEPENDANCIES
];
145 vm_address_t loaded_addresses
[MAX_DEPENDANCIES
];
146 int dependency_count
= 0;
147 struct mach_header
*rld_header
;
149 char * module_path
= "";
150 vm_address_t module_info
= 0;
151 char *module_addr
= 0;
152 long module_size
= 0;
153 vm_address_t module_faked_address
= 0;
154 kmod_t module_id
= 0;
155 kmod_info_t
*file_kinfo
;
157 if ((progname
= rindex(argv
[0], '/')) == NULL
)
162 kmodsyms
= !strcmp(progname
, "kmodsyms");
164 // XXX things to add:
165 // -p data string to send as outofband data on start
166 // -P data file to send as outofband data on start
168 while ((c
= getopt(argc
, argv
, "d:o:k:v")) != -1)
171 dependencies
[dependency_count
] = optarg
;
174 if ((address
= rindex(optarg
, '@'))) {
176 loaded_addresses
[dependency_count
] = strtoul(address
, NULL
, 0);
179 loaded_addresses
[dependency_count
] = 0;
182 if (++dependency_count
== MAX_DEPENDANCIES
) {
183 fprintf(stderr
, "%s: internal error, dependency count overflow.\n", progname
);
184 exit(KMOD_ERROR_INTERNAL
);
203 dependencies
[dependency_count
] = 0;
204 loaded_addresses
[dependency_count
] = 0;
206 if (argc
!= 1) usage();
208 module_path
= argv
[0];
213 if (!gdbfile
) usage();
215 // check for @address
216 if ((address
= rindex(module_path
, '@'))) {
218 module_faked_address
= strtoul(address
, NULL
, 0);
221 module_faked_address
= 0;
224 // if any arg uses @address then they all must be and the kernel must be set
225 if (link_addrs_set
) {
226 if (!kernel_set
) usage();
227 if (!module_faked_address
) usage();
228 for (i
=0; i
< dependency_count
; i
++) {
229 if (!loaded_addresses
[i
]) usage();
234 // map module and then check if it has been loaded
235 map_module(module_path
, &module_addr
, &module_size
, &file_kinfo
);
237 if (!link_addrs_set
) {
240 // we only need the kernel port if we need to lookup loaded kmods
241 r
= task_for_pid(mach_task_self(), 0, &kernel_port
);
242 machwarn(r
, "unable to get kernel task port");
244 fprintf(stderr
, "%s: You must be running as root to load/check modules in the kernel.\n", progname
);
245 exit(KMOD_ERROR_PERMS
);
249 r
= kmod_get_info(kernel_port
, (void *)&loaded_modules
, &loaded_count
); // never freed
250 macherr(r
, "kmod_get_info() failed");
252 // check to see if the module has been loaded
255 if (!strcmp(k
->name
, file_kinfo
->name
)) {
257 fprintf(stderr
, "%s: the module named '%s' is already loaded.\n", progname
, k
->name
);
258 exit(KMOD_ERROR_ALREADY
);
260 module_faked_address
= k
->address
;
264 k
= (k
->next
) ? (k
+ 1) : 0;
267 if (kmodsyms
&& !module_faked_address
) {
268 fprintf(stderr
, "%s: the module named '%s' has not been loaded.\n", progname
, file_kinfo
->name
);
269 exit(KMOD_ERROR_USAGE
);
272 //XXX it would be nice to be able to verify this is the correct kernel
273 //XXX by comparing the kernel version strings (once we have them)
276 // link the kernel along with any dependencies
277 link_base(kernel
, dependencies
, loaded_addresses
);
280 faked_kernel_load_address
= module_faked_address
;
282 if (!faked_kernel_load_address
) {
283 fprintf(stderr
, "%s: internal error, fell thru without setting module load address.\n", progname
);
284 exit(KMOD_ERROR_INTERNAL
);
288 rld_header
= link_module(module_path
, gdbfile
);
289 module_info
= patch_module(rld_header
);
291 if (kmodsyms
) return 0;
293 // we need the priv port to load modules into the kernel
294 kernel_priv_port
= mach_host_self(); /* if we are privileged */
296 module_id
= load_module(rld_header
, module_info
);
297 set_module_dependencies(module_id
);
298 start_module(module_id
);
304 machwarn(int error
, const char *message
)
306 if (error
== KERN_SUCCESS
) return;
307 fprintf(stderr
, "%s: %s: %s\n", progname
, message
, mach_error_string(error
));
311 macherr(int error
, const char *message
)
313 if (error
== KERN_SUCCESS
) return;
314 fprintf(stderr
, "%s: %s: %s\n", progname
, message
, mach_error_string(error
));
316 cleanup_kernel_memory();
318 exit(KMOD_ERROR_INTERNAL
);
322 map_module(char *module_path
, char **object_addr
, long *object_size
, kmod_info_t
**kinfo
)
325 struct stat stat_buf
;
326 struct mach_header
*mh
;
329 struct nlist nl
[] = {
334 if((fd
= open(module_path
, O_RDONLY
)) == -1){
335 fprintf(stderr
, "%s: Can't open: %s\n", progname
, module_path
);
336 exit(KMOD_ERROR_USAGE
);
338 if (nlist(module_path
, nl
)) {
339 fprintf(stderr
, "%s: %s is not a valid kernel module.\n", progname
, module_path
);
340 exit(KMOD_ERROR_USAGE
);
342 if(fstat(fd
, &stat_buf
) == -1){
343 fprintf(stderr
, "%s: Can't stat file: %s\n", progname
, module_path
);
344 exit(KMOD_ERROR_PERMS
);
346 *object_size
= stat_buf
.st_size
;
347 if(map_fd(fd
, 0, (vm_offset_t
*)object_addr
, TRUE
, *object_size
) != KERN_SUCCESS
){
348 fprintf(stderr
, "%s: Can't map file: %s\n", progname
, module_path
);
349 exit(KMOD_ERROR_INTERNAL
);
353 if (NXSwapBigLongToHost(*((long *)*object_addr
)) == FAT_MAGIC
) {
354 struct host_basic_info hbi
;
355 struct fat_header
*fh
;
356 struct fat_arch
*fat_archs
, *fap
;
357 unsigned i
, nfat_arch
;
359 /* Get our host info */
360 i
= HOST_BASIC_INFO_COUNT
;
361 if (host_info(mach_host_self(), HOST_BASIC_INFO
, (host_info_t
)(&hbi
), &i
) != KERN_SUCCESS
) {
362 fprintf(stderr
, "%s: Can't get host's basic info\n", progname
);
363 exit(KMOD_ERROR_INTERNAL
);
366 // get number of architectures
367 fh
= (struct fat_header
*)*object_addr
;
368 nfat_arch
= NXSwapBigLongToHost(fh
->nfat_arch
);
370 // find beginning of fat_arch struct
371 fat_archs
= (struct fat_arch
*)((char *)fh
+ sizeof(struct fat_header
));
374 * Convert archs to host byte ordering (a constraint of
375 * cpusubtype_getbestarch()
377 for (i
= 0; i
< nfat_arch
; i
++) {
378 fat_archs
[i
].cputype
=
379 NXSwapBigLongToHost(fat_archs
[i
].cputype
);
380 fat_archs
[i
].cpusubtype
=
381 NXSwapBigLongToHost(fat_archs
[i
].cpusubtype
);
382 fat_archs
[i
].offset
=
383 NXSwapBigLongToHost(fat_archs
[i
].offset
);
385 NXSwapBigLongToHost(fat_archs
[i
].size
);
387 NXSwapBigLongToHost(fat_archs
[i
].align
);
390 // this code was lifted from Darwin/Libraries/NeXT/libc/gen.subproj/nlist.c
391 // when cpusubtype_getbestarch exists this code should also be changed.
392 #define CPUSUBTYPE_SUPPORT 0
394 #if CPUSUBTYPE_SUPPORT
395 fap
= cpusubtype_getbestarch(hbi
.cpu_type
, hbi
.cpu_subtype
,
396 fat_archs
, nfat_arch
);
397 #else CPUSUBTYPE_SUPPORT
398 #warning Use the cpusubtype functions!!!
400 for (i
= 0; i
< nfat_arch
; i
++) {
401 if (fat_archs
[i
].cputype
== hbi
.cpu_type
) {
406 #endif CPUSUBTYPE_SUPPORT
408 fprintf(stderr
, "%s: could not find the correct architecture in %s.\n", progname
, module_path
);
409 exit(KMOD_ERROR_USAGE
);
412 *object_addr
+= fap
->offset
;
413 *object_size
= fap
->size
;
416 mh
= (struct mach_header
*)*object_addr
;
417 if (*((long *)mh
) != MH_MAGIC
) {
418 fprintf(stderr
, "%s: invalid file format for file: %s\n", progname
, module_path
);
419 exit(KMOD_ERROR_USAGE
);
421 p
= *object_addr
+ sizeof(struct mach_header
) + mh
->sizeofcmds
+ nl
->n_value
;
422 *kinfo
= (kmod_info_t
*)p
;
426 link_base(const char *base
,
427 const char **dependency_paths
,
428 const vm_address_t
*dependency_addrs
)
430 struct mach_header
*rld_header
;
433 ok
= kld_load_basefile(base
);
436 fprintf(stderr
, "%s: kld_load_basefile(%s) failed.\n", progname
, base
);
437 exit(KMOD_ERROR_LOADING
);
440 if (*dependency_paths
) {
441 char **dependency
= dependency_paths
;
442 const vm_address_t
*load_addr
= dependency_addrs
;
444 while (*dependency
) {
447 kmod_info_t
*file_kinfo
;
449 map_module(*dependency
, &object_addr
, &object_size
, &file_kinfo
);
451 // find the address that this dependency is loaded at
452 if (kmodsyms
&& *load_addr
) {
453 faked_kernel_load_address
= *load_addr
;
459 // match up file version of kmod_info with kernel version
462 if (!strcmp(k
->name
, file_kinfo
->name
)) {
463 if (strcmp(k
->version
, file_kinfo
->version
)) {
464 fprintf(stderr
, "%s: loaded kernel module '%s' version differs.\n",
465 progname
, *dependency
);
466 fprintf(stderr
, "%s: loaded version '%s', file version '%s'.\n",
467 progname
, k
->version
, file_kinfo
->version
);
468 exit(KMOD_ERROR_LOADING
);
473 k
= (k
->next
) ? (k
+ 1) : 0;
476 fprintf(stderr
, "%s: kernel module '%s' is not loaded.\n",
477 progname
, *dependency
);
478 exit(KMOD_ERROR_USAGE
);
481 tmp
= malloc(sizeof(kmod_info_t
));
483 fprintf(stderr
, "%s: no memory.\n", progname
);
484 exit(KMOD_ERROR_LOADING
);
487 tmp
->next
= module_dependencies
;
488 module_dependencies
= tmp
;
490 faked_kernel_load_address
= k
->address
;
493 rld_header
= link_module(*dependency
, 0);
495 (void)patch_module(rld_header
);
497 dependency
++; load_addr
++;
499 /* make sure we clear these so clean up does the right thing. */
504 #if !defined(page_round)
505 #define page_trunc(p) ((int)(p)&~(vm_page_size-1))
506 #define page_round(p) page_trunc((int)(p)+vm_page_size-1)
510 linkedit_address(unsigned long size
, unsigned long headers_size
)
513 unsigned long round_segments_size
;
514 unsigned long round_headers_size
;
515 unsigned long round_size
;
517 kernel_load_size
= size
; // The actual size allocated by kld_load...
519 round_headers_size
= page_round(headers_size
);
520 round_segments_size
= page_round(size
- headers_size
);
521 round_size
= round_headers_size
+ round_segments_size
;
523 kernel_alloc_size
= round_size
;
524 kernel_hdr_size
= headers_size
; // will need to be rounded to page *after* link.
525 kernel_hdr_pad
= round_headers_size
- headers_size
;
527 if (faked_kernel_load_address
) {
528 kernel_load_address
= faked_kernel_load_address
+ kernel_hdr_pad
;
529 v_printf("%s: Returning fake load address of 0x%8x\n",
530 progname
, kernel_load_address
);
531 return kernel_load_address
;
534 fprintf(stderr
, "%s: internal error, almost tried to alloc kernel memory.\n", progname
);
535 exit(KMOD_ERROR_INTERNAL
);
538 r
= vm_allocate(kernel_port
, &kernel_alloc_address
,
539 kernel_alloc_size
, TRUE
);
540 macherr(r
, "unable to allocate kernel memory");
542 v_printf("%s: allocated %ld bytes in kernel space at 0x%8x\n",
543 progname
, kernel_alloc_size
, kernel_alloc_address
);
545 kernel_load_address
= kernel_alloc_address
+ kernel_hdr_pad
;
547 v_printf("%s: Returning load address of 0x%x\n",
548 progname
, kernel_load_address
);
550 return kernel_load_address
;
554 cleanup_kernel_memory()
558 if (faked_kernel_load_address
) return;
560 if (kernel_alloc_address
|| kernel_alloc_size
) {
561 v_printf("%s: freeing %ld bytes in kernel space at 0x%x\n",
562 progname
, kernel_alloc_size
, kernel_alloc_address
);
563 r
= vm_deallocate(kernel_port
, kernel_alloc_address
, kernel_alloc_size
);
565 kernel_load_address
= kernel_load_size
= 0;
566 machwarn(r
, "unable to cleanup kernel memory");
573 faked_kernel_load_address
= 0;
574 kernel_alloc_address
= 0;
575 kernel_alloc_size
= 0;
576 kernel_load_address
= 0;
577 kernel_load_size
= 0;
583 static struct mach_header
*
584 link_module(const char *filename
, const char *output
)
586 struct mach_header
*rld_header
;
589 kld_address_func(linkedit_address
);
591 ok
= kld_load(&rld_header
, filename
, output
);
594 fprintf(stderr
, "%s: kld_load() failed.\n", progname
);
595 cleanup_kernel_memory();
596 exit(KMOD_ERROR_LOADING
);
603 patch_module(struct mach_header
*mach_header
)
605 char * symbol
= "_kmod_info";
610 ok
= kld_lookup(symbol
, &value
);
613 fprintf(stderr
, "%s: kld_lookup(%s) failed.\n", progname
, symbol
);
614 cleanup_kernel_memory();
615 exit(KMOD_ERROR_LOADING
);
618 ok
= kld_forget_symbol(symbol
);
621 fprintf(stderr
, "%s: kld_forget_symbol(%s) failed.\n", progname
, symbol
);
622 cleanup_kernel_memory();
623 exit(KMOD_ERROR_INTERNAL
);
626 /* Get the kmod info by translating from the kernel address at value.
628 info
= (kmod_info_t
*)(value
- (unsigned long)kernel_load_address
+ (unsigned long)mach_header
);
629 v_printf("%s: kmod name: %s\n", progname
, info
->name
);
630 v_printf("%s: kmod start @ 0x%x\n", progname
, (vm_address_t
)info
->start
);
631 v_printf("%s: kmod stop @ 0x%x\n", progname
, (vm_address_t
)info
->stop
);
633 /* Record link info in kmod info struct, rounding the hdr_size to fit
634 * the adjustment that was made.
636 info
->address
= kernel_alloc_address
;
637 info
->size
= kernel_alloc_size
;
638 info
->hdr_size
= page_round(kernel_hdr_size
);
641 fprintf(stderr
, "%s: invalid start address?\n", progname
);
642 cleanup_kernel_memory();
643 exit(KMOD_ERROR_LOADING
);
646 fprintf(stderr
, "%s: invalid stop address?\n", progname
);
647 cleanup_kernel_memory();
648 exit(KMOD_ERROR_LOADING
);
651 return (vm_address_t
)value
;
655 load_module(struct mach_header
*mach_header
, vm_address_t info
)
659 vm_address_t vm_buffer
= 0;
661 r
= vm_allocate(mach_task_self(), &vm_buffer
,
662 kernel_alloc_size
, TRUE
);
663 macherr(r
, "unable to vm_allocate() copy buffer");
665 /* Copy the linked segment data into the page-aligned buffer.
666 * Do not round the header size here.
668 bzero((void *)vm_buffer
, kernel_alloc_size
);
669 memcpy((void *)vm_buffer
, mach_header
, kernel_hdr_size
);
670 memcpy((void *)vm_buffer
+ page_round(kernel_hdr_size
),
671 (void *)((unsigned long)mach_header
+ kernel_hdr_size
),
672 kernel_load_size
- kernel_hdr_size
);
674 // copy linked header into kernel address space
675 r
= vm_write(kernel_port
, kernel_alloc_address
,
676 vm_buffer
, kernel_alloc_size
);
677 macherr(r
, "unable to write module into kernel memory");
679 // let the kernel know about it
680 r
= kmod_create(kernel_priv_port
, info
, &id
);
681 macherr(r
, "unable to register module with kernel");
683 v_printf("%s: kmod id %d successfully created at 0x%x size %ld.\n",
684 progname
, id
, kernel_alloc_address
, kernel_alloc_size
);
686 // FIXME: make sure this happens even on failure
688 vm_deallocate(mach_task_self(), vm_buffer
, kernel_alloc_size
);
693 set_module_dependencies(kmod_t id
)
698 kmod_info_t
*module = module_dependencies
;
702 r
= kmod_control(kernel_priv_port
, KMOD_PACK_IDS(id
, module->id
), KMOD_CNTL_RETAIN
, &args
, &argsCount
);
703 machwarn(r
, "kmod_control(retain) failed");
706 r
= kmod_destroy(kernel_priv_port
, id
);
707 macherr(r
, "kmod_destroy failed");
708 exit(KMOD_ERROR_INTERNAL
);
711 v_printf("%s: kmod id %d reference count was sucessfully incremented.\n", progname
, module->id
);
713 module = module->next
;
718 start_module(kmod_t id
)
724 r
= kmod_control(kernel_priv_port
, id
, KMOD_CNTL_START
, &args
, &argsCount
);
725 machwarn(r
, "kmod_control(start) failed");
728 kmod_destroy(kernel_priv_port
, id
);
729 macherr(r
, "kmod_destroy failed");
730 exit(KMOD_ERROR_INTERNAL
);
733 v_printf("%s: kmod id %d successfully started.\n", progname
, id
);
737 kld_error_vprintf(const char *format
, va_list ap
){
738 vfprintf(stderr
, format
, ap
);