]> git.saurik.com Git - apple/xnu.git/blame - osfmk/mach-o/mach_header.c
xnu-1228.tar.gz
[apple/xnu.git] / osfmk / mach-o / mach_header.c
CommitLineData
1c79356b 1/*
2d21ac55 2 * Copyright (c) 2000-2006 Apple Computer, Inc. All rights reserved.
1c79356b 3 *
2d21ac55 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
1c79356b 5 *
2d21ac55
A
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.
8f6c56a5 14 *
2d21ac55
A
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
8f6c56a5
A
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
2d21ac55
A
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.
8f6c56a5 25 *
2d21ac55 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
1c79356b
A
27 */
28/*
29 * File: kern/mach_header.c
30 *
31 * Functions for accessing mach-o headers.
32 *
91447636
A
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 *
1c79356b
A
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>
91447636 53#include <string.h> // from libsa
1c79356b
A
54
55#ifdef __MACHO__
56
57extern struct mach_header _mh_execute_header;
58
1c79356b
A
59/*
60 * return the last address (first avail)
91447636
A
61 *
62 * This routine operates against the currently executing kernel only
1c79356b
A
63 */
64#ifdef MACH_BSD
65__private_extern__
66#endif
91447636
A
67vm_offset_t
68getlastaddr(void)
1c79356b
A
69{
70 struct segment_command *sgp;
71 vm_offset_t last_addr = 0;
72 struct mach_header *header = &_mh_execute_header;
91447636 73 unsigned long i;
1c79356b
A
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
91447636
A
90/*
91 * This routine operates against the currently executing kernel only
92 */
1c79356b
A
93struct mach_header **
94getmachheaders(void)
95{
1c79356b
A
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.
91447636
A
111 *
112 * This routine can operate against any 32 bit mach header.
1c79356b
A
113 */
114#ifdef MACH_BSD
115__private_extern__
116#endif
117void *
118getsectdatafromheader(
119 struct mach_header *mhp,
91447636
A
120 const char *segname,
121 const char *sectname,
1c79356b
A
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
146void *
147getsegdatafromheader(
148 struct mach_header *mhp,
91447636 149 const char *segname,
1c79356b
A
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.
91447636
A
169 *
170 * This routine can operate against any 32 bit mach header.
1c79356b
A
171 */
172#ifdef MACH_BSD
173__private_extern__
174#endif
175struct section *
176getsectbynamefromheader(
177 struct mach_header *mhp,
91447636
A
178 const char *segname,
179 const char *sectname)
1c79356b
A
180{
181 struct segment_command *sgp;
182 struct section *sp;
91447636 183 unsigned long i, j;
1c79356b
A
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
91447636
A
211/*
212 * This routine can operate against any 32 bit mach header.
213 */
214struct segment_command *
215getsegbynamefromheader(
1c79356b 216 struct mach_header *header,
91447636 217 const char *seg_name)
1c79356b
A
218{
219 struct segment_command *sgp;
91447636 220 unsigned long i;
1c79356b
A
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 */
240static 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
91447636
A
266 0, // flags
267 0, // reserved1
268 0 // reserved2
1c79356b
A
269 }
270};
271
272#ifdef MACH_BSD
273static
274#endif
275struct segment_command *fvm_seg;
276
277static struct fvmfile_command *fvmfilefromheader(struct mach_header *header);
278static 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
91447636
A
286struct segment_command *
287firstseg(void)
1c79356b
A
288{
289 return firstsegfromheader(&_mh_execute_header);
290}
291
292#ifdef MACH_BSD
293__private_extern__
294#endif
91447636
A
295struct segment_command *
296firstsegfromheader(struct mach_header *header)
1c79356b
A
297{
298 struct segment_command *sgp;
91447636 299 unsigned long i;
1c79356b
A
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
91447636
A
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 */
320struct segment_command *
321nextseg(struct segment_command *sgp)
1c79356b
A
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
91447636
A
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 */
345struct segment_command *
346nextsegfromheader(
1c79356b
A
347 struct mach_header *header,
348 struct segment_command *seg)
349{
350 struct segment_command *sgp;
91447636 351 unsigned long i;
1c79356b
A
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/*
91447636
A
376 * Return the address of the named Mach-O segment from the currently
377 * executing 32 bit kernel, or NULL.
1c79356b
A
378 */
379#ifdef MACH_BSD
380__private_extern__
381#endif
91447636
A
382struct segment_command *
383getsegbyname(const char *seg_name)
1c79356b
A
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 */
2d21ac55
A
393 if (!this && strncmp(seg_name, fvm_seg->segname,
394 sizeof(fvm_seg->segname)) == 0)
1c79356b
A
395 this = fvm_seg;
396
397 return this;
398}
399
400/*
401 * This routine returns the a pointer the section structure of the named
91447636
A
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.
1c79356b
A
404 */
405#ifdef MACH_BSD
406__private_extern__
407#endif
408struct section *
409getsectbyname(
91447636
A
410 const char *segname,
411 const char *sectname)
1c79356b
A
412{
413 return(getsectbynamefromheader(
414 (struct mach_header *)&_mh_execute_header, segname, sectname));
415}
416
417#ifdef MACH_BSD
418__private_extern__
419#endif
91447636
A
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 */
426struct section *
427firstsect(struct segment_command *sgp)
1c79356b 428{
1c79356b
A
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
91447636
A
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 */
444struct section *
445nextsect(struct segment_command *sgp, struct section *sp)
1c79356b
A
446{
447 struct section *fsp = firstsect(sgp);
448
91447636 449 if (((unsigned long)(sp - fsp) + 1) >= sgp->nsects)
1c79356b
A
450 return (struct section *)0;
451
452 return sp+1;
453}
454
91447636
A
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 */
460static struct fvmfile_command *
461fvmfilefromheader(struct mach_header *header)
1c79356b
A
462{
463 struct fvmfile_command *fvp;
91447636 464 unsigned long i;
1c79356b
A
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.
91447636
A
478 *
479 * This routine operates against the currently executing kernel only
1c79356b
A
480 */
481#ifdef MACH_BSD
482__private_extern__
483#endif
91447636
A
484struct segment_command *
485getfakefvmseg(void)
1c79356b
A
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
2d21ac55 504 strlcpy(sp->sectname, fvp->name.ptr, sizeof(sp->sectname) /* 16 */);
1c79356b
A
505 sp->addr = sgp->vmaddr;
506 sp->size = sgp->vmsize;
507
508#if DEBUG
2d21ac55 509 printf("fake fvm seg __USER/\"%s\" at 0x%lx, size 0x%lx\n",
1c79356b
A
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.
91447636
A
518 *
519 * This routine operates against the currently executing kernel only
1c79356b 520 */
91447636
A
521static vm_offset_t
522getsizeofmacho(struct mach_header *header)
1c79356b
A
523{
524 struct segment_command *sgp;
1c79356b
A
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 */
545struct symtab_command *
546getsectcmdsymtabfromheader(
547 struct mach_header *mhp)
548{
549 struct segment_command *sgp;
91447636 550 unsigned long i;
1c79356b
A
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
562boolean_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 */
2d21ac55 574 printf("Attempt to use invalid header (magic = %08lX) to find symbol table\n",
1c79356b
A
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
607void * getsegdatafromheader( struct mach_header *mhp, char *segname, int *size)
608{
609 return 0;
610}
611
612#endif