]> git.saurik.com Git - apple/xnu.git/blob - bsd/kern/mach_header.c
fc52bbd55d77e3b89368922ad3b0fa630d0c32b6
[apple/xnu.git] / bsd / kern / mach_header.c
1 /*
2 * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23 /*
24 * File: kern/mach_header.c
25 *
26 * Functions for accessing mach-o headers.
27 *
28 * NOTE: This file supports only 32 bit mach headers at the present
29 * time; it's primary use is by kld, and all externally
30 * referenced routines at the present time operate against
31 * the 32 bit mach header _mh_execute_header, which is the
32 * header for the currently executing kernel. Adding support
33 * for 64 bit kernels is possible, but is not necessary at the
34 * present time.
35 *
36 * HISTORY
37 * 27-MAR-97 Umesh Vaishampayan (umeshv@NeXT.com)
38 * Added getsegdatafromheader();
39 *
40 * 29-Jan-92 Mike DeMoney (mike@next.com)
41 * Made into machine independent form from machdep/m68k/mach_header.c.
42 * Ifdef'ed out most of this since I couldn't find any references.
43 */
44
45 #if !defined(KERNEL_PRELOAD)
46 #include <kern/mach_header.h>
47 #include <string.h> // from libsa
48
49 extern struct mach_header _mh_execute_header;
50
51 /*
52 * return the last address (first avail)
53 *
54 * This routine operates against the currently executing kernel only
55 */
56 vm_offset_t
57 getlastaddr(void)
58 {
59 struct segment_command *sgp;
60 vm_offset_t last_addr = 0;
61 struct mach_header *header = &_mh_execute_header;
62 unsigned long i;
63
64 sgp = (struct segment_command *)
65 ((char *)header + sizeof(struct mach_header));
66 for (i = 0; i < header->ncmds; i++){
67 if ( sgp->cmd == LC_SEGMENT) {
68 if (sgp->vmaddr + sgp->vmsize > last_addr)
69 last_addr = sgp->vmaddr + sgp->vmsize;
70 }
71 sgp = (struct segment_command *)((char *)sgp + sgp->cmdsize);
72 }
73 return last_addr;
74 }
75
76 #if FIXME /* [ */
77 /*
78 * This routine operates against the currently executing kernel only
79 */
80 struct mach_header **
81 getmachheaders(void)
82 {
83 struct mach_header **tl;
84 tl = (struct mach_header **)malloc(2*sizeof(struct mach_header *));
85 tl[0] = &_mh_execute_header;
86 tl[1] = (struct mach_header *)0;
87 return tl;
88 }
89 #endif /* FIXME ] */
90
91 /*
92 * This routine returns the a pointer to the data for the named section in the
93 * named segment if it exist in the mach header passed to it. Also it returns
94 * the size of the section data indirectly through the pointer size. Otherwise
95 * it returns zero for the pointer and the size.
96 *
97 * This routine can operate against any 32 bit mach header.
98 */
99 void *
100 getsectdatafromheader(
101 struct mach_header *mhp,
102 const char *segname,
103 const char *sectname,
104 int *size)
105 {
106 const struct section *sp;
107 void *result;
108
109 sp = getsectbynamefromheader(mhp, segname, sectname);
110 if(sp == (struct section *)0){
111 *size = 0;
112 return((char *)0);
113 }
114 *size = sp->size;
115 result = (void *)sp->addr;
116 return result;
117 }
118
119 /*
120 * This routine returns the a pointer to the data for the named segment
121 * if it exist in the mach header passed to it. Also it returns
122 * the size of the segment data indirectly through the pointer size.
123 * Otherwise it returns zero for the pointer and the size.
124 *
125 * This routine can operate against any 32 bit mach header.
126 */
127 void *
128 getsegdatafromheader(
129 struct mach_header *mhp,
130 const char *segname,
131 int *size)
132 {
133 const struct segment_command *sc;
134 void *result;
135
136 sc = getsegbynamefromheader(mhp, segname);
137 if(sc == (struct segment_command *)0){
138 *size = 0;
139 return((char *)0);
140 }
141 *size = sc->vmsize;
142 result = (void *)sc->vmaddr;
143 return result;
144 }
145
146 /*
147 * This routine returns the section structure for the named section in the
148 * named segment for the mach_header pointer passed to it if it exist.
149 * Otherwise it returns zero.
150 *
151 * This routine can operate against any 32 bit mach header.
152 */
153 struct section *
154 getsectbynamefromheader(
155 struct mach_header *mhp,
156 const char *segname,
157 const char *sectname)
158 {
159 struct segment_command *sgp;
160 struct section *sp;
161 unsigned long i, j;
162
163 sgp = (struct segment_command *)
164 ((char *)mhp + sizeof(struct mach_header));
165 for(i = 0; i < mhp->ncmds; i++){
166 if(sgp->cmd == LC_SEGMENT)
167 if(strncmp(sgp->segname, segname, sizeof(sgp->segname)) == 0 ||
168 mhp->filetype == MH_OBJECT){
169 sp = (struct section *)((char *)sgp +
170 sizeof(struct segment_command));
171 for(j = 0; j < sgp->nsects; j++){
172 if(strncmp(sp->sectname, sectname,
173 sizeof(sp->sectname)) == 0 &&
174 strncmp(sp->segname, segname,
175 sizeof(sp->segname)) == 0)
176 return(sp);
177 sp = (struct section *)((char *)sp +
178 sizeof(struct section));
179 }
180 }
181 sgp = (struct segment_command *)((char *)sgp + sgp->cmdsize);
182 }
183 return((struct section *)0);
184 }
185
186 /*
187 * This routine can operate against any 32 bit mach header.
188 */
189 struct segment_command *
190 getsegbynamefromheader(
191 struct mach_header *header,
192 const char *seg_name)
193 {
194 struct segment_command *sgp;
195 unsigned long i;
196
197 sgp = (struct segment_command *)
198 ((char *)header + sizeof(struct mach_header));
199 for (i = 0; i < header->ncmds; i++){
200 if ( sgp->cmd == LC_SEGMENT
201 && !strncmp(sgp->segname, seg_name, sizeof(sgp->segname)))
202 return sgp;
203 sgp = (struct segment_command *)((char *)sgp + sgp->cmdsize);
204 }
205 return (struct segment_command *)0;
206 }
207
208
209 /*
210 * For now at least, all the rest of this seems unused.
211 * NOTE: The constant in here for segment alignment is machine-dependent,
212 * so if you include this, define a machine dependent constant for it's
213 * value.
214 */
215 static struct {
216 struct segment_command seg;
217 struct section sect;
218 } fvm_data = {
219 {
220 LC_SEGMENT, // cmd
221 sizeof(fvm_data), // cmdsize
222 "__USER", // segname
223 0, // vmaddr
224 0, // vmsize
225 0, // fileoff
226 0, // filesize
227 VM_PROT_READ, // maxprot
228 VM_PROT_READ, // initprot,
229 1, // nsects
230 0 // flags
231 },
232 {
233 "", // sectname
234 "__USER", // segname
235 0, // addr
236 0, // size
237 0, // offset
238 4, // align
239 0, // reloff
240 0, // nreloc
241 0, // flags
242 0, // reserved1
243 0 // reserved2
244 }
245 };
246
247 struct segment_command *fvm_seg;
248
249 static struct fvmfile_command *fvmfilefromheader(struct mach_header *header);
250 static vm_offset_t getsizeofmacho(struct mach_header *header);
251
252 /*
253 * Return the first segment_command in the header.
254 *
255 * This routine operates against the currently executing kernel only
256 */
257 struct segment_command *
258 firstseg(void)
259 {
260 return firstsegfromheader(&_mh_execute_header);
261 }
262
263 /*
264 * This routine can operate against any 32 bit mach header, and returns a
265 * pointer to a 32 bit segment_command structure from the file prefixed by
266 * the header it is passed as its argument.
267 */
268 struct segment_command *
269 firstsegfromheader(struct mach_header *header)
270 {
271 struct segment_command *sgp;
272 unsigned long i;
273
274 sgp = (struct segment_command *)
275 ((char *)header + sizeof(struct mach_header));
276 for (i = 0; i < header->ncmds; i++){
277 if (sgp->cmd == LC_SEGMENT)
278 return sgp;
279 sgp = (struct segment_command *)((char *)sgp + sgp->cmdsize);
280 }
281 return (struct segment_command *)0;
282 }
283
284 /*
285 * This routine operates against a 32 bit mach segment_command structure
286 * pointer from the currently executing kernel only, to obtain the
287 * sequentially next segment_command structure in the currently executing
288 * kernel
289 */
290 struct segment_command *
291 nextseg(struct segment_command *sgp)
292 {
293 struct segment_command *this;
294
295 this = nextsegfromheader(&_mh_execute_header, sgp);
296
297 /*
298 * For the kernel's header add on the faked segment for the
299 * USER boot code identified by a FVMFILE_COMMAND in the mach header.
300 */
301 if (!this && sgp != fvm_seg)
302 this = fvm_seg;
303
304 return this;
305 }
306
307 /*
308 * This routine operates against any 32 bit mach segment_command structure
309 * pointer and the provided 32 bit header, to obtain the sequentially next
310 * segment_command structure in that header.
311 */
312 struct segment_command *
313 nextsegfromheader(
314 struct mach_header *header,
315 struct segment_command *seg)
316 {
317 struct segment_command *sgp;
318 unsigned long i;
319
320 sgp = (struct segment_command *)
321 ((char *)header + sizeof(struct mach_header));
322 for (i = 0; i < header->ncmds; i++) {
323 if (sgp == seg)
324 break;
325 sgp = (struct segment_command *)((char *)sgp + sgp->cmdsize);
326 }
327
328 if (i == header->ncmds)
329 return (struct segment_command *)0;
330
331 sgp = (struct segment_command *)((char *)sgp + sgp->cmdsize);
332 for (; i < header->ncmds; i++) {
333 if (sgp->cmd == LC_SEGMENT)
334 return sgp;
335 sgp = (struct segment_command *)((char *)sgp + sgp->cmdsize);
336 }
337
338 return (struct segment_command *)0;
339 }
340
341
342 /*
343 * Return the address of the named Mach-O segment from the currently
344 * executing 32 bit kernel, or NULL.
345 */
346 struct segment_command *
347 getsegbyname(const char *seg_name)
348 {
349 struct segment_command *this;
350
351 this = getsegbynamefromheader(&_mh_execute_header, seg_name);
352
353 /*
354 * For the kernel's header add on the faked segment for the
355 * USER boot code identified by a FVMFILE_COMMAND in the mach header.
356 */
357 if (!this && strcmp(seg_name, fvm_seg->segname) == 0)
358 this = fvm_seg;
359
360 return this;
361 }
362
363 /*
364 * This routine returns the a pointer the section structure of the named
365 * section in the named segment if it exists in the currently executing
366 * kernel, which it is presumed to be linked into. Otherwise it returns NULL.
367 */
368 struct section *
369 getsectbyname(
370 const char *segname,
371 const char *sectname)
372 {
373 return(getsectbynamefromheader(
374 (struct mach_header *)&_mh_execute_header, segname, sectname));
375 }
376
377 /*
378 * This routine can operate against any 32 bit segment_command structure to
379 * return the first 32 bit section immediately following that structure. If
380 * there are no sections associated with the segment_command structure, it
381 * returns NULL.
382 */
383 struct section *
384 firstsect(struct segment_command *sgp)
385 {
386 if (!sgp || sgp->nsects == 0)
387 return (struct section *)0;
388
389 return (struct section *)(sgp+1);
390 }
391
392 /*
393 * This routine can operate against any 32 bit segment_command structure and
394 * 32 bit section to return the next consecutive 32 bit section immediately
395 * following the 32 bit section provided. If there are no sections following
396 * the provided section, it returns NULL.
397 */
398 struct section *
399 nextsect(struct segment_command *sgp, struct section *sp)
400 {
401 struct section *fsp = firstsect(sgp);
402
403 if (((unsigned long)(sp - fsp) + 1) >= sgp->nsects)
404 return (struct section *)0;
405
406 return sp+1;
407 }
408
409 /*
410 * This routine can operate against any 32 bit mach header to return the
411 * first occurring 32 bit fvmfile_command section. If one is not present,
412 * it returns NULL.
413 */
414 static struct fvmfile_command *
415 fvmfilefromheader(struct mach_header *header)
416 {
417 struct fvmfile_command *fvp;
418 unsigned long i;
419
420 fvp = (struct fvmfile_command *)
421 ((char *)header + sizeof(struct mach_header));
422 for (i = 0; i < header->ncmds; i++){
423 if (fvp->cmd == LC_FVMFILE)
424 return fvp;
425 fvp = (struct fvmfile_command *)((char *)fvp + fvp->cmdsize);
426 }
427 return (struct fvmfile_command *)0;
428 }
429
430 /*
431 * Create a fake USER seg if a fvmfile_command is present.
432 *
433 * This routine operates against the currently executing kernel only
434 */
435 struct segment_command *
436 getfakefvmseg(void)
437 {
438 struct segment_command *sgp = getsegbyname("__USER");
439 struct fvmfile_command *fvp = fvmfilefromheader(&_mh_execute_header);
440 struct section *sp;
441
442 if (sgp)
443 return sgp;
444
445 if (!fvp)
446 return (struct segment_command *)0;
447
448 fvm_seg = &fvm_data.seg;
449 sgp = fvm_seg;
450 sp = &fvm_data.sect;
451
452 sgp->vmaddr = fvp->header_addr;
453 sgp->vmsize = getsizeofmacho((struct mach_header *)(sgp->vmaddr));
454
455 strcpy(sp->sectname, fvp->name.ptr);
456 sp->addr = sgp->vmaddr;
457 sp->size = sgp->vmsize;
458
459 #if DEBUG
460 printf("fake fvm seg __USER/\"%s\" at 0x%x, size 0x%x\n",
461 sp->sectname, sp->addr, sp->size);
462 #endif /* DEBUG */
463
464 return sgp;
465 }
466
467 /*
468 * Figure out the size the size of the data associated with a
469 * loaded mach_header.
470 *
471 * This routine can operate against any 32 bit mach header.
472 */
473 static vm_offset_t
474 getsizeofmacho(struct mach_header *header)
475 {
476 struct segment_command *sgp;
477 vm_offset_t last_addr;
478
479 last_addr = 0;
480 for ( sgp = firstsegfromheader(header)
481 ; sgp
482 ; sgp = nextsegfromheader(header, sgp))
483 {
484 if (sgp->fileoff + sgp->filesize > last_addr)
485 last_addr = sgp->fileoff + sgp->filesize;
486 }
487
488 return last_addr;
489 }
490 #endif /* !defined(KERNEL_PRELOAD) */