xnu-344.21.74.tar.gz
[apple/xnu.git] / bsd / kern / mach_header.c
1 /*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
7 *
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * file.
14 *
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
22 *
23 * @APPLE_LICENSE_HEADER_END@
24 */
25 /*
26 * File: kern/mach_header.c
27 *
28 * Functions for accessing mach-o headers.
29 *
30 * HISTORY
31 * 27-MAR-97 Umesh Vaishampayan (umeshv@NeXT.com)
32 * Added getsegdatafromheader();
33 *
34 * 29-Jan-92 Mike DeMoney (mike@next.com)
35 * Made into machine independent form from machdep/m68k/mach_header.c.
36 * Ifdef'ed out most of this since I couldn't find any references.
37 */
38
39 #if !defined(KERNEL_PRELOAD)
40 #include <kern/mach_header.h>
41
42 extern struct mach_header _mh_execute_header;
43
44 struct section *getsectbynamefromheader(
45 struct mach_header *header,
46 char *seg_name,
47 char *sect_name);
48 struct segment_command *getsegbynamefromheader(
49 struct mach_header *header,
50 char *seg_name);
51
52 /*
53 * return the last address (first avail)
54 */
55 vm_offset_t getlastaddr(void)
56 {
57 struct segment_command *sgp;
58 vm_offset_t last_addr = 0;
59 struct mach_header *header = &_mh_execute_header;
60 int i;
61
62 sgp = (struct segment_command *)
63 ((char *)header + sizeof(struct mach_header));
64 for (i = 0; i < header->ncmds; i++){
65 if ( sgp->cmd == LC_SEGMENT) {
66 if (sgp->vmaddr + sgp->vmsize > last_addr)
67 last_addr = sgp->vmaddr + sgp->vmsize;
68 }
69 sgp = (struct segment_command *)((char *)sgp + sgp->cmdsize);
70 }
71 return last_addr;
72 }
73
74 #if FIXME /* [ */
75 struct mach_header **
76 getmachheaders(void)
77 {
78 extern struct mach_header _mh_execute_header;
79 struct mach_header **tl;
80 tl = (struct mach_header **)malloc(2*sizeof(struct mach_header *));
81 tl[0] = &_mh_execute_header;
82 tl[1] = (struct mach_header *)0;
83 return tl;
84 }
85 #endif /* FIXME ] */
86
87 /*
88 * This routine returns the a pointer to the data for the named section in the
89 * named segment if it exist in the mach header passed to it. Also it returns
90 * the size of the section data indirectly through the pointer size. Otherwise
91 * it returns zero for the pointer and the size.
92 */
93 void *
94 getsectdatafromheader(
95 struct mach_header *mhp,
96 char *segname,
97 char *sectname,
98 int *size)
99 {
100 const struct section *sp;
101 void *result;
102
103 sp = getsectbynamefromheader(mhp, segname, sectname);
104 if(sp == (struct section *)0){
105 *size = 0;
106 return((char *)0);
107 }
108 *size = sp->size;
109 result = (void *)sp->addr;
110 return result;
111 }
112
113 /*
114 * This routine returns the a pointer to the data for the named segment
115 * if it exist in the mach header passed to it. Also it returns
116 * the size of the segment data indirectly through the pointer size.
117 * Otherwise it returns zero for the pointer and the size.
118 */
119 void *
120 getsegdatafromheader(
121 struct mach_header *mhp,
122 char *segname,
123 int *size)
124 {
125 const struct segment_command *sc;
126 void *result;
127
128 sc = getsegbynamefromheader(mhp, segname);
129 if(sc == (struct segment_command *)0){
130 *size = 0;
131 return((char *)0);
132 }
133 *size = sc->vmsize;
134 result = (void *)sc->vmaddr;
135 return result;
136 }
137
138 /*
139 * This routine returns the section structure for the named section in the
140 * named segment for the mach_header pointer passed to it if it exist.
141 * Otherwise it returns zero.
142 */
143 struct section *
144 getsectbynamefromheader(
145 struct mach_header *mhp,
146 char *segname,
147 char *sectname)
148 {
149 struct segment_command *sgp;
150 struct section *sp;
151 long i, j;
152
153 sgp = (struct segment_command *)
154 ((char *)mhp + sizeof(struct mach_header));
155 for(i = 0; i < mhp->ncmds; i++){
156 if(sgp->cmd == LC_SEGMENT)
157 if(strncmp(sgp->segname, segname, sizeof(sgp->segname)) == 0 ||
158 mhp->filetype == MH_OBJECT){
159 sp = (struct section *)((char *)sgp +
160 sizeof(struct segment_command));
161 for(j = 0; j < sgp->nsects; j++){
162 if(strncmp(sp->sectname, sectname,
163 sizeof(sp->sectname)) == 0 &&
164 strncmp(sp->segname, segname,
165 sizeof(sp->segname)) == 0)
166 return(sp);
167 sp = (struct section *)((char *)sp +
168 sizeof(struct section));
169 }
170 }
171 sgp = (struct segment_command *)((char *)sgp + sgp->cmdsize);
172 }
173 return((struct section *)0);
174 }
175
176 struct segment_command *getsegbynamefromheader(
177 struct mach_header *header,
178 char *seg_name)
179 {
180 struct segment_command *sgp;
181 int i;
182
183 sgp = (struct segment_command *)
184 ((char *)header + sizeof(struct mach_header));
185 for (i = 0; i < header->ncmds; i++){
186 if ( sgp->cmd == LC_SEGMENT
187 && !strncmp(sgp->segname, seg_name, sizeof(sgp->segname)))
188 return sgp;
189 sgp = (struct segment_command *)((char *)sgp + sgp->cmdsize);
190 }
191 return (struct segment_command *)0;
192 }
193
194
195 /*
196 * For now at least, all the rest of this seems unused.
197 * NOTE: The constant in here for segment alignment is machine-dependent,
198 * so if you include this, define a machine dependent constant for it's
199 * value.
200 */
201 static struct {
202 struct segment_command seg;
203 struct section sect;
204 } fvm_data = {
205 {
206 LC_SEGMENT, // cmd
207 sizeof(fvm_data), // cmdsize
208 "__USER", // segname
209 0, // vmaddr
210 0, // vmsize
211 0, // fileoff
212 0, // filesize
213 VM_PROT_READ, // maxprot
214 VM_PROT_READ, // initprot,
215 1, // nsects
216 0 // flags
217 },
218 {
219 "", // sectname
220 "__USER", // segname
221 0, // addr
222 0, // size
223 0, // offset
224 4, // align
225 0, // reloff
226 0, // nreloc
227 0 // flags
228 }
229 };
230
231 struct segment_command *fvm_seg;
232
233 static struct fvmfile_command *fvmfilefromheader(struct mach_header *header);
234 static vm_offset_t getsizeofmacho(struct mach_header *header);
235
236 /*
237 * Return the first segment_command in the header.
238 */
239 struct segment_command *firstseg(void)
240 {
241 return firstsegfromheader(&_mh_execute_header);
242 }
243
244 struct segment_command *firstsegfromheader(struct mach_header *header)
245 {
246 struct segment_command *sgp;
247 int i;
248
249 sgp = (struct segment_command *)
250 ((char *)header + sizeof(struct mach_header));
251 for (i = 0; i < header->ncmds; i++){
252 if (sgp->cmd == LC_SEGMENT)
253 return sgp;
254 sgp = (struct segment_command *)((char *)sgp + sgp->cmdsize);
255 }
256 return (struct segment_command *)0;
257 }
258
259 struct segment_command *nextseg(struct segment_command *sgp)
260 {
261 struct segment_command *this;
262
263 this = nextsegfromheader(&_mh_execute_header, sgp);
264
265 /*
266 * For the kernel's header add on the faked segment for the
267 * USER boot code identified by a FVMFILE_COMMAND in the mach header.
268 */
269 if (!this && sgp != fvm_seg)
270 this = fvm_seg;
271
272 return this;
273 }
274
275 struct segment_command *nextsegfromheader(
276 struct mach_header *header,
277 struct segment_command *seg)
278 {
279 struct segment_command *sgp;
280 int 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 == seg)
286 break;
287 sgp = (struct segment_command *)((char *)sgp + sgp->cmdsize);
288 }
289
290 if (i == header->ncmds)
291 return (struct segment_command *)0;
292
293 sgp = (struct segment_command *)((char *)sgp + sgp->cmdsize);
294 for (; i < header->ncmds; i++) {
295 if (sgp->cmd == LC_SEGMENT)
296 return sgp;
297 sgp = (struct segment_command *)((char *)sgp + sgp->cmdsize);
298 }
299
300 return (struct segment_command *)0;
301 }
302
303
304 /*
305 * Return the address of the named Mach-O segment, or NULL.
306 */
307 struct segment_command *getsegbyname(char *seg_name)
308 {
309 struct segment_command *this;
310
311 this = getsegbynamefromheader(&_mh_execute_header, seg_name);
312
313 /*
314 * For the kernel's header add on the faked segment for the
315 * USER boot code identified by a FVMFILE_COMMAND in the mach header.
316 */
317 if (!this && strcmp(seg_name, fvm_seg->segname) == 0)
318 this = fvm_seg;
319
320 return this;
321 }
322
323 /*
324 * This routine returns the a pointer the section structure of the named
325 * section in the named segment if it exist in the mach executable it is
326 * linked into. Otherwise it returns zero.
327 */
328 struct section *
329 getsectbyname(
330 char *segname,
331 char *sectname)
332 {
333 return(getsectbynamefromheader(
334 (struct mach_header *)&_mh_execute_header, segname, sectname));
335 }
336
337 struct section *firstsect(struct segment_command *sgp)
338 {
339 struct section *sp;
340
341 if (!sgp || sgp->nsects == 0)
342 return (struct section *)0;
343
344 return (struct section *)(sgp+1);
345 }
346
347 struct section *nextsect(struct segment_command *sgp, struct section *sp)
348 {
349 struct section *fsp = firstsect(sgp);
350
351 if (sp - fsp >= sgp->nsects-1)
352 return (struct section *)0;
353
354 return sp+1;
355 }
356
357 static struct fvmfile_command *fvmfilefromheader(struct mach_header *header)
358 {
359 struct fvmfile_command *fvp;
360 int i;
361
362 fvp = (struct fvmfile_command *)
363 ((char *)header + sizeof(struct mach_header));
364 for (i = 0; i < header->ncmds; i++){
365 if (fvp->cmd == LC_FVMFILE)
366 return fvp;
367 fvp = (struct fvmfile_command *)((char *)fvp + fvp->cmdsize);
368 }
369 return (struct fvmfile_command *)0;
370 }
371
372 /*
373 * Create a fake USER seg if a fvmfile_command is present.
374 */
375 struct segment_command *getfakefvmseg(void)
376 {
377 struct segment_command *sgp = getsegbyname("__USER");
378 struct fvmfile_command *fvp = fvmfilefromheader(&_mh_execute_header);
379 struct section *sp;
380
381 if (sgp)
382 return sgp;
383
384 if (!fvp)
385 return (struct segment_command *)0;
386
387 fvm_seg = &fvm_data.seg;
388 sgp = fvm_seg;
389 sp = &fvm_data.sect;
390
391 sgp->vmaddr = fvp->header_addr;
392 sgp->vmsize = getsizeofmacho((struct mach_header *)(sgp->vmaddr));
393
394 strcpy(sp->sectname, fvp->name.ptr);
395 sp->addr = sgp->vmaddr;
396 sp->size = sgp->vmsize;
397
398 #if DEBUG
399 printf("fake fvm seg __USER/\"%s\" at 0x%x, size 0x%x\n",
400 sp->sectname, sp->addr, sp->size);
401 #endif DEBUG
402 }
403
404 /*
405 * Figure out the size the size of the data associated with a
406 * loaded mach_header.
407 */
408 static vm_offset_t getsizeofmacho(struct mach_header *header)
409 {
410 struct segment_command *sgp;
411 struct section *sp;
412 vm_offset_t last_addr;
413
414 last_addr = 0;
415 for ( sgp = firstsegfromheader(header)
416 ; sgp
417 ; sgp = nextsegfromheader(header, sgp))
418 {
419 if (sgp->fileoff + sgp->filesize > last_addr)
420 last_addr = sgp->fileoff + sgp->filesize;
421 }
422
423 return last_addr;
424 }
425 #endif /* !defined(KERNEL_PRELOAD) */