2 * Copyright (c) 2000-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@
28 /* Copyright (c) 1998 Apple Computer, Inc. All rights reserved.
30 * File: bsd/kern/kern_symfile.c
32 * This file contains creates a dummy symbol file for mach_kernel
33 * based on the symbol table information passed by the
34 * SecondaryLoader/PlatformExpert. This allows us to correctly
35 * link other executables (drivers, etc) against the the kernel in
36 * cases where the kernel image on the root device does not match
37 * the live kernel. This can occur during net-booting where the
38 * actual kernel image is obtained from the network via tftp rather
39 * than the root device.
41 * If a symbol table is available, then the file /mach.sym will be
42 * created containing a Mach Header and a LC_SYMTAB load command
43 * followed by the the symbol table data for mach_kernel.
45 * NOTE: This file supports only 32 bit kernels at the present time;
46 * adding support for 64 bit kernels is possible, but is not
47 * necessary at the present time.
54 #include <mach/vm_param.h>
56 #include <sys/param.h>
57 #include <sys/systm.h>
58 #include <sys/signalvar.h>
59 #include <sys/resourcevar.h>
60 #include <sys/namei.h>
61 #include <sys/vnode_internal.h>
62 #include <sys/proc_internal.h>
63 #include <sys/kauth.h>
64 #include <sys/timeb.h>
65 #include <sys/times.h>
67 #include <sys/file_internal.h>
69 #include <sys/kernel.h>
74 #include <mach-o/loader.h>
75 #include <mach-o/nlist.h>
77 #include <kern/kalloc.h>
78 #include <vm/vm_kern.h>
79 #include <pexpert/pexpert.h>
80 #include <IOKit/IOHibernatePrivate.h>
82 extern unsigned char rootdevice
[];
83 extern struct mach_header _mh_execute_header
;
85 static int kernel_symfile_opened
= 0;
86 static int error_code
= 0;
88 extern int IODTGetLoaderInfo(char *key
, void **infoAddr
, int *infoSize
);
89 extern void IODTFreeLoaderInfo(char *key
, void *infoAddr
, int infoSize
);
92 * Can only operate against currently running 32 bit mach_kernel
95 output_kernel_symbols(struct proc
*p
)
98 kauth_cred_t cred
= p
->p_ucred
; /* XXX unsafe */
100 struct vfs_context context
;
101 struct load_command
*cmd
;
102 struct mach_header
*orig_mh
, *mh
;
103 struct segment_command
*orig_ds
, *orig_ts
, *orig_le
, *sg
;
104 struct section
*se
, *const_text
;
105 struct symtab_command
*st
, *orig_st
;
107 vm_size_t orig_mhsize
, orig_st_size
;
109 vm_size_t header_size
= 0; /* out: protected by header */
124 context
.vc_ucred
= kauth_cred_proc_ref(p
);
126 // Dispose of unnecessary gumf, the booter doesn't need to load these
127 rc_mh
= IODTGetLoaderInfo("Kernel-__HEADER",
128 (void **)&orig_mh
, &orig_mhsize
);
129 if (rc_mh
== 0 && orig_mh
)
130 IODTFreeLoaderInfo("Kernel-__HEADER",
131 (void *)orig_mh
, round_page_32(orig_mhsize
));
133 rc_sc
= IODTGetLoaderInfo("Kernel-__SYMTAB",
134 (void **) &orig_st
, &orig_st_size
);
135 if (rc_sc
== 0 && orig_st
)
136 IODTFreeLoaderInfo("Kernel-__SYMTAB",
137 (void *)orig_st
, round_page_32(orig_st_size
));
139 if (cred
->cr_svuid
!= cred
->cr_ruid
|| cred
->cr_svgid
!= cred
->cr_rgid
)
142 // Check to see if the root is 'e' or 'n', is this a test for network?
143 if (rootdevice
[0] == 'e' && rootdevice
[1] == 'n')
146 if ((error
= vnode_open("mach.sym", (O_CREAT
| FWRITE
), (S_IRUSR
| S_IRGRP
| S_IROTH
), 0, &vp
, &context
)))
149 /* Don't dump to non-regular files or files with links. */
152 VATTR_WANTED(&va
, va_nlink
);
153 if ((vp
->v_type
!= VREG
) || vnode_getattr(vp
, &va
, &context
) || (va
.va_nlink
!= 1))
156 VATTR_INIT(&va
); /* better to do it here than waste more stack in vnode_getsize */
157 VATTR_SET(&va
, va_data_size
, 0);
158 vnode_setattr(vp
, &va
, &context
);
159 p
->p_acflag
|= ACORE
;
161 // If the file type is MH_EXECUTE then this must be a kernel
162 // as all Kernel extensions must be of type MH_OBJECT
163 orig_ds
= orig_ts
= orig_le
= NULL
;
165 orig_mh
= &_mh_execute_header
;
166 cmd
= (struct load_command
*) &orig_mh
[1];
167 for (i
= 0; i
< orig_mh
->ncmds
; i
++) {
168 if (cmd
->cmd
== LC_SEGMENT
) {
169 struct segment_command
*orig_sg
= (struct segment_command
*) cmd
;
171 if (!strcmp(SEG_TEXT
, orig_sg
->segname
))
173 else if (!strcmp(SEG_DATA
, orig_sg
->segname
))
175 else if (!strcmp(SEG_LINKEDIT
, orig_sg
->segname
))
178 else if (cmd
->cmd
== LC_SYMTAB
)
179 orig_st
= (struct symtab_command
*) cmd
;
181 cmd
= (struct load_command
*) ((caddr_t
) cmd
+ cmd
->cmdsize
);
184 if (!orig_ts
|| !orig_ds
|| !orig_le
|| !orig_st
)
188 se
= (struct section
*) &orig_ts
[1];
189 for (i
= 0; i
< orig_ts
->nsects
; i
++, se
++) {
190 if (!strcmp("__const", se
->sectname
)) {
198 header_size
= sizeof(struct mach_header
)
201 + sizeof(struct symtab_command
);
203 (void) kmem_alloc(kernel_map
,
204 (vm_offset_t
*) &header
,
205 (vm_size_t
) header_size
);
207 bzero((void *) header
, header_size
);
212 * Set up Mach-O header.
214 mh
= (struct mach_header
*) header
;
215 mh
->magic
= orig_mh
->magic
;
216 mh
->cputype
= orig_mh
->cputype
;
217 mh
->cpusubtype
= orig_mh
->cpusubtype
;
218 mh
->filetype
= orig_mh
->filetype
;
220 mh
->sizeofcmds
= header_size
- sizeof(struct mach_header
);
221 mh
->flags
= orig_mh
->flags
;
223 // Initialise the current file offset and addr
224 offset
= round_page(header_size
);
225 addr
= (caddr_t
) const_text
->addr
; // Load address of __TEXT,__const
228 * Construct a TEXT segment load command
229 * the only part of the TEXT segment we keep is the __TEXT,__const
230 * which contains the kernel vtables.
232 sg
= (struct segment_command
*) &mh
[1];
233 bcopy(orig_ts
, sg
, orig_ts
->cmdsize
);
234 sg
->vmaddr
= (unsigned long) addr
;
235 sg
->vmsize
= const_text
->size
;
237 sg
->filesize
= const_text
->size
+ round_page(header_size
);
241 se
= (struct section
*)(sg
+1);
242 for ( j
= 0; j
< sg
->nsects
; j
++, se
++ ) {
243 se
->addr
= (unsigned long) addr
;
247 if (!strcmp("__const", se
->sectname
)) {
248 se
->size
= const_text
->size
;
249 addr
+= const_text
->size
;
250 offset
+= const_text
->size
;
254 offset
= round_page(offset
);
256 // Now copy of the __DATA segment load command, the image need
257 // not be stored to disk nobody needs it, yet!
258 sg
= (struct segment_command
*)((int)sg
+ sg
->cmdsize
);
259 bcopy(orig_ds
, sg
, orig_ds
->cmdsize
);
261 sg
->vmaddr
= (unsigned long) addr
;
262 sg
->vmsize
= 0x1000; // One page for some reason?
263 sg
->fileoff
= offset
;
268 se
= (struct section
*)(sg
+1);
269 for ( j
= 0; j
< sg
->nsects
; j
++, se
++ ) {
270 se
->addr
= (unsigned long) addr
;
275 offset
= round_page(offset
);
279 * Set up LC_SYMTAB command
281 st
= (struct symtab_command
*)((int)sg
+ sg
->cmdsize
);
283 st
->cmdsize
= sizeof(struct symtab_command
);
285 st
->nsyms
= orig_st
->nsyms
;
286 st
->strsize
= orig_st
->strsize
;
287 st
->stroff
= offset
+ st
->nsyms
* sizeof(struct nlist
);
290 * Convert the symbol table in place from section references
291 * to absolute references.
293 sym
= (struct nlist
*) orig_le
->vmaddr
;
294 for (i
= 0; i
< st
->nsyms
; i
++, sym
++ ) {
295 if ( (sym
->n_type
& N_TYPE
) == N_SECT
) {
296 sym
->n_sect
= NO_SECT
;
297 sym
->n_type
= (sym
->n_type
& ~N_TYPE
) | N_ABS
;
302 * Write out the load commands at the beginning of the file.
304 error
= vn_rdwr(UIO_WRITE
, vp
, (caddr_t
) mh
, header_size
, (off_t
) 0,
305 UIO_SYSSPACE32
, IO_NODELOCKED
|IO_UNIT
, cred
, (int *) 0, p
);
310 * Write out the __TEXT,__const data segment.
312 error
= vn_rdwr(UIO_WRITE
, vp
, (caddr_t
) const_text
->addr
,
313 const_text
->size
, const_text
->offset
,
314 UIO_SYSSPACE32
, IO_NODELOCKED
|IO_UNIT
, cred
, (int *) 0, p
);
319 * Write out kernel symbols
321 offset
= st
->nsyms
* sizeof(struct nlist
) + st
->strsize
; // symtab size
322 error
= vn_rdwr(UIO_WRITE
, vp
,
323 (caddr_t
) orig_le
->vmaddr
, offset
, st
->symoff
,
324 UIO_SYSSPACE32
, IO_NODELOCKED
|IO_UNIT
, cred
, (int *) 0, p
);
327 kmem_free(kernel_map
, header
, header_size
);
330 error1
= vnode_close(vp
, FWRITE
, &context
);
331 if (!error
) error
= error1
;
334 kauth_cred_unref(&context
.vc_ucred
);
340 int get_kernel_symfile(struct proc
*p
, char **symfile
)
342 if (!kernel_symfile_opened
) {
343 kernel_symfile_opened
= 1;
344 error_code
= output_kernel_symbols(p
);
347 *symfile
= "\\mach.sym";
352 struct kern_direct_file_io_ref_t
354 struct vfs_context context
;
359 static int file_ioctl(void * p1
, void * p2
, int theIoctl
, caddr_t result
)
361 dev_t device
= (dev_t
) p1
;
363 return ((*bdevsw
[major(device
)].d_ioctl
)
364 (device
, theIoctl
, result
, S_IFBLK
, p2
));
367 static int device_ioctl(void * p1
, __unused
void * p2
, int theIoctl
, caddr_t result
)
369 return (VNOP_IOCTL(p1
, theIoctl
, result
, 0, p2
));
372 struct kern_direct_file_io_ref_t
*
373 kern_open_file_for_direct_io(const char * name
,
374 kern_get_file_extents_callback_t callback
,
376 dev_t
* device_result
,
377 uint64_t * partitionbase_result
,
378 uint64_t * maxiocount_result
)
380 struct kern_direct_file_io_ref_t
* ref
;
383 struct vnode_attr va
;
389 off_t maxiocount
, count
;
391 int (*do_ioctl
)(void * p1
, void * p2
, int theIoctl
, caddr_t result
);
397 ref
= (struct kern_direct_file_io_ref_t
*) kalloc(sizeof(struct kern_direct_file_io_ref_t
));
405 p
= current_proc(); // kernproc;
406 ref
->context
.vc_proc
= p
;
407 ref
->context
.vc_ucred
= kauth_cred_proc_ref(p
);
409 if ((error
= vnode_open(name
, (O_CREAT
| FWRITE
), (0), 0, &ref
->vp
, &ref
->context
)))
413 VATTR_WANTED(&va
, va_rdev
);
414 VATTR_WANTED(&va
, va_fsid
);
415 VATTR_WANTED(&va
, va_data_size
);
416 VATTR_WANTED(&va
, va_nlink
);
418 if (vnode_getattr(ref
->vp
, &va
, &ref
->context
))
421 kprintf("vp va_rdev major %d minor %d\n", major(va
.va_rdev
), minor(va
.va_rdev
));
422 kprintf("vp va_fsid major %d minor %d\n", major(va
.va_fsid
), minor(va
.va_fsid
));
423 kprintf("vp size %qd\n", va
.va_data_size
);
425 if (ref
->vp
->v_type
== VREG
)
427 /* Don't dump files with links. */
428 if (va
.va_nlink
!= 1)
432 p1
= (void *) device
;
434 do_ioctl
= &file_ioctl
;
436 else if ((ref
->vp
->v_type
== VBLK
) || (ref
->vp
->v_type
== VCHR
))
443 do_ioctl
= &device_ioctl
;
447 /* Don't dump to non-regular files. */
452 // get partition base
454 error
= do_ioctl(p1
, p2
, DKIOCGETBASE
, (caddr_t
) partitionbase_result
);
458 // get block size & constraints
460 error
= do_ioctl(p1
, p2
, DKIOCGETBLOCKSIZE
, (caddr_t
) &blksize
);
464 maxiocount
= 1*1024*1024*1024;
466 error
= do_ioctl(p1
, p2
, DKIOCGETMAXBLOCKCOUNTREAD
, (caddr_t
) &count
);
470 if (count
&& (count
< maxiocount
))
473 error
= do_ioctl(p1
, p2
, DKIOCGETMAXBLOCKCOUNTWRITE
, (caddr_t
) &count
);
477 if (count
&& (count
< maxiocount
))
480 error
= do_ioctl(p1
, p2
, DKIOCGETMAXBYTECOUNTREAD
, (caddr_t
) &count
);
483 if (count
&& (count
< maxiocount
))
486 error
= do_ioctl(p1
, p2
, DKIOCGETMAXBYTECOUNTWRITE
, (caddr_t
) &count
);
489 if (count
&& (count
< maxiocount
))
492 error
= do_ioctl(p1
, p2
, DKIOCGETMAXSEGMENTBYTECOUNTREAD
, (caddr_t
) &count
);
495 if (count
&& (count
< maxiocount
))
498 error
= do_ioctl(p1
, p2
, DKIOCGETMAXSEGMENTBYTECOUNTWRITE
, (caddr_t
) &count
);
501 if (count
&& (count
< maxiocount
))
504 kprintf("max io 0x%qx bytes\n", maxiocount
);
505 if (maxiocount_result
)
506 *maxiocount_result
= maxiocount
;
508 // generate the block list
511 if (ref
->vp
->v_type
== VREG
)
514 while(f_offset
< (off_t
) va
.va_data_size
)
516 size_t io_size
= 1*1024*1024*1024;
519 error
= VNOP_BLOCKMAP(ref
->vp
, f_offset
, io_size
, &blkno
, (size_t *)&io_size
, NULL
, 0, NULL
);
522 callback(callback_ref
, ((uint64_t) blkno
) * blksize
, (uint64_t) io_size
);
525 callback(callback_ref
, 0ULL, 0ULL);
527 else if ((ref
->vp
->v_type
== VBLK
) || (ref
->vp
->v_type
== VCHR
))
529 error
= do_ioctl(p1
, p2
, DKIOCGETBLOCKCOUNT
, (caddr_t
) &size
);
533 callback(callback_ref
, 0ULL, size
);
534 callback(callback_ref
, size
, 0ULL);
538 *device_result
= device
;
541 kprintf("kern_open_file_for_direct_io(%d)\n", error
);
545 vnode_close(ref
->vp
, FWRITE
, &ref
->context
);
549 kauth_cred_unref(&ref
->context
.vc_ucred
);
550 kfree(ref
, sizeof(struct kern_direct_file_io_ref_t
));
558 kern_write_file(struct kern_direct_file_io_ref_t
* ref
, off_t offset
, caddr_t addr
, vm_size_t len
)
560 return (vn_rdwr(UIO_WRITE
, ref
->vp
,
562 UIO_SYSSPACE32
, IO_SYNC
|IO_NODELOCKED
|IO_UNIT
,
563 ref
->context
.vc_ucred
, (int *) 0, ref
->context
.vc_proc
));
567 kern_close_file_for_direct_io(struct kern_direct_file_io_ref_t
* ref
)
569 kprintf("kern_close_file_for_direct_io\n");
575 error
= vnode_close(ref
->vp
, FWRITE
, &ref
->context
);
576 kprintf("vnode_close(%d)\n", error
);
579 kauth_cred_unref(&ref
->context
.vc_ucred
);
580 kfree(ref
, sizeof(struct kern_direct_file_io_ref_t
));