]>
Commit | Line | Data |
---|---|---|
1c79356b A |
1 | /* |
2 | * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
de355530 A |
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. | |
1c79356b | 11 | * |
de355530 A |
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 | |
1c79356b A |
14 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
15 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
de355530 A |
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. | |
1c79356b A |
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[]; | |
0b4e3aa0 | 67 | extern struct mach_header _mh_execute_header; |
1c79356b | 68 | |
0b4e3aa0 A |
69 | static int kernel_symfile_opened = 0; |
70 | static int error_code = 0; | |
1c79356b | 71 | |
0b4e3aa0 A |
72 | extern int IODTGetLoaderInfo(char *key, void **infoAddr, int *infoSize); |
73 | extern void IODTFreeLoaderInfo(char *key, void *infoAddr, int infoSize); | |
1c79356b A |
74 | |
75 | /* | |
76 | * | |
77 | */ | |
0b4e3aa0 | 78 | static int output_kernel_symbols(struct proc *p) |
1c79356b | 79 | { |
0b4e3aa0 A |
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", | |
de355530 | 112 | (void *)orig_mh, round_page(orig_mhsize)); |
0b4e3aa0 A |
113 | |
114 | rc_sc = IODTGetLoaderInfo("Kernel-__SYMTAB", | |
115 | (void **) &orig_st, &orig_st_size); | |
116 | if (rc_sc && orig_st) | |
117 | IODTFreeLoaderInfo("Kernel-__SYMTAB", | |
de355530 | 118 | (void *)orig_st, round_page(orig_st_size)); |
0b4e3aa0 A |
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); | |
1c79356b | 165 | } |
1c79356b | 166 | |
0b4e3aa0 A |
167 | if (!orig_ts || !orig_ds || !orig_le || !orig_st) |
168 | goto out; | |
1c79356b | 169 | |
0b4e3aa0 A |
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 | |
de355530 | 207 | offset = round_page(header_size); |
0b4e3aa0 A |
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; | |
de355530 | 220 | sg->filesize = const_text->size + round_page(header_size); |
0b4e3aa0 A |
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 | } | |
de355530 | 237 | offset = round_page((vm_address_t) offset); |
0b4e3aa0 A |
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 | } | |
de355530 | 258 | offset = round_page(offset); |
0b4e3aa0 A |
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; | |
1c79356b A |
310 | |
311 | out: | |
0b4e3aa0 A |
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 | } | |
1c79356b | 320 | |
0b4e3aa0 A |
321 | return(error); |
322 | } | |
1c79356b A |
323 | /* |
324 | * | |
325 | */ | |
0b4e3aa0 | 326 | int get_kernel_symfile(struct proc *p, char **symfile) |
1c79356b | 327 | { |
0b4e3aa0 A |
328 | if (!kernel_symfile_opened) { |
329 | kernel_symfile_opened = 1; | |
330 | error_code = output_kernel_symbols(p); | |
1c79356b | 331 | } |
0b4e3aa0 A |
332 | if (!error_code) |
333 | *symfile = "\\mach.sym"; | |
1c79356b | 334 | |
0b4e3aa0 A |
335 | return error_code; |
336 | } |