File:  [Apple Darwin 0.x] / objc / objcedit.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Tue Apr 24 19:13:57 2018 UTC (8 years, 2 months ago) by root
Branches: MAIN, Apple
CVS tags: HEAD, Darwin03, Darwin01
Darwin 0.1 In-kernel Objective-C runtime

/*
 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * "Portions Copyright (c) 1999 Apple Computer, Inc.  All Rights
 * Reserved.  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 1.0 (the 'License').  You may not use this file
 * except in compliance with the License.  Please obtain a copy of the
 * License at http://www.apple.com/publicsource 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 OR NON-INFRINGEMENT.  Please see the
 * License for the specific language governing rights and limitations
 * under the License."
 * 
 * @APPLE_LICENSE_HEADER_END@
 */
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <sys/file.h>
#include <mach-o/loader.h>
#define SECT_OBJC_RUNTIME "__runtime_setup"
#include <sys/types.h>
#include <sys/stat.h>
#include <mach/mach.h>
#include <mach/mach_error.h>
#include <libc.h>

/*
 * This really is the segment alignment of the input file and is an assumption
 * on what the segment alignment is since it can't exactly be determined from
 * the file itself.
 */
const long SEGALIGN = 0x2000;

/* This is set in the routine main() */
extern char *progname;

/* These variables are set in the routine map_input() */
static char *input_addr = NULL;	/* address of where the input file is mapped */
static long input_size = 0;	/* size of the input file */
static long input_mode = 0;	/* mode of the input file */
static struct mach_header
		*mhp = NULL;	/* pointer to the input file's mach header */
static struct load_command
	*load_commands = NULL;	/* pointer to the input file's load commands */
static int swapped;	/* TRUE if the input is to be swapped */
static enum NXByteOrder host_byte_order = NX_UnknownByteOrder;
static enum NXByteOrder target_byte_order = NX_UnknownByteOrder;

/*
 * Structures used in objc_runtime_setup in updating sections in the segments
 * of the input file.  There is one such structure for each segment and section.
 */
struct rep_seg {
    long fileoff;		/* original file offset */
    long filesize;		/* original file size */
    long vmsize;		/* original vm size */
    long padsize;		/* new pad size */
    struct segment_command *sgp;/* pointer to the segment_command */
};
static struct rep_seg *segs = NULL;

struct rep_sect {
    long offset;		/* original file offset */
    struct section *sp;		/* pointer to the section structure */
};
static struct rep_sect *sects = NULL;

/* Internal routines */
static long map_input(
    char *input);
static long objc_runtime_setup(
    char *input,
    char *output,
    char *objc_runtime_setup_contents,
    unsigned long objc_runtime_setup_size);
static int qsort_vmaddr(
    const struct rep_seg *seg1,
    const struct rep_seg *seg2);
static int qsort_fileoff(
    const struct rep_seg *seg1,
    const struct rep_seg *seg2);
static int qsort_offset(
    const struct rep_sect *sect1,
    const struct rep_sect *sect2);
static void *allocate(
    long size);
static void error(
    const char *format,
    ...);
static void fatal(
    const char *format,
    ...);
static void system_error(
    const char *format,
    ...);
static void system_fatal(
    const char *format,
    ...);
static void machkern_error(
    kern_return_t r,
    char *format,
    ...);
long round(
    long v,
    unsigned long r);

/*
 * add_objc_runtime_setup is passed a name of an object file and pointer to the
 * contents of the objc_runtime_setup section and it's size and creates an
 * output file with that section added or releaced.  It returns 1 if successfull
 * and zero otherwize.
 */
long
add_objc_runtime_setup(
char *input,
char *output,
char *contents,
long size)
{
	/* map in the input */
	if(map_input(input) == 0)
	    return(0);

	/* create the output with the contents */
	if(objc_runtime_setup(input, output, contents, size) == 0)
	    return(0);

	return(1);
}

/*
 * map_input maps the input file into memory.  The address it is mapped at is
 * left in input_addr and the size is left in input_size.  The input file is
 * checked to be an object file and that the headers are checked to be correct
 * enough to loop through them.  The pointer to the mach header is left in mhp
 * and the pointer to the load commands is left in load_commands.  It returns
 * 1 if it is successfull and zero otherwise.
 */
static
long
map_input(
char *input)
{
    int fd;
    long i;
    struct stat stat_buf;
    kern_return_t r;
    struct load_command l, *lcp;
    struct segment_command *sgp;
    struct section *sp;
    struct symtab_command *stp;
    struct symseg_command *ssp;

	/* Open the input file and map it in */
	if((fd = open(input, O_RDONLY)) == -1){
	    system_error("can't open input file: %s", input);
	    return(0);
	}
	if(fstat(fd, &stat_buf) == -1){
	    system_error("Can't stat input file: %s", input);
	    close(fd);
	    return(0);
	}
	input_size = stat_buf.st_size;
	input_mode = stat_buf.st_mode;
	if((r = map_fd(fd, 0, (vm_offset_t *) &input_addr, TRUE, input_size))
	   != KERN_SUCCESS){
	    machkern_error(r, "Can't map input file: %s", input);
	    close(fd);
	    return(0);
	}
	close(fd);

	if(sizeof(struct mach_header) > input_size){
	    error("truncated or malformed object (mach header would extend "
		  "past the end of the file) in: %s", input);
	    goto map_input_cleanup;
	}
	mhp = (struct mach_header *)input_addr;
	host_byte_order = NXHostByteOrder();
	if(mhp->magic == NXSwapLong(MH_MAGIC)){
	    swapped = 1;
	    target_byte_order = host_byte_order == NX_BigEndian ?
			        NX_LittleEndian : NX_BigEndian;
	    swap_mach_header(mhp, host_byte_order);
	}
	else if(mhp->magic == MH_MAGIC){
	    swapped = 0;
	    target_byte_order = host_byte_order;
	}
	else{
	    error("bad magic number (file is not a Mach-O file) in: %s", input);
	    goto map_input_cleanup;
	}

	if(mhp->sizeofcmds + sizeof(struct mach_header) > input_size)
	    fatal("truncated or malformed object (load commands would extend "
		  "past the end of the file) in: %s", input);
	load_commands = (struct load_command *)((char *)input_addr +
			    sizeof(struct mach_header));
	lcp = load_commands;
	for(i = 0; i < mhp->ncmds; i++){
	    l = *lcp;
	    if(swapped)
		swap_load_command(&l, host_byte_order);
	    if(l.cmdsize % sizeof(long) != 0){
		error("load command %ld size not a multiple of sizeof(long) "
		      "in: %s", i, input);
		goto map_input_cleanup;
	    }
	    if(l.cmdsize <= 0){
		error("load command %ld size is less than or equal to zero "
		      "in: %s", i, input);
		goto map_input_cleanup;
	    }
	    if((char *)lcp + l.cmdsize >
	       (char *)load_commands + mhp->sizeofcmds){
		error("load command %ld extends past end of all load commands "
		      "in: %s", i, input);
		goto map_input_cleanup;
	    }
	    switch(l.cmd){
	    case LC_SEGMENT:
		sgp = (struct segment_command *)lcp;
		sp = (struct section *)((char *)sgp +
					sizeof(struct segment_command));
		if(swapped)
		    swap_segment_command(sgp, host_byte_order);
		if(swapped)
		    swap_section(sp, sgp->nsects, host_byte_order);
		break;
	    case LC_SYMTAB:
		stp = (struct symtab_command *)lcp;
		if(swapped)
		    swap_symtab_command(stp, host_byte_order);
		break;
	    case LC_SYMSEG:
		ssp = (struct symseg_command *)lcp;
		if(swapped)
		    swap_symseg_command(ssp, host_byte_order);
		break;
	    default:
		*lcp = l;
		break;
	    }
	    lcp = (struct load_command *)((char *)lcp + l.cmdsize);
	}
	return(1);

map_input_cleanup:
	/*
	 * Cleanup if there were errors in map_input().
	 */
	if(input_addr != NULL){
	    if((r = vm_deallocate(task_self(), (vm_address_t)input_addr,
				  input_size)) != KERN_SUCCESS)
		machkern_error(r,"Can't deallocate input file's mapped memory");
	}
	return(0);
}

/*
 * objc_runtime_setup writes a modified version of the input file to output
 * adding or replacing the (__OBJC,__runtime_setup) section with the contents
 * passed to it.
 */
static
long
objc_runtime_setup(
char *input,
char *output,
char *objc_runtime_setup_contents,
unsigned long objc_runtime_setup_size)
{
    long i, j, k, nsegs, nsects;
    long high_reloc_seg, low_noreloc_seg, high_noreloc_seg, low_linkedit;
    long oldvmaddr, oldoffset, newvmaddr, newoffset, newsectsize;
    struct mach_header *new_mhp;
    struct load_command lc, *lcp, *new_lcp, *new_load_commands;
    struct segment_command *sgp, *linkedit_sgp, *objc_sgp;
    struct section *sp, *first_sp, *objc_runtime_setup_sp, *last_objc_sp;
    struct symtab_command *stp;
    struct symseg_command *ssp;
    int outfd;
    vm_address_t pad_addr;
    long size;
    kern_return_t r;

	segs = NULL;
	new_mhp = NULL;
	sects = NULL;
	pad_addr = 0;
	outfd = -1;

	high_reloc_seg = 0;
	low_noreloc_seg = input_size;
	high_noreloc_seg = 0;
	low_linkedit = input_size;

	nsegs = 0;
	segs = allocate(mhp->ncmds * sizeof(struct rep_seg));
	bzero(segs, mhp->ncmds * sizeof(struct rep_seg));
	nsects = 0;

	stp = NULL;
	ssp = NULL;
	linkedit_sgp = NULL;
	first_sp = NULL;
	objc_sgp = NULL;
	objc_runtime_setup_sp = NULL;

	/*
	 * First pass over the load commands and determine if the file is laided
	 * out in an order that the new section can be replaced or added.
	 */
	lcp = load_commands;
	for(i = 0; i < mhp->ncmds; i++){
	    switch(lcp->cmd){
	    case LC_SEGMENT:
		sgp = (struct segment_command *)lcp;
		sp = (struct section *)((char *)sgp +
					sizeof(struct segment_command));
		segs[nsegs++].sgp = sgp;
		nsects += sgp->nsects;
		if(strcmp(sgp->segname, SEG_LINKEDIT) != 0){
		    if(strncmp(SEG_OBJC, sgp->segname,
			       sizeof(sgp->segname)) == 0){
			if(objc_sgp != NULL){
			    error("more than one " SEG_OBJC " segment found "
				  "in: %s", input);
			    goto objc_runtime_setup_cleanup;
			}
			objc_sgp = sgp;
		    }
		    if(sgp->flags & SG_NORELOC){
			if(sgp->filesize != 0){
			    if(sgp->fileoff + sgp->filesize > high_noreloc_seg)
				high_noreloc_seg = sgp->fileoff + sgp->filesize;
			    if(sgp->fileoff < low_noreloc_seg)
				low_noreloc_seg = sgp->fileoff;
			}
		    }
		    else{
			if(sgp->filesize != 0 &&
			   sgp->fileoff + sgp->filesize > high_reloc_seg)
			    high_reloc_seg = sgp->fileoff + sgp->filesize;
		    }
		}
		else{
		    if(linkedit_sgp != NULL){
			error("more than one " SEG_LINKEDIT " segment found "
			      "in: %s", input);
			goto objc_runtime_setup_cleanup;
		    }
		    linkedit_sgp = sgp;
		}
		for(j = 0; j < sgp->nsects; j++){
		    if(sp->nreloc != 0 && sp->reloff < low_linkedit)
			low_linkedit = sp->reloff;
		    if(first_sp == NULL)
			first_sp = sp;
		    if(strncmp(SEG_OBJC, sp->segname,
			       sizeof(sp->segname)) == 0 &&
		       strncmp(SECT_OBJC_RUNTIME, sp->sectname,
			       sizeof(sp->sectname)) == 0){
			if(objc_runtime_setup_sp != NULL){
			    error("more than one (%0.16s,%0.16s) section in: "
				  "%s", sp->segname, sp->sectname, input);
			    goto objc_runtime_setup_cleanup;
			}
			objc_runtime_setup_sp = sp;
			if(sp->flags & S_ZEROFILL){
			    error("(%0.16s,%0.16s) is a fill section in: %s",
				  sp->segname, sp->sectname, input);
			    goto objc_runtime_setup_cleanup;
			}
			if(j != sgp->nsects - 1){
			    error("(%0.16s,%0.16s) is not the last section in "
				  "the " SEG_OBJC " segment in: %s",
				  sp->segname, sp->sectname, input);
			    goto objc_runtime_setup_cleanup;
			}
			if(sp->offset + sp->size > input_size)
			    error("truncated or malformed object (section "
				  "contents of (%0.16s,%0.16s) extends "
				  "past the end of the file) in: %s",
				  sp->segname, sp->sectname, input);
		    }
		    sp++;
		}
		break;
	    case LC_SYMTAB:
		if(stp != NULL)
		    fatal("more than one symtab_command found in: %s", input);
		stp = (struct symtab_command *)lcp;
		if(stp->nsyms != 0 && stp->symoff < low_linkedit)
		    low_linkedit = stp->symoff;
		if(stp->strsize != 0 && stp->stroff < low_linkedit)
		    low_linkedit = stp->stroff;
		break;
	    case LC_SYMSEG:
		if(ssp != NULL)
		    fatal("more than one symseg_command found in: %s", input);
		ssp = (struct symseg_command *)lcp;
		if(ssp->size != 0 && ssp->offset < low_linkedit)
		    low_linkedit = ssp->offset;
		break;
	    case LC_THREAD:
	    case LC_UNIXTHREAD:
	    case LC_LOADFVMLIB:
	    case LC_IDFVMLIB:
	    case LC_IDENT:
		break;
	    default:
		error("unknown load command %d in: %s (result maybe bad)", i,
		      input);
		break;
	    }
	    lcp = (struct load_command *)((char *)lcp + lcp->cmdsize);
	}

	if(objc_sgp == NULL){
	    error("file: %s does not contain an " SEG_OBJC " segment", input);
	    goto objc_runtime_setup_cleanup;
	}
	if(objc_runtime_setup_sp == NULL && 
	   first_sp->offset - (sizeof(struct mach_header) + mhp->sizeofcmds) <
	   sizeof(struct section)){
	    error("file: %s does not have enough header padding to add a "
		  "section", input);
	    goto objc_runtime_setup_cleanup;
	}
	if(high_reloc_seg > low_noreloc_seg ||
	   high_reloc_seg > low_linkedit ||
	   high_noreloc_seg > low_linkedit){
	    error("contents of input file: %s not in an order that the "
		  "new section can be added or replaced by this program",
		  input);
	    goto objc_runtime_setup_cleanup;
	}
	if(low_noreloc_seg != input_size &&
	   low_noreloc_seg != objc_sgp->fileoff + objc_sgp->filesize){
	    error("file's: %s " SEG_OBJC " segment is not the last segment "
		  "before the segments requiring no relocation", input);
	    goto objc_runtime_setup_cleanup;
	}

	/*
	 * If the section must be added rather than just updated then rebuild
	 * the headers with the new section in it.  And reset the pointers into
	 * the load commands to point to the newly allocated load commands.
	 */
	if(objc_runtime_setup_sp == NULL){
	    new_mhp = (struct mach_header *)allocate(
			sizeof(struct mach_header) + mhp->sizeofcmds +
			sizeof(struct section));
	    *new_mhp = *mhp;
	    new_mhp->sizeofcmds += sizeof(struct section);
	    new_load_commands = (struct load_command *)((char *)new_mhp +
				sizeof(struct mach_header));
	    lcp = load_commands;
	    new_lcp = new_load_commands;
	    nsegs = 0;

	    for(i = 0; i < mhp->ncmds; i++){
		memcpy((char *)new_lcp, (char *)lcp, lcp->cmdsize);
	    	if(new_lcp->cmd == LC_SEGMENT){
		    segs[nsegs++].sgp = (struct segment_command *)new_lcp;
		    if((struct segment_command *)lcp == linkedit_sgp)
			linkedit_sgp = (struct segment_command *)new_lcp;
		}
		if((struct symtab_command *)lcp == stp)
		    stp = (struct symtab_command *)new_lcp;
		if((struct symseg_command *)lcp == ssp)
		    ssp = (struct symseg_command *)new_lcp;
		if((struct segment_command *)lcp == objc_sgp){
		    objc_sgp = (struct segment_command *)new_lcp;
		    objc_runtime_setup_sp =
				   (struct section *)((char *)objc_sgp +
				   sizeof(struct segment_command) +
				   objc_sgp->nsects * sizeof(struct section));
		    last_objc_sp = (struct section *)((char *)objc_sgp +
				    sizeof(struct segment_command) +
				    (objc_sgp->nsects - 1) *
				    sizeof(struct section));
		    objc_sgp->cmdsize += sizeof(struct section);
		    objc_sgp->nsects++;
		    nsects++;
		    memset(objc_runtime_setup_sp, '\0', sizeof(struct section));
		    strcpy(objc_runtime_setup_sp->sectname, SECT_OBJC_RUNTIME);
		    strcpy(objc_runtime_setup_sp->segname, SEG_OBJC);
		    objc_runtime_setup_sp->addr = round(last_objc_sp->addr +
							last_objc_sp->size,
							sizeof(long));
		    objc_runtime_setup_sp->size = 0;
		    objc_runtime_setup_sp->offset = last_objc_sp->offset +
						    objc_runtime_setup_sp->addr-
						    last_objc_sp->addr;
		    objc_runtime_setup_sp->align = 2;
		    objc_runtime_setup_sp->reloff = 0;
		    objc_runtime_setup_sp->nreloc = 0;
		    objc_runtime_setup_sp->flags = 0;
		}
		new_lcp = (struct load_command *)((char *)new_lcp +
						 new_lcp->cmdsize);
		lcp = (struct load_command *)((char *)lcp + lcp->cmdsize);
	    }
	    mhp = new_mhp;
	    load_commands = new_load_commands;
	}

	qsort(segs, nsegs, sizeof(struct rep_seg),
	      (int (*)(const void *, const void *))qsort_vmaddr);

	sects = allocate(nsects * sizeof(struct rep_sect));
	bzero(sects, nsects * sizeof(struct rep_sect));

	/*
	 * First go through the segments and adjust the segment offsets, sizes
	 * and addresses without adjusting the offset to the relocation entries.
	 * This program can only handle object files that have contigious
	 * address spaces starting at zero and that the offsets in the file for
	 * the contents of the segments also being contiguious and in the same
	 * order as the vmaddresses.
	 */
	oldvmaddr = 0;
	newvmaddr = 0;
	k = 0;
	for(i = 0; i < nsegs; i++){
	    if(segs[i].sgp->vmaddr != oldvmaddr){
		newvmaddr = segs[i].sgp->vmaddr;
		oldvmaddr = newvmaddr;
	    }
	    segs[i].filesize = segs[i].sgp->filesize;
	    segs[i].vmsize = segs[i].sgp->vmsize;
	    segs[i].sgp->vmaddr = newvmaddr;
	    sp = (struct section *)((char *)(segs[i].sgp) +
				    sizeof(struct segment_command));
	    if(segs[i].sgp->filesize != 0){
		newsectsize = 0;
		if(segs[i].sgp == objc_sgp || segs[i].sgp->flags == SG_NORELOC){
		    for(j = 0; j < segs[i].sgp->nsects; j++){

			/* begin bug fix - snaroff (8/3/90) */
			if ((sp->offset == 0) && (sp->flags != S_ZEROFILL)) 
		          sp->offset = (sp+1)->offset;
			/* end bug fix - snaroff (8/3/90) */

			sects[k + j].sp = sp;
			if(sp == objc_runtime_setup_sp)
			    sp->size = objc_runtime_setup_size;
			sp->addr += newvmaddr - oldvmaddr;
			newsectsize = (sp->addr - newvmaddr) + sp->size;
			sp++;
		    }
		    if(strcmp(segs[i].sgp->segname, SEG_LINKEDIT) != 0){
			segs[i].sgp->filesize = round(newsectsize, SEGALIGN);
			segs[i].sgp->vmsize = round(newsectsize, SEGALIGN);
			segs[i].padsize = segs[i].sgp->filesize  - newsectsize;
		    }
		}
		else{
		    for(j = 0; j < segs[i].sgp->nsects; j++)
			sects[k + j].sp = sp;
		}
	    }
	    else{
		for(j = 0; j < segs[i].sgp->nsects; j++)
		    sects[k + j].sp = sp;
	    }
	    oldvmaddr += segs[i].vmsize;
	    newvmaddr += segs[i].sgp->vmsize;
	    k += segs[i].sgp->nsects;
	}

	qsort(segs, nsegs, sizeof(struct rep_seg),
	      (int (*)(const void *, const void *))qsort_fileoff);
	qsort(sects, nsects, sizeof(struct rep_sect),
	      (int (*)(const void *, const void *))qsort_offset);

	oldoffset = 0;
	newoffset = 0;
	k = 0;
	for(i = 0; i < nsegs; i++){
	    if(segs[i].sgp->filesize != 0){
		if(segs[i].sgp->fileoff != oldoffset){
		    oldoffset = segs[i].sgp->fileoff;
		    newoffset = oldoffset;
		}
		segs[i].fileoff = segs[i].sgp->fileoff;
		if(strcmp(segs[i].sgp->segname, SEG_LINKEDIT) != 0)
		    segs[i].sgp->fileoff = newoffset;
		sp = (struct section *)((char *)(segs[i].sgp) +
					sizeof(struct segment_command));
		if(segs[i].sgp == objc_sgp || segs[i].sgp->flags == SG_NORELOC){
		    for(j = 0; j < segs[i].sgp->nsects; j++){
			sects[k + j].offset = sp->offset;
			sp->offset += newoffset - oldoffset;
			sp++;
		    }
		}
		/*
		 * If it is not the LINKEDIT segment or the last segment then
		 * move up the offsets.
		 */
		if(strcmp(segs[i].sgp->segname, SEG_LINKEDIT) != 0 ||
		   i != nsegs - 1){
		    oldoffset += segs[i].filesize;
		    newoffset += segs[i].sgp->filesize;
		}
	    }
	    k += segs[i].sgp->nsects;
	}

	/*
	 * Now update the offsets to the linkedit information.
	 */
	if(oldoffset != low_linkedit){
	    error("contents of input file: %s not in an order that the "
		  "specified sections can be replaced by this program", input);
	    goto objc_runtime_setup_cleanup;
	}
	for(i = 0; i < nsegs; i++){
	    sp = (struct section *)((char *)(segs[i].sgp) +
				    sizeof(struct segment_command));
	    for(j = 0; j < segs[i].sgp->nsects; j++){
		if(sp->nreloc != 0)
		    sp->reloff += newoffset - oldoffset;
		sp++;
	    }
	}
	if(stp != NULL){
	    if(stp->nsyms != 0);
		stp->symoff += newoffset - oldoffset;
	    if(stp->strsize != 0)
		stp->stroff += newoffset - oldoffset;
	}
	if(ssp != NULL){
	    if(ssp->size != 0)
		ssp->offset += newoffset - oldoffset;
	}
	if(linkedit_sgp != NULL){
	    linkedit_sgp->fileoff += newoffset - oldoffset;
	}

	/*
	 * Now write the new file by writing the header and modified load
	 * commands, then the segments with any new sections and finally
	 * the link edit info.
	 */
	if((outfd = open(output, O_CREAT | O_WRONLY | O_TRUNC ,input_mode)) ==
	   								    -1){
	    system_error("can't create output file: %s", output);
	    goto objc_runtime_setup_cleanup;
	}

	if(r = vm_allocate(task_self(), &pad_addr, SEGALIGN, 1) !=
	   							  KERN_SUCCESS){
	    machkern_error(r, "vm_allocate() failed");
	    goto objc_runtime_setup_cleanup;
	}

	k = 0;
	for(i = 0; i < nsegs; i++){
	    if(segs[i].sgp == objc_sgp){
		for(j = 0; j < segs[i].sgp->nsects; j++){
		    sp = sects[k + j].sp;
		    if(sp == objc_runtime_setup_sp){
			lseek(outfd, sp->offset, L_SET);
			if(write(outfd, objc_runtime_setup_contents,
				 objc_runtime_setup_size) !=
						       objc_runtime_setup_size){
			    system_error("can't write section contents for "
					 "section (%s,%s) to output file: %s", 
					 sp->segname, sp->sectname, output);
			    goto objc_runtime_setup_cleanup;
			}
		    }
		    else{
			/* write the original section */
			if(sects[k + j].offset + sp->size > input_size){
			    error("truncated or malformed object file: %s "
				  "(section (%0.16s,%0.16s) extends past the "
				  "end of the file)",input, sp->segname,
				  sp->sectname);
			    goto objc_runtime_setup_cleanup;
			}
			lseek(outfd, sp->offset, L_SET);
		 
			if(write(outfd,(char *)input_addr + sects[k + j].offset,
			   sp->size) != sp->size){
			    system_fatal("can't write section contents for "
					 "section (%s,%s) to output file: %s", 
					 sp->segname, sp->sectname, output);
			    goto objc_runtime_setup_cleanup;
			}
		    }
		}
		/* write the segment padding */
		if(write(outfd, (char *)pad_addr, segs[i].padsize) !=
			 segs[i].padsize){
		    system_error("can't write segment padding for segment %s to"
				 " output file: %s", segs[i].sgp->segname,
				 output);
		    goto objc_runtime_setup_cleanup;
		}
	    }
	    else{
		/* write the original segment */
		if(strcmp(segs[i].sgp->segname, SEG_LINKEDIT) != 0 ||
		   i != nsegs - 1){
		    if(segs[i].fileoff + segs[i].sgp->filesize > input_size){
			error("truncated or malformed object file: %s "
			      "(segment: %s extends past the end of "
			      "the file)", input, segs[i].sgp->segname);
			goto objc_runtime_setup_cleanup;
		    }
		    if(write(outfd, (char *)input_addr + segs[i].fileoff,
		       segs[i].sgp->filesize) != segs[i].sgp->filesize){
			system_error("can't write segment contents for "
				     "segment: %s to output file: %s", 
				     segs[i].sgp->segname, output);
			goto objc_runtime_setup_cleanup;
		    }
		}
	    }
	    k += segs[i].sgp->nsects;
	}
	/* write the linkedit info */
	size = input_size - low_linkedit;
	if(write(outfd, (char *)input_addr + low_linkedit, size) != size){
	    system_error("can't write link edit information to output file: %s",
			 output);
	    goto objc_runtime_setup_cleanup;
	}
	lseek(outfd, 0, L_SET);
	size = sizeof(struct mach_header) + mhp->sizeofcmds;
	if(swapped){
	    lcp = load_commands;
	    for(i = 0; i < mhp->ncmds; i++){
		lc = *lcp;
		switch(lcp->cmd){
		case LC_SEGMENT:
		    sgp = (struct segment_command *)lcp;
		    sp = (struct section *)((char *)sgp +
					    sizeof(struct segment_command));
		    swap_section(sp, sgp->nsects, host_byte_order);
		    swap_segment_command(sgp, host_byte_order);
		    break;
		case LC_SYMTAB:
		    stp = (struct symtab_command *)lcp;
		    swap_symtab_command(stp, host_byte_order);
		    break;
		case LC_SYMSEG:
		    ssp = (struct symseg_command *)lcp;
		    swap_symseg_command(ssp, host_byte_order);
		    break;
		default:
		    swap_load_command(lcp, host_byte_order);
		    break;
		}
		lcp = (struct load_command *)((char *)lcp + lc.cmdsize);
	    }
	    swap_mach_header(mhp, host_byte_order);
	}

	if(write(outfd, mhp, size) != size){
	    system_error("can't write headers to output file: %s", output);
	    goto objc_runtime_setup_cleanup;
	}

	if(close(outfd) == -1){
	    system_error("can't close output file: %s", output);
	    outfd = -1;
	    goto objc_runtime_setup_cleanup;
	}

/*
	if(segs != NULL)
	    free(segs);
	if(sects != NULL)
	    free(sects);
	if(new_mhp != NULL)
	    free(new_mhp);
	if(pad_addr != 0){
	    if((r = vm_deallocate(task_self(), (vm_address_t)pad_addr,
				  SEGALIGN)) != KERN_SUCCESS){
		machkern_error(r, "Can't deallocate segment pad buffer");
		return(0);
	    }
	}
*/
	return(1);

objc_runtime_setup_cleanup:
	/*
	 * Cleanup if there were errors.
	 */
	if(outfd != -1)
	    close(outfd);
	if(segs != NULL)
	    free(segs);
	if(sects != NULL)
	    free(sects);
	if(new_mhp != NULL)
	    free(new_mhp);
	if(pad_addr != 0){
	    if((r = vm_deallocate(task_self(), (vm_address_t)pad_addr,
				  SEGALIGN)) != KERN_SUCCESS){
		machkern_error(r, "Can't deallocate segment pad buffer");
	    }
	}
	return(0);
}

/*
 * Function for qsort for comparing segments vmaddr feilds
 */
static
int
qsort_vmaddr(
const struct rep_seg *seg1,
const struct rep_seg *seg2)
{
	return((long)(seg1->sgp->vmaddr) - (long)(seg2->sgp->vmaddr));
}

/*
 * Function for qsort for comparing segments fileoff fields
 */
static
int
qsort_fileoff(
const struct rep_seg *seg1,
const struct rep_seg *seg2)
{
	return((long)(seg1->sgp->fileoff) - (long)(seg2->sgp->fileoff));
}

/*
 * Function for qsort for comparing sections offset fields
 */
static
int
qsort_offset(
const struct rep_sect *sect1,
const struct rep_sect *sect2)
{
	return((long)(sect1->sp->offset) - (long)(sect2->sp->offset));
}

/*
 * allocate is just a wrapper around malloc that prints and error message and
 * exits if the malloc fails.
 */
static
void *
allocate(
long size)
{
    void *p;

	if((p = (void *)malloc(size)) == (char *)0)
	    system_fatal("virtual memory exhausted (malloc failed)");
	return(p);
}

/*
 * Print the error message and set the non-fatal error indication.
 */
static
void
error(
const char *format,
...)
{
    va_list ap;

	va_start(ap, format);
        fprintf(stderr, "%s: ", progname);
	vfprintf(stderr, format, ap);
        fprintf(stderr, "\n");
	va_end(ap);
}

/*
 * Print the fatal error message, and exit non-zero.
 */
static
void
fatal(
const char *format,
...)
{
    va_list ap;

	va_start(ap, format);
        fprintf(stderr, "%s: ", progname);
	vfprintf(stderr, format, ap);
        fprintf(stderr, "\n");
	va_end(ap);
	exit(1);
}

/*
 * Print the error message along with the system error message, set the
 * non-fatal error indication.
 */
static
void
system_error(
const char *format,
...)
{
    va_list ap;

	va_start(ap, format);
        fprintf(stderr, "%s: ", progname);
	vfprintf(stderr, format, ap);
	fprintf(stderr, " (%s)\n", strerror(errno));
	va_end(ap);
}

/*
 * Print the fatal message along with the system error message, and exit
 * non-zero.
 */
static
void
system_fatal(
const char *format,
...)
{
    va_list ap;

	va_start(ap, format);
        fprintf(stderr, "%s: ", progname);
	vfprintf(stderr, format, ap);
	fprintf(stderr, " (%s)\n", strerror(errno));
	va_end(ap);
	exit(1);
}

/*
 * Print the error message along with the mach error string.
 */
static
void
machkern_error(
kern_return_t r,
char *format,
...)
{
    va_list ap;

	va_start(ap, format);
        fprintf(stderr, "%s: ", progname);
	vfprintf(stderr, format, ap);
	fprintf(stderr, " (%s)\n", mach_error_string(r));
	va_end(ap);
}

/*
 * Round v to a multiple of r.
 */

long
round(
long v,
unsigned long r)
{
	r--;
	v += r;
	v &= ~(long)r;
	return(v);
}

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.