text: /*
   * Copyright (c) 2000-2008 Apple Inc. All rights reserved.
   *
   * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
   *
   * This file contains Original Code and/or Modifications of Original Code
   * as defined in and that are subject to the Apple Public Source License
   * Version 2.0 (the 'License'). You may not use this file except in
   * compliance with the License. The rights granted to you under the License
   * may not be used to create, or enable the creation or redistribution of,
   * unlawful or unlicensed copies of an Apple operating system, or to
   * circumvent, violate, or enable the circumvention or violation of, any
   * terms of an Apple operating system software license agreement.
   *
   * Please obtain a copy of the License at
   * http://www.opensource.apple.com/apsl/ and read it before using this file.
   *
   * The Original Code and all software distributed under the License are
   * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
   * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
   * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
   * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
   * Please see the License for the specific language governing rights and
   * limitations under the License.
   *
   * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
   */
  /*
   *	File: libkern/kernel_mach_header.c
   *
   *	Functions for accessing mach-o headers.
   *
   * NOTE:	This file supports only kernel mach headers at the present
   *		time; it's primary use is by kld, and all externally
   *		referenced routines at the present time operate against
   *		the kernel mach header _mh_execute_header, which is the
   *		header for the currently executing kernel.
   *
   */
  
  #include <vm/vm_map.h>
  #include <vm/vm_kern.h>
  #include <libkern/kernel_mach_header.h>
  #include <string.h>             // from libsa
  
  /*
   * return the last address (first avail)
   *
   * This routine operates against the currently executing kernel only
   */
  vm_offset_t
  getlastaddr(void)
  {
  	kernel_segment_command_t        *sgp;
  	vm_offset_t             last_addr = 0;
  	kernel_mach_header_t *header = &_mh_execute_header;
  	unsigned long i;
  
  	sgp = (kernel_segment_command_t *)
  	    ((uintptr_t)header + sizeof(kernel_mach_header_t));
  	for (i = 0; i < header->ncmds; i++) {
  		if (sgp->cmd == LC_SEGMENT_KERNEL) {
  			if (sgp->vmaddr + sgp->vmsize > last_addr) {
  				last_addr = sgp->vmaddr + sgp->vmsize;
  			}
  		}
  		sgp = (kernel_segment_command_t *)((uintptr_t)sgp + sgp->cmdsize);
  	}
  	return last_addr;
  }
  
  /*
   * Find the specified load command in the Mach-O headers, and return
   * the command. If there is no such load command, NULL is returned.
   */
  void *
  getcommandfromheader(kernel_mach_header_t *mhp, uint32_t cmd)
  {
  	struct load_command *lcp;
  	unsigned long i;
  
  	lcp = (struct load_command *) (mhp + 1);
  	for (i = 0; i < mhp->ncmds; i++) {
  		if (lcp->cmd == cmd) {
  			return (void *)lcp;
  		}
  
  		lcp = (struct load_command *)((uintptr_t)lcp + lcp->cmdsize);
  	}
  
  	return NULL;
  }
  
  /*
   * Find the UUID load command in the Mach-O headers, and return
   * the address of the UUID blob and size in "*size". If the
   * Mach-O image is missing a UUID, NULL is returned.
   */
  void *
  getuuidfromheader(kernel_mach_header_t *mhp, unsigned long *size)
  {
  	struct uuid_command *cmd = (struct uuid_command *)
  	    getcommandfromheader(mhp, LC_UUID);
  
  	if (cmd != NULL) {
  		if (size) {
  			*size = sizeof(cmd->uuid);
  		}
  		return cmd->uuid;
  	}
  
  	return NULL;
  }
  
  /*
   * This routine returns the a pointer to the data for the named section in the
   * named segment if it exist in the mach header passed to it.  Also it returns
   * the size of the section data indirectly through the pointer size.  Otherwise
   *  it returns zero for the pointer and the size.
   *
   * This routine can operate against any kernel mach header.
   */
  void *
  getsectdatafromheader(
  	kernel_mach_header_t *mhp,
  	const char *segname,
  	const char *sectname,
  	unsigned long *size)
  {
  	const kernel_section_t *sp;
  	void *result;
  
  	sp = getsectbynamefromheader(mhp, segname, sectname);
  	if (sp == (kernel_section_t *)0) {
  		*size = 0;
  		return (char *)0;
  	}
  	*size = sp->size;
  	result = (void *)sp->addr;
  	return result;
  }
  
  /*
   * This routine returns the offset for the named section in the
   * named segment if it exist in the mach header passed to it. Otherwise
   *  it returns zero.
   *
   * This routine can operate against any kernel mach header.
   */
  uint32_t
  getsectoffsetfromheader(
  	kernel_mach_header_t *mhp,
  	const char *segname,
  	const char *sectname)
  {
  	const kernel_section_t *sp;
  
  	sp = getsectbynamefromheader(mhp, segname, sectname);
  	if (sp == (kernel_section_t *)0) {
  		return 0;
  	}
  
  	return sp->offset;
  }
  
  /*
   * This routine returns the a pointer to the data for the named segment
   * if it exist in the mach header passed to it.  Also it returns
   * the size of the segment data indirectly through the pointer size.
   * Otherwise it returns zero for the pointer and the size.
   */
  void *
  getsegdatafromheader(
  	kernel_mach_header_t *mhp,
  	const char *segname,
  	unsigned long *size)
  {
  	const kernel_segment_command_t *sc;
  	void *result;
  
  	sc = getsegbynamefromheader(mhp, segname);
  	if (sc == (kernel_segment_command_t *)0) {
  		*size = 0;
  		return (char *)0;
  	}
  	*size = sc->vmsize;
  	result = (void *)sc->vmaddr;
  	return result;
  }
  
  /*
   * This routine returns the section structure for the named section in the
   * named segment for the mach_header pointer passed to it if it exist.
   * Otherwise it returns zero.
   *
   * This routine can operate against any kernel mach header.
   */
  kernel_section_t *
  getsectbynamefromheader(
  	kernel_mach_header_t *mhp,
  	const char *segname,
  	const char *sectname)
  {
  	kernel_segment_command_t *sgp;
  	kernel_section_t *sp;
  	unsigned long i, j;
  
  	sgp = (kernel_segment_command_t *)
  	    ((uintptr_t)mhp + sizeof(kernel_mach_header_t));
  	for (i = 0; i < mhp->ncmds; i++) {
  		if (sgp->cmd == LC_SEGMENT_KERNEL) {
  			if (strncmp(sgp->segname, segname, sizeof(sgp->segname)) == 0 ||
  			    mhp->filetype == MH_OBJECT) {
  				sp = (kernel_section_t *)((uintptr_t)sgp +
  				    sizeof(kernel_segment_command_t));
  				for (j = 0; j < sgp->nsects; j++) {
  					if (strncmp(sp->sectname, sectname,
  					    sizeof(sp->sectname)) == 0 &&
  					    strncmp(sp->segname, segname,
  					    sizeof(sp->segname)) == 0) {
  						return sp;
  					}
  					sp = (kernel_section_t *)((uintptr_t)sp +
  					    sizeof(kernel_section_t));
  				}
  			}
  		}
  		sgp = (kernel_segment_command_t *)((uintptr_t)sgp + sgp->cmdsize);
  	}
  	return (kernel_section_t *)NULL;
  }
  
  /*
   * This routine can operate against any kernel mach header.
   */
  kernel_segment_command_t *
  getsegbynamefromheader(
  	kernel_mach_header_t    *header,
  	const char              *seg_name)
  {
  	kernel_segment_command_t *sgp;
  	unsigned long i;
  
  	sgp = (kernel_segment_command_t *)
  	    ((uintptr_t)header + sizeof(kernel_mach_header_t));
  	for (i = 0; i < header->ncmds; i++) {
  		if (sgp->cmd == LC_SEGMENT_KERNEL
  		    && !strncmp(sgp->segname, seg_name, sizeof(sgp->segname))) {
  			return sgp;
  		}
  		sgp = (kernel_segment_command_t *)((uintptr_t)sgp + sgp->cmdsize);
  	}
  	return (kernel_segment_command_t *)NULL;
  }
  
  /*
   * Return the first segment_command in the header.
   */
  kernel_segment_command_t *
  firstseg(void)
  {
  	return firstsegfromheader(&_mh_execute_header);
  }
  
  kernel_segment_command_t *
  firstsegfromheader(kernel_mach_header_t *header)
  {
  	u_int i = 0;
  	kernel_segment_command_t *sgp = (kernel_segment_command_t *)
  	    ((uintptr_t)header + sizeof(*header));
  
  	for (i = 0; i < header->ncmds; i++) {
  		if (sgp->cmd == LC_SEGMENT_KERNEL) {
  			return sgp;
  		}
  		sgp = (kernel_segment_command_t *)((uintptr_t)sgp + sgp->cmdsize);
  	}
  	return (kernel_segment_command_t *)NULL;
  }
  
  /*
   * This routine operates against any kernel mach segment_command structure
   * pointer and the provided kernel header, to obtain the sequentially next
   * segment_command structure in that header.
   */
  kernel_segment_command_t *
  nextsegfromheader(
  	kernel_mach_header_t    *header,
  	kernel_segment_command_t        *seg)
  {
  	u_int i = 0;
  	kernel_segment_command_t *sgp = (kernel_segment_command_t *)
  	    ((uintptr_t)header + sizeof(*header));
  
  	/* Find the index of the passed-in segment */
  	for (i = 0; sgp != seg && i < header->ncmds; i++) {
  		sgp = (kernel_segment_command_t *)((uintptr_t)sgp + sgp->cmdsize);
  	}
  
  	/* Increment to the next load command */
  	i++;
  	sgp = (kernel_segment_command_t *)((uintptr_t)sgp + sgp->cmdsize);
  
  	/* Return the next segment command, if any */
  	for (; i < header->ncmds; i++) {
  		if (sgp->cmd == LC_SEGMENT_KERNEL) {
  			return sgp;
  		}
  
  		sgp = (kernel_segment_command_t *)((uintptr_t)sgp + sgp->cmdsize);
  	}
  
  	return (kernel_segment_command_t *)NULL;
  }
  
  
  /*
   * Return the address of the named Mach-O segment from the currently
   * executing kernel kernel, or NULL.
   */
  kernel_segment_command_t *
  getsegbyname(const char *seg_name)
  {
  	return getsegbynamefromheader(&_mh_execute_header, seg_name);
  }
  
  /*
   * This routine returns the a pointer the section structure of the named
   * section in the named segment if it exists in the currently executing
   * kernel, which it is presumed to be linked into.  Otherwise it returns NULL.
   */
  kernel_section_t *
  getsectbyname(
  	const char *segname,
  	const char *sectname)
  {
  	return getsectbynamefromheader(
  		(kernel_mach_header_t *)&_mh_execute_header, segname, sectname);
  }
  
  /*
   * This routine can operate against any kernel segment_command structure to
   * return the first kernel section immediately following that structure.  If
   * there are no sections associated with the segment_command structure, it
   * returns NULL.
   */
  kernel_section_t *
  firstsect(kernel_segment_command_t *sgp)
  {
  	if (!sgp || sgp->nsects == 0) {
  		return (kernel_section_t *)NULL;
  	}
  
  	return (kernel_section_t *)(sgp + 1);
  }
  
  /*
   * This routine can operate against any kernel segment_command structure and
   * kernel section to return the next consecutive  kernel section immediately
   * following the kernel section provided.  If there are no sections following
   * the provided section, it returns NULL.
   */
  kernel_section_t *
  nextsect(kernel_segment_command_t *sgp, kernel_section_t *sp)
  {
  	kernel_section_t *fsp = firstsect(sgp);
  
  	if (((uintptr_t)(sp - fsp) + 1) >= sgp->nsects) {
  		return (kernel_section_t *)NULL;
  	}
  
  	return sp + 1;
  }
  