]> git.saurik.com Git - apple/xnu.git/blob - bsd/kern/kern_symfile.c
xnu-792.1.5.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_HEADER_START@
5 *
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.
11 *
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
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22 /* Copyright (c) 1998 Apple Computer, Inc. All rights reserved.
23 *
24 * File: bsd/kern/kern_symfile.c
25 *
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.
34 *
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.
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>
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>
60 #include <sys/acct.h>
61 #include <sys/file_internal.h>
62 #include <sys/uio.h>
63 #include <sys/kernel.h>
64 #include <sys/stat.h>
65 #include <sys/disk.h>
66 #include <sys/conf.h>
67
68 #include <mach-o/loader.h>
69 #include <mach-o/nlist.h>
70
71 #include <kern/kalloc.h>
72 #include <vm/vm_kern.h>
73 #include <pexpert/pexpert.h>
74
75 extern unsigned char rootdevice[];
76 extern struct mach_header _mh_execute_header;
77
78 static int kernel_symfile_opened = 0;
79 static int error_code = 0;
80
81 extern int IODTGetLoaderInfo(char *key, void **infoAddr, int *infoSize);
82 extern void IODTFreeLoaderInfo(char *key, void *infoAddr, int infoSize);
83
84 /*
85 * Can only operate against currently running 32 bit mach_kernel
86 */
87 static int
88 output_kernel_symbols(struct proc *p)
89 {
90 struct vnode *vp;
91 kauth_cred_t cred = p->p_ucred; /* XXX */
92 struct vnode_attr va;
93 struct vfs_context context;
94 struct load_command *cmd;
95 struct mach_header *orig_mh, *mh;
96 struct segment_command *orig_ds, *orig_ts, *orig_le, *sg;
97 struct section *se, *const_text;
98 struct symtab_command *st, *orig_st;
99 struct nlist *sym;
100 vm_size_t orig_mhsize, orig_st_size;
101 vm_offset_t header;
102 vm_size_t header_size = 0; /* out: protected by header */
103 int error, error1;
104 unsigned int i, j;
105 caddr_t addr;
106 vm_offset_t offset;
107 int rc_mh, rc_sc;
108
109 error = EFAULT;
110
111 vp = NULL;
112 header = NULL;
113 orig_mh = NULL;
114 orig_st = NULL;
115
116 // Dispose of unnecessary gumf, the booter doesn't need to load these
117 rc_mh = IODTGetLoaderInfo("Kernel-__HEADER",
118 (void **)&orig_mh, &orig_mhsize);
119 if (rc_mh == 0 && orig_mh)
120 IODTFreeLoaderInfo("Kernel-__HEADER",
121 (void *)orig_mh, round_page_32(orig_mhsize));
122
123 rc_sc = IODTGetLoaderInfo("Kernel-__SYMTAB",
124 (void **) &orig_st, &orig_st_size);
125 if (rc_sc == 0 && orig_st)
126 IODTFreeLoaderInfo("Kernel-__SYMTAB",
127 (void *)orig_st, round_page_32(orig_st_size));
128
129 if (cred->cr_svuid != cred->cr_ruid || cred->cr_svgid != cred->cr_rgid)
130 goto out;
131
132 // Check to see if the root is 'e' or 'n', is this a test for network?
133 if (rootdevice[0] == 'e' && rootdevice[1] == 'n')
134 goto out;
135
136 context.vc_proc = p;
137 context.vc_ucred = cred;
138
139 if ((error = vnode_open("mach.sym", (O_CREAT | FWRITE), (S_IRUSR | S_IRGRP | S_IROTH), 0, &vp, &context)))
140 goto out;
141
142 /* Don't dump to non-regular files or files with links. */
143 error = EFAULT;
144 VATTR_INIT(&va);
145 VATTR_WANTED(&va, va_nlink);
146 if ((vp->v_type != VREG) || vnode_getattr(vp, &va, &context) || (va.va_nlink != 1))
147 goto out;
148
149 VATTR_INIT(&va); /* better to do it here than waste more stack in vnode_getsize */
150 VATTR_SET(&va, va_data_size, 0);
151 vnode_setattr(vp, &va, &context);
152 p->p_acflag |= ACORE;
153
154 // If the file type is MH_EXECUTE then this must be a kernel
155 // as all Kernel extensions must be of type MH_OBJECT
156 orig_ds = orig_ts = orig_le = NULL;
157 orig_st = NULL;
158 orig_mh = &_mh_execute_header;
159 cmd = (struct load_command *) &orig_mh[1];
160 for (i = 0; i < orig_mh->ncmds; i++) {
161 if (cmd->cmd == LC_SEGMENT) {
162 struct segment_command *orig_sg = (struct segment_command *) cmd;
163
164 if (!strcmp(SEG_TEXT, orig_sg->segname))
165 orig_ts = orig_sg;
166 else if (!strcmp(SEG_DATA, orig_sg->segname))
167 orig_ds = orig_sg;
168 else if (!strcmp(SEG_LINKEDIT, orig_sg->segname))
169 orig_le = orig_sg;
170 }
171 else if (cmd->cmd == LC_SYMTAB)
172 orig_st = (struct symtab_command *) cmd;
173
174 cmd = (struct load_command *) ((caddr_t) cmd + cmd->cmdsize);
175 }
176
177 if (!orig_ts || !orig_ds || !orig_le || !orig_st)
178 goto out;
179
180 const_text = NULL;
181 se = (struct section *) &orig_ts[1];
182 for (i = 0; i < orig_ts->nsects; i++, se++) {
183 if (!strcmp("__const", se->sectname)) {
184 const_text = se;
185 break;
186 }
187 }
188 if (!const_text)
189 goto out;
190
191 header_size = sizeof(struct mach_header)
192 + orig_ts->cmdsize
193 + orig_ds->cmdsize
194 + sizeof(struct symtab_command);
195
196 (void) kmem_alloc(kernel_map,
197 (vm_offset_t *) &header,
198 (vm_size_t) header_size);
199 if (header)
200 bzero((void *) header, header_size);
201 else
202 goto out;
203
204 /*
205 * Set up Mach-O header.
206 */
207 mh = (struct mach_header *) header;
208 mh->magic = orig_mh->magic;
209 mh->cputype = orig_mh->cputype;
210 mh->cpusubtype = orig_mh->cpusubtype;
211 mh->filetype = orig_mh->filetype;
212 mh->ncmds = 3;
213 mh->sizeofcmds = header_size - sizeof(struct mach_header);
214 mh->flags = orig_mh->flags;
215
216 // Initialise the current file offset and addr
217 offset = round_page(header_size);
218 addr = (caddr_t) const_text->addr; // Load address of __TEXT,__const
219
220 /*
221 * Construct a TEXT segment load command
222 * the only part of the TEXT segment we keep is the __TEXT,__const
223 * which contains the kernel vtables.
224 */
225 sg = (struct segment_command *) &mh[1];
226 bcopy(orig_ts, sg, orig_ts->cmdsize);
227 sg->vmaddr = (unsigned long) addr;
228 sg->vmsize = const_text->size;
229 sg->fileoff = 0;
230 sg->filesize = const_text->size + round_page(header_size);
231 sg->maxprot = 0;
232 sg->initprot = 0;
233 sg->flags = 0;
234 se = (struct section *)(sg+1);
235 for ( j = 0; j < sg->nsects; j++, se++ ) {
236 se->addr = (unsigned long) addr;
237 se->size = 0;
238 se->offset = offset;
239 se->nreloc = 0;
240 if (!strcmp("__const", se->sectname)) {
241 se->size = const_text->size;
242 addr += const_text->size;
243 offset += const_text->size;
244 const_text = se;
245 }
246 }
247 offset = round_page(offset);
248
249 // Now copy of the __DATA segment load command, the image need
250 // not be stored to disk nobody needs it, yet!
251 sg = (struct segment_command *)((int)sg + sg->cmdsize);
252 bcopy(orig_ds, sg, orig_ds->cmdsize);
253
254 sg->vmaddr = (unsigned long) addr;
255 sg->vmsize = 0x1000; // One page for some reason?
256 sg->fileoff = offset;
257 sg->filesize = 0;
258 sg->maxprot = 0;
259 sg->initprot = 0;
260 sg->flags = 0;
261 se = (struct section *)(sg+1);
262 for ( j = 0; j < sg->nsects; j++, se++ ) {
263 se->addr = (unsigned long) addr;
264 se->size = 0;
265 se->offset = offset;
266 se->nreloc = 0;
267 }
268 offset = round_page(offset);
269
270
271 /*
272 * Set up LC_SYMTAB command
273 */
274 st = (struct symtab_command *)((int)sg + sg->cmdsize);
275 st->cmd = LC_SYMTAB;
276 st->cmdsize = sizeof(struct symtab_command);
277 st->symoff = offset;
278 st->nsyms = orig_st->nsyms;
279 st->strsize = orig_st->strsize;
280 st->stroff = offset + st->nsyms * sizeof(struct nlist);
281
282 /*
283 * Convert the symbol table in place from section references
284 * to absolute references.
285 */
286 sym = (struct nlist *) orig_le->vmaddr;
287 for (i = 0; i < st->nsyms; i++, sym++ ) {
288 if ( (sym->n_type & N_TYPE) == N_SECT) {
289 sym->n_sect = NO_SECT;
290 sym->n_type = (sym->n_type & ~N_TYPE) | N_ABS;
291 }
292 }
293
294 /*
295 * Write out the load commands at the beginning of the file.
296 */
297 error = vn_rdwr(UIO_WRITE, vp, (caddr_t) mh, header_size, (off_t) 0,
298 UIO_SYSSPACE32, IO_NODELOCKED|IO_UNIT, cred, (int *) 0, p);
299 if (error)
300 goto out;
301
302 /*
303 * Write out the __TEXT,__const data segment.
304 */
305 error = vn_rdwr(UIO_WRITE, vp, (caddr_t) const_text->addr,
306 const_text->size, const_text->offset,
307 UIO_SYSSPACE32, IO_NODELOCKED|IO_UNIT, cred, (int *) 0, p);
308 if (error)
309 goto out;
310
311 /*
312 * Write out kernel symbols
313 */
314 offset = st->nsyms * sizeof(struct nlist) + st->strsize; // symtab size
315 error = vn_rdwr(UIO_WRITE, vp,
316 (caddr_t) orig_le->vmaddr, offset, st->symoff,
317 UIO_SYSSPACE32, IO_NODELOCKED|IO_UNIT, cred, (int *) 0, p);
318 out:
319 if (header)
320 kmem_free(kernel_map, header, header_size);
321
322 if (vp) {
323 error1 = vnode_close(vp, FWRITE, &context);
324 if (!error) error = error1;
325 }
326
327 return(error);
328 }
329 /*
330 *
331 */
332 int get_kernel_symfile(struct proc *p, char **symfile)
333 {
334 if (!kernel_symfile_opened) {
335 kernel_symfile_opened = 1;
336 error_code = output_kernel_symbols(p);
337 }
338 if (!error_code)
339 *symfile = "\\mach.sym";
340
341 return error_code;
342 }
343