]> git.saurik.com Git - apple/xnu.git/blame - bsd/kern/mach_header.c
xnu-1228.7.58.tar.gz
[apple/xnu.git] / bsd / kern / mach_header.c
CommitLineData
1c79356b 1/*
2d21ac55 2 * Copyright (c) 2000-2006 Apple Computer, Inc. All rights reserved.
5d5c5d0d 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#if !defined(KERNEL_PRELOAD)
51#include <kern/mach_header.h>
91447636 52#include <string.h> // from libsa
2d21ac55
A
53#if DEBUG
54#include <libkern/libkern.h>
55#endif
1c79356b
A
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 63 */
91447636
A
64vm_offset_t
65getlastaddr(void)
1c79356b
A
66{
67 struct segment_command *sgp;
68 vm_offset_t last_addr = 0;
69 struct mach_header *header = &_mh_execute_header;
91447636 70 unsigned long i;
1c79356b
A
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 /* [ */
91447636
A
85/*
86 * This routine operates against the currently executing kernel only
87 */
1c79356b
A
88struct mach_header **
89getmachheaders(void)
90{
1c79356b
A
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.
91447636
A
104 *
105 * This routine can operate against any 32 bit mach header.
1c79356b
A
106 */
107void *
108getsectdatafromheader(
109 struct mach_header *mhp,
91447636
A
110 const char *segname,
111 const char *sectname,
1c79356b
A
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.
91447636
A
132 *
133 * This routine can operate against any 32 bit mach header.
1c79356b
A
134 */
135void *
136getsegdatafromheader(
91447636
A
137 struct mach_header *mhp,
138 const char *segname,
1c79356b
A
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.
91447636
A
158 *
159 * This routine can operate against any 32 bit mach header.
1c79356b
A
160 */
161struct section *
162getsectbynamefromheader(
163 struct mach_header *mhp,
91447636
A
164 const char *segname,
165 const char *sectname)
1c79356b
A
166{
167 struct segment_command *sgp;
168 struct section *sp;
91447636 169 unsigned long i, j;
1c79356b
A
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
91447636
A
194/*
195 * This routine can operate against any 32 bit mach header.
196 */
197struct segment_command *
198getsegbynamefromheader(
1c79356b 199 struct mach_header *header,
91447636 200 const char *seg_name)
1c79356b
A
201{
202 struct segment_command *sgp;
91447636 203 unsigned long i;
1c79356b
A
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 */
223static 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
91447636
A
249 0, // flags
250 0, // reserved1
251 0 // reserved2
1c79356b
A
252 }
253};
254
255struct segment_command *fvm_seg;
256
257static struct fvmfile_command *fvmfilefromheader(struct mach_header *header);
258static vm_offset_t getsizeofmacho(struct mach_header *header);
259
260/*
261 * Return the first segment_command in the header.
91447636
A
262 *
263 * This routine operates against the currently executing kernel only
1c79356b 264 */
91447636
A
265struct segment_command *
266firstseg(void)
1c79356b
A
267{
268 return firstsegfromheader(&_mh_execute_header);
269}
270
91447636
A
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 */
276struct segment_command *
277firstsegfromheader(struct mach_header *header)
1c79356b
A
278{
279 struct segment_command *sgp;
91447636 280 unsigned long i;
1c79356b
A
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
91447636
A
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 */
298struct segment_command *
299nextseg(struct segment_command *sgp)
1c79356b
A
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
91447636
A
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 */
320struct segment_command *
321nextsegfromheader(
1c79356b
A
322 struct mach_header *header,
323 struct segment_command *seg)
324{
325 struct segment_command *sgp;
91447636 326 unsigned long i;
1c79356b
A
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/*
91447636
A
351 * Return the address of the named Mach-O segment from the currently
352 * executing 32 bit kernel, or NULL.
1c79356b 353 */
91447636
A
354struct segment_command *
355getsegbyname(const char *seg_name)
1c79356b
A
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 */
2d21ac55
A
365 if (!this && strncmp(seg_name, fvm_seg->segname,
366 sizeof(fvm_seg->segname)) == 0)
1c79356b
A
367 this = fvm_seg;
368
369 return this;
370}
371
372/*
373 * This routine returns the a pointer the section structure of the named
91447636
A
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.
1c79356b
A
376 */
377struct section *
378getsectbyname(
91447636
A
379 const char *segname,
380 const char *sectname)
1c79356b
A
381{
382 return(getsectbynamefromheader(
383 (struct mach_header *)&_mh_execute_header, segname, sectname));
384}
385
91447636
A
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 */
392struct section *
393firstsect(struct segment_command *sgp)
1c79356b 394{
1c79356b
A
395 if (!sgp || sgp->nsects == 0)
396 return (struct section *)0;
397
398 return (struct section *)(sgp+1);
399}
400
91447636
A
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 */
407struct section *
408nextsect(struct segment_command *sgp, struct section *sp)
1c79356b
A
409{
410 struct section *fsp = firstsect(sgp);
411
91447636 412 if (((unsigned long)(sp - fsp) + 1) >= sgp->nsects)
1c79356b
A
413 return (struct section *)0;
414
415 return sp+1;
416}
417
91447636
A
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 */
423static struct fvmfile_command *
424fvmfilefromheader(struct mach_header *header)
1c79356b
A
425{
426 struct fvmfile_command *fvp;
91447636 427 unsigned long i;
1c79356b
A
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.
91447636
A
441 *
442 * This routine operates against the currently executing kernel only
1c79356b 443 */
91447636
A
444struct segment_command *
445getfakefvmseg(void)
1c79356b
A
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
2d21ac55 464 strlcpy(sp->sectname, fvp->name.ptr, sizeof(sp->sectname));
1c79356b
A
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);
55e303ae 471#endif /* DEBUG */
91447636
A
472
473 return sgp;
1c79356b
A
474}
475
476/*
477 * Figure out the size the size of the data associated with a
478 * loaded mach_header.
91447636
A
479 *
480 * This routine can operate against any 32 bit mach header.
1c79356b 481 */
91447636
A
482static vm_offset_t
483getsizeofmacho(struct mach_header *header)
1c79356b
A
484{
485 struct segment_command *sgp;
1c79356b
A
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) */