1/*
2 * Copyright (c) 2000-2008 Apple 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: libkern/kernel_mach_header.c
30 *
31 *	Functions for accessing mach-o headers.
32 *
33 * NOTE:	This file supports only kernel 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 kernel mach header _mh_execute_header, which is the
37 *		header for the currently executing kernel.
38 *
39 */
40
41#include <vm/vm_map.h>
42#include <vm/vm_kern.h>
43#include <libkern/kernel_mach_header.h>
44#include <string.h>		// from libsa
45
46/*
47 * return the last address (first avail)
48 *
49 * This routine operates against the currently executing kernel only
50 */
51vm_offset_t
52getlastaddr(void)
53{
54	kernel_segment_command_t	*sgp;
55	vm_offset_t		last_addr = 0;
56	kernel_mach_header_t *header = &_mh_execute_header;
57	unsigned long i;
58
59	sgp = (kernel_segment_command_t *)
60		((uintptr_t)header + sizeof(kernel_mach_header_t));
61	for (i = 0; i < header->ncmds; i++){
62		if (sgp->cmd == LC_SEGMENT_KERNEL) {
63			if (sgp->vmaddr + sgp->vmsize > last_addr)
64				last_addr = sgp->vmaddr + sgp->vmsize;
65		}
66		sgp = (kernel_segment_command_t *)((uintptr_t)sgp + sgp->cmdsize);
67	}
68	return last_addr;
69}
70
71/*
72 * Find the specified load command in the Mach-O headers, and return
73 * the command. If there is no such load command, NULL is returned.
74 */
75void *
76getcommandfromheader(kernel_mach_header_t *mhp, uint32_t cmd) {
77	struct load_command *lcp;
78	unsigned long i;
79
80	lcp = (struct load_command *) (mhp + 1);
81	for(i = 0; i < mhp->ncmds; i++){
82		if(lcp->cmd == cmd) {
83			return (void *)lcp;
84		}
85
86		lcp = (struct load_command *)((uintptr_t)lcp + lcp->cmdsize);
87	}
88
89	return NULL;
90}
91
92/*
93 * Find the UUID load command in the Mach-O headers, and return
94 * the address of the UUID blob and size in "*size". If the
95 * Mach-O image is missing a UUID, NULL is returned.
96 */
97void *
98getuuidfromheader(kernel_mach_header_t *mhp, unsigned long *size)
99{
100    struct uuid_command *cmd = (struct uuid_command *)
101        getcommandfromheader(mhp, LC_UUID);
102
103    if (cmd != NULL) {
104        if (size) {
105            *size = sizeof(cmd->uuid);
106        }
107        return cmd->uuid;
108    }
109
110    return NULL;
111}
112
113/*
114 * This routine returns the a pointer to the data for the named section in the
115 * named segment if it exist in the mach header passed to it.  Also it returns
116 * the size of the section data indirectly through the pointer size.  Otherwise
117 *  it returns zero for the pointer and the size.
118 *
119 * This routine can operate against any kernel mach header.
120 */
121void *
122getsectdatafromheader(
123    kernel_mach_header_t *mhp,
124    const char *segname,
125    const char *sectname,
126    unsigned long *size)
127{
128	const kernel_section_t *sp;
129	void *result;
130
131	sp = getsectbynamefromheader(mhp, segname, sectname);
132	if(sp == (kernel_section_t *)0){
133	    *size = 0;
134	    return((char *)0);
135	}
136	*size = sp->size;
137	result = (void *)sp->addr;
138	return result;
139}
140
141/*
142 * This routine returns the a pointer to the data for the named segment
143 * if it exist in the mach header passed to it.  Also it returns
144 * the size of the segment data indirectly through the pointer size.
145 * Otherwise it returns zero for the pointer and the size.
146 */
147void *
148getsegdatafromheader(
149    kernel_mach_header_t *mhp,
150	const char *segname,
151	unsigned long *size)
152{
153	const kernel_segment_command_t *sc;
154	void *result;
155
156	sc = getsegbynamefromheader(mhp, segname);
157	if(sc == (kernel_segment_command_t *)0){
158	    *size = 0;
159	    return((char *)0);
160	}
161	*size = sc->vmsize;
162	result = (void *)sc->vmaddr;
163	return result;
164}
165
166/*
167 * This routine returns the section structure for the named section in the
168 * named segment for the mach_header pointer passed to it if it exist.
169 * Otherwise it returns zero.
170 *
171 * This routine can operate against any kernel mach header.
172 */
173kernel_section_t *
174getsectbynamefromheader(
175    kernel_mach_header_t *mhp,
176    const char *segname,
177    const char *sectname)
178{
179	kernel_segment_command_t *sgp;
180	kernel_section_t *sp;
181	unsigned long i, j;
182
183	sgp = (kernel_segment_command_t *)
184	      ((uintptr_t)mhp + sizeof(kernel_mach_header_t));
185	for(i = 0; i < mhp->ncmds; i++){
186	    if(sgp->cmd == LC_SEGMENT_KERNEL)
187		if(strncmp(sgp->segname, segname, sizeof(sgp->segname)) == 0 ||
188		   mhp->filetype == MH_OBJECT){
189		    sp = (kernel_section_t *)((uintptr_t)sgp +
190			 sizeof(kernel_segment_command_t));
191		    for(j = 0; j < sgp->nsects; j++){
192			if(strncmp(sp->sectname, sectname,
193			   sizeof(sp->sectname)) == 0 &&
194			   strncmp(sp->segname, segname,
195			   sizeof(sp->segname)) == 0)
196			    return(sp);
197			sp = (kernel_section_t *)((uintptr_t)sp +
198			     sizeof(kernel_section_t));
199		    }
200		}
201	    sgp = (kernel_segment_command_t *)((uintptr_t)sgp + sgp->cmdsize);
202	}
203	return((kernel_section_t *)NULL);
204}
205
206/*
207 * This routine can operate against any kernel mach header.
208 */
209kernel_segment_command_t *
210getsegbynamefromheader(
211	kernel_mach_header_t	*header,
212	const char		*seg_name)
213{
214	kernel_segment_command_t *sgp;
215	unsigned long i;
216
217	sgp = (kernel_segment_command_t *)
218		((uintptr_t)header + sizeof(kernel_mach_header_t));
219	for (i = 0; i < header->ncmds; i++){
220		if (   sgp->cmd == LC_SEGMENT_KERNEL
221		    && !strncmp(sgp->segname, seg_name, sizeof(sgp->segname)))
222			return sgp;
223		sgp = (kernel_segment_command_t *)((uintptr_t)sgp + sgp->cmdsize);
224	}
225	return (kernel_segment_command_t *)NULL;
226}
227
228/*
229 * Return the first segment_command in the header.
230 */
231kernel_segment_command_t *
232firstseg(void)
233{
234    return firstsegfromheader(&_mh_execute_header);
235}
236
237kernel_segment_command_t *
238firstsegfromheader(kernel_mach_header_t *header)
239{
240    u_int i = 0;
241    kernel_segment_command_t *sgp = (kernel_segment_command_t *)
242        ((uintptr_t)header + sizeof(*header));
243
244    for (i = 0; i < header->ncmds; i++){
245        if (sgp->cmd == LC_SEGMENT_KERNEL)
246            return sgp;
247        sgp = (kernel_segment_command_t *)((uintptr_t)sgp + sgp->cmdsize);
248    }
249    return (kernel_segment_command_t *)NULL;
250}
251
252/*
253 * This routine operates against any kernel mach segment_command structure
254 * pointer and the provided kernel header, to obtain the sequentially next
255 * segment_command structure in that header.
256 */
257kernel_segment_command_t *
258nextsegfromheader(
259        kernel_mach_header_t	*header,
260        kernel_segment_command_t	*seg)
261{
262    u_int i = 0;
263    kernel_segment_command_t *sgp = (kernel_segment_command_t *)
264        ((uintptr_t)header + sizeof(*header));
265
266    /* Find the index of the passed-in segment */
267    for (i = 0; sgp != seg && i < header->ncmds; i++) {
268        sgp = (kernel_segment_command_t *)((uintptr_t)sgp + sgp->cmdsize);
269    }
270
271    /* Increment to the next load command */
272    i++;
273    sgp = (kernel_segment_command_t *)((uintptr_t)sgp + sgp->cmdsize);
274
275    /* Return the next segment command, if any */
276    for (; i < header->ncmds; i++) {
277        if (sgp->cmd == LC_SEGMENT_KERNEL) return sgp;
278
279        sgp = (kernel_segment_command_t *)((uintptr_t)sgp + sgp->cmdsize);
280    }
281
282    return (kernel_segment_command_t *)NULL;
283}
284
285
286/*
287 * Return the address of the named Mach-O segment from the currently
288 * executing kernel kernel, or NULL.
289 */
290kernel_segment_command_t *
291getsegbyname(const char *seg_name)
292{
293	return(getsegbynamefromheader(&_mh_execute_header, seg_name));
294}
295
296/*
297 * This routine returns the a pointer the section structure of the named
298 * section in the named segment if it exists in the currently executing
299 * kernel, which it is presumed to be linked into.  Otherwise it returns NULL.
300 */
301kernel_section_t *
302getsectbyname(
303    const char *segname,
304    const char *sectname)
305{
306	return(getsectbynamefromheader(
307		(kernel_mach_header_t *)&_mh_execute_header, segname, sectname));
308}
309
310/*
311 * This routine can operate against any kernel segment_command structure to
312 * return the first kernel section immediately following that structure.  If
313 * there are no sections associated with the segment_command structure, it
314 * returns NULL.
315 */
316kernel_section_t *
317firstsect(kernel_segment_command_t *sgp)
318{
319	if (!sgp || sgp->nsects == 0)
320		return (kernel_section_t *)NULL;
321
322	return (kernel_section_t *)(sgp+1);
323}
324
325/*
326 * This routine can operate against any kernel segment_command structure and
327 * kernel section to return the next consecutive  kernel section immediately
328 * following the kernel section provided.  If there are no sections following
329 * the provided section, it returns NULL.
330 */
331kernel_section_t *
332nextsect(kernel_segment_command_t *sgp, kernel_section_t *sp)
333{
334	kernel_section_t *fsp = firstsect(sgp);
335
336	if (((uintptr_t)(sp - fsp) + 1) >= sgp->nsects)
337		return (kernel_section_t *)NULL;
338
339	return sp+1;
340}
341