2 * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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.
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
20 * @APPLE_LICENSE_HEADER_END@
22 /* Copyright (c) 1998 Apple Computer, Inc. All rights reserved.
24 * File: bsd/kern/kern_symfile.c
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.
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.
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.
48 #include <mach/vm_param.h>
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>
61 #include <sys/file_internal.h>
63 #include <sys/kernel.h>
68 #include <mach-o/loader.h>
69 #include <mach-o/nlist.h>
71 #include <kern/kalloc.h>
72 #include <vm/vm_kern.h>
73 #include <pexpert/pexpert.h>
75 extern unsigned char rootdevice
[];
76 extern struct mach_header _mh_execute_header
;
78 static int kernel_symfile_opened
= 0;
79 static int error_code
= 0;
81 extern int IODTGetLoaderInfo(char *key
, void **infoAddr
, int *infoSize
);
82 extern void IODTFreeLoaderInfo(char *key
, void *infoAddr
, int infoSize
);
85 * Can only operate against currently running 32 bit mach_kernel
88 output_kernel_symbols(struct proc
*p
)
91 kauth_cred_t cred
= p
->p_ucred
; /* XXX */
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
;
100 vm_size_t orig_mhsize
, orig_st_size
;
102 vm_size_t header_size
= 0; /* out: protected by header */
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
));
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
));
129 if (cred
->cr_svuid
!= cred
->cr_ruid
|| cred
->cr_svgid
!= cred
->cr_rgid
)
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')
137 context
.vc_ucred
= cred
;
139 if ((error
= vnode_open("mach.sym", (O_CREAT
| FWRITE
), (S_IRUSR
| S_IRGRP
| S_IROTH
), 0, &vp
, &context
)))
142 /* Don't dump to non-regular files or files with links. */
145 VATTR_WANTED(&va
, va_nlink
);
146 if ((vp
->v_type
!= VREG
) || vnode_getattr(vp
, &va
, &context
) || (va
.va_nlink
!= 1))
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
;
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
;
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
;
164 if (!strcmp(SEG_TEXT
, orig_sg
->segname
))
166 else if (!strcmp(SEG_DATA
, orig_sg
->segname
))
168 else if (!strcmp(SEG_LINKEDIT
, orig_sg
->segname
))
171 else if (cmd
->cmd
== LC_SYMTAB
)
172 orig_st
= (struct symtab_command
*) cmd
;
174 cmd
= (struct load_command
*) ((caddr_t
) cmd
+ cmd
->cmdsize
);
177 if (!orig_ts
|| !orig_ds
|| !orig_le
|| !orig_st
)
181 se
= (struct section
*) &orig_ts
[1];
182 for (i
= 0; i
< orig_ts
->nsects
; i
++, se
++) {
183 if (!strcmp("__const", se
->sectname
)) {
191 header_size
= sizeof(struct mach_header
)
194 + sizeof(struct symtab_command
);
196 (void) kmem_alloc(kernel_map
,
197 (vm_offset_t
*) &header
,
198 (vm_size_t
) header_size
);
200 bzero((void *) header
, header_size
);
205 * Set up Mach-O header.
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
;
213 mh
->sizeofcmds
= header_size
- sizeof(struct mach_header
);
214 mh
->flags
= orig_mh
->flags
;
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
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.
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
;
230 sg
->filesize
= const_text
->size
+ round_page(header_size
);
234 se
= (struct section
*)(sg
+1);
235 for ( j
= 0; j
< sg
->nsects
; j
++, se
++ ) {
236 se
->addr
= (unsigned long) addr
;
240 if (!strcmp("__const", se
->sectname
)) {
241 se
->size
= const_text
->size
;
242 addr
+= const_text
->size
;
243 offset
+= const_text
->size
;
247 offset
= round_page(offset
);
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
);
254 sg
->vmaddr
= (unsigned long) addr
;
255 sg
->vmsize
= 0x1000; // One page for some reason?
256 sg
->fileoff
= offset
;
261 se
= (struct section
*)(sg
+1);
262 for ( j
= 0; j
< sg
->nsects
; j
++, se
++ ) {
263 se
->addr
= (unsigned long) addr
;
268 offset
= round_page(offset
);
272 * Set up LC_SYMTAB command
274 st
= (struct symtab_command
*)((int)sg
+ sg
->cmdsize
);
276 st
->cmdsize
= sizeof(struct symtab_command
);
278 st
->nsyms
= orig_st
->nsyms
;
279 st
->strsize
= orig_st
->strsize
;
280 st
->stroff
= offset
+ st
->nsyms
* sizeof(struct nlist
);
283 * Convert the symbol table in place from section references
284 * to absolute references.
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
;
295 * Write out the load commands at the beginning of the file.
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
);
303 * Write out the __TEXT,__const data segment.
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
);
312 * Write out kernel symbols
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
);
320 kmem_free(kernel_map
, header
, header_size
);
323 error1
= vnode_close(vp
, FWRITE
, &context
);
324 if (!error
) error
= error1
;
332 int get_kernel_symfile(struct proc
*p
, char **symfile
)
334 if (!kernel_symfile_opened
) {
335 kernel_symfile_opened
= 1;
336 error_code
= output_kernel_symbols(p
);
339 *symfile
= "\\mach.sym";