]> git.saurik.com Git - apple/xnu.git/blob - osfmk/mach-o/mach_header.c
xnu-1228.5.18.tar.gz
[apple/xnu.git] / osfmk / mach-o / mach_header.c
1 /*
2 * Copyright (c) 2000-2006 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_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. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28 /*
29 * File: kern/mach_header.c
30 *
31 * Functions for accessing mach-o headers.
32 *
33 * NOTE: This file supports only 32 bit mach headers at the present
34 * time; it's primary use is by kld, and all externally
35 * referenced routines at the present time operate against
36 * the 32 bit mach header _mh_execute_header, which is the
37 * header for the currently executing kernel. Adding support
38 * for 64 bit kernels is possible, but is not necessary at the
39 * present time.
40 *
41 * HISTORY
42 * 27-MAR-97 Umesh Vaishampayan (umeshv@NeXT.com)
43 * Added getsegdatafromheader();
44 *
45 * 29-Jan-92 Mike DeMoney (mike@next.com)
46 * Made into machine independent form from machdep/m68k/mach_header.c.
47 * Ifdef'ed out most of this since I couldn't find any references.
48 */
49
50 #include <vm/vm_map.h>
51 #include <vm/vm_kern.h>
52 #include <mach-o/mach_header.h>
53 #include <string.h> // from libsa
54
55 #ifdef __MACHO__
56
57 extern struct mach_header _mh_execute_header;
58
59 /*
60 * return the last address (first avail)
61 *
62 * This routine operates against the currently executing kernel only
63 */
64 #ifdef MACH_BSD
65 __private_extern__
66 #endif
67 vm_offset_t
68 getlastaddr(void)
69 {
70 struct segment_command *sgp;
71 vm_offset_t last_addr = 0;
72 struct mach_header *header = &_mh_execute_header;
73 unsigned long i;
74
75 sgp = (struct segment_command *)
76 ((char *)header + sizeof(struct mach_header));
77 for (i = 0; i < header->ncmds; i++){
78 if ( sgp->cmd == LC_SEGMENT) {
79 if (sgp->vmaddr + sgp->vmsize > last_addr)
80 last_addr = sgp->vmaddr + sgp->vmsize;
81 }
82 sgp = (struct segment_command *)((char *)sgp + sgp->cmdsize);
83 }
84 return last_addr;
85 }
86
87 #ifdef XXX_MACH_BSD
88 __private_extern__
89 #endif
90 /*
91 * This routine operates against the currently executing kernel only
92 */
93 struct mach_header **
94 getmachheaders(void)
95 {
96 struct mach_header **tl;
97
98 if (kmem_alloc(kernel_map, (vm_offset_t *) &tl, 2*sizeof(struct mach_header *)) != KERN_SUCCESS)
99 return NULL;
100
101 tl[0] = &_mh_execute_header;
102 tl[1] = (struct mach_header *)0;
103 return tl;
104 }
105
106 /*
107 * This routine returns the a pointer to the data for the named section in the
108 * named segment if it exist in the mach header passed to it. Also it returns
109 * the size of the section data indirectly through the pointer size. Otherwise
110 * it returns zero for the pointer and the size.
111 *
112 * This routine can operate against any 32 bit mach header.
113 */
114 #ifdef MACH_BSD
115 __private_extern__
116 #endif
117 void *
118 getsectdatafromheader(
119 struct mach_header *mhp,
120 const char *segname,
121 const char *sectname,
122 int *size)
123 {
124 const struct section *sp;
125 void *result;
126
127 sp = getsectbynamefromheader(mhp, segname, sectname);
128 if(sp == (struct section *)0){
129 *size = 0;
130 return((char *)0);
131 }
132 *size = sp->size;
133 result = (void *)sp->addr;
134 return result;
135 }
136
137 /*
138 * This routine returns the a pointer to the data for the named segment
139 * if it exist in the mach header passed to it. Also it returns
140 * the size of the segment data indirectly through the pointer size.
141 * Otherwise it returns zero for the pointer and the size.
142 */
143 #ifdef MACH_BSD
144 __private_extern__
145 #endif
146 void *
147 getsegdatafromheader(
148 struct mach_header *mhp,
149 const char *segname,
150 int *size)
151 {
152 const struct segment_command *sc;
153 void *result;
154
155 sc = getsegbynamefromheader(mhp, segname);
156 if(sc == (struct segment_command *)0){
157 *size = 0;
158 return((char *)0);
159 }
160 *size = sc->vmsize;
161 result = (void *)sc->vmaddr;
162 return result;
163 }
164
165 /*
166 * This routine returns the section structure for the named section in the
167 * named segment for the mach_header pointer passed to it if it exist.
168 * Otherwise it returns zero.
169 *
170 * This routine can operate against any 32 bit mach header.
171 */
172 #ifdef MACH_BSD
173 __private_extern__
174 #endif
175 struct section *
176 getsectbynamefromheader(
177 struct mach_header *mhp,
178 const char *segname,
179 const char *sectname)
180 {
181 struct segment_command *sgp;
182 struct section *sp;
183 unsigned long i, j;
184
185 sgp = (struct segment_command *)
186 ((char *)mhp + sizeof(struct mach_header));
187 for(i = 0; i < mhp->ncmds; i++){
188 if(sgp->cmd == LC_SEGMENT)
189 if(strncmp(sgp->segname, segname, sizeof(sgp->segname)) == 0 ||
190 mhp->filetype == MH_OBJECT){
191 sp = (struct section *)((char *)sgp +
192 sizeof(struct segment_command));
193 for(j = 0; j < sgp->nsects; j++){
194 if(strncmp(sp->sectname, sectname,
195 sizeof(sp->sectname)) == 0 &&
196 strncmp(sp->segname, segname,
197 sizeof(sp->segname)) == 0)
198 return(sp);
199 sp = (struct section *)((char *)sp +
200 sizeof(struct section));
201 }
202 }
203 sgp = (struct segment_command *)((char *)sgp + sgp->cmdsize);
204 }
205 return((struct section *)0);
206 }
207
208 #ifdef MACH_BSD
209 __private_extern__
210 #endif
211 /*
212 * This routine can operate against any 32 bit mach header.
213 */
214 struct segment_command *
215 getsegbynamefromheader(
216 struct mach_header *header,
217 const char *seg_name)
218 {
219 struct segment_command *sgp;
220 unsigned long i;
221
222 sgp = (struct segment_command *)
223 ((char *)header + sizeof(struct mach_header));
224 for (i = 0; i < header->ncmds; i++){
225 if ( sgp->cmd == LC_SEGMENT
226 && !strncmp(sgp->segname, seg_name, sizeof(sgp->segname)))
227 return sgp;
228 sgp = (struct segment_command *)((char *)sgp + sgp->cmdsize);
229 }
230 return (struct segment_command *)0;
231 }
232
233
234 /*
235 * For now at least, all the rest of this seems unused.
236 * NOTE: The constant in here for segment alignment is machine-dependent,
237 * so if you include this, define a machine dependent constant for it's
238 * value.
239 */
240 static struct {
241 struct segment_command seg;
242 struct section sect;
243 } fvm_data = {
244 {
245 LC_SEGMENT, // cmd
246 sizeof(fvm_data), // cmdsize
247 "__USER", // segname
248 0, // vmaddr
249 0, // vmsize
250 0, // fileoff
251 0, // filesize
252 VM_PROT_READ, // maxprot
253 VM_PROT_READ, // initprot,
254 1, // nsects
255 0 // flags
256 },
257 {
258 "", // sectname
259 "__USER", // segname
260 0, // addr
261 0, // size
262 0, // offset
263 4, // align
264 0, // reloff
265 0, // nreloc
266 0, // flags
267 0, // reserved1
268 0 // reserved2
269 }
270 };
271
272 #ifdef MACH_BSD
273 static
274 #endif
275 struct segment_command *fvm_seg;
276
277 static struct fvmfile_command *fvmfilefromheader(struct mach_header *header);
278 static vm_offset_t getsizeofmacho(struct mach_header *header);
279
280 /*
281 * Return the first segment_command in the header.
282 */
283 #ifdef MACH_BSD
284 __private_extern__
285 #endif
286 struct segment_command *
287 firstseg(void)
288 {
289 return firstsegfromheader(&_mh_execute_header);
290 }
291
292 #ifdef MACH_BSD
293 __private_extern__
294 #endif
295 struct segment_command *
296 firstsegfromheader(struct mach_header *header)
297 {
298 struct segment_command *sgp;
299 unsigned long i;
300
301 sgp = (struct segment_command *)
302 ((char *)header + sizeof(struct mach_header));
303 for (i = 0; i < header->ncmds; i++){
304 if (sgp->cmd == LC_SEGMENT)
305 return sgp;
306 sgp = (struct segment_command *)((char *)sgp + sgp->cmdsize);
307 }
308 return (struct segment_command *)0;
309 }
310
311 #ifdef MACH_BSD
312 __private_extern__
313 #endif
314 /*
315 * This routine operates against a 32 bit mach segment_command structure
316 * pointer from the currently executing kernel only, to obtain the
317 * sequentially next segment_command structure in the currently executing
318 * kernel
319 */
320 struct segment_command *
321 nextseg(struct segment_command *sgp)
322 {
323 struct segment_command *this;
324
325 this = nextsegfromheader(&_mh_execute_header, sgp);
326
327 /*
328 * For the kernel's header add on the faked segment for the
329 * USER boot code identified by a FVMFILE_COMMAND in the mach header.
330 */
331 if (!this && sgp != fvm_seg)
332 this = fvm_seg;
333
334 return this;
335 }
336
337 #ifdef MACH_BSD
338 __private_extern__
339 #endif
340 /*
341 * This routine operates against any 32 bit mach segment_command structure
342 * pointer and the provided 32 bit header, to obtain the sequentially next
343 * segment_command structure in that header.
344 */
345 struct segment_command *
346 nextsegfromheader(
347 struct mach_header *header,
348 struct segment_command *seg)
349 {
350 struct segment_command *sgp;
351 unsigned long i;
352
353 sgp = (struct segment_command *)
354 ((char *)header + sizeof(struct mach_header));
355 for (i = 0; i < header->ncmds; i++) {
356 if (sgp == seg)
357 break;
358 sgp = (struct segment_command *)((char *)sgp + sgp->cmdsize);
359 }
360
361 if (i == header->ncmds)
362 return (struct segment_command *)0;
363
364 sgp = (struct segment_command *)((char *)sgp + sgp->cmdsize);
365 for (; i < header->ncmds; i++) {
366 if (sgp->cmd == LC_SEGMENT)
367 return sgp;
368 sgp = (struct segment_command *)((char *)sgp + sgp->cmdsize);
369 }
370
371 return (struct segment_command *)0;
372 }
373
374
375 /*
376 * Return the address of the named Mach-O segment from the currently
377 * executing 32 bit kernel, or NULL.
378 */
379 #ifdef MACH_BSD
380 __private_extern__
381 #endif
382 struct segment_command *
383 getsegbyname(const char *seg_name)
384 {
385 struct segment_command *this;
386
387 this = getsegbynamefromheader(&_mh_execute_header, seg_name);
388
389 /*
390 * For the kernel's header add on the faked segment for the
391 * USER boot code identified by a FVMFILE_COMMAND in the mach header.
392 */
393 if (!this && strncmp(seg_name, fvm_seg->segname,
394 sizeof(fvm_seg->segname)) == 0)
395 this = fvm_seg;
396
397 return this;
398 }
399
400 /*
401 * This routine returns the a pointer the section structure of the named
402 * section in the named segment if it exists in the currently executing
403 * kernel, which it is presumed to be linked into. Otherwise it returns NULL.
404 */
405 #ifdef MACH_BSD
406 __private_extern__
407 #endif
408 struct section *
409 getsectbyname(
410 const char *segname,
411 const char *sectname)
412 {
413 return(getsectbynamefromheader(
414 (struct mach_header *)&_mh_execute_header, segname, sectname));
415 }
416
417 #ifdef MACH_BSD
418 __private_extern__
419 #endif
420 /*
421 * This routine can operate against any 32 bit segment_command structure to
422 * return the first 32 bit section immediately following that structure. If
423 * there are no sections associated with the segment_command structure, it
424 * returns NULL.
425 */
426 struct section *
427 firstsect(struct segment_command *sgp)
428 {
429 if (!sgp || sgp->nsects == 0)
430 return (struct section *)0;
431
432 return (struct section *)(sgp+1);
433 }
434
435 #ifdef MACH_BSD
436 __private_extern__
437 #endif
438 /*
439 * This routine can operate against any 32 bit segment_command structure and
440 * 32 bit section to return the next consecutive 32 bit section immediately
441 * following the 32 bit section provided. If there are no sections following
442 * the provided section, it returns NULL.
443 */
444 struct section *
445 nextsect(struct segment_command *sgp, struct section *sp)
446 {
447 struct section *fsp = firstsect(sgp);
448
449 if (((unsigned long)(sp - fsp) + 1) >= sgp->nsects)
450 return (struct section *)0;
451
452 return sp+1;
453 }
454
455 /*
456 * This routine can operate against any 32 bit mach header to return the
457 * first occurring 32 bit fvmfile_command section. If one is not present,
458 * it returns NULL.
459 */
460 static struct fvmfile_command *
461 fvmfilefromheader(struct mach_header *header)
462 {
463 struct fvmfile_command *fvp;
464 unsigned long i;
465
466 fvp = (struct fvmfile_command *)
467 ((char *)header + sizeof(struct mach_header));
468 for (i = 0; i < header->ncmds; i++){
469 if (fvp->cmd == LC_FVMFILE)
470 return fvp;
471 fvp = (struct fvmfile_command *)((char *)fvp + fvp->cmdsize);
472 }
473 return (struct fvmfile_command *)0;
474 }
475
476 /*
477 * Create a fake USER seg if a fvmfile_command is present.
478 *
479 * This routine operates against the currently executing kernel only
480 */
481 #ifdef MACH_BSD
482 __private_extern__
483 #endif
484 struct segment_command *
485 getfakefvmseg(void)
486 {
487 struct segment_command *sgp = getsegbyname("__USER");
488 struct fvmfile_command *fvp = fvmfilefromheader(&_mh_execute_header);
489 struct section *sp;
490
491 if (sgp)
492 return sgp;
493
494 if (!fvp)
495 return (struct segment_command *)0;
496
497 fvm_seg = &fvm_data.seg;
498 sgp = fvm_seg;
499 sp = &fvm_data.sect;
500
501 sgp->vmaddr = fvp->header_addr;
502 sgp->vmsize = getsizeofmacho((struct mach_header *)(sgp->vmaddr));
503
504 strlcpy(sp->sectname, fvp->name.ptr, sizeof(sp->sectname) /* 16 */);
505 sp->addr = sgp->vmaddr;
506 sp->size = sgp->vmsize;
507
508 #if DEBUG
509 printf("fake fvm seg __USER/\"%s\" at 0x%lx, size 0x%lx\n",
510 sp->sectname, sp->addr, sp->size);
511 #endif /*DEBUG*/
512 return sgp;
513 }
514
515 /*
516 * Figure out the size the size of the data associated with a
517 * loaded mach_header.
518 *
519 * This routine operates against the currently executing kernel only
520 */
521 static vm_offset_t
522 getsizeofmacho(struct mach_header *header)
523 {
524 struct segment_command *sgp;
525 vm_offset_t last_addr;
526
527 last_addr = 0;
528 for ( sgp = firstsegfromheader(header)
529 ; sgp
530 ; sgp = nextsegfromheader(header, sgp))
531 {
532 if (sgp->fileoff + sgp->filesize > last_addr)
533 last_addr = sgp->fileoff + sgp->filesize;
534 }
535
536 return last_addr;
537 }
538
539 #ifdef MACH_KDB
540 /*
541 * This routine returns the section command for the symbol table in the
542 * named segment for the mach_header pointer passed to it if it exist.
543 * Otherwise it returns zero.
544 */
545 struct symtab_command *
546 getsectcmdsymtabfromheader(
547 struct mach_header *mhp)
548 {
549 struct segment_command *sgp;
550 unsigned long i;
551
552 sgp = (struct segment_command *)
553 ((char *)mhp + sizeof(struct mach_header));
554 for(i = 0; i < mhp->ncmds; i++){
555 if(sgp->cmd == LC_SYMTAB)
556 return((struct symtab_command *)sgp);
557 sgp = (struct segment_command *)((char *)sgp + sgp->cmdsize);
558 }
559 return(NULL);
560 }
561
562 boolean_t getsymtab(struct mach_header *header,
563 vm_offset_t *symtab,
564 int *nsyms,
565 vm_offset_t *strtab,
566 vm_size_t *strtabsize)
567 {
568 struct segment_command *seglink_cmd;
569 struct symtab_command *symtab_cmd;
570
571 seglink_cmd = NULL;
572
573 if(header->magic != MH_MAGIC) { /* Check if this is a valid header format */
574 printf("Attempt to use invalid header (magic = %08lX) to find symbol table\n",
575 header->magic); /* Tell them what's wrong */
576 return (FALSE); /* Bye y'all... */
577 }
578
579 seglink_cmd = getsegbynamefromheader(header,"__LINKEDIT");
580 if (seglink_cmd == NULL) {
581 return(FALSE);
582 }
583
584 symtab_cmd = NULL;
585 symtab_cmd = getsectcmdsymtabfromheader(header);
586 if (symtab_cmd == NULL)
587 return(FALSE);
588
589 *nsyms = symtab_cmd->nsyms;
590 if(symtab_cmd->nsyms == 0) return (FALSE); /* No symbols */
591
592 *strtabsize = symtab_cmd->strsize;
593 if(symtab_cmd->strsize == 0) return (FALSE); /* Symbol length is 0 */
594
595 *symtab = seglink_cmd->vmaddr + symtab_cmd->symoff -
596 seglink_cmd->fileoff;
597
598 *strtab = seglink_cmd->vmaddr + symtab_cmd->stroff -
599 seglink_cmd->fileoff;
600
601 return(TRUE);
602 }
603 #endif
604
605 #else
606
607 void * getsegdatafromheader( struct mach_header *mhp, char *segname, int *size)
608 {
609 return 0;
610 }
611
612 #endif