]> git.saurik.com Git - apple/xnu.git/blob - bsd/kern/kern_symfile.c
f4607b9d3aa85078766c1787689b09a2f33cceeb
[apple/xnu.git] / bsd / kern / kern_symfile.c
1 /*
2 * Copyright (c) 2000 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 based on
27 * the symbol table information passed by the SecondaryLoader/PlatformExpert.
28 * This allows us to correctly link other executables (drivers, etc) against the
29 * the kernel in cases where the kernel image on the root device does not match
30 * the live kernel. This can occur during net-booting where the actual kernel
31 * image is obtained from the network via tftp rather than the root
32 * device.
33 *
34 * If a symbol table is available, then the file /mach.sym will be created
35 * containing a Mach Header and a LC_SYMTAB load command followed by the
36 * the symbol table data for mach_kernel.
37 *
38 * HISTORY
39 *
40 * .
41 */
42
43 #include <mach/vm_param.h>
44
45 #include <sys/param.h>
46 #include <sys/systm.h>
47 #include <sys/signalvar.h>
48 #include <sys/resourcevar.h>
49 #include <sys/namei.h>
50 #include <sys/vnode.h>
51 #include <sys/proc.h>
52 #include <sys/timeb.h>
53 #include <sys/times.h>
54 #include <sys/buf.h>
55 #include <sys/acct.h>
56 #include <sys/file.h>
57 #include <sys/uio.h>
58 #include <sys/kernel.h>
59 #include <sys/stat.h>
60
61 #include <mach-o/loader.h>
62 #include <mach-o/nlist.h>
63
64 #include <vm/vm_kern.h>
65
66 extern unsigned char rootdevice[];
67 extern struct mach_header _mh_execute_header;
68
69 static int kernel_symfile_opened = 0;
70 static int error_code = 0;
71
72 extern int IODTGetLoaderInfo(char *key, void **infoAddr, int *infoSize);
73 extern void IODTFreeLoaderInfo(char *key, void *infoAddr, int infoSize);
74
75 /*
76 *
77 */
78 static int output_kernel_symbols(struct proc *p)
79 {
80 struct vnode *vp;
81 struct pcred *pcred = p->p_cred;
82 struct ucred *cred = pcred->pc_ucred;
83 struct nameidata nd;
84 struct vattr vattr;
85 struct load_command *cmd;
86 struct mach_header *orig_mh, *mh;
87 struct segment_command *orig_ds, *orig_ts, *orig_le, *sg;
88 struct section *se, *const_text;
89 struct symtab_command *st, *orig_st;
90 struct nlist *sym;
91 vm_size_t orig_mhsize, orig_st_size;
92 vm_offset_t header;
93 vm_size_t header_size;
94 int error, error1;
95 int i, j;
96 caddr_t addr;
97 vm_offset_t offset;
98 int rc_mh, rc_sc;
99
100 error = EFAULT;
101
102 vp = NULL;
103 header = NULL;
104 orig_mh = NULL;
105 orig_st = NULL;
106
107 // Dispose of unnecessary gumf, the booter doesn't need to load these
108 rc_mh = IODTGetLoaderInfo("Kernel-__HEADER",
109 (void **)&orig_mh, &orig_mhsize);
110 if (rc_mh && orig_mh)
111 IODTFreeLoaderInfo("Kernel-__HEADER",
112 (void *)orig_mh, round_page(orig_mhsize));
113
114 rc_sc = IODTGetLoaderInfo("Kernel-__SYMTAB",
115 (void **) &orig_st, &orig_st_size);
116 if (rc_sc && orig_st)
117 IODTFreeLoaderInfo("Kernel-__SYMTAB",
118 (void *)orig_st, round_page(orig_st_size));
119
120 if (pcred->p_svuid != pcred->p_ruid || pcred->p_svgid != pcred->p_rgid)
121 goto out;
122
123 // Check to see if the root is 'e' or 'n', is this a test for network?
124 if (rootdevice[0] == 'e' && rootdevice[1] == 'n')
125 goto out;
126
127 NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, "mach.sym", p);
128 if((error = vn_open(&nd, O_CREAT | FWRITE, S_IRUSR | S_IRGRP | S_IROTH))) goto out;
129
130 vp = nd.ni_vp;
131
132 /* Don't dump to non-regular files or files with links. */
133 error = EFAULT;
134 if (vp->v_type != VREG || VOP_GETATTR(vp, &vattr, cred, p)
135 || vattr.va_nlink != 1)
136 goto out;
137
138 VATTR_NULL(&vattr);
139 vattr.va_size = 0;
140 VOP_LEASE(vp, p, cred, LEASE_WRITE);
141 VOP_SETATTR(vp, &vattr, cred, p);
142 p->p_acflag |= ACORE;
143
144 // If the file type is MH_EXECUTE then this must be a kernel
145 // as all Kernel extensions must be of type MH_OBJECT
146 orig_ds = orig_ts = orig_le = NULL;
147 orig_st = NULL;
148 orig_mh = &_mh_execute_header;
149 cmd = (struct load_command *) &orig_mh[1];
150 for (i = 0; i < orig_mh->ncmds; i++) {
151 if (cmd->cmd == LC_SEGMENT) {
152 struct segment_command *sg = (struct segment_command *) cmd;
153
154 if (!strcmp(SEG_TEXT, sg->segname))
155 orig_ts = sg;
156 else if (!strcmp(SEG_DATA, sg->segname))
157 orig_ds = sg;
158 else if (!strcmp(SEG_LINKEDIT, sg->segname))
159 orig_le = sg;
160 }
161 else if (cmd->cmd == LC_SYMTAB)
162 orig_st = (struct symtab_command *) cmd;
163
164 cmd = (struct load_command *) ((caddr_t) cmd + cmd->cmdsize);
165 }
166
167 if (!orig_ts || !orig_ds || !orig_le || !orig_st)
168 goto out;
169
170 const_text = NULL;
171 se = (struct section *) &orig_ts[1];
172 for (i = 0; i < orig_ts->nsects; i++, se++) {
173 if (!strcmp("__const", se->sectname)) {
174 const_text = se;
175 break;
176 }
177 }
178 if (!const_text)
179 goto out;
180
181 header_size = sizeof(struct mach_header)
182 + orig_ts->cmdsize
183 + orig_ds->cmdsize
184 + sizeof(struct symtab_command);
185
186 (void) kmem_alloc_wired(kernel_map,
187 (vm_offset_t *) &header,
188 (vm_size_t) header_size);
189 if (header)
190 bzero((void *) header, header_size);
191 else
192 goto out;
193
194 /*
195 * Set up Mach-O header.
196 */
197 mh = (struct mach_header *) header;
198 mh->magic = orig_mh->magic;
199 mh->cputype = orig_mh->cputype;
200 mh->cpusubtype = orig_mh->cpusubtype;
201 mh->filetype = orig_mh->filetype;
202 mh->ncmds = 3;
203 mh->sizeofcmds = header_size - sizeof(struct mach_header);
204 mh->flags = orig_mh->flags;
205
206 // Initialise the current file offset and addr
207 offset = round_page(header_size);
208 addr = (caddr_t) const_text->addr; // Load address of __TEXT,__const
209
210 /*
211 * Construct a TEXT segment load command
212 * the only part of the TEXT segment we keep is the __TEXT,__const
213 * which contains the kernel vtables.
214 */
215 sg = (struct segment_command *) &mh[1];
216 bcopy(orig_ts, sg, orig_ts->cmdsize);
217 sg->vmaddr = (unsigned long) addr;
218 sg->vmsize = const_text->size;
219 sg->fileoff = 0;
220 sg->filesize = const_text->size + round_page(header_size);
221 sg->maxprot = 0;
222 sg->initprot = 0;
223 sg->flags = 0;
224 se = (struct section *)(sg+1);
225 for ( j = 0; j < sg->nsects; j++, se++ ) {
226 se->addr = (unsigned long) addr;
227 se->size = 0;
228 se->offset = offset;
229 se->nreloc = 0;
230 if (!strcmp("__const", se->sectname)) {
231 se->size = const_text->size;
232 addr += const_text->size;
233 offset += const_text->size;
234 const_text = se;
235 }
236 }
237 offset = round_page((vm_address_t) offset);
238
239 // Now copy of the __DATA segment load command, the image need
240 // not be stored to disk nobody needs it, yet!
241 sg = (struct segment_command *)((int)sg + sg->cmdsize);
242 bcopy(orig_ds, sg, orig_ds->cmdsize);
243
244 sg->vmaddr = (unsigned long) addr;
245 sg->vmsize = 0x1000; // One page for some reason?
246 sg->fileoff = offset;
247 sg->filesize = 0;
248 sg->maxprot = 0;
249 sg->initprot = 0;
250 sg->flags = 0;
251 se = (struct section *)(sg+1);
252 for ( j = 0; j < sg->nsects; j++, se++ ) {
253 se->addr = (unsigned long) addr;
254 se->size = 0;
255 se->offset = offset;
256 se->nreloc = 0;
257 }
258 offset = round_page(offset);
259
260
261 /*
262 * Set up LC_SYMTAB command
263 */
264 st = (struct symtab_command *)((int)sg + sg->cmdsize);
265 st->cmd = LC_SYMTAB;
266 st->cmdsize = sizeof(struct symtab_command);
267 st->symoff = offset;
268 st->nsyms = orig_st->nsyms;
269 st->strsize = orig_st->strsize;
270 st->stroff = offset + st->nsyms * sizeof(struct nlist);
271
272 /*
273 * Convert the symbol table in place from section references
274 * to absolute references.
275 */
276 sym = (struct nlist *) orig_le->vmaddr;
277 for (i = 0; i < st->nsyms; i++, sym++ ) {
278 if ( (sym->n_type & N_TYPE) == N_SECT) {
279 sym->n_sect = NO_SECT;
280 sym->n_type = (sym->n_type & ~N_TYPE) | N_ABS;
281 }
282 }
283
284 /*
285 * Write out the load commands at the beginning of the file.
286 */
287 error = vn_rdwr(UIO_WRITE, vp, (caddr_t) mh, header_size, (off_t) 0,
288 UIO_SYSSPACE, IO_NODELOCKED|IO_UNIT, cred, (int *) 0, p);
289 if (error)
290 goto out;
291
292 /*
293 * Write out the __TEXT,__const data segment.
294 */
295 error = vn_rdwr(UIO_WRITE, vp, (caddr_t) const_text->addr,
296 const_text->size, const_text->offset,
297 UIO_SYSSPACE, IO_NODELOCKED|IO_UNIT, cred, (int *) 0, p);
298 if (error)
299 goto out;
300
301 /*
302 * Write out kernel symbols
303 */
304 offset = st->nsyms * sizeof(struct nlist) + st->strsize; // symtab size
305 error = vn_rdwr(UIO_WRITE, vp,
306 (caddr_t) orig_le->vmaddr, offset, st->symoff,
307 UIO_SYSSPACE, IO_NODELOCKED|IO_UNIT, cred, (int *) 0, p);
308 if (error)
309 goto out;
310
311 out:
312 if (header)
313 kmem_free(kernel_map, header, header_size);
314
315 if (vp) {
316 VOP_UNLOCK(vp, 0, p);
317 error1 = vn_close(vp, FWRITE, cred, p);
318 if (!error) error = error1;
319 }
320
321 return(error);
322 }
323 /*
324 *
325 */
326 int get_kernel_symfile(struct proc *p, char **symfile)
327 {
328 if (!kernel_symfile_opened) {
329 kernel_symfile_opened = 1;
330 error_code = output_kernel_symbols(p);
331 }
332 if (!error_code)
333 *symfile = "\\mach.sym";
334
335 return error_code;
336 }