]> git.saurik.com Git - apple/xnu.git/blob - bsd/kern/kern_symfile.c
xnu-792.13.8.tar.gz
[apple/xnu.git] / bsd / kern / kern_symfile.c
1 /*
2 * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_OSREFERENCE_HEADER_START@
5 *
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@
29 */
30 /* Copyright (c) 1998 Apple Computer, Inc. All rights reserved.
31 *
32 * File: bsd/kern/kern_symfile.c
33 *
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.
42 *
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.
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>
63 #include <sys/vnode_internal.h>
64 #include <sys/proc_internal.h>
65 #include <sys/kauth.h>
66 #include <sys/timeb.h>
67 #include <sys/times.h>
68 #include <sys/acct.h>
69 #include <sys/file_internal.h>
70 #include <sys/uio.h>
71 #include <sys/kernel.h>
72 #include <sys/stat.h>
73 #include <sys/disk.h>
74 #include <sys/conf.h>
75
76 #include <mach-o/loader.h>
77 #include <mach-o/nlist.h>
78
79 #include <kern/kalloc.h>
80 #include <vm/vm_kern.h>
81 #include <pexpert/pexpert.h>
82 #include <IOKit/IOHibernatePrivate.h>
83
84 extern unsigned char rootdevice[];
85 extern struct mach_header _mh_execute_header;
86
87 static int kernel_symfile_opened = 0;
88 static int error_code = 0;
89
90 extern int IODTGetLoaderInfo(char *key, void **infoAddr, int *infoSize);
91 extern void IODTFreeLoaderInfo(char *key, void *infoAddr, int infoSize);
92
93 /*
94 * Can only operate against currently running 32 bit mach_kernel
95 */
96 static int
97 output_kernel_symbols(struct proc *p)
98 {
99 struct vnode *vp;
100 kauth_cred_t cred = p->p_ucred; /* XXX */
101 struct vnode_attr va;
102 struct vfs_context context;
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;
111 vm_size_t header_size = 0; /* out: protected by header */
112 int error, error1;
113 unsigned int i, j;
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);
128 if (rc_mh == 0 && orig_mh)
129 IODTFreeLoaderInfo("Kernel-__HEADER",
130 (void *)orig_mh, round_page_32(orig_mhsize));
131
132 rc_sc = IODTGetLoaderInfo("Kernel-__SYMTAB",
133 (void **) &orig_st, &orig_st_size);
134 if (rc_sc == 0 && orig_st)
135 IODTFreeLoaderInfo("Kernel-__SYMTAB",
136 (void *)orig_st, round_page_32(orig_st_size));
137
138 if (cred->cr_svuid != cred->cr_ruid || cred->cr_svgid != cred->cr_rgid)
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
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;
150
151 /* Don't dump to non-regular files or files with links. */
152 error = EFAULT;
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))
156 goto out;
157
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);
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) {
171 struct segment_command *orig_sg = (struct segment_command *) cmd;
172
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;
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);
184 }
185
186 if (!orig_ts || !orig_ds || !orig_le || !orig_st)
187 goto out;
188
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
205 (void) kmem_alloc(kernel_map,
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
226 offset = round_page(header_size);
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;
239 sg->filesize = const_text->size + round_page(header_size);
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 }
256 offset = round_page(offset);
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 }
277 offset = round_page(offset);
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,
307 UIO_SYSSPACE32, IO_NODELOCKED|IO_UNIT, cred, (int *) 0, p);
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,
316 UIO_SYSSPACE32, IO_NODELOCKED|IO_UNIT, cred, (int *) 0, p);
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,
326 UIO_SYSSPACE32, IO_NODELOCKED|IO_UNIT, cred, (int *) 0, p);
327 out:
328 if (header)
329 kmem_free(kernel_map, header, header_size);
330
331 if (vp) {
332 error1 = vnode_close(vp, FWRITE, &context);
333 if (!error) error = error1;
334 }
335
336 return(error);
337 }
338 /*
339 *
340 */
341 int get_kernel_symfile(struct proc *p, char **symfile)
342 {
343 if (!kernel_symfile_opened) {
344 kernel_symfile_opened = 1;
345 error_code = output_kernel_symbols(p);
346 }
347 if (!error_code)
348 *symfile = "\\mach.sym";
349
350 return error_code;
351 }
352
353 struct kern_direct_file_io_ref_t
354 {
355 struct vfs_context context;
356 struct vnode *vp;
357 };
358
359
360 static 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
368 static 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
373 struct kern_direct_file_io_ref_t *
374 kern_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
543 out:
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
556 int
557 kern_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
565 void
566 kern_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 }