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