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