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