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