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