2 * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
4 * @APPLE_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. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
23 /* Copyright (c) 1998 Apple Computer, Inc. All rights reserved.
25 * File: bsd/kern/kern_symfile.c
27 * This file contains creates a dummy symbol file for mach_kernel
28 * based on the symbol table information passed by the
29 * SecondaryLoader/PlatformExpert. This allows us to correctly
30 * link other executables (drivers, etc) against the the kernel in
31 * cases where the kernel image on the root device does not match
32 * the live kernel. This can occur during net-booting where the
33 * actual kernel image is obtained from the network via tftp rather
34 * than the root device.
36 * If a symbol table is available, then the file /mach.sym will be
37 * created containing a Mach Header and a LC_SYMTAB load command
38 * followed by the the symbol table data for mach_kernel.
40 * NOTE: This file supports only 32 bit kernels at the present time;
41 * adding support for 64 bit kernels is possible, but is not
42 * necessary at the present time.
49 #include <mach/vm_param.h>
51 #include <sys/param.h>
52 #include <sys/systm.h>
53 #include <sys/signalvar.h>
54 #include <sys/resourcevar.h>
55 #include <sys/namei.h>
56 #include <sys/vnode_internal.h>
57 #include <sys/proc_internal.h>
58 #include <sys/kauth.h>
59 #include <sys/timeb.h>
60 #include <sys/times.h>
62 #include <sys/file_internal.h>
64 #include <sys/kernel.h>
69 #include <mach-o/loader.h>
70 #include <mach-o/nlist.h>
72 #include <kern/kalloc.h>
73 #include <vm/vm_kern.h>
74 #include <pexpert/pexpert.h>
75 #include <IOKit/IOHibernatePrivate.h>
77 extern unsigned char rootdevice
[];
78 extern struct mach_header _mh_execute_header
;
80 static int kernel_symfile_opened
= 0;
81 static int error_code
= 0;
83 extern int IODTGetLoaderInfo(char *key
, void **infoAddr
, int *infoSize
);
84 extern void IODTFreeLoaderInfo(char *key
, void *infoAddr
, int infoSize
);
87 * Can only operate against currently running 32 bit mach_kernel
90 output_kernel_symbols(struct proc
*p
)
93 kauth_cred_t cred
= p
->p_ucred
; /* XXX */
95 struct vfs_context context
;
96 struct load_command
*cmd
;
97 struct mach_header
*orig_mh
, *mh
;
98 struct segment_command
*orig_ds
, *orig_ts
, *orig_le
, *sg
;
99 struct section
*se
, *const_text
;
100 struct symtab_command
*st
, *orig_st
;
102 vm_size_t orig_mhsize
, orig_st_size
;
104 vm_size_t header_size
= 0; /* out: protected by header */
118 // Dispose of unnecessary gumf, the booter doesn't need to load these
119 rc_mh
= IODTGetLoaderInfo("Kernel-__HEADER",
120 (void **)&orig_mh
, &orig_mhsize
);
121 if (rc_mh
== 0 && orig_mh
)
122 IODTFreeLoaderInfo("Kernel-__HEADER",
123 (void *)orig_mh
, round_page_32(orig_mhsize
));
125 rc_sc
= IODTGetLoaderInfo("Kernel-__SYMTAB",
126 (void **) &orig_st
, &orig_st_size
);
127 if (rc_sc
== 0 && orig_st
)
128 IODTFreeLoaderInfo("Kernel-__SYMTAB",
129 (void *)orig_st
, round_page_32(orig_st_size
));
131 if (cred
->cr_svuid
!= cred
->cr_ruid
|| cred
->cr_svgid
!= cred
->cr_rgid
)
134 // Check to see if the root is 'e' or 'n', is this a test for network?
135 if (rootdevice
[0] == 'e' && rootdevice
[1] == 'n')
139 context
.vc_ucred
= cred
;
141 if ((error
= vnode_open("mach.sym", (O_CREAT
| FWRITE
), (S_IRUSR
| S_IRGRP
| S_IROTH
), 0, &vp
, &context
)))
144 /* Don't dump to non-regular files or files with links. */
147 VATTR_WANTED(&va
, va_nlink
);
148 if ((vp
->v_type
!= VREG
) || vnode_getattr(vp
, &va
, &context
) || (va
.va_nlink
!= 1))
151 VATTR_INIT(&va
); /* better to do it here than waste more stack in vnode_getsize */
152 VATTR_SET(&va
, va_data_size
, 0);
153 vnode_setattr(vp
, &va
, &context
);
154 p
->p_acflag
|= ACORE
;
156 // If the file type is MH_EXECUTE then this must be a kernel
157 // as all Kernel extensions must be of type MH_OBJECT
158 orig_ds
= orig_ts
= orig_le
= NULL
;
160 orig_mh
= &_mh_execute_header
;
161 cmd
= (struct load_command
*) &orig_mh
[1];
162 for (i
= 0; i
< orig_mh
->ncmds
; i
++) {
163 if (cmd
->cmd
== LC_SEGMENT
) {
164 struct segment_command
*orig_sg
= (struct segment_command
*) cmd
;
166 if (!strcmp(SEG_TEXT
, orig_sg
->segname
))
168 else if (!strcmp(SEG_DATA
, orig_sg
->segname
))
170 else if (!strcmp(SEG_LINKEDIT
, orig_sg
->segname
))
173 else if (cmd
->cmd
== LC_SYMTAB
)
174 orig_st
= (struct symtab_command
*) cmd
;
176 cmd
= (struct load_command
*) ((caddr_t
) cmd
+ cmd
->cmdsize
);
179 if (!orig_ts
|| !orig_ds
|| !orig_le
|| !orig_st
)
183 se
= (struct section
*) &orig_ts
[1];
184 for (i
= 0; i
< orig_ts
->nsects
; i
++, se
++) {
185 if (!strcmp("__const", se
->sectname
)) {
193 header_size
= sizeof(struct mach_header
)
196 + sizeof(struct symtab_command
);
198 (void) kmem_alloc(kernel_map
,
199 (vm_offset_t
*) &header
,
200 (vm_size_t
) header_size
);
202 bzero((void *) header
, header_size
);
207 * Set up Mach-O header.
209 mh
= (struct mach_header
*) header
;
210 mh
->magic
= orig_mh
->magic
;
211 mh
->cputype
= orig_mh
->cputype
;
212 mh
->cpusubtype
= orig_mh
->cpusubtype
;
213 mh
->filetype
= orig_mh
->filetype
;
215 mh
->sizeofcmds
= header_size
- sizeof(struct mach_header
);
216 mh
->flags
= orig_mh
->flags
;
218 // Initialise the current file offset and addr
219 offset
= round_page(header_size
);
220 addr
= (caddr_t
) const_text
->addr
; // Load address of __TEXT,__const
223 * Construct a TEXT segment load command
224 * the only part of the TEXT segment we keep is the __TEXT,__const
225 * which contains the kernel vtables.
227 sg
= (struct segment_command
*) &mh
[1];
228 bcopy(orig_ts
, sg
, orig_ts
->cmdsize
);
229 sg
->vmaddr
= (unsigned long) addr
;
230 sg
->vmsize
= const_text
->size
;
232 sg
->filesize
= const_text
->size
+ round_page(header_size
);
236 se
= (struct section
*)(sg
+1);
237 for ( j
= 0; j
< sg
->nsects
; j
++, se
++ ) {
238 se
->addr
= (unsigned long) addr
;
242 if (!strcmp("__const", se
->sectname
)) {
243 se
->size
= const_text
->size
;
244 addr
+= const_text
->size
;
245 offset
+= const_text
->size
;
249 offset
= round_page(offset
);
251 // Now copy of the __DATA segment load command, the image need
252 // not be stored to disk nobody needs it, yet!
253 sg
= (struct segment_command
*)((int)sg
+ sg
->cmdsize
);
254 bcopy(orig_ds
, sg
, orig_ds
->cmdsize
);
256 sg
->vmaddr
= (unsigned long) addr
;
257 sg
->vmsize
= 0x1000; // One page for some reason?
258 sg
->fileoff
= offset
;
263 se
= (struct section
*)(sg
+1);
264 for ( j
= 0; j
< sg
->nsects
; j
++, se
++ ) {
265 se
->addr
= (unsigned long) addr
;
270 offset
= round_page(offset
);
274 * Set up LC_SYMTAB command
276 st
= (struct symtab_command
*)((int)sg
+ sg
->cmdsize
);
278 st
->cmdsize
= sizeof(struct symtab_command
);
280 st
->nsyms
= orig_st
->nsyms
;
281 st
->strsize
= orig_st
->strsize
;
282 st
->stroff
= offset
+ st
->nsyms
* sizeof(struct nlist
);
285 * Convert the symbol table in place from section references
286 * to absolute references.
288 sym
= (struct nlist
*) orig_le
->vmaddr
;
289 for (i
= 0; i
< st
->nsyms
; i
++, sym
++ ) {
290 if ( (sym
->n_type
& N_TYPE
) == N_SECT
) {
291 sym
->n_sect
= NO_SECT
;
292 sym
->n_type
= (sym
->n_type
& ~N_TYPE
) | N_ABS
;
297 * Write out the load commands at the beginning of the file.
299 error
= vn_rdwr(UIO_WRITE
, vp
, (caddr_t
) mh
, header_size
, (off_t
) 0,
300 UIO_SYSSPACE32
, IO_NODELOCKED
|IO_UNIT
, cred
, (int *) 0, p
);
305 * Write out the __TEXT,__const data segment.
307 error
= vn_rdwr(UIO_WRITE
, vp
, (caddr_t
) const_text
->addr
,
308 const_text
->size
, const_text
->offset
,
309 UIO_SYSSPACE32
, IO_NODELOCKED
|IO_UNIT
, cred
, (int *) 0, p
);
314 * Write out kernel symbols
316 offset
= st
->nsyms
* sizeof(struct nlist
) + st
->strsize
; // symtab size
317 error
= vn_rdwr(UIO_WRITE
, vp
,
318 (caddr_t
) orig_le
->vmaddr
, offset
, st
->symoff
,
319 UIO_SYSSPACE32
, IO_NODELOCKED
|IO_UNIT
, cred
, (int *) 0, p
);
322 kmem_free(kernel_map
, header
, header_size
);
325 error1
= vnode_close(vp
, FWRITE
, &context
);
326 if (!error
) error
= error1
;
334 int get_kernel_symfile(struct proc
*p
, char **symfile
)
336 if (!kernel_symfile_opened
) {
337 kernel_symfile_opened
= 1;
338 error_code
= output_kernel_symbols(p
);
341 *symfile
= "\\mach.sym";
346 struct kern_direct_file_io_ref_t
348 struct vfs_context context
;
353 static int file_ioctl(void * p1
, void * p2
, int theIoctl
, caddr_t result
)
355 dev_t device
= (dev_t
) p1
;
357 return ((*bdevsw
[major(device
)].d_ioctl
)
358 (device
, theIoctl
, result
, S_IFBLK
, p2
));
361 static int device_ioctl(void * p1
, __unused
void * p2
, int theIoctl
, caddr_t result
)
363 return (VNOP_IOCTL(p1
, theIoctl
, result
, 0, p2
));
366 struct kern_direct_file_io_ref_t
*
367 kern_open_file_for_direct_io(const char * name
,
368 kern_get_file_extents_callback_t callback
,
370 dev_t
* device_result
,
371 uint64_t * partitionbase_result
,
372 uint64_t * maxiocount_result
)
374 struct kern_direct_file_io_ref_t
* ref
;
378 struct vnode_attr va
;
384 off_t maxiocount
, count
;
386 int (*do_ioctl
)(void * p1
, void * p2
, int theIoctl
, caddr_t result
);
392 ref
= (struct kern_direct_file_io_ref_t
*) kalloc(sizeof(struct kern_direct_file_io_ref_t
));
400 p
= current_proc(); // kernproc;
402 ref
->context
.vc_proc
= p
;
403 ref
->context
.vc_ucred
= cred
;
405 if ((error
= vnode_open(name
, (O_CREAT
| FWRITE
), (0), 0, &ref
->vp
, &ref
->context
)))
409 VATTR_WANTED(&va
, va_rdev
);
410 VATTR_WANTED(&va
, va_fsid
);
411 VATTR_WANTED(&va
, va_data_size
);
412 VATTR_WANTED(&va
, va_nlink
);
414 if (vnode_getattr(ref
->vp
, &va
, &ref
->context
))
417 kprintf("vp va_rdev major %d minor %d\n", major(va
.va_rdev
), minor(va
.va_rdev
));
418 kprintf("vp va_fsid major %d minor %d\n", major(va
.va_fsid
), minor(va
.va_fsid
));
419 kprintf("vp size %qd\n", va
.va_data_size
);
421 if (ref
->vp
->v_type
== VREG
)
423 /* Don't dump files with links. */
424 if (va
.va_nlink
!= 1)
428 p1
= (void *) device
;
430 do_ioctl
= &file_ioctl
;
432 else if ((ref
->vp
->v_type
== VBLK
) || (ref
->vp
->v_type
== VCHR
))
439 do_ioctl
= &device_ioctl
;
443 /* Don't dump to non-regular files. */
448 // get partition base
450 error
= do_ioctl(p1
, p2
, DKIOCGETBASE
, (caddr_t
) partitionbase_result
);
454 // get block size & constraints
456 error
= do_ioctl(p1
, p2
, DKIOCGETBLOCKSIZE
, (caddr_t
) &blksize
);
460 maxiocount
= 1*1024*1024*1024;
462 error
= do_ioctl(p1
, p2
, DKIOCGETMAXBLOCKCOUNTREAD
, (caddr_t
) &count
);
466 if (count
&& (count
< maxiocount
))
469 error
= do_ioctl(p1
, p2
, DKIOCGETMAXBLOCKCOUNTWRITE
, (caddr_t
) &count
);
473 if (count
&& (count
< maxiocount
))
476 error
= do_ioctl(p1
, p2
, DKIOCGETMAXBYTECOUNTREAD
, (caddr_t
) &count
);
479 if (count
&& (count
< maxiocount
))
482 error
= do_ioctl(p1
, p2
, DKIOCGETMAXBYTECOUNTWRITE
, (caddr_t
) &count
);
485 if (count
&& (count
< maxiocount
))
488 error
= do_ioctl(p1
, p2
, DKIOCGETMAXSEGMENTBYTECOUNTREAD
, (caddr_t
) &count
);
491 if (count
&& (count
< maxiocount
))
494 error
= do_ioctl(p1
, p2
, DKIOCGETMAXSEGMENTBYTECOUNTWRITE
, (caddr_t
) &count
);
497 if (count
&& (count
< maxiocount
))
500 kprintf("max io 0x%qx bytes\n", maxiocount
);
501 if (maxiocount_result
)
502 *maxiocount_result
= maxiocount
;
504 // generate the block list
507 if (ref
->vp
->v_type
== VREG
)
510 while(f_offset
< (off_t
) va
.va_data_size
)
512 size_t io_size
= 1*1024*1024*1024;
515 error
= VNOP_BLOCKMAP(ref
->vp
, f_offset
, io_size
, &blkno
, (size_t *)&io_size
, NULL
, 0, NULL
);
518 callback(callback_ref
, ((uint64_t) blkno
) * blksize
, (uint64_t) io_size
);
521 callback(callback_ref
, 0ULL, 0ULL);
523 else if ((ref
->vp
->v_type
== VBLK
) || (ref
->vp
->v_type
== VCHR
))
525 error
= do_ioctl(p1
, p2
, DKIOCGETBLOCKCOUNT
, (caddr_t
) &size
);
529 callback(callback_ref
, 0ULL, size
);
530 callback(callback_ref
, size
, 0ULL);
534 *device_result
= device
;
537 kprintf("kern_open_file_for_direct_io(%d)\n", error
);
541 vnode_close(ref
->vp
, FWRITE
, &ref
->context
);
543 kfree(ref
, sizeof(struct kern_direct_file_io_ref_t
));
550 kern_write_file(struct kern_direct_file_io_ref_t
* ref
, off_t offset
, caddr_t addr
, vm_size_t len
)
552 return (vn_rdwr(UIO_WRITE
, ref
->vp
,
554 UIO_SYSSPACE32
, IO_SYNC
|IO_NODELOCKED
|IO_UNIT
,
555 ref
->context
.vc_ucred
, (int *) 0, ref
->context
.vc_proc
));
559 kern_close_file_for_direct_io(struct kern_direct_file_io_ref_t
* ref
)
561 kprintf("kern_close_file_for_direct_io\n");
567 error
= vnode_close(ref
->vp
, FWRITE
, &ref
->context
);
568 kprintf("vnode_close(%d)\n", error
);
570 kfree(ref
, sizeof(struct kern_direct_file_io_ref_t
));