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
57extern 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 */
64vm_offset_t
65getlastaddr(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 */
88struct mach_header **
89getmachheaders(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 */
107void *
108getsectdatafromheader(
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 */
135void *
136getsegdatafromheader(
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 */
161struct section *
162getsectbynamefromheader(
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 */
197struct segment_command *
198getsegbynamefromheader(
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 */
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
249		0,			// flags
250		0,			// reserved1
251		0			// reserved2
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.
262 *
263 * This routine operates against the currently executing kernel only
264 */
265struct segment_command *
266firstseg(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 */
276struct segment_command *
277firstsegfromheader(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 */
298struct segment_command *
299nextseg(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 */
320struct segment_command *
321nextsegfromheader(
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 */
354struct segment_command *
355getsegbyname(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 */
377struct section *
378getsectbyname(
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 */
392struct section *
393firstsect(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 */
407struct section *
408nextsect(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 */
423static struct fvmfile_command *
424fvmfilefromheader(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 */
444struct segment_command *
445getfakefvmseg(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 */
482static vm_offset_t
483getsizeofmacho(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) */
500