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 offset for the named section in the
143 * named segment if it exist in the mach header passed to it. Otherwise
144 * it returns zero.
145 *
146 * This routine can operate against any kernel mach header.
147 */
148uint32_t
149getsectoffsetfromheader(
150 kernel_mach_header_t *mhp,
151 const char *segname,
152 const char *sectname)
153{
154 const kernel_section_t *sp;
155
156 sp = getsectbynamefromheader(mhp, segname, sectname);
157 if(sp == (kernel_section_t *)0){
158 return(0);
159 }
160
161 return sp->offset;
162}
163
164/*
165 * This routine returns the a pointer to the data for the named segment
166 * if it exist in the mach header passed to it. Also it returns
167 * the size of the segment data indirectly through the pointer size.
168 * Otherwise it returns zero for the pointer and the size.
169 */
170void *
171getsegdatafromheader(
172 kernel_mach_header_t *mhp,
173 const char *segname,
174 unsigned long *size)
175{
176 const kernel_segment_command_t *sc;
177 void *result;
178
179 sc = getsegbynamefromheader(mhp, segname);
180 if(sc == (kernel_segment_command_t *)0){
181 *size = 0;
182 return((char *)0);
183 }
184 *size = sc->vmsize;
185 result = (void *)sc->vmaddr;
186 return result;
187}
188
189/*
190 * This routine returns the section structure for the named section in the
191 * named segment for the mach_header pointer passed to it if it exist.
192 * Otherwise it returns zero.
193 *
194 * This routine can operate against any kernel mach header.
195 */
196kernel_section_t *
197getsectbynamefromheader(
198 kernel_mach_header_t *mhp,
199 const char *segname,
200 const char *sectname)
201{
202 kernel_segment_command_t *sgp;
203 kernel_section_t *sp;
204 unsigned long i, j;
205
206 sgp = (kernel_segment_command_t *)
207 ((uintptr_t)mhp + sizeof(kernel_mach_header_t));
208 for(i = 0; i < mhp->ncmds; i++){
209 if(sgp->cmd == LC_SEGMENT_KERNEL)
210 if(strncmp(sgp->segname, segname, sizeof(sgp->segname)) == 0 ||
211 mhp->filetype == MH_OBJECT){
212 sp = (kernel_section_t *)((uintptr_t)sgp +
213 sizeof(kernel_segment_command_t));
214 for(j = 0; j < sgp->nsects; j++){
215 if(strncmp(sp->sectname, sectname,
216 sizeof(sp->sectname)) == 0 &&
217 strncmp(sp->segname, segname,
218 sizeof(sp->segname)) == 0)
219 return(sp);
220 sp = (kernel_section_t *)((uintptr_t)sp +
221 sizeof(kernel_section_t));
222 }
223 }
224 sgp = (kernel_segment_command_t *)((uintptr_t)sgp + sgp->cmdsize);
225 }
226 return((kernel_section_t *)NULL);
227}
228
229/*
230 * This routine can operate against any kernel mach header.
231 */
232kernel_segment_command_t *
233getsegbynamefromheader(
234 kernel_mach_header_t *header,
235 const char *seg_name)
236{
237 kernel_segment_command_t *sgp;
238 unsigned long i;
239
240 sgp = (kernel_segment_command_t *)
241 ((uintptr_t)header + sizeof(kernel_mach_header_t));
242 for (i = 0; i < header->ncmds; i++){
243 if ( sgp->cmd == LC_SEGMENT_KERNEL
244 && !strncmp(sgp->segname, seg_name, sizeof(sgp->segname)))
245 return sgp;
246 sgp = (kernel_segment_command_t *)((uintptr_t)sgp + sgp->cmdsize);
247 }
248 return (kernel_segment_command_t *)NULL;
249}
250
251/*
252 * Return the first segment_command in the header.
253 */
254kernel_segment_command_t *
255firstseg(void)
256{
257 return firstsegfromheader(&_mh_execute_header);
258}
259
260kernel_segment_command_t *
261firstsegfromheader(kernel_mach_header_t *header)
262{
263 u_int i = 0;
264 kernel_segment_command_t *sgp = (kernel_segment_command_t *)
265 ((uintptr_t)header + sizeof(*header));
266
267 for (i = 0; i < header->ncmds; i++){
268 if (sgp->cmd == LC_SEGMENT_KERNEL)
269 return sgp;
270 sgp = (kernel_segment_command_t *)((uintptr_t)sgp + sgp->cmdsize);
271 }
272 return (kernel_segment_command_t *)NULL;
273}
274
275/*
276 * This routine operates against any kernel mach segment_command structure
277 * pointer and the provided kernel header, to obtain the sequentially next
278 * segment_command structure in that header.
279 */
280kernel_segment_command_t *
281nextsegfromheader(
282 kernel_mach_header_t *header,
283 kernel_segment_command_t *seg)
284{
285 u_int i = 0;
286 kernel_segment_command_t *sgp = (kernel_segment_command_t *)
287 ((uintptr_t)header + sizeof(*header));
288
289 /* Find the index of the passed-in segment */
290 for (i = 0; sgp != seg && i < header->ncmds; i++) {
291 sgp = (kernel_segment_command_t *)((uintptr_t)sgp + sgp->cmdsize);
292 }
293
294 /* Increment to the next load command */
295 i++;
296 sgp = (kernel_segment_command_t *)((uintptr_t)sgp + sgp->cmdsize);
297
298 /* Return the next segment command, if any */
299 for (; i < header->ncmds; i++) {
300 if (sgp->cmd == LC_SEGMENT_KERNEL) return sgp;
301
302 sgp = (kernel_segment_command_t *)((uintptr_t)sgp + sgp->cmdsize);
303 }
304
305 return (kernel_segment_command_t *)NULL;
306}
307
308
309/*
310 * Return the address of the named Mach-O segment from the currently
311 * executing kernel kernel, or NULL.
312 */
313kernel_segment_command_t *
314getsegbyname(const char *seg_name)
315{
316 return(getsegbynamefromheader(&_mh_execute_header, seg_name));
317}
318
319/*
320 * This routine returns the a pointer the section structure of the named
321 * section in the named segment if it exists in the currently executing
322 * kernel, which it is presumed to be linked into. Otherwise it returns NULL.
323 */
324kernel_section_t *
325getsectbyname(
326 const char *segname,
327 const char *sectname)
328{
329 return(getsectbynamefromheader(
330 (kernel_mach_header_t *)&_mh_execute_header, segname, sectname));
331}
332
333/*
334 * This routine can operate against any kernel segment_command structure to
335 * return the first kernel section immediately following that structure. If
336 * there are no sections associated with the segment_command structure, it
337 * returns NULL.
338 */
339kernel_section_t *
340firstsect(kernel_segment_command_t *sgp)
341{
342 if (!sgp || sgp->nsects == 0)
343 return (kernel_section_t *)NULL;
344
345 return (kernel_section_t *)(sgp+1);
346}
347
348/*
349 * This routine can operate against any kernel segment_command structure and
350 * kernel section to return the next consecutive kernel section immediately
351 * following the kernel section provided. If there are no sections following
352 * the provided section, it returns NULL.
353 */
354kernel_section_t *
355nextsect(kernel_segment_command_t *sgp, kernel_section_t *sp)
356{
357 kernel_section_t *fsp = firstsect(sgp);
358
359 if (((uintptr_t)(sp - fsp) + 1) >= sgp->nsects)
360 return (kernel_section_t *)NULL;
361
362 return sp+1;
363}
364