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