]> git.saurik.com Git - apple/xnu.git/blame - bsd/kern/kern_symfile.c
xnu-792.13.8.tar.gz
[apple/xnu.git] / bsd / kern / kern_symfile.c
CommitLineData
1c79356b 1/*
5d5c5d0d
A
2 * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
3 *
8ad349bb 4 * @APPLE_LICENSE_OSREFERENCE_HEADER_START@
1c79356b 5 *
8ad349bb
A
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
14 * agreement.
15 *
16 * Please obtain a copy of the License at
17 * http://www.opensource.apple.com/apsl/ and read it before using this
18 * file.
19 *
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.
27 *
28 * @APPLE_LICENSE_OSREFERENCE_HEADER_END@
1c79356b
A
29 */
30/* Copyright (c) 1998 Apple Computer, Inc. All rights reserved.
31 *
32 * File: bsd/kern/kern_symfile.c
33 *
91447636
A
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.
1c79356b 42 *
91447636
A
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.
46 *
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.
1c79356b
A
50 *
51 * HISTORY
52 *
53 * .
54 */
55
56#include <mach/vm_param.h>
57
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>
91447636
A
63#include <sys/vnode_internal.h>
64#include <sys/proc_internal.h>
65#include <sys/kauth.h>
1c79356b
A
66#include <sys/timeb.h>
67#include <sys/times.h>
1c79356b 68#include <sys/acct.h>
91447636 69#include <sys/file_internal.h>
1c79356b
A
70#include <sys/uio.h>
71#include <sys/kernel.h>
72#include <sys/stat.h>
91447636
A
73#include <sys/disk.h>
74#include <sys/conf.h>
1c79356b
A
75
76#include <mach-o/loader.h>
77#include <mach-o/nlist.h>
78
91447636 79#include <kern/kalloc.h>
1c79356b 80#include <vm/vm_kern.h>
91447636 81#include <pexpert/pexpert.h>
3a60a9f5 82#include <IOKit/IOHibernatePrivate.h>
1c79356b
A
83
84extern unsigned char rootdevice[];
0b4e3aa0 85extern struct mach_header _mh_execute_header;
1c79356b 86
0b4e3aa0
A
87static int kernel_symfile_opened = 0;
88static int error_code = 0;
1c79356b 89
0b4e3aa0
A
90extern int IODTGetLoaderInfo(char *key, void **infoAddr, int *infoSize);
91extern void IODTFreeLoaderInfo(char *key, void *infoAddr, int infoSize);
1c79356b
A
92
93/*
91447636 94 * Can only operate against currently running 32 bit mach_kernel
1c79356b 95 */
91447636
A
96static int
97output_kernel_symbols(struct proc *p)
1c79356b 98{
0b4e3aa0 99 struct vnode *vp;
91447636
A
100 kauth_cred_t cred = p->p_ucred; /* XXX */
101 struct vnode_attr va;
102 struct vfs_context context;
0b4e3aa0
A
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;
108 struct nlist *sym;
109 vm_size_t orig_mhsize, orig_st_size;
110 vm_offset_t header;
91447636 111 vm_size_t header_size = 0; /* out: protected by header */
0b4e3aa0 112 int error, error1;
91447636 113 unsigned int i, j;
0b4e3aa0
A
114 caddr_t addr;
115 vm_offset_t offset;
116 int rc_mh, rc_sc;
117
118 error = EFAULT;
119
120 vp = NULL;
121 header = NULL;
122 orig_mh = NULL;
123 orig_st = NULL;
124
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);
55e303ae 128 if (rc_mh == 0 && orig_mh)
0b4e3aa0 129 IODTFreeLoaderInfo("Kernel-__HEADER",
55e303ae 130 (void *)orig_mh, round_page_32(orig_mhsize));
0b4e3aa0
A
131
132 rc_sc = IODTGetLoaderInfo("Kernel-__SYMTAB",
133 (void **) &orig_st, &orig_st_size);
55e303ae 134 if (rc_sc == 0 && orig_st)
0b4e3aa0 135 IODTFreeLoaderInfo("Kernel-__SYMTAB",
55e303ae 136 (void *)orig_st, round_page_32(orig_st_size));
0b4e3aa0 137
91447636 138 if (cred->cr_svuid != cred->cr_ruid || cred->cr_svgid != cred->cr_rgid)
0b4e3aa0
A
139 goto out;
140
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')
143 goto out;
144
91447636
A
145 context.vc_proc = p;
146 context.vc_ucred = cred;
147
148 if ((error = vnode_open("mach.sym", (O_CREAT | FWRITE), (S_IRUSR | S_IRGRP | S_IROTH), 0, &vp, &context)))
149 goto out;
0b4e3aa0 150
0b4e3aa0
A
151 /* Don't dump to non-regular files or files with links. */
152 error = EFAULT;
91447636
A
153 VATTR_INIT(&va);
154 VATTR_WANTED(&va, va_nlink);
155 if ((vp->v_type != VREG) || vnode_getattr(vp, &va, &context) || (va.va_nlink != 1))
0b4e3aa0
A
156 goto out;
157
91447636
A
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);
0b4e3aa0
A
161 p->p_acflag |= ACORE;
162
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;
166 orig_st = 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) {
91447636 171 struct segment_command *orig_sg = (struct segment_command *) cmd;
0b4e3aa0 172
91447636
A
173 if (!strcmp(SEG_TEXT, orig_sg->segname))
174 orig_ts = orig_sg;
175 else if (!strcmp(SEG_DATA, orig_sg->segname))
176 orig_ds = orig_sg;
177 else if (!strcmp(SEG_LINKEDIT, orig_sg->segname))
178 orig_le = orig_sg;
0b4e3aa0
A
179 }
180 else if (cmd->cmd == LC_SYMTAB)
181 orig_st = (struct symtab_command *) cmd;
182
183 cmd = (struct load_command *) ((caddr_t) cmd + cmd->cmdsize);
1c79356b 184 }
1c79356b 185
0b4e3aa0
A
186 if (!orig_ts || !orig_ds || !orig_le || !orig_st)
187 goto out;
1c79356b 188
0b4e3aa0
A
189 const_text = NULL;
190 se = (struct section *) &orig_ts[1];
191 for (i = 0; i < orig_ts->nsects; i++, se++) {
192 if (!strcmp("__const", se->sectname)) {
193 const_text = se;
194 break;
195 }
196 }
197 if (!const_text)
198 goto out;
199
200 header_size = sizeof(struct mach_header)
201 + orig_ts->cmdsize
202 + orig_ds->cmdsize
203 + sizeof(struct symtab_command);
204
91447636 205 (void) kmem_alloc(kernel_map,
0b4e3aa0
A
206 (vm_offset_t *) &header,
207 (vm_size_t) header_size);
208 if (header)
209 bzero((void *) header, header_size);
210 else
211 goto out;
212
213 /*
214 * Set up Mach-O header.
215 */
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;
221 mh->ncmds = 3;
222 mh->sizeofcmds = header_size - sizeof(struct mach_header);
223 mh->flags = orig_mh->flags;
224
225 // Initialise the current file offset and addr
91447636 226 offset = round_page(header_size);
0b4e3aa0
A
227 addr = (caddr_t) const_text->addr; // Load address of __TEXT,__const
228
229 /*
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.
233 */
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;
238 sg->fileoff = 0;
91447636 239 sg->filesize = const_text->size + round_page(header_size);
0b4e3aa0
A
240 sg->maxprot = 0;
241 sg->initprot = 0;
242 sg->flags = 0;
243 se = (struct section *)(sg+1);
244 for ( j = 0; j < sg->nsects; j++, se++ ) {
245 se->addr = (unsigned long) addr;
246 se->size = 0;
247 se->offset = offset;
248 se->nreloc = 0;
249 if (!strcmp("__const", se->sectname)) {
250 se->size = const_text->size;
251 addr += const_text->size;
252 offset += const_text->size;
253 const_text = se;
254 }
255 }
91447636 256 offset = round_page(offset);
0b4e3aa0
A
257
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);
262
263 sg->vmaddr = (unsigned long) addr;
264 sg->vmsize = 0x1000; // One page for some reason?
265 sg->fileoff = offset;
266 sg->filesize = 0;
267 sg->maxprot = 0;
268 sg->initprot = 0;
269 sg->flags = 0;
270 se = (struct section *)(sg+1);
271 for ( j = 0; j < sg->nsects; j++, se++ ) {
272 se->addr = (unsigned long) addr;
273 se->size = 0;
274 se->offset = offset;
275 se->nreloc = 0;
276 }
91447636 277 offset = round_page(offset);
0b4e3aa0
A
278
279
280 /*
281 * Set up LC_SYMTAB command
282 */
283 st = (struct symtab_command *)((int)sg + sg->cmdsize);
284 st->cmd = LC_SYMTAB;
285 st->cmdsize = sizeof(struct symtab_command);
286 st->symoff = offset;
287 st->nsyms = orig_st->nsyms;
288 st->strsize = orig_st->strsize;
289 st->stroff = offset + st->nsyms * sizeof(struct nlist);
290
291 /*
292 * Convert the symbol table in place from section references
293 * to absolute references.
294 */
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;
300 }
301 }
302
303 /*
304 * Write out the load commands at the beginning of the file.
305 */
306 error = vn_rdwr(UIO_WRITE, vp, (caddr_t) mh, header_size, (off_t) 0,
91447636 307 UIO_SYSSPACE32, IO_NODELOCKED|IO_UNIT, cred, (int *) 0, p);
0b4e3aa0
A
308 if (error)
309 goto out;
310
311 /*
312 * Write out the __TEXT,__const data segment.
313 */
314 error = vn_rdwr(UIO_WRITE, vp, (caddr_t) const_text->addr,
315 const_text->size, const_text->offset,
91447636 316 UIO_SYSSPACE32, IO_NODELOCKED|IO_UNIT, cred, (int *) 0, p);
0b4e3aa0
A
317 if (error)
318 goto out;
319
320 /*
321 * Write out kernel symbols
322 */
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,
91447636 326 UIO_SYSSPACE32, IO_NODELOCKED|IO_UNIT, cred, (int *) 0, p);
1c79356b 327out:
0b4e3aa0
A
328 if (header)
329 kmem_free(kernel_map, header, header_size);
330
331 if (vp) {
91447636 332 error1 = vnode_close(vp, FWRITE, &context);
0b4e3aa0
A
333 if (!error) error = error1;
334 }
1c79356b 335
0b4e3aa0
A
336 return(error);
337}
1c79356b
A
338/*
339 *
340 */
0b4e3aa0 341int get_kernel_symfile(struct proc *p, char **symfile)
1c79356b 342{
0b4e3aa0
A
343 if (!kernel_symfile_opened) {
344 kernel_symfile_opened = 1;
345 error_code = output_kernel_symbols(p);
1c79356b 346 }
0b4e3aa0
A
347 if (!error_code)
348 *symfile = "\\mach.sym";
1c79356b 349
0b4e3aa0
A
350 return error_code;
351}
91447636 352
3a60a9f5
A
353struct kern_direct_file_io_ref_t
354{
355 struct vfs_context context;
356 struct vnode *vp;
357};
358
359
360static int file_ioctl(void * p1, void * p2, int theIoctl, caddr_t result)
361{
362 dev_t device = (dev_t) p1;
363
364 return ((*bdevsw[major(device)].d_ioctl)
365 (device, theIoctl, result, S_IFBLK, p2));
366}
367
368static int device_ioctl(void * p1, __unused void * p2, int theIoctl, caddr_t result)
369{
370 return (VNOP_IOCTL(p1, theIoctl, result, 0, p2));
371}
372
373struct kern_direct_file_io_ref_t *
374kern_open_file_for_direct_io(const char * name,
375 kern_get_file_extents_callback_t callback,
376 void * callback_ref,
377 dev_t * device_result,
378 uint64_t * partitionbase_result,
379 uint64_t * maxiocount_result)
380{
381 struct kern_direct_file_io_ref_t * ref;
382
383 struct proc *p;
384 struct ucred *cred;
385 struct vnode_attr va;
386 int error;
387 off_t f_offset;
388 uint32_t blksize;
389 uint64_t size;
390 dev_t device;
391 off_t maxiocount, count;
392
393 int (*do_ioctl)(void * p1, void * p2, int theIoctl, caddr_t result);
394 void * p1;
395 void * p2;
396
397 error = EFAULT;
398
399 ref = (struct kern_direct_file_io_ref_t *) kalloc(sizeof(struct kern_direct_file_io_ref_t));
400 if (!ref)
401 {
402 error = EFAULT;
403 goto out;
404 }
405
406 ref->vp = NULL;
407 p = current_proc(); // kernproc;
408 cred = p->p_ucred;
409 ref->context.vc_proc = p;
410 ref->context.vc_ucred = cred;
411
412 if ((error = vnode_open(name, (O_CREAT | FWRITE), (0), 0, &ref->vp, &ref->context)))
413 goto out;
414
415 VATTR_INIT(&va);
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);
420 error = EFAULT;
421 if (vnode_getattr(ref->vp, &va, &ref->context))
422 goto out;
423
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);
427
428 if (ref->vp->v_type == VREG)
429 {
430 /* Don't dump files with links. */
431 if (va.va_nlink != 1)
432 goto out;
433
434 device = va.va_fsid;
435 p1 = (void *) device;
436 p2 = p;
437 do_ioctl = &file_ioctl;
438 }
439 else if ((ref->vp->v_type == VBLK) || (ref->vp->v_type == VCHR))
440 {
441 /* Partition. */
442 device = va.va_rdev;
443
444 p1 = ref->vp;
445 p2 = &ref->context;
446 do_ioctl = &device_ioctl;
447 }
448 else
449 {
450 /* Don't dump to non-regular files. */
451 error = EFAULT;
452 goto out;
453 }
454
455 // get partition base
456
457 error = do_ioctl(p1, p2, DKIOCGETBASE, (caddr_t) partitionbase_result);
458 if (error)
459 goto out;
460
461 // get block size & constraints
462
463 error = do_ioctl(p1, p2, DKIOCGETBLOCKSIZE, (caddr_t) &blksize);
464 if (error)
465 goto out;
466
467 maxiocount = 1*1024*1024*1024;
468
469 error = do_ioctl(p1, p2, DKIOCGETMAXBLOCKCOUNTREAD, (caddr_t) &count);
470 if (error)
471 count = 0;
472 count *= blksize;
473 if (count && (count < maxiocount))
474 maxiocount = count;
475
476 error = do_ioctl(p1, p2, DKIOCGETMAXBLOCKCOUNTWRITE, (caddr_t) &count);
477 if (error)
478 count = 0;
479 count *= blksize;
480 if (count && (count < maxiocount))
481 maxiocount = count;
482
483 error = do_ioctl(p1, p2, DKIOCGETMAXBYTECOUNTREAD, (caddr_t) &count);
484 if (error)
485 count = 0;
486 if (count && (count < maxiocount))
487 maxiocount = count;
488
489 error = do_ioctl(p1, p2, DKIOCGETMAXBYTECOUNTWRITE, (caddr_t) &count);
490 if (error)
491 count = 0;
492 if (count && (count < maxiocount))
493 maxiocount = count;
494
495 error = do_ioctl(p1, p2, DKIOCGETMAXSEGMENTBYTECOUNTREAD, (caddr_t) &count);
496 if (error)
497 count = 0;
498 if (count && (count < maxiocount))
499 maxiocount = count;
500
501 error = do_ioctl(p1, p2, DKIOCGETMAXSEGMENTBYTECOUNTWRITE, (caddr_t) &count);
502 if (error)
503 count = 0;
504 if (count && (count < maxiocount))
505 maxiocount = count;
506
507 kprintf("max io 0x%qx bytes\n", maxiocount);
508 if (maxiocount_result)
509 *maxiocount_result = maxiocount;
510
511 // generate the block list
512
513 error = 0;
514 if (ref->vp->v_type == VREG)
515 {
516 f_offset = 0;
517 while(f_offset < (off_t) va.va_data_size)
518 {
519 size_t io_size = 1*1024*1024*1024;
520 daddr64_t blkno;
521
522 error = VNOP_BLOCKMAP(ref->vp, f_offset, io_size, &blkno, (size_t *)&io_size, NULL, 0, NULL);
523 if (error)
524 goto out;
525 callback(callback_ref, ((uint64_t) blkno) * blksize, (uint64_t) io_size);
526 f_offset += io_size;
527 }
528 callback(callback_ref, 0ULL, 0ULL);
529 }
530 else if ((ref->vp->v_type == VBLK) || (ref->vp->v_type == VCHR))
531 {
532 error = do_ioctl(p1, p2, DKIOCGETBLOCKCOUNT, (caddr_t) &size);
533 if (error)
534 goto out;
535 size *= blksize;
536 callback(callback_ref, 0ULL, size);
537 callback(callback_ref, size, 0ULL);
538 }
539
540 if (device_result)
541 *device_result = device;
542
543out:
544 kprintf("kern_open_file_for_direct_io(%d)\n", error);
545
546 if (error && ref) {
547 if (ref->vp)
548 vnode_close(ref->vp, FWRITE, &ref->context);
549
550 kfree(ref, sizeof(struct kern_direct_file_io_ref_t));
551 }
552
553 return(ref);
554}
555
556int
557kern_write_file(struct kern_direct_file_io_ref_t * ref, off_t offset, caddr_t addr, vm_size_t len)
558{
559 return (vn_rdwr(UIO_WRITE, ref->vp,
560 addr, len, offset,
561 UIO_SYSSPACE32, IO_SYNC|IO_NODELOCKED|IO_UNIT,
562 ref->context.vc_ucred, (int *) 0, ref->context.vc_proc));
563}
564
565void
566kern_close_file_for_direct_io(struct kern_direct_file_io_ref_t * ref)
567{
568 kprintf("kern_close_file_for_direct_io\n");
569
570 if (ref) {
571 int error;
572
573 if (ref->vp) {
574 error = vnode_close(ref->vp, FWRITE, &ref->context);
575 kprintf("vnode_close(%d)\n", error);
576 }
577 kfree(ref, sizeof(struct kern_direct_file_io_ref_t));
578 }
579}