]>
Commit | Line | Data |
---|---|---|
1c79356b A |
1 | /* |
2 | * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
43866e37 | 6 | * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. |
1c79356b | 7 | * |
43866e37 A |
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 | |
1c79356b A |
17 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
18 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
43866e37 A |
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. | |
1c79356b A |
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[]; | |
0b4e3aa0 | 70 | extern struct mach_header _mh_execute_header; |
1c79356b | 71 | |
0b4e3aa0 A |
72 | static int kernel_symfile_opened = 0; |
73 | static int error_code = 0; | |
1c79356b | 74 | |
0b4e3aa0 A |
75 | extern int IODTGetLoaderInfo(char *key, void **infoAddr, int *infoSize); |
76 | extern void IODTFreeLoaderInfo(char *key, void *infoAddr, int infoSize); | |
1c79356b A |
77 | |
78 | /* | |
79 | * | |
80 | */ | |
0b4e3aa0 | 81 | static int output_kernel_symbols(struct proc *p) |
1c79356b | 82 | { |
0b4e3aa0 A |
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", | |
de355530 | 115 | (void *)orig_mh, round_page(orig_mhsize)); |
0b4e3aa0 A |
116 | |
117 | rc_sc = IODTGetLoaderInfo("Kernel-__SYMTAB", | |
118 | (void **) &orig_st, &orig_st_size); | |
119 | if (rc_sc && orig_st) | |
120 | IODTFreeLoaderInfo("Kernel-__SYMTAB", | |
de355530 | 121 | (void *)orig_st, round_page(orig_st_size)); |
0b4e3aa0 A |
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); | |
1c79356b | 168 | } |
1c79356b | 169 | |
0b4e3aa0 A |
170 | if (!orig_ts || !orig_ds || !orig_le || !orig_st) |
171 | goto out; | |
1c79356b | 172 | |
0b4e3aa0 A |
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 | |
de355530 | 210 | offset = round_page(header_size); |
0b4e3aa0 A |
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; | |
de355530 | 223 | sg->filesize = const_text->size + round_page(header_size); |
0b4e3aa0 A |
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 | } | |
de355530 | 240 | offset = round_page((vm_address_t) offset); |
0b4e3aa0 A |
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 | } | |
de355530 | 261 | offset = round_page(offset); |
0b4e3aa0 A |
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; | |
1c79356b A |
313 | |
314 | out: | |
0b4e3aa0 A |
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 | } | |
1c79356b | 323 | |
0b4e3aa0 A |
324 | return(error); |
325 | } | |
1c79356b A |
326 | /* |
327 | * | |
328 | */ | |
0b4e3aa0 | 329 | int get_kernel_symfile(struct proc *p, char **symfile) |
1c79356b | 330 | { |
0b4e3aa0 A |
331 | if (!kernel_symfile_opened) { |
332 | kernel_symfile_opened = 1; | |
333 | error_code = output_kernel_symbols(p); | |
1c79356b | 334 | } |
0b4e3aa0 A |
335 | if (!error_code) |
336 | *symfile = "\\mach.sym"; | |
1c79356b | 337 | |
0b4e3aa0 A |
338 | return error_code; |
339 | } |