2 * Copyright (c) 2000-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@
30 /* Copyright (c) 1998 Apple Computer, Inc. All rights reserved.
32 * File: bsd/kern/kern_symfile.c
34 * This file contains creates a dummy symbol file for mach_kernel
35 * based on the symbol table information passed by the
36 * SecondaryLoader/PlatformExpert. This allows us to correctly
37 * link other executables (drivers, etc) against the the kernel in
38 * cases where the kernel image on the root device does not match
39 * the live kernel. This can occur during net-booting where the
40 * actual kernel image is obtained from the network via tftp rather
41 * than the root device.
43 * If a symbol table is available, then the file /mach.sym will be
44 * created containing a Mach Header and a LC_SYMTAB load command
45 * followed by the the symbol table data for mach_kernel.
47 * NOTE: This file supports only 32 bit kernels at the present time;
48 * adding support for 64 bit kernels is possible, but is not
49 * necessary at the present time.
56 #include <mach/vm_param.h>
58 #include <sys/param.h>
59 #include <sys/systm.h>
60 #include <sys/signalvar.h>
61 #include <sys/resourcevar.h>
62 #include <sys/namei.h>
63 #include <sys/vnode_internal.h>
64 #include <sys/proc_internal.h>
65 #include <sys/kauth.h>
66 #include <sys/timeb.h>
67 #include <sys/times.h>
69 #include <sys/file_internal.h>
71 #include <sys/kernel.h>
76 #include <mach-o/loader.h>
77 #include <mach-o/nlist.h>
79 #include <kern/kalloc.h>
80 #include <vm/vm_kern.h>
81 #include <pexpert/pexpert.h>
82 #include <IOKit/IOHibernatePrivate.h>
84 extern unsigned char rootdevice
[];
85 extern struct mach_header _mh_execute_header
;
87 static int kernel_symfile_opened
= 0;
88 static int error_code
= 0;
90 extern int IODTGetLoaderInfo(char *key
, void **infoAddr
, int *infoSize
);
91 extern void IODTFreeLoaderInfo(char *key
, void *infoAddr
, int infoSize
);
94 * Can only operate against currently running 32 bit mach_kernel
97 output_kernel_symbols(struct proc
*p
)
100 kauth_cred_t cred
= p
->p_ucred
; /* XXX */
101 struct vnode_attr va
;
102 struct vfs_context context
;
103 struct load_command
*cmd
;
104 struct mach_header
*orig_mh
, *mh
;
105 struct segment_command
*orig_ds
, *orig_ts
, *orig_le
, *sg
;
106 struct section
*se
, *const_text
;
107 struct symtab_command
*st
, *orig_st
;
109 vm_size_t orig_mhsize
, orig_st_size
;
111 vm_size_t header_size
= 0; /* out: protected by header */
125 // Dispose of unnecessary gumf, the booter doesn't need to load these
126 rc_mh
= IODTGetLoaderInfo("Kernel-__HEADER",
127 (void **)&orig_mh
, &orig_mhsize
);
128 if (rc_mh
== 0 && orig_mh
)
129 IODTFreeLoaderInfo("Kernel-__HEADER",
130 (void *)orig_mh
, round_page_32(orig_mhsize
));
132 rc_sc
= IODTGetLoaderInfo("Kernel-__SYMTAB",
133 (void **) &orig_st
, &orig_st_size
);
134 if (rc_sc
== 0 && orig_st
)
135 IODTFreeLoaderInfo("Kernel-__SYMTAB",
136 (void *)orig_st
, round_page_32(orig_st_size
));
138 if (cred
->cr_svuid
!= cred
->cr_ruid
|| cred
->cr_svgid
!= cred
->cr_rgid
)
141 // Check to see if the root is 'e' or 'n', is this a test for network?
142 if (rootdevice
[0] == 'e' && rootdevice
[1] == 'n')
146 context
.vc_ucred
= cred
;
148 if ((error
= vnode_open("mach.sym", (O_CREAT
| FWRITE
), (S_IRUSR
| S_IRGRP
| S_IROTH
), 0, &vp
, &context
)))
151 /* Don't dump to non-regular files or files with links. */
154 VATTR_WANTED(&va
, va_nlink
);
155 if ((vp
->v_type
!= VREG
) || vnode_getattr(vp
, &va
, &context
) || (va
.va_nlink
!= 1))
158 VATTR_INIT(&va
); /* better to do it here than waste more stack in vnode_getsize */
159 VATTR_SET(&va
, va_data_size
, 0);
160 vnode_setattr(vp
, &va
, &context
);
161 p
->p_acflag
|= ACORE
;
163 // If the file type is MH_EXECUTE then this must be a kernel
164 // as all Kernel extensions must be of type MH_OBJECT
165 orig_ds
= orig_ts
= orig_le
= NULL
;
167 orig_mh
= &_mh_execute_header
;
168 cmd
= (struct load_command
*) &orig_mh
[1];
169 for (i
= 0; i
< orig_mh
->ncmds
; i
++) {
170 if (cmd
->cmd
== LC_SEGMENT
) {
171 struct segment_command
*orig_sg
= (struct segment_command
*) cmd
;
173 if (!strcmp(SEG_TEXT
, orig_sg
->segname
))
175 else if (!strcmp(SEG_DATA
, orig_sg
->segname
))
177 else if (!strcmp(SEG_LINKEDIT
, orig_sg
->segname
))
180 else if (cmd
->cmd
== LC_SYMTAB
)
181 orig_st
= (struct symtab_command
*) cmd
;
183 cmd
= (struct load_command
*) ((caddr_t
) cmd
+ cmd
->cmdsize
);
186 if (!orig_ts
|| !orig_ds
|| !orig_le
|| !orig_st
)
190 se
= (struct section
*) &orig_ts
[1];
191 for (i
= 0; i
< orig_ts
->nsects
; i
++, se
++) {
192 if (!strcmp("__const", se
->sectname
)) {
200 header_size
= sizeof(struct mach_header
)
203 + sizeof(struct symtab_command
);
205 (void) kmem_alloc(kernel_map
,
206 (vm_offset_t
*) &header
,
207 (vm_size_t
) header_size
);
209 bzero((void *) header
, header_size
);
214 * Set up Mach-O header.
216 mh
= (struct mach_header
*) header
;
217 mh
->magic
= orig_mh
->magic
;
218 mh
->cputype
= orig_mh
->cputype
;
219 mh
->cpusubtype
= orig_mh
->cpusubtype
;
220 mh
->filetype
= orig_mh
->filetype
;
222 mh
->sizeofcmds
= header_size
- sizeof(struct mach_header
);
223 mh
->flags
= orig_mh
->flags
;
225 // Initialise the current file offset and addr
226 offset
= round_page(header_size
);
227 addr
= (caddr_t
) const_text
->addr
; // Load address of __TEXT,__const
230 * Construct a TEXT segment load command
231 * the only part of the TEXT segment we keep is the __TEXT,__const
232 * which contains the kernel vtables.
234 sg
= (struct segment_command
*) &mh
[1];
235 bcopy(orig_ts
, sg
, orig_ts
->cmdsize
);
236 sg
->vmaddr
= (unsigned long) addr
;
237 sg
->vmsize
= const_text
->size
;
239 sg
->filesize
= const_text
->size
+ round_page(header_size
);
243 se
= (struct section
*)(sg
+1);
244 for ( j
= 0; j
< sg
->nsects
; j
++, se
++ ) {
245 se
->addr
= (unsigned long) addr
;
249 if (!strcmp("__const", se
->sectname
)) {
250 se
->size
= const_text
->size
;
251 addr
+= const_text
->size
;
252 offset
+= const_text
->size
;
256 offset
= round_page(offset
);
258 // Now copy of the __DATA segment load command, the image need
259 // not be stored to disk nobody needs it, yet!
260 sg
= (struct segment_command
*)((int)sg
+ sg
->cmdsize
);
261 bcopy(orig_ds
, sg
, orig_ds
->cmdsize
);
263 sg
->vmaddr
= (unsigned long) addr
;
264 sg
->vmsize
= 0x1000; // One page for some reason?
265 sg
->fileoff
= offset
;
270 se
= (struct section
*)(sg
+1);
271 for ( j
= 0; j
< sg
->nsects
; j
++, se
++ ) {
272 se
->addr
= (unsigned long) addr
;
277 offset
= round_page(offset
);
281 * Set up LC_SYMTAB command
283 st
= (struct symtab_command
*)((int)sg
+ sg
->cmdsize
);
285 st
->cmdsize
= sizeof(struct symtab_command
);
287 st
->nsyms
= orig_st
->nsyms
;
288 st
->strsize
= orig_st
->strsize
;
289 st
->stroff
= offset
+ st
->nsyms
* sizeof(struct nlist
);
292 * Convert the symbol table in place from section references
293 * to absolute references.
295 sym
= (struct nlist
*) orig_le
->vmaddr
;
296 for (i
= 0; i
< st
->nsyms
; i
++, sym
++ ) {
297 if ( (sym
->n_type
& N_TYPE
) == N_SECT
) {
298 sym
->n_sect
= NO_SECT
;
299 sym
->n_type
= (sym
->n_type
& ~N_TYPE
) | N_ABS
;
304 * Write out the load commands at the beginning of the file.
306 error
= vn_rdwr(UIO_WRITE
, vp
, (caddr_t
) mh
, header_size
, (off_t
) 0,
307 UIO_SYSSPACE32
, IO_NODELOCKED
|IO_UNIT
, cred
, (int *) 0, p
);
312 * Write out the __TEXT,__const data segment.
314 error
= vn_rdwr(UIO_WRITE
, vp
, (caddr_t
) const_text
->addr
,
315 const_text
->size
, const_text
->offset
,
316 UIO_SYSSPACE32
, IO_NODELOCKED
|IO_UNIT
, cred
, (int *) 0, p
);
321 * Write out kernel symbols
323 offset
= st
->nsyms
* sizeof(struct nlist
) + st
->strsize
; // symtab size
324 error
= vn_rdwr(UIO_WRITE
, vp
,
325 (caddr_t
) orig_le
->vmaddr
, offset
, st
->symoff
,
326 UIO_SYSSPACE32
, IO_NODELOCKED
|IO_UNIT
, cred
, (int *) 0, p
);
329 kmem_free(kernel_map
, header
, header_size
);
332 error1
= vnode_close(vp
, FWRITE
, &context
);
333 if (!error
) error
= error1
;
341 int get_kernel_symfile(struct proc
*p
, char **symfile
)
343 if (!kernel_symfile_opened
) {
344 kernel_symfile_opened
= 1;
345 error_code
= output_kernel_symbols(p
);
348 *symfile
= "\\mach.sym";
353 struct kern_direct_file_io_ref_t
355 struct vfs_context context
;
360 static int file_ioctl(void * p1
, void * p2
, int theIoctl
, caddr_t result
)
362 dev_t device
= (dev_t
) p1
;
364 return ((*bdevsw
[major(device
)].d_ioctl
)
365 (device
, theIoctl
, result
, S_IFBLK
, p2
));
368 static int device_ioctl(void * p1
, __unused
void * p2
, int theIoctl
, caddr_t result
)
370 return (VNOP_IOCTL(p1
, theIoctl
, result
, 0, p2
));
373 struct kern_direct_file_io_ref_t
*
374 kern_open_file_for_direct_io(const char * name
,
375 kern_get_file_extents_callback_t callback
,
377 dev_t
* device_result
,
378 uint64_t * partitionbase_result
,
379 uint64_t * maxiocount_result
)
381 struct kern_direct_file_io_ref_t
* ref
;
385 struct vnode_attr va
;
391 off_t maxiocount
, count
;
393 int (*do_ioctl
)(void * p1
, void * p2
, int theIoctl
, caddr_t result
);
399 ref
= (struct kern_direct_file_io_ref_t
*) kalloc(sizeof(struct kern_direct_file_io_ref_t
));
407 p
= current_proc(); // kernproc;
409 ref
->context
.vc_proc
= p
;
410 ref
->context
.vc_ucred
= cred
;
412 if ((error
= vnode_open(name
, (O_CREAT
| FWRITE
), (0), 0, &ref
->vp
, &ref
->context
)))
416 VATTR_WANTED(&va
, va_rdev
);
417 VATTR_WANTED(&va
, va_fsid
);
418 VATTR_WANTED(&va
, va_data_size
);
419 VATTR_WANTED(&va
, va_nlink
);
421 if (vnode_getattr(ref
->vp
, &va
, &ref
->context
))
424 kprintf("vp va_rdev major %d minor %d\n", major(va
.va_rdev
), minor(va
.va_rdev
));
425 kprintf("vp va_fsid major %d minor %d\n", major(va
.va_fsid
), minor(va
.va_fsid
));
426 kprintf("vp size %qd\n", va
.va_data_size
);
428 if (ref
->vp
->v_type
== VREG
)
430 /* Don't dump files with links. */
431 if (va
.va_nlink
!= 1)
435 p1
= (void *) device
;
437 do_ioctl
= &file_ioctl
;
439 else if ((ref
->vp
->v_type
== VBLK
) || (ref
->vp
->v_type
== VCHR
))
446 do_ioctl
= &device_ioctl
;
450 /* Don't dump to non-regular files. */
455 // get partition base
457 error
= do_ioctl(p1
, p2
, DKIOCGETBASE
, (caddr_t
) partitionbase_result
);
461 // get block size & constraints
463 error
= do_ioctl(p1
, p2
, DKIOCGETBLOCKSIZE
, (caddr_t
) &blksize
);
467 maxiocount
= 1*1024*1024*1024;
469 error
= do_ioctl(p1
, p2
, DKIOCGETMAXBLOCKCOUNTREAD
, (caddr_t
) &count
);
473 if (count
&& (count
< maxiocount
))
476 error
= do_ioctl(p1
, p2
, DKIOCGETMAXBLOCKCOUNTWRITE
, (caddr_t
) &count
);
480 if (count
&& (count
< maxiocount
))
483 error
= do_ioctl(p1
, p2
, DKIOCGETMAXBYTECOUNTREAD
, (caddr_t
) &count
);
486 if (count
&& (count
< maxiocount
))
489 error
= do_ioctl(p1
, p2
, DKIOCGETMAXBYTECOUNTWRITE
, (caddr_t
) &count
);
492 if (count
&& (count
< maxiocount
))
495 error
= do_ioctl(p1
, p2
, DKIOCGETMAXSEGMENTBYTECOUNTREAD
, (caddr_t
) &count
);
498 if (count
&& (count
< maxiocount
))
501 error
= do_ioctl(p1
, p2
, DKIOCGETMAXSEGMENTBYTECOUNTWRITE
, (caddr_t
) &count
);
504 if (count
&& (count
< maxiocount
))
507 kprintf("max io 0x%qx bytes\n", maxiocount
);
508 if (maxiocount_result
)
509 *maxiocount_result
= maxiocount
;
511 // generate the block list
514 if (ref
->vp
->v_type
== VREG
)
517 while(f_offset
< (off_t
) va
.va_data_size
)
519 size_t io_size
= 1*1024*1024*1024;
522 error
= VNOP_BLOCKMAP(ref
->vp
, f_offset
, io_size
, &blkno
, (size_t *)&io_size
, NULL
, 0, NULL
);
525 callback(callback_ref
, ((uint64_t) blkno
) * blksize
, (uint64_t) io_size
);
528 callback(callback_ref
, 0ULL, 0ULL);
530 else if ((ref
->vp
->v_type
== VBLK
) || (ref
->vp
->v_type
== VCHR
))
532 error
= do_ioctl(p1
, p2
, DKIOCGETBLOCKCOUNT
, (caddr_t
) &size
);
536 callback(callback_ref
, 0ULL, size
);
537 callback(callback_ref
, size
, 0ULL);
541 *device_result
= device
;
544 kprintf("kern_open_file_for_direct_io(%d)\n", error
);
548 vnode_close(ref
->vp
, FWRITE
, &ref
->context
);
550 kfree(ref
, sizeof(struct kern_direct_file_io_ref_t
));
557 kern_write_file(struct kern_direct_file_io_ref_t
* ref
, off_t offset
, caddr_t addr
, vm_size_t len
)
559 return (vn_rdwr(UIO_WRITE
, ref
->vp
,
561 UIO_SYSSPACE32
, IO_SYNC
|IO_NODELOCKED
|IO_UNIT
,
562 ref
->context
.vc_ucred
, (int *) 0, ref
->context
.vc_proc
));
566 kern_close_file_for_direct_io(struct kern_direct_file_io_ref_t
* ref
)
568 kprintf("kern_close_file_for_direct_io\n");
574 error
= vnode_close(ref
->vp
, FWRITE
, &ref
->context
);
575 kprintf("vnode_close(%d)\n", error
);
577 kfree(ref
, sizeof(struct kern_direct_file_io_ref_t
));