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