]> git.saurik.com Git - apple/xnu.git/blob - bsd/kern/mach_header.c
xnu-1228.7.58.tar.gz
[apple/xnu.git] / bsd / kern / 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 #if !defined(KERNEL_PRELOAD)
51 #include <kern/mach_header.h>
52 #include <string.h> // from libsa
53 #if DEBUG
54 #include <libkern/libkern.h>
55 #endif
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 vm_offset_t
65 getlastaddr(void)
66 {
67 struct segment_command *sgp;
68 vm_offset_t last_addr = 0;
69 struct mach_header *header = &_mh_execute_header;
70 unsigned long i;
71
72 sgp = (struct segment_command *)
73 ((char *)header + sizeof(struct mach_header));
74 for (i = 0; i < header->ncmds; i++){
75 if ( sgp->cmd == LC_SEGMENT) {
76 if (sgp->vmaddr + sgp->vmsize > last_addr)
77 last_addr = sgp->vmaddr + sgp->vmsize;
78 }
79 sgp = (struct segment_command *)((char *)sgp + sgp->cmdsize);
80 }
81 return last_addr;
82 }
83
84 #if FIXME /* [ */
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 tl = (struct mach_header **)malloc(2*sizeof(struct mach_header *));
93 tl[0] = &_mh_execute_header;
94 tl[1] = (struct mach_header *)0;
95 return tl;
96 }
97 #endif /* FIXME ] */
98
99 /*
100 * This routine returns the a pointer to the data for the named section in the
101 * named segment if it exist in the mach header passed to it. Also it returns
102 * the size of the section data indirectly through the pointer size. Otherwise
103 * it returns zero for the pointer and the size.
104 *
105 * This routine can operate against any 32 bit mach header.
106 */
107 void *
108 getsectdatafromheader(
109 struct mach_header *mhp,
110 const char *segname,
111 const char *sectname,
112 int *size)
113 {
114 const struct section *sp;
115 void *result;
116
117 sp = getsectbynamefromheader(mhp, segname, sectname);
118 if(sp == (struct section *)0){
119 *size = 0;
120 return((char *)0);
121 }
122 *size = sp->size;
123 result = (void *)sp->addr;
124 return result;
125 }
126
127 /*
128 * This routine returns the a pointer to the data for the named segment
129 * if it exist in the mach header passed to it. Also it returns
130 * the size of the segment data indirectly through the pointer size.
131 * Otherwise it returns zero for the pointer and the size.
132 *
133 * This routine can operate against any 32 bit mach header.
134 */
135 void *
136 getsegdatafromheader(
137 struct mach_header *mhp,
138 const char *segname,
139 int *size)
140 {
141 const struct segment_command *sc;
142 void *result;
143
144 sc = getsegbynamefromheader(mhp, segname);
145 if(sc == (struct segment_command *)0){
146 *size = 0;
147 return((char *)0);
148 }
149 *size = sc->vmsize;
150 result = (void *)sc->vmaddr;
151 return result;
152 }
153
154 /*
155 * This routine returns the section structure for the named section in the
156 * named segment for the mach_header pointer passed to it if it exist.
157 * Otherwise it returns zero.
158 *
159 * This routine can operate against any 32 bit mach header.
160 */
161 struct section *
162 getsectbynamefromheader(
163 struct mach_header *mhp,
164 const char *segname,
165 const char *sectname)
166 {
167 struct segment_command *sgp;
168 struct section *sp;
169 unsigned long i, j;
170
171 sgp = (struct segment_command *)
172 ((char *)mhp + sizeof(struct mach_header));
173 for(i = 0; i < mhp->ncmds; i++){
174 if(sgp->cmd == LC_SEGMENT)
175 if(strncmp(sgp->segname, segname, sizeof(sgp->segname)) == 0 ||
176 mhp->filetype == MH_OBJECT){
177 sp = (struct section *)((char *)sgp +
178 sizeof(struct segment_command));
179 for(j = 0; j < sgp->nsects; j++){
180 if(strncmp(sp->sectname, sectname,
181 sizeof(sp->sectname)) == 0 &&
182 strncmp(sp->segname, segname,
183 sizeof(sp->segname)) == 0)
184 return(sp);
185 sp = (struct section *)((char *)sp +
186 sizeof(struct section));
187 }
188 }
189 sgp = (struct segment_command *)((char *)sgp + sgp->cmdsize);
190 }
191 return((struct section *)0);
192 }
193
194 /*
195 * This routine can operate against any 32 bit mach header.
196 */
197 struct segment_command *
198 getsegbynamefromheader(
199 struct mach_header *header,
200 const char *seg_name)
201 {
202 struct segment_command *sgp;
203 unsigned long i;
204
205 sgp = (struct segment_command *)
206 ((char *)header + sizeof(struct mach_header));
207 for (i = 0; i < header->ncmds; i++){
208 if ( sgp->cmd == LC_SEGMENT
209 && !strncmp(sgp->segname, seg_name, sizeof(sgp->segname)))
210 return sgp;
211 sgp = (struct segment_command *)((char *)sgp + sgp->cmdsize);
212 }
213 return (struct segment_command *)0;
214 }
215
216
217 /*
218 * For now at least, all the rest of this seems unused.
219 * NOTE: The constant in here for segment alignment is machine-dependent,
220 * so if you include this, define a machine dependent constant for it's
221 * value.
222 */
223 static struct {
224 struct segment_command seg;
225 struct section sect;
226 } fvm_data = {
227 {
228 LC_SEGMENT, // cmd
229 sizeof(fvm_data), // cmdsize
230 "__USER", // segname
231 0, // vmaddr
232 0, // vmsize
233 0, // fileoff
234 0, // filesize
235 VM_PROT_READ, // maxprot
236 VM_PROT_READ, // initprot,
237 1, // nsects
238 0 // flags
239 },
240 {
241 "", // sectname
242 "__USER", // segname
243 0, // addr
244 0, // size
245 0, // offset
246 4, // align
247 0, // reloff
248 0, // nreloc
249 0, // flags
250 0, // reserved1
251 0 // reserved2
252 }
253 };
254
255 struct segment_command *fvm_seg;
256
257 static struct fvmfile_command *fvmfilefromheader(struct mach_header *header);
258 static vm_offset_t getsizeofmacho(struct mach_header *header);
259
260 /*
261 * Return the first segment_command in the header.
262 *
263 * This routine operates against the currently executing kernel only
264 */
265 struct segment_command *
266 firstseg(void)
267 {
268 return firstsegfromheader(&_mh_execute_header);
269 }
270
271 /*
272 * This routine can operate against any 32 bit mach header, and returns a
273 * pointer to a 32 bit segment_command structure from the file prefixed by
274 * the header it is passed as its argument.
275 */
276 struct segment_command *
277 firstsegfromheader(struct mach_header *header)
278 {
279 struct segment_command *sgp;
280 unsigned long i;
281
282 sgp = (struct segment_command *)
283 ((char *)header + sizeof(struct mach_header));
284 for (i = 0; i < header->ncmds; i++){
285 if (sgp->cmd == LC_SEGMENT)
286 return sgp;
287 sgp = (struct segment_command *)((char *)sgp + sgp->cmdsize);
288 }
289 return (struct segment_command *)0;
290 }
291
292 /*
293 * This routine operates against a 32 bit mach segment_command structure
294 * pointer from the currently executing kernel only, to obtain the
295 * sequentially next segment_command structure in the currently executing
296 * kernel
297 */
298 struct segment_command *
299 nextseg(struct segment_command *sgp)
300 {
301 struct segment_command *this;
302
303 this = nextsegfromheader(&_mh_execute_header, sgp);
304
305 /*
306 * For the kernel's header add on the faked segment for the
307 * USER boot code identified by a FVMFILE_COMMAND in the mach header.
308 */
309 if (!this && sgp != fvm_seg)
310 this = fvm_seg;
311
312 return this;
313 }
314
315 /*
316 * This routine operates against any 32 bit mach segment_command structure
317 * pointer and the provided 32 bit header, to obtain the sequentially next
318 * segment_command structure in that header.
319 */
320 struct segment_command *
321 nextsegfromheader(
322 struct mach_header *header,
323 struct segment_command *seg)
324 {
325 struct segment_command *sgp;
326 unsigned long i;
327
328 sgp = (struct segment_command *)
329 ((char *)header + sizeof(struct mach_header));
330 for (i = 0; i < header->ncmds; i++) {
331 if (sgp == seg)
332 break;
333 sgp = (struct segment_command *)((char *)sgp + sgp->cmdsize);
334 }
335
336 if (i == header->ncmds)
337 return (struct segment_command *)0;
338
339 sgp = (struct segment_command *)((char *)sgp + sgp->cmdsize);
340 for (; i < header->ncmds; i++) {
341 if (sgp->cmd == LC_SEGMENT)
342 return sgp;
343 sgp = (struct segment_command *)((char *)sgp + sgp->cmdsize);
344 }
345
346 return (struct segment_command *)0;
347 }
348
349
350 /*
351 * Return the address of the named Mach-O segment from the currently
352 * executing 32 bit kernel, or NULL.
353 */
354 struct segment_command *
355 getsegbyname(const char *seg_name)
356 {
357 struct segment_command *this;
358
359 this = getsegbynamefromheader(&_mh_execute_header, seg_name);
360
361 /*
362 * For the kernel's header add on the faked segment for the
363 * USER boot code identified by a FVMFILE_COMMAND in the mach header.
364 */
365 if (!this && strncmp(seg_name, fvm_seg->segname,
366 sizeof(fvm_seg->segname)) == 0)
367 this = fvm_seg;
368
369 return this;
370 }
371
372 /*
373 * This routine returns the a pointer the section structure of the named
374 * section in the named segment if it exists in the currently executing
375 * kernel, which it is presumed to be linked into. Otherwise it returns NULL.
376 */
377 struct section *
378 getsectbyname(
379 const char *segname,
380 const char *sectname)
381 {
382 return(getsectbynamefromheader(
383 (struct mach_header *)&_mh_execute_header, segname, sectname));
384 }
385
386 /*
387 * This routine can operate against any 32 bit segment_command structure to
388 * return the first 32 bit section immediately following that structure. If
389 * there are no sections associated with the segment_command structure, it
390 * returns NULL.
391 */
392 struct section *
393 firstsect(struct segment_command *sgp)
394 {
395 if (!sgp || sgp->nsects == 0)
396 return (struct section *)0;
397
398 return (struct section *)(sgp+1);
399 }
400
401 /*
402 * This routine can operate against any 32 bit segment_command structure and
403 * 32 bit section to return the next consecutive 32 bit section immediately
404 * following the 32 bit section provided. If there are no sections following
405 * the provided section, it returns NULL.
406 */
407 struct section *
408 nextsect(struct segment_command *sgp, struct section *sp)
409 {
410 struct section *fsp = firstsect(sgp);
411
412 if (((unsigned long)(sp - fsp) + 1) >= sgp->nsects)
413 return (struct section *)0;
414
415 return sp+1;
416 }
417
418 /*
419 * This routine can operate against any 32 bit mach header to return the
420 * first occurring 32 bit fvmfile_command section. If one is not present,
421 * it returns NULL.
422 */
423 static struct fvmfile_command *
424 fvmfilefromheader(struct mach_header *header)
425 {
426 struct fvmfile_command *fvp;
427 unsigned long i;
428
429 fvp = (struct fvmfile_command *)
430 ((char *)header + sizeof(struct mach_header));
431 for (i = 0; i < header->ncmds; i++){
432 if (fvp->cmd == LC_FVMFILE)
433 return fvp;
434 fvp = (struct fvmfile_command *)((char *)fvp + fvp->cmdsize);
435 }
436 return (struct fvmfile_command *)0;
437 }
438
439 /*
440 * Create a fake USER seg if a fvmfile_command is present.
441 *
442 * This routine operates against the currently executing kernel only
443 */
444 struct segment_command *
445 getfakefvmseg(void)
446 {
447 struct segment_command *sgp = getsegbyname("__USER");
448 struct fvmfile_command *fvp = fvmfilefromheader(&_mh_execute_header);
449 struct section *sp;
450
451 if (sgp)
452 return sgp;
453
454 if (!fvp)
455 return (struct segment_command *)0;
456
457 fvm_seg = &fvm_data.seg;
458 sgp = fvm_seg;
459 sp = &fvm_data.sect;
460
461 sgp->vmaddr = fvp->header_addr;
462 sgp->vmsize = getsizeofmacho((struct mach_header *)(sgp->vmaddr));
463
464 strlcpy(sp->sectname, fvp->name.ptr, sizeof(sp->sectname));
465 sp->addr = sgp->vmaddr;
466 sp->size = sgp->vmsize;
467
468 #if DEBUG
469 printf("fake fvm seg __USER/\"%s\" at 0x%x, size 0x%x\n",
470 sp->sectname, sp->addr, sp->size);
471 #endif /* DEBUG */
472
473 return sgp;
474 }
475
476 /*
477 * Figure out the size the size of the data associated with a
478 * loaded mach_header.
479 *
480 * This routine can operate against any 32 bit mach header.
481 */
482 static vm_offset_t
483 getsizeofmacho(struct mach_header *header)
484 {
485 struct segment_command *sgp;
486 vm_offset_t last_addr;
487
488 last_addr = 0;
489 for ( sgp = firstsegfromheader(header)
490 ; sgp
491 ; sgp = nextsegfromheader(header, sgp))
492 {
493 if (sgp->fileoff + sgp->filesize > last_addr)
494 last_addr = sgp->fileoff + sgp->filesize;
495 }
496
497 return last_addr;
498 }
499 #endif /* !defined(KERNEL_PRELOAD) */