2 * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
20 * @APPLE_LICENSE_HEADER_END@
22 /* Copyright (c) 1998 Apple Computer, Inc. All rights reserved.
24 * File: bsd/kern/kern_symfile.c
26 * This file contains creates a dummy symbol file for mach_kernel
27 * based on the symbol table information passed by the
28 * SecondaryLoader/PlatformExpert. This allows us to correctly
29 * link other executables (drivers, etc) against the the kernel in
30 * cases where the kernel image on the root device does not match
31 * the live kernel. This can occur during net-booting where the
32 * actual kernel image is obtained from the network via tftp rather
33 * than the root device.
35 * If a symbol table is available, then the file /mach.sym will be
36 * created containing a Mach Header and a LC_SYMTAB load command
37 * followed by the the symbol table data for mach_kernel.
39 * NOTE: This file supports only 32 bit kernels at the present time;
40 * adding support for 64 bit kernels is possible, but is not
41 * necessary at the present time.
48 #include <mach/vm_param.h>
50 #include <sys/param.h>
51 #include <sys/systm.h>
52 #include <sys/signalvar.h>
53 #include <sys/resourcevar.h>
54 #include <sys/namei.h>
55 #include <sys/vnode_internal.h>
56 #include <sys/proc_internal.h>
57 #include <sys/kauth.h>
58 #include <sys/timeb.h>
59 #include <sys/times.h>
61 #include <sys/file_internal.h>
63 #include <sys/kernel.h>
68 #include <mach-o/loader.h>
69 #include <mach-o/nlist.h>
71 #include <kern/kalloc.h>
72 #include <vm/vm_kern.h>
73 #include <pexpert/pexpert.h>
74 #include <IOKit/IOHibernatePrivate.h>
76 extern unsigned char rootdevice
[];
77 extern struct mach_header _mh_execute_header
;
79 static int kernel_symfile_opened
= 0;
80 static int error_code
= 0;
82 extern int IODTGetLoaderInfo(char *key
, void **infoAddr
, int *infoSize
);
83 extern void IODTFreeLoaderInfo(char *key
, void *infoAddr
, int infoSize
);
86 * Can only operate against currently running 32 bit mach_kernel
89 output_kernel_symbols(struct proc
*p
)
92 kauth_cred_t cred
= p
->p_ucred
; /* XXX */
94 struct vfs_context context
;
95 struct load_command
*cmd
;
96 struct mach_header
*orig_mh
, *mh
;
97 struct segment_command
*orig_ds
, *orig_ts
, *orig_le
, *sg
;
98 struct section
*se
, *const_text
;
99 struct symtab_command
*st
, *orig_st
;
101 vm_size_t orig_mhsize
, orig_st_size
;
103 vm_size_t header_size
= 0; /* out: protected by header */
117 // Dispose of unnecessary gumf, the booter doesn't need to load these
118 rc_mh
= IODTGetLoaderInfo("Kernel-__HEADER",
119 (void **)&orig_mh
, &orig_mhsize
);
120 if (rc_mh
== 0 && orig_mh
)
121 IODTFreeLoaderInfo("Kernel-__HEADER",
122 (void *)orig_mh
, round_page_32(orig_mhsize
));
124 rc_sc
= IODTGetLoaderInfo("Kernel-__SYMTAB",
125 (void **) &orig_st
, &orig_st_size
);
126 if (rc_sc
== 0 && orig_st
)
127 IODTFreeLoaderInfo("Kernel-__SYMTAB",
128 (void *)orig_st
, round_page_32(orig_st_size
));
130 if (cred
->cr_svuid
!= cred
->cr_ruid
|| cred
->cr_svgid
!= cred
->cr_rgid
)
133 // Check to see if the root is 'e' or 'n', is this a test for network?
134 if (rootdevice
[0] == 'e' && rootdevice
[1] == 'n')
138 context
.vc_ucred
= cred
;
140 if ((error
= vnode_open("mach.sym", (O_CREAT
| FWRITE
), (S_IRUSR
| S_IRGRP
| S_IROTH
), 0, &vp
, &context
)))
143 /* Don't dump to non-regular files or files with links. */
146 VATTR_WANTED(&va
, va_nlink
);
147 if ((vp
->v_type
!= VREG
) || vnode_getattr(vp
, &va
, &context
) || (va
.va_nlink
!= 1))
150 VATTR_INIT(&va
); /* better to do it here than waste more stack in vnode_getsize */
151 VATTR_SET(&va
, va_data_size
, 0);
152 vnode_setattr(vp
, &va
, &context
);
153 p
->p_acflag
|= ACORE
;
155 // If the file type is MH_EXECUTE then this must be a kernel
156 // as all Kernel extensions must be of type MH_OBJECT
157 orig_ds
= orig_ts
= orig_le
= NULL
;
159 orig_mh
= &_mh_execute_header
;
160 cmd
= (struct load_command
*) &orig_mh
[1];
161 for (i
= 0; i
< orig_mh
->ncmds
; i
++) {
162 if (cmd
->cmd
== LC_SEGMENT
) {
163 struct segment_command
*orig_sg
= (struct segment_command
*) cmd
;
165 if (!strcmp(SEG_TEXT
, orig_sg
->segname
))
167 else if (!strcmp(SEG_DATA
, orig_sg
->segname
))
169 else if (!strcmp(SEG_LINKEDIT
, orig_sg
->segname
))
172 else if (cmd
->cmd
== LC_SYMTAB
)
173 orig_st
= (struct symtab_command
*) cmd
;
175 cmd
= (struct load_command
*) ((caddr_t
) cmd
+ cmd
->cmdsize
);
178 if (!orig_ts
|| !orig_ds
|| !orig_le
|| !orig_st
)
182 se
= (struct section
*) &orig_ts
[1];
183 for (i
= 0; i
< orig_ts
->nsects
; i
++, se
++) {
184 if (!strcmp("__const", se
->sectname
)) {
192 header_size
= sizeof(struct mach_header
)
195 + sizeof(struct symtab_command
);
197 (void) kmem_alloc(kernel_map
,
198 (vm_offset_t
*) &header
,
199 (vm_size_t
) header_size
);
201 bzero((void *) header
, header_size
);
206 * Set up Mach-O header.
208 mh
= (struct mach_header
*) header
;
209 mh
->magic
= orig_mh
->magic
;
210 mh
->cputype
= orig_mh
->cputype
;
211 mh
->cpusubtype
= orig_mh
->cpusubtype
;
212 mh
->filetype
= orig_mh
->filetype
;
214 mh
->sizeofcmds
= header_size
- sizeof(struct mach_header
);
215 mh
->flags
= orig_mh
->flags
;
217 // Initialise the current file offset and addr
218 offset
= round_page(header_size
);
219 addr
= (caddr_t
) const_text
->addr
; // Load address of __TEXT,__const
222 * Construct a TEXT segment load command
223 * the only part of the TEXT segment we keep is the __TEXT,__const
224 * which contains the kernel vtables.
226 sg
= (struct segment_command
*) &mh
[1];
227 bcopy(orig_ts
, sg
, orig_ts
->cmdsize
);
228 sg
->vmaddr
= (unsigned long) addr
;
229 sg
->vmsize
= const_text
->size
;
231 sg
->filesize
= const_text
->size
+ round_page(header_size
);
235 se
= (struct section
*)(sg
+1);
236 for ( j
= 0; j
< sg
->nsects
; j
++, se
++ ) {
237 se
->addr
= (unsigned long) addr
;
241 if (!strcmp("__const", se
->sectname
)) {
242 se
->size
= const_text
->size
;
243 addr
+= const_text
->size
;
244 offset
+= const_text
->size
;
248 offset
= round_page(offset
);
250 // Now copy of the __DATA segment load command, the image need
251 // not be stored to disk nobody needs it, yet!
252 sg
= (struct segment_command
*)((int)sg
+ sg
->cmdsize
);
253 bcopy(orig_ds
, sg
, orig_ds
->cmdsize
);
255 sg
->vmaddr
= (unsigned long) addr
;
256 sg
->vmsize
= 0x1000; // One page for some reason?
257 sg
->fileoff
= offset
;
262 se
= (struct section
*)(sg
+1);
263 for ( j
= 0; j
< sg
->nsects
; j
++, se
++ ) {
264 se
->addr
= (unsigned long) addr
;
269 offset
= round_page(offset
);
273 * Set up LC_SYMTAB command
275 st
= (struct symtab_command
*)((int)sg
+ sg
->cmdsize
);
277 st
->cmdsize
= sizeof(struct symtab_command
);
279 st
->nsyms
= orig_st
->nsyms
;
280 st
->strsize
= orig_st
->strsize
;
281 st
->stroff
= offset
+ st
->nsyms
* sizeof(struct nlist
);
284 * Convert the symbol table in place from section references
285 * to absolute references.
287 sym
= (struct nlist
*) orig_le
->vmaddr
;
288 for (i
= 0; i
< st
->nsyms
; i
++, sym
++ ) {
289 if ( (sym
->n_type
& N_TYPE
) == N_SECT
) {
290 sym
->n_sect
= NO_SECT
;
291 sym
->n_type
= (sym
->n_type
& ~N_TYPE
) | N_ABS
;
296 * Write out the load commands at the beginning of the file.
298 error
= vn_rdwr(UIO_WRITE
, vp
, (caddr_t
) mh
, header_size
, (off_t
) 0,
299 UIO_SYSSPACE32
, IO_NODELOCKED
|IO_UNIT
, cred
, (int *) 0, p
);
304 * Write out the __TEXT,__const data segment.
306 error
= vn_rdwr(UIO_WRITE
, vp
, (caddr_t
) const_text
->addr
,
307 const_text
->size
, const_text
->offset
,
308 UIO_SYSSPACE32
, IO_NODELOCKED
|IO_UNIT
, cred
, (int *) 0, p
);
313 * Write out kernel symbols
315 offset
= st
->nsyms
* sizeof(struct nlist
) + st
->strsize
; // symtab size
316 error
= vn_rdwr(UIO_WRITE
, vp
,
317 (caddr_t
) orig_le
->vmaddr
, offset
, st
->symoff
,
318 UIO_SYSSPACE32
, IO_NODELOCKED
|IO_UNIT
, cred
, (int *) 0, p
);
321 kmem_free(kernel_map
, header
, header_size
);
324 error1
= vnode_close(vp
, FWRITE
, &context
);
325 if (!error
) error
= error1
;
333 int get_kernel_symfile(struct proc
*p
, char **symfile
)
335 if (!kernel_symfile_opened
) {
336 kernel_symfile_opened
= 1;
337 error_code
= output_kernel_symbols(p
);
340 *symfile
= "\\mach.sym";
345 struct kern_direct_file_io_ref_t
347 struct vfs_context context
;
352 static int file_ioctl(void * p1
, void * p2
, int theIoctl
, caddr_t result
)
354 dev_t device
= (dev_t
) p1
;
356 return ((*bdevsw
[major(device
)].d_ioctl
)
357 (device
, theIoctl
, result
, S_IFBLK
, p2
));
360 static int device_ioctl(void * p1
, __unused
void * p2
, int theIoctl
, caddr_t result
)
362 return (VNOP_IOCTL(p1
, theIoctl
, result
, 0, p2
));
365 struct kern_direct_file_io_ref_t
*
366 kern_open_file_for_direct_io(const char * name
,
367 kern_get_file_extents_callback_t callback
,
369 dev_t
* device_result
,
370 uint64_t * partitionbase_result
,
371 uint64_t * maxiocount_result
)
373 struct kern_direct_file_io_ref_t
* ref
;
377 struct vnode_attr va
;
383 off_t maxiocount
, count
;
385 int (*do_ioctl
)(void * p1
, void * p2
, int theIoctl
, caddr_t result
);
391 ref
= (struct kern_direct_file_io_ref_t
*) kalloc(sizeof(struct kern_direct_file_io_ref_t
));
399 p
= current_proc(); // kernproc;
401 ref
->context
.vc_proc
= p
;
402 ref
->context
.vc_ucred
= cred
;
404 if ((error
= vnode_open(name
, (O_CREAT
| FWRITE
), (0), 0, &ref
->vp
, &ref
->context
)))
408 VATTR_WANTED(&va
, va_rdev
);
409 VATTR_WANTED(&va
, va_fsid
);
410 VATTR_WANTED(&va
, va_data_size
);
411 VATTR_WANTED(&va
, va_nlink
);
413 if (vnode_getattr(ref
->vp
, &va
, &ref
->context
))
416 kprintf("vp va_rdev major %d minor %d\n", major(va
.va_rdev
), minor(va
.va_rdev
));
417 kprintf("vp va_fsid major %d minor %d\n", major(va
.va_fsid
), minor(va
.va_fsid
));
418 kprintf("vp size %qd\n", va
.va_data_size
);
420 if (ref
->vp
->v_type
== VREG
)
422 /* Don't dump files with links. */
423 if (va
.va_nlink
!= 1)
427 p1
= (void *) device
;
429 do_ioctl
= &file_ioctl
;
431 else if ((ref
->vp
->v_type
== VBLK
) || (ref
->vp
->v_type
== VCHR
))
438 do_ioctl
= &device_ioctl
;
442 /* Don't dump to non-regular files. */
447 // get partition base
449 error
= do_ioctl(p1
, p2
, DKIOCGETBASE
, (caddr_t
) partitionbase_result
);
453 // get block size & constraints
455 error
= do_ioctl(p1
, p2
, DKIOCGETBLOCKSIZE
, (caddr_t
) &blksize
);
459 maxiocount
= 1*1024*1024*1024;
461 error
= do_ioctl(p1
, p2
, DKIOCGETMAXBLOCKCOUNTREAD
, (caddr_t
) &count
);
465 if (count
&& (count
< maxiocount
))
468 error
= do_ioctl(p1
, p2
, DKIOCGETMAXBLOCKCOUNTWRITE
, (caddr_t
) &count
);
472 if (count
&& (count
< maxiocount
))
475 error
= do_ioctl(p1
, p2
, DKIOCGETMAXBYTECOUNTREAD
, (caddr_t
) &count
);
478 if (count
&& (count
< maxiocount
))
481 error
= do_ioctl(p1
, p2
, DKIOCGETMAXBYTECOUNTWRITE
, (caddr_t
) &count
);
484 if (count
&& (count
< maxiocount
))
487 error
= do_ioctl(p1
, p2
, DKIOCGETMAXSEGMENTBYTECOUNTREAD
, (caddr_t
) &count
);
490 if (count
&& (count
< maxiocount
))
493 error
= do_ioctl(p1
, p2
, DKIOCGETMAXSEGMENTBYTECOUNTWRITE
, (caddr_t
) &count
);
496 if (count
&& (count
< maxiocount
))
499 kprintf("max io 0x%qx bytes\n", maxiocount
);
500 if (maxiocount_result
)
501 *maxiocount_result
= maxiocount
;
503 // generate the block list
506 if (ref
->vp
->v_type
== VREG
)
509 while(f_offset
< (off_t
) va
.va_data_size
)
511 size_t io_size
= 1*1024*1024*1024;
514 error
= VNOP_BLOCKMAP(ref
->vp
, f_offset
, io_size
, &blkno
, (size_t *)&io_size
, NULL
, 0, NULL
);
517 callback(callback_ref
, ((uint64_t) blkno
) * blksize
, (uint64_t) io_size
);
520 callback(callback_ref
, 0ULL, 0ULL);
522 else if ((ref
->vp
->v_type
== VBLK
) || (ref
->vp
->v_type
== VCHR
))
524 error
= do_ioctl(p1
, p2
, DKIOCGETBLOCKCOUNT
, (caddr_t
) &size
);
528 callback(callback_ref
, 0ULL, size
);
529 callback(callback_ref
, size
, 0ULL);
533 *device_result
= device
;
536 kprintf("kern_open_file_for_direct_io(%d)\n", error
);
540 vnode_close(ref
->vp
, FWRITE
, &ref
->context
);
542 kfree(ref
, sizeof(struct kern_direct_file_io_ref_t
));
549 kern_write_file(struct kern_direct_file_io_ref_t
* ref
, off_t offset
, caddr_t addr
, vm_size_t len
)
551 return (vn_rdwr(UIO_WRITE
, ref
->vp
,
553 UIO_SYSSPACE32
, IO_SYNC
|IO_NODELOCKED
|IO_UNIT
,
554 ref
->context
.vc_ucred
, (int *) 0, ref
->context
.vc_proc
));
558 kern_close_file_for_direct_io(struct kern_direct_file_io_ref_t
* ref
)
560 kprintf("kern_close_file_for_direct_io\n");
566 error
= vnode_close(ref
->vp
, FWRITE
, &ref
->context
);
567 kprintf("vnode_close(%d)\n", error
);
569 kfree(ref
, sizeof(struct kern_direct_file_io_ref_t
));