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