sections.c   [plain text]


/*
 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_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. 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_LICENSE_HEADER_END@
 */
#ifdef SHLIB
#include "shlib.h"
#endif /* SHLIB */
/*
 * This file contains the routines to manage the merging of the sections that
 * appear in the headers of the input files.  It builds a merged section table
 * (which is a linked list of merged_segments with merged_sections linked to
 * them).  The merged section list becomes the output files's section list.
 */
#include <stdlib.h>
#if !(defined(KLD) && defined(__STATIC__))
#include <stdio.h>
#include <mach/mach.h>
#else /* defined(KLD) && defined(__STATIC__) */
#include <mach/kern_return.h>
#endif /* !(defined(KLD) && defined(__STATIC__)) */
#include <stdarg.h>
#include <string.h>
#include "stuff/openstep_mach.h"
#include <mach-o/loader.h>
#include <mach-o/nlist.h>
#include <mach-o/reloc.h>
#include <mach-o/hppa/reloc.h>
#include <ar.h>
#include "stuff/arch.h"
#include "stuff/reloc.h"

#include "ld.h"
#include "specs.h"
#include "objects.h"
#include "pass1.h"
#include "symbols.h"
#include "sections.h"
#include "cstring_literals.h"
#include "4byte_literals.h"
#include "8byte_literals.h"
#include "literal_pointers.h"
#include "indirect_sections.h"
#include "mod_sections.h"
#include "coalesced_sections.h"
#include "pass2.h"
#include "generic_reloc.h"
#include "i860_reloc.h"
#include "ppc_reloc.h"
#include "m88k_reloc.h"
#include "hppa_reloc.h"
#include "sparc_reloc.h"
#include "sets.h"
#include "hash_string.h"
#include "layout.h"
#include "dylibs.h"

/* the pointer to the head of the output file's section list */
__private_extern__ struct merged_segment *merged_segments = NULL;
#ifdef RLD
/*
 * The pointer to the head of the output file's section list before they are
 * all placed in one segment for the MH_OBJECT format in layout().  This is
 * used in reset_merged_sections() to put the list back with it's original
 * segments.
 */
__private_extern__ struct merged_segment *original_merged_segments = NULL;
#endif /* RLD */

/*
 * The total number relocation entries, used only in layout() to help
 * calculate the size of the link edit segment.
 */
__private_extern__ unsigned long nreloc = 0;

/* table for S_* flags (section types) for error messages */
static const char *
#ifndef __DYNAMIC__
const
#endif
section_flags[] = {
	"S_REGULAR",
	"S_ZEROFILL",
	"S_CSTRING_LITERALS",
	"S_4BYTE_LITERALS",
	"S_8BYTE_LITERALS",
	"S_LITERAL_POINTERS",
	"S_NON_LAZY_SYMBOL_POINTERS",
	"S_LAZY_SYMBOL_POINTERS",
	"S_SYMBOL_STUBS",
	"S_MOD_INIT_FUNC_POINTERS",
	"S_MOD_TERM_FUNC_POINTERS",
	"S_COALESCED"
};

#ifndef RLD
/*
 * These are the arrays used for finding archive,object,symbol name
 * triples and object,symbol name pairs when processing -sectorder options.
 * The array of symbol names is the load_order structure pointed to by
 * the cur_load_orders field of the object_file structure.
 */
struct archive_name {
    char *archive_name;	/* name of the archive file */
    struct object_name
	*object_names;	/* names of the archive members */
    unsigned long
	nobject_names;	/* number of archive members */
};
static struct archive_name *archive_names = NULL;
static unsigned long narchive_names = 0;

struct object_name {
    char *object_name;	/* name of object file */
    unsigned long
	index_length;	/* if this is not in an archive its an index into the */
			/*  object_name to the base name of the object file */
			/*  name else it is the length of the object name */
			/*  which is an archive member name that may have */
			/*  been truncated. */
    struct object_file	/* pointer to the object file */
	*object_file;
};
static struct object_name *object_names = NULL;
static unsigned long nobject_names = 0;

struct load_symbol {
    char *symbol_name;	/* the symbol name this is hashed on */
    char *object_name;	/* the loaded object that contains this symbol */
    char *archive_name;	/* the loaded archive that contains this object */
			/*  or NULL if not in an archive */
    unsigned long
	index_length;	/* if archive_name is NULL the this is index into the */
			/*  object_name to the base name of the object file */
			/*  name else it is the length of the object name */
			/*  which is an archive member name that may have */
			/*  been truncated. */
    struct load_order
	*load_order;	/* the load order for the above triple names */
    struct load_symbol
	*other_names;	/* other load symbols for the same symbol_name */
    struct load_symbol
	*next;		/* next hash table pointer */
};
#define LOAD_SYMBOL_HASHTABLE_SIZE 10000
static struct load_symbol **load_symbol_hashtable = NULL;
static struct load_symbol *load_symbols = NULL;
static unsigned long load_symbols_size = 0;
static unsigned long load_symbols_used = 0;
static unsigned long ambiguous_specifications = 0;

static void layout_ordered_section(
    struct merged_section *ms);
static void create_name_arrays(
    void);
static struct archive_name *create_archive_name(
    char *archive_name);
static void create_object_name(
    struct object_name **object_names,
    unsigned long *nobject_names,
    char *object_name,
    unsigned long index_length,
    char *archive_name);
static void free_name_arrays(
    void);
static void create_load_symbol_hash_table(
    unsigned long nsection_symbols);
static void free_load_symbol_hash_table(
    void);
static void create_load_symbol_hash_table_for_object(
    char *archive_name,
    char *object_name,
    unsigned long index_length,
    struct load_order *load_orders,
    unsigned long nload_orders);
static struct load_order *lookup_load_order(
    char *archive_name,
    char *object_name,
    char *symbol_name,
    struct merged_section *ms,
    unsigned long line_number);
static char * trim(
    char *name);
static struct section_map *lookup_section_map(
    char *archive_name,
    char *object_name);
static int qsort_load_order_names(
    const struct load_order *load_order1,
    const struct load_order *load_order2);
static int bsearch_load_order_names(
    char *symbol_name,
    const struct load_order *load_order);
static int qsort_archive_names(
    const struct archive_name *archive_name1,
    const struct archive_name *archive_name2);
static int bsearch_archive_names(
    const char *name,
    const struct archive_name *archive_name);
static int qsort_object_names(
    const struct object_name *object_name1,
    const struct object_name *object_name2);
static int bsearch_object_names(
    const char *name,
    const struct object_name *object_name);
static int qsort_fine_reloc_input_offset(
    const struct fine_reloc *fine_reloc1,
    const struct fine_reloc *fine_reloc2);
static int qsort_order_load_map_orders(
    const struct order_load_map *order_load_map1,
    const struct order_load_map *order_load_map2);
static void create_order_load_maps(
    struct merged_section *ms,
    unsigned long norders);
static void count_relocs(
    struct section_map *map,
    struct relocation_info *relocs,
    unsigned long *nlocrel,
    unsigned long *nextrel);
static void scatter_copy(
    struct section_map *map,
    char *contents);
static void reloc_output_for_dyld(
    struct section_map *map,
    struct relocation_info *relocs,
    struct relocation_info *output_locrel,
    struct relocation_info *output_extrel,
    unsigned long *nlocrel,
    unsigned long *nextrel);
static enum bool is_merged_section_read_only(
    struct merged_section *key);
static unsigned long scatter_copy_relocs(
    struct section_map *map,
    struct relocation_info *relocs,
    struct relocation_info *output_relocs);
#endif /* !defined(RLD) */
#ifdef DEBUG
static void print_load_symbol_hash_table(
    void);
#endif /* DEBUG */

/*
 * merge_sections() merges the sections of the current object file (cur_obj)
 * into the merged section list that will be in the output file.  For each
 * section in the current object file it records the offset that section will
 * start in the output file.  It also accumulates the size of each merged
 * section, the number of relocation entries in it and the maximum alignment.
 */
__private_extern__
void
merge_sections(void)
{
    unsigned long i;
    struct section *s;
    struct merged_section *ms;

	for(i = 0; i < cur_obj->nsection_maps; i++){
	    s = cur_obj->section_maps[i].s;
	    ms = create_merged_section(s);
	    if(errors)
		return;
	    cur_obj->section_maps[i].output_section = ms;
	    switch(ms->s.flags & SECTION_TYPE){
	    case S_REGULAR:
	    case S_ZEROFILL:
		/*
		 * For the base file of an incremental link all that is needed
		 * is the section (and it's alignment) so the symbols can refer
		 * to them.  Their contents do not appear in the output file.
		 * If the section size is zero then do NOT adjust the merged
		 * section size to the alignment because if the merged size
		 * was not aligned then that area created does not get flushed
		 * because its associated with a section of size 0.
		 */
		if(cur_obj != base_obj && s->size != 0){
		    cur_obj->section_maps[i].flush_offset = ms->s.size;
		    ms->s.size = round(ms->s.size, 1 << s->align);
		    cur_obj->section_maps[i].offset = ms->s.size;
		    ms->s.size   += s->size;
		    ms->s.nreloc += s->nreloc;
		    nreloc += s->nreloc;
		}
#ifdef KLD
		/*
		 * For KLD the section's alignment from the base file is NOT
		 * picked up.
		 */
		if(cur_obj != base_obj)
#endif /* KLD */
		    if(s->align > ms->s.align)
			ms->s.align = s->align;
		if(dynamic != TRUE)
		    ms->s.flags |= (ms->s.flags & SECTION_ATTRIBUTES);
		break;

	    case S_CSTRING_LITERALS:
	    case S_4BYTE_LITERALS:
	    case S_8BYTE_LITERALS:
	    case S_LITERAL_POINTERS:
	    case S_SYMBOL_STUBS:
	    case S_NON_LAZY_SYMBOL_POINTERS:
	    case S_LAZY_SYMBOL_POINTERS:
	    case S_MOD_INIT_FUNC_POINTERS:
	    case S_MOD_TERM_FUNC_POINTERS:
	    case S_COALESCED:
		if(arch_flag.cputype == CPU_TYPE_I860)
			error_with_cur_obj("literal section (%.16s,%.16s) "
		    		       "not allowed in I860 cputype objects",
				       ms->s.segname, ms->s.sectname);
#ifdef KLD
		/*
		 * For KLD the section's alignment from the base file is NOT
		 * picked up.
		 */
		if(cur_obj != base_obj)
#endif /* KLD */
		    if(s->align > ms->s.align)
			ms->s.align = s->align;
		if(dynamic != TRUE)
		    ms->s.flags |= (ms->s.flags & SECTION_ATTRIBUTES);
		break;

	    default:
		fatal("internal error: merge_section() called "
		    "with unknown section type (0x%x) for section (%.16s,"
		    "%.16s)", (unsigned int)(ms->s.flags & SECTION_TYPE),
		    ms->s.segname, ms->s.sectname);
		break;
	    }
#ifndef RLD
	    /*
	     * For dynamic shared libraries record the section map of the
	     * (__OBJC,__module_info) section so it can be used to fill in
	     * objc_module_info_{addr,size} of the module table entries.
	     * Also check to see that it is a regular section.
	     */
	    if(filetype == MH_DYLIB &&
	       strcmp(s->segname, SEG_OBJC) == 0 &&
	       strcmp(s->sectname, SECT_OBJC_MODULES) == 0){
		if((ms->s.flags & SECTION_TYPE) != S_REGULAR)
		    error_with_cur_obj("for MH_DYLIB output files section "
			"(%.16s,%.16s) must have a section type of S_REGULAR",
			s->segname, s->sectname);
		cur_obj->objc_module_info = cur_obj->section_maps + i;
	    }
#endif
	}
}

/*
 * create_merged_section() looks for the section passed to it in the merged
 * section list.  If the section is found then it is check to see the flags
 * of the section matches and if so returns a pointer to the merged section
 * structure for it.  If the flags don't match it is an error.  If no merged
 * section structure is found then one is created and added to the end of the
 * list and a pointer to it is returned.
 */
__private_extern__
struct merged_section *
create_merged_section(
struct section *s)
{
    struct merged_segment **p, *msg;
    struct merged_section **q, **r, *ms;

	p = &merged_segments;
	while(*p){
	    msg = *p;
	    /* see if this is section is in this segment */
	    if(strncmp(msg->sg.segname, s->segname, sizeof(s->segname)) == 0){
		/*
		 * Depending on the flags of the section depends on which list
		 * it might be found in.  In either case it must not be found in
		 * the other list.
		 */
		if((s->flags & SECTION_TYPE) == S_ZEROFILL){
		    q = &(msg->zerofill_sections);
		    r = &(msg->content_sections);
		}
		else{
		    q = &(msg->content_sections);
		    r = &(msg->zerofill_sections);
		}
		/* check to see if it is in the list it might be found in */
		while(*q){
		    ms = *q;
		    if(strncmp(ms->s.sectname, s->sectname,
			       sizeof(s->sectname)) == 0){
			if((ms->s.flags & SECTION_TYPE) !=
			   (s->flags & SECTION_TYPE)){
			    error_with_cur_obj("section's (%.16s,%.16s) type "
				"%s does not match previous objects type %s",
				s->segname, s->sectname,
				section_flags[s->flags & SECTION_TYPE],
				section_flags[ms->s.flags & SECTION_TYPE]);
			    return(NULL);
			}
			if((ms->s.flags & SECTION_TYPE) == S_SYMBOL_STUBS &&
			   ms->s.reserved2 != s->reserved2){
			    error_with_cur_obj("section's (%.16s,%.16s) sizeof "
				"stub %lu does not match previous objects "
				"sizeof stub %lu", s->segname, s->sectname,
				s->reserved2, ms->s.reserved2);
			    return(NULL);
			}
			return(ms);
		    }
		    q = &(ms->next);
		}
		/*
		 * It was not found in the list it might be in so check to make
		 * sure it is not in the other list where it shouldn't be. 
		 */
		while(*r){
		    ms = *r;
		    if(strncmp(ms->s.sectname, s->sectname,
			       sizeof(s->sectname)) == 0){
			error_with_cur_obj("section's (%.16s,%.16s) type %s "
			    "does not match previous objects type %s",
			    s->segname, s->sectname,
			    section_flags[s->flags & SECTION_TYPE],
			    section_flags[ms->s.flags & SECTION_TYPE]);
			return(NULL);
		    }
		    r = &(ms->next);
		}
		/* add it to the list it should be in */
		msg->sg.nsects++;
		*q = allocate(sizeof(struct merged_section));
		ms = *q;
		memset(ms, '\0', sizeof(struct merged_section));
		strncpy(ms->s.sectname, s->sectname, sizeof(s->sectname));
		strncpy(ms->s.segname, s->segname, sizeof(s->segname));
		/*
		 * This needs to be something other than zero (NO_SECT) so the
		 * call to the *_reloc() routines in count_reloc() can determine
		 * if a relocation to a symbol stub is now referencing an
		 * absolute symbol with a pcrel relocation entry.
		 */
		ms->output_sectnum = 1;
		if(dynamic != TRUE)
		    ms->s.flags = (s->flags & ~SECTION_ATTRIBUTES);
		else
		    ms->s.flags = s->flags;
		if((ms->s.flags & SECTION_TYPE) == S_CSTRING_LITERALS){
		    ms->literal_data = allocate(sizeof(struct cstring_data));
		    memset(ms->literal_data, '\0', sizeof(struct cstring_data));
		    ms->literal_merge = cstring_merge;
		    ms->literal_order = cstring_order;
		    ms->literal_output = cstring_output;
		    ms->literal_free = cstring_free;
		}
		else if((ms->s.flags & SECTION_TYPE) == S_4BYTE_LITERALS){
		    ms->literal_data = allocate(sizeof(struct literal4_data));
		    memset(ms->literal_data, '\0',sizeof(struct literal4_data));
		    ms->literal_merge = literal4_merge;
		    ms->literal_order = literal4_order;
		    ms->literal_output = literal4_output;
		    ms->literal_free = literal4_free;
		}
		else if((ms->s.flags & SECTION_TYPE) == S_8BYTE_LITERALS){
		    ms->literal_data = allocate(sizeof(struct literal8_data));
		    memset(ms->literal_data, '\0',sizeof(struct literal8_data));
		    ms->literal_merge = literal8_merge;
		    ms->literal_order = literal8_order;
		    ms->literal_output = literal8_output;
		    ms->literal_free = literal8_free;
		}
		else if((ms->s.flags & SECTION_TYPE) == S_LITERAL_POINTERS){
		    ms->literal_data =
				  allocate(sizeof(struct literal_pointer_data));
		    memset(ms->literal_data, '\0',
					   sizeof(struct literal_pointer_data));
		    ms->literal_merge = literal_pointer_merge;
		    ms->literal_order = literal_pointer_order;
		    ms->literal_output = literal_pointer_output;
 		    ms->literal_free = literal_pointer_free;
		}
#ifndef SA_RLD
		else if((ms->s.flags & SECTION_TYPE) == S_SYMBOL_STUBS ||
		   (ms->s.flags & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS ||
		   (ms->s.flags & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS){
		    ms->literal_data =
			allocate(sizeof(struct indirect_section_data));
		    memset(ms->literal_data, '\0',
			   sizeof(struct indirect_section_data));
		    ms->literal_merge = indirect_section_merge;
		    ms->literal_order = indirect_section_order;
		    ms->literal_output = NULL;
		    ms->literal_free = indirect_section_free;
		    if((ms->s.flags & SECTION_TYPE) == S_SYMBOL_STUBS)
			ms->s.reserved2 = s->reserved2;
		}
#endif /* !defined(SA_RLD) */
		else if((ms->s.flags & SECTION_TYPE) ==
			S_MOD_INIT_FUNC_POINTERS ||
		        (ms->s.flags & SECTION_TYPE) ==
			S_MOD_TERM_FUNC_POINTERS){
		    ms->literal_data = NULL;
		    ms->literal_merge = mod_section_merge;
		    ms->literal_order = mod_section_order;
		    ms->literal_output = NULL;
 		    ms->literal_free = NULL;
		}
		else if((ms->s.flags & SECTION_TYPE) == S_COALESCED){
		    ms->literal_data = NULL;
		    ms->literal_merge = coalesced_section_merge;
		    ms->literal_order = coalesced_section_order;
		    ms->literal_output = NULL;
		    ms->literal_free = NULL;
		}
#ifdef RLD
		ms->set_num = cur_set;
#endif /* RLD */
		return(ms);
	    }
	    p = &(msg->next);
	}
	/*
	 * The segment this section is in wasn't found so add a merged segment
	 * for it and add a merged section to that segment for this section.
	 */
	*p = allocate(sizeof(struct merged_segment));
	msg = *p;
	memset(msg, '\0', sizeof(struct merged_segment));
	strncpy(msg->sg.segname, s->segname, sizeof(s->segname));
	msg->sg.nsects = 1;
	msg->filename = outputfile;
#ifdef RLD
	msg->set_num = cur_set;
#endif /* RLD */
	if((s->flags & SECTION_TYPE) == S_ZEROFILL)
	    q = &(msg->zerofill_sections);
	else
	    q = &(msg->content_sections);
	*q = allocate(sizeof(struct merged_section));
	ms = *q;
	memset(ms, '\0', sizeof(struct merged_section));
	strncpy(ms->s.sectname, s->sectname, sizeof(s->sectname));
	strncpy(ms->s.segname, s->segname, sizeof(s->segname));
	/*
	 * This needs to be something other than zero (NO_SECT) so the
	 * call to the *_reloc() routines in count_reloc() can determine
	 * if a relocation to a symbol stub is now referencing an
	 * absolute symbol with a pcrel relocation entry.
	 */
	ms->output_sectnum = 1;
	if(dynamic != TRUE)
	    ms->s.flags = (s->flags & ~SECTION_ATTRIBUTES);
	else
	    ms->s.flags = s->flags;
	if((ms->s.flags & SECTION_TYPE) == S_CSTRING_LITERALS){
	    ms->literal_data = allocate(sizeof(struct cstring_data));
	    memset(ms->literal_data, '\0', sizeof(struct cstring_data));
	    ms->literal_merge = cstring_merge;
	    ms->literal_order = cstring_order;
	    ms->literal_output = cstring_output;
	    ms->literal_free = cstring_free;
	}
	else if((ms->s.flags & SECTION_TYPE) == S_4BYTE_LITERALS){
	    ms->literal_data = allocate(sizeof(struct literal4_data));
	    memset(ms->literal_data, '\0', sizeof(struct literal4_data));
	    ms->literal_merge = literal4_merge;
	    ms->literal_order = literal4_order;
	    ms->literal_output = literal4_output;
	    ms->literal_free = literal4_free;
	}
	else if((ms->s.flags & SECTION_TYPE) == S_8BYTE_LITERALS){
	    ms->literal_data = allocate(sizeof(struct literal8_data));
	    memset(ms->literal_data, '\0', sizeof(struct literal8_data));
	    ms->literal_merge = literal8_merge;
	    ms->literal_order = literal8_order;
	    ms->literal_output = literal8_output;
	    ms->literal_free = literal8_free;
	}
	else if((ms->s.flags & SECTION_TYPE) == S_LITERAL_POINTERS) {
	    ms->literal_data = allocate(sizeof(struct literal_pointer_data));
	    memset(ms->literal_data, '\0', sizeof(struct literal_pointer_data));
	    ms->literal_merge = literal_pointer_merge;
	    ms->literal_order = literal_pointer_order;
	    ms->literal_output = literal_pointer_output;
	    ms->literal_free = literal_pointer_free;
	}
#ifndef SA_RLD
	else if((ms->s.flags & SECTION_TYPE) == S_SYMBOL_STUBS ||
	   (ms->s.flags & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS ||
	   (ms->s.flags & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS){
	    ms->literal_data = allocate(sizeof(struct indirect_section_data));
	    memset(ms->literal_data, '\0',sizeof(struct indirect_section_data));
	    ms->literal_merge = indirect_section_merge;
	    ms->literal_order = indirect_section_order;
	    ms->literal_output = NULL;
	    ms->literal_free = indirect_section_free;
	    if((ms->s.flags & SECTION_TYPE) == S_SYMBOL_STUBS)
		ms->s.reserved2 = s->reserved2;
	}
#endif /* !defined(SA_RLD) */
	else if((ms->s.flags & SECTION_TYPE) == S_MOD_INIT_FUNC_POINTERS ||
		(ms->s.flags & SECTION_TYPE) == S_MOD_TERM_FUNC_POINTERS){
	    ms->literal_data = NULL;
	    ms->literal_merge = mod_section_merge;
	    ms->literal_order = mod_section_order;
	    ms->literal_output = NULL;
	    ms->literal_free = NULL;
	}
	else if((ms->s.flags & SECTION_TYPE) == S_COALESCED){
	    ms->literal_data = NULL;
	    ms->literal_merge = coalesced_section_merge;
	    ms->literal_order = coalesced_section_order;
	    ms->literal_output = NULL;
	    ms->literal_free = NULL;
	}
#ifdef RLD
	ms->set_num = cur_set;
#endif /* RLD */
	return(ms);
}

/*
 * lookup_merged_segment() looks up the specified segment name 
 * in the merged segment list and returns a pointer to the
 * merged segment if it exist.  It returns NULL if it doesn't exist.
 */
__private_extern__
struct merged_segment *
lookup_merged_segment(
char *segname)
{
    struct merged_segment **p, *msg;

	p = &merged_segments;
	while(*p){
	    msg = *p;
	    if(strncmp(msg->sg.segname, segname, sizeof(msg->sg.segname)) == 0)
		return(msg);
	    p = &(msg->next);
	}
	return(NULL);
}

/*
 * lookup_merged_section() looks up the specified section name 
 * (segname,sectname) in the merged section list and returns a pointer to the
 * merged section if it exist.  It returns NULL if it doesn't exist.
 */
__private_extern__
struct merged_section *
lookup_merged_section(
char *segname,
char *sectname)
{
    struct merged_segment **p, *msg;
    struct merged_section **q, *ms;

	p = &merged_segments;
	while(*p){
	    msg = *p;
	    if(strncmp(msg->sg.segname, segname, sizeof(msg->sg.segname)) == 0){
		q = &(msg->content_sections);
		while(*q){
		    ms = *q;
		    if(strncmp(ms->s.sectname, sectname,
			       sizeof(ms->s.sectname)) == 0){
			return(ms);
		    }
		    q = &(ms->next);
		}
		q = &(msg->zerofill_sections);
		while(*q){
		    ms = *q;
		    if(strncmp(ms->s.sectname, sectname,
			       sizeof(ms->s.sectname)) == 0){
			return(ms);
		    }
		    q = &(ms->next);
		}
		return(NULL);
	    }
	    p = &(msg->next);
	}
	return(NULL);
}

/*
 * merge_literal_sections() goes through all the object files to be loaded and
 * merges the literal sections from them.  This is called from layout() and has
 * to be done after all the alignment from all the sections headers have been
 * merged and the command line section alignment has been folded in.  This way
 * the individual literal items from all the objects can be aligned to the
 * output alignment.
 */
__private_extern__
void
merge_literal_sections(void)
{
    unsigned long i, j;
    struct object_list *object_list, **p;
    struct merged_section *ms;
#ifndef RLD
    struct merged_segment **q, *msg;
    struct merged_section **content;

	/*
	 * If any literal (except literal pointer) section has an order file
	 * then process it for that section.
	 */
	q = &merged_segments;
	while(*q){
	    msg = *q;
	    content = &(msg->content_sections);
	    while(*content){
		ms = *content;
		if(ms->order_filename != NULL &&
		   ((ms->s.flags & SECTION_TYPE) == S_CSTRING_LITERALS ||
		    (ms->s.flags & SECTION_TYPE) == S_4BYTE_LITERALS ||
		    (ms->s.flags & SECTION_TYPE) == S_8BYTE_LITERALS ||
		    (ms->s.flags & SECTION_TYPE) == S_SYMBOL_STUBS ||
		    (ms->s.flags & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS||
		    (ms->s.flags & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS ||
		    (ms->s.flags & SECTION_TYPE) == S_MOD_INIT_FUNC_POINTERS ||
		    (ms->s.flags & SECTION_TYPE) == S_MOD_TERM_FUNC_POINTERS ||
		    (ms->s.flags & SECTION_TYPE) == S_COALESCED)){
		    (*ms->literal_order)(ms->literal_data, ms);
		}
		content = &(ms->next);
	    }
	    q = &(msg->next);
	}
#endif /* !defined(RLD) */

	/*
	 * Merged the literals for each object for each section that is a 
	 * literal (but not a literal pointer section).
	 */
	for(p = &objects; *p; p = &(object_list->next)){
	    object_list = *p;
	    for(i = 0; i < object_list->used; i++){
		cur_obj = &(object_list->object_files[i]);
		if(cur_obj == base_obj)
		    continue;
		if(cur_obj->dylib)
		    continue;
		if(cur_obj->bundle_loader)
		    continue;
		if(cur_obj->dylinker)
		    continue;
#ifdef RLD
		if(cur_obj->set_num != cur_set)
		    continue;
#endif /* RLD */
		for(j = 0; j < cur_obj->nsection_maps; j++){
		    ms = cur_obj->section_maps[j].output_section;
		    if((ms->s.flags & SECTION_TYPE) == S_CSTRING_LITERALS ||
		       (ms->s.flags & SECTION_TYPE) == S_4BYTE_LITERALS ||
		       (ms->s.flags & SECTION_TYPE) == S_8BYTE_LITERALS ||
		       (ms->s.flags & SECTION_TYPE) == S_SYMBOL_STUBS ||
		       (ms->s.flags & SECTION_TYPE) ==
						S_NON_LAZY_SYMBOL_POINTERS ||
		       (ms->s.flags & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS ||
		       (ms->s.flags & SECTION_TYPE) ==
						S_MOD_INIT_FUNC_POINTERS ||
		       (ms->s.flags & SECTION_TYPE) ==
						S_MOD_TERM_FUNC_POINTERS ||
		       (ms->s.flags & SECTION_TYPE) == S_COALESCED)
		       (*ms->literal_merge)(ms->literal_data, ms,
					     cur_obj->section_maps[j].s,
					     &(cur_obj->section_maps[j]));
		}
	    }
	}

#ifndef RLD
	/*
	 * Now that the the literals are all merged if any literal pointer
	 * section has an order file then process it for that section.
	 */
	q = &merged_segments;
	while(*q){
	    msg = *q;
	    content = &(msg->content_sections);
	    while(*content){
		ms = *content;
		if(ms->order_filename != NULL &&
		   (ms->s.flags & SECTION_TYPE) == S_LITERAL_POINTERS){
		    (*ms->literal_order)(ms->literal_data, ms);
		}
		content = &(ms->next);
	    }
	    q = &(msg->next);
	}
#endif /* !defined(RLD) */
	/*
	 * Now that the the literals are all merged merge the literal pointers
	 * for each object for each section that is a a literal pointer section.
	 */
	for(p = &objects; *p; p = &(object_list->next)){
	    object_list = *p;
	    for(i = 0; i < object_list->used; i++){
		cur_obj = &(object_list->object_files[i]);
		if(cur_obj == base_obj)
		    continue;
		if(cur_obj->dylib)
		    continue;
		if(cur_obj->bundle_loader)
		    continue;
		if(cur_obj->dylinker)
		    continue;
#ifdef RLD
		if(cur_obj->set_num != cur_set)
		    continue;
#endif /* RLD */
		for(j = 0; j < cur_obj->nsection_maps; j++){
		    ms = cur_obj->section_maps[j].output_section;
		    if((ms->s.flags & SECTION_TYPE) == S_LITERAL_POINTERS)
			(*ms->literal_merge)(ms->literal_data, ms,
					     cur_obj->section_maps[j].s,
					     &(cur_obj->section_maps[j]));
		}
	    }
	}
}

#ifndef RLD
/*
 * layout_ordered_sections() calles layout_ordered_section() for each section
 * that has an order file specified with -sectorder.
 */
__private_extern__
void
layout_ordered_sections(void)
{
    enum bool ordered_sections;
    struct merged_segment **p, *msg;
    struct merged_section **content, **zerofill, *ms;
    struct object_file *last_object;

	/*
	 * Determine if their are any sections that have an order file and if
	 * not just return.  This saves creating the name arrays when there is
	 * no need to.
	 */
	ordered_sections = FALSE;
	p = &merged_segments;
	while(*p){
	    msg = *p;
	    content = &(msg->content_sections);
	    while(*content){
		ms = *content;
		if(ms->order_filename != NULL){
		    ordered_sections = TRUE;
		    break;
		}
		content = &(ms->next);
	    }
	    zerofill = &(msg->zerofill_sections);
	    while(*zerofill){
		ms = *zerofill;
		if(ms->order_filename != NULL){
		    ordered_sections = TRUE;
		    break;
		}
		zerofill = &(ms->next);
	    }
	    if(ordered_sections == TRUE)
		break;
	    p = &(msg->next);
	}
	if(ordered_sections == FALSE)
	    return;

	/*
	 * Add the object file the common symbols that the link editor allocated
	 * into the object file list.
	 */
	last_object = add_last_object_file(&link_edit_common_object);

	/*
	 * Build the arrays of archive names and object names which along
	 * with the load order maps will be use to search for archive,object,
	 * symbol name triples from the load order files specified by the user.
	 */
	create_name_arrays();
#ifdef DEBUG
	if(debug & (1 << 13))
	    print_name_arrays();
#endif /* DEBUG */

	/*
	 * For each merged section that has a load order file layout all objects
	 * that have this section in it.
	 */
	p = &merged_segments;
	while(*p){
	    msg = *p;
	    content = &(msg->content_sections);
	    while(*content){
		ms = *content;
		/* no load order for this section continue */
		if(ms->order_filename == NULL){
		    content = &(ms->next);
		    continue;
		}
		/*
		 * If a regular section (not a literal section) then layout
		 * the sections using symbol names.  Literal sections are
		 * handled by their specific literal merge functions.
		 */
		if((ms->s.flags & SECTION_TYPE) == S_REGULAR)
		    layout_ordered_section(ms);
		content = &(ms->next);
	    }
	    zerofill = &(msg->zerofill_sections);
	    while(*zerofill){
		ms = *zerofill;
		/* no load order for this section continue */
		if(ms->order_filename == NULL){
		    zerofill = &(ms->next);
		    continue;
		}
		layout_ordered_section(ms);
		zerofill = &(ms->next);
	    }
	    p = &(msg->next);
	}

	/*
	 * Free the space for the symbol table.
	 */
	free_load_symbol_hash_table();

	/*
	 * Free the space for the name arrays if there has been no load order
	 * map specified (this is because the map has pointers to the object
	 * names that were allocated in the name arrarys).
	 */
	if(load_map == FALSE)
	    free_name_arrays();

	/*
	 * Remove the object file the common symbols that the link editor
	 * allocated from the object file list.
	 */
	remove_last_object_file(last_object);
}

/*
 * layout_ordered_section() creates the fine reloc maps for the section in
 * each object from the load order file specified with -sectorder.
 */
static
void
layout_ordered_section(
struct merged_section *ms)
{
    unsigned long i, j, k, l;
    struct object_list *object_list, **q;

    unsigned long nsect, nload_orders, nsection_symbols;
    struct load_order *load_orders;
    enum bool start_section, any_order;

    struct nlist *object_symbols;
    char *object_strings;

    unsigned long n, order, output_offset, line_number, line_length;
    unsigned long prev_output_offset;
    unsigned long unused_specifications, no_specifications;
    char *line, *archive_name, *object_name, *symbol_name;
    struct load_order *load_order;
    struct section_map *section_map;
    kern_return_t r;

    struct fine_reloc *fine_relocs;

	/*
	 * Reset the count of the number of symbol for this
	 * section (used as the number of load_symbol structs
	 * to allocate).
	 */
	nsection_symbols = 0;

	/*
	 * For each object file that has this section process it.
	 */
	for(q = &objects; *q; q = &(object_list->next)){
	    object_list = *q;
	    for(i = 0; i < object_list->used; i++){
		cur_obj = &(object_list->object_files[i]);
		if(cur_obj == base_obj)
		    continue;
		if(cur_obj->dylib)
		    continue;
		if(cur_obj->bundle_loader)
		    continue;
		if(cur_obj->dylinker)
		    continue;
		/*
		 * Reset the current section map which points to the
		 * load order map and count in this object that
		 * is being processed for this merged section.  This
		 * will be used in later loops to avoid going through
		 * the section maps again.
		 */
		cur_obj->cur_section_map = NULL;

		for(j = 0; j < cur_obj->nsection_maps; j++){
		    if(cur_obj->section_maps[j].output_section != ms)
			continue;
		    if(cur_obj->section_maps[j].s->size == 0)
			continue;
		    /*
		     * Count the number of symbols in this section in
		     * this object file.  For this object nsect is the
		     * section number for the merged section.  Also
		     * acount for one extra symbol if there is no symbol
		     * at the beginning of the section.
		     */
		    object_symbols = (struct nlist *)(cur_obj->obj_addr 
					     + cur_obj->symtab->symoff);
		    object_strings = (char *)(cur_obj->obj_addr +
					       cur_obj->symtab->stroff);
		    nsect = j + 1;
		    nload_orders = 0;
		    start_section = FALSE;
		    for(k = 0; k < cur_obj->symtab->nsyms; k++){
			if(object_symbols[k].n_sect == nsect &&
			   (object_symbols[k].n_type & N_STAB) == 0){
			    nload_orders++;
			    if(object_symbols[k].n_value == 
			       cur_obj->section_maps[j].s->addr)
				start_section = TRUE;
			}
		    }
		    if(start_section == FALSE)
			nload_orders++;

		    /*
		     * Allocate the load order map for this section in
		     * this object file and set the current section map
		     * in this object that will point to the load order
		     * map and count.
		     */
		    load_orders = allocate(sizeof(struct load_order) *
					   nload_orders);
		    memset(load_orders, '\0',
			   sizeof(struct load_order) * nload_orders);
		    cur_obj->section_maps[j].nload_orders= nload_orders;
		    cur_obj->section_maps[j].load_orders = load_orders;
		    cur_obj->cur_section_map =
					    &(cur_obj->section_maps[j]);

		    /*
		     * Fill in symbol names and values the load order
		     * map for this section in this object file.
		     */
		    l = 0;
		    if(start_section == FALSE){
			load_orders[l].name = ".section_start";
			load_orders[l].value =
				       cur_obj->section_maps[j].s->addr;
			l++;
		    }
		    for(k = 0; k < cur_obj->symtab->nsyms; k++){
			if(object_symbols[k].n_sect == nsect &&
			   (object_symbols[k].n_type & N_STAB) == 0){
			    load_orders[l].name = object_strings +
					  object_symbols[k].n_un.n_strx;
			    load_orders[l].value =
					  object_symbols[k].n_value;
			    l++;
			}
		    }
#ifdef DEBUG
		    if(debug & (1 << 14))
			print_load_order(load_orders, nload_orders, ms,
					 cur_obj, "names and values");
#endif /* DEBUG */

		    /*
		     * Sort the load order map by symbol value so the
		     * size and input offset fields can be set.
		     */
		    qsort(load_orders,
			  nload_orders,
			  sizeof(struct load_order),
			  (int (*)(const void *, const void *))
					       qsort_load_order_values);
		    for(l = 0; l < nload_orders - 1; l++){
			load_orders[l].input_offset =
				       load_orders[l].value -
				       cur_obj->section_maps[j].s->addr;
			load_orders[l].input_size =
					      load_orders[l + 1].value -
					      load_orders[l].value;
		    }
		    load_orders[l].input_offset = load_orders[l].value -
				       cur_obj->section_maps[j].s->addr;
		    load_orders[l].input_size =
				      cur_obj->section_maps[j].s->addr +
				      cur_obj->section_maps[j].s->size -
				      load_orders[l].value;
#ifdef DEBUG
		    if(debug & (1 << 15))
			print_load_order(load_orders, nload_orders, ms,
					 cur_obj, "sizes and offsets");
#endif /* DEBUG */

		    /*
		     * Now sort the load order map by symbol name so
		     * that it can be used for lookup.
		     */
		    qsort(load_orders,
			  nload_orders,
			  sizeof(struct load_order),
			  (int (*)(const void *, const void *))
					       qsort_load_order_names);
#ifdef DEBUG
		    if(debug & (1 << 16))
			print_load_order(load_orders, nload_orders, ms,
					 cur_obj, "sorted by name");
#endif /* DEBUG */
		    /*
		     * Increment the number of load_symbol needed for
		     * this section by the number of symbols in this
		     * object.
		     */
		    nsection_symbols += nload_orders;

		    /*
		     * Since there can only be one of these sections in
		     * the section map and it was found just break out
		     * of the loop looking for it.
		     */
		    break;
		}
	    }
	}
	/*
	 * Create the load_symbol hash table.  Used for looking up
	 * symbol names and trying to match load order file lines to
	 * them if the line is not a perfect match.
	 */
	create_load_symbol_hash_table(nsection_symbols);
	
	/*
	 * Clear the counter of ambiguous secifications before the next
	 * section is processed.
	 */
	ambiguous_specifications = 0;
#ifdef DEBUG
	if(debug & (1 << 13))
	    print_load_symbol_hash_table();
#endif /* DEBUG */

	/*
	 * Parse the load order file by changing '\n' to '\0'.
	 */
	for(i = 0; i < ms->order_size; i++){
	    if(ms->order_addr[i] == '\n')
		ms->order_addr[i] = '\0';
	}

	/*
	 * For lines in the order file set the orders and output_offset
	 * in the load maps for this section in all the object files
	 * that have this section.
	 */
	order = 1;
	output_offset = 0;
	prev_output_offset = 0;
	line_number = 1;
	unused_specifications = 0;
	for(i = 0; i < ms->order_size; ){
	    line = ms->order_addr + i;
	    line_length = strlen(line);

	    parse_order_line(line, &archive_name, &object_name, &symbol_name,
			     ms, line_number);

	    load_order = lookup_load_order(archive_name, object_name,
					   symbol_name, ms, line_number);
	    if(load_order != NULL){
		if(load_order->order != 0){
		    if(archive_name == NULL)
			warning("multiple specification of %s:%s in "
				"-sectorder file: %s line %lu for "
				"section (%.16s,%.16s)", object_name,
				symbol_name, ms->order_filename,
				line_number, ms->s.segname,
				ms->s.sectname);
		    else
			warning("multiple specification of %s:%s:%s in "
				"-sectorder file: %s line %lu for "
				"section (%.16s,%.16s)", archive_name,
				object_name, symbol_name,
				ms->order_filename, line_number,
				ms->s.segname, ms->s.sectname);
		}
		else{
		    load_order->order = order++;
		    output_offset = round(output_offset,
					  (1 << ms->s.align));
		    load_order->output_offset = output_offset;
		    prev_output_offset = output_offset;
		    output_offset += load_order->input_size;
		}
	    }
	    else{
		if(strncmp(symbol_name, ".section_offset",
			   sizeof(".section_offset") - 1) == 0){
		    char *p, *endp;
		    unsigned long offset;

		    p = symbol_name + sizeof(".section_offset");
		    offset = strtoul(p, &endp, 0);
		    if(*endp != '\0')
			error("bad specification of .section_offset in "
			      "-sectorder file: %s line %lu for section "
			      "(%.16s,%.16s) (junk after offset value)",
			      ms->order_filename, line_number, ms->s.segname,
			      ms->s.sectname);
		    else if(offset < output_offset)
			error("bad offset value (0x%x) of .section_offset in "
			      "-sectorder file: %s line %lu for section "
			      "(%.16s,%.16s) (value less than current "
			      "offset 0x%x)", (unsigned int)offset,
			      ms->order_filename, line_number, ms->s.segname,
			      ms->s.sectname, (unsigned int)output_offset);
		    else
			output_offset = offset;
		}
		if(strncmp(symbol_name, ".section_align",
			   sizeof(".section_align") - 1) == 0){
		    char *p, *endp;
		    unsigned long align;

		    p = symbol_name + sizeof(".section_align");
		    align = strtoul(p, &endp, 0);
		    if(*endp != '\0')
			error("bad specification of .section_align in "
			      "-sectorder file: %s line %lu for section "
			      "(%.16s,%.16s) (junk after align value)",
			      ms->order_filename, line_number, ms->s.segname,
			      ms->s.sectname);
		    else if(align > MAXSECTALIGN)
			error("bad align value (%lu) of .section_align in "
			      "-sectorder file: %s line %lu for section "
			      "(%.16s,%.16s) (value must be equal to or less "
			      "than %d)", align, ms->order_filename,
			      line_number, ms->s.segname, ms->s.sectname,
			      MAXSECTALIGN);
		    else
			output_offset = round(output_offset, 1 << align);
		}
		else if(strcmp(symbol_name, ".section_all") == 0){
		    section_map = lookup_section_map(archive_name,
						     object_name);
		    if(section_map != NULL){
			section_map->no_load_order = TRUE;
			output_offset = round(output_offset,
					      (1 << section_map->s->align));
			section_map->offset = output_offset;
			output_offset += section_map->s->size;
		    }
		    else if(sectorder_detail == TRUE){
			if(archive_name == NULL){
			    warning("specification of %s:%s in "
				    "-sectorder file: %s line %lu for "
				    "section (%.16s,%.16s) not used "
				    "(object with that section not in "
				    "loaded objects)", object_name,
				    symbol_name, ms->order_filename,
				    line_number, ms->s.segname,
				    ms->s.sectname);
			}
			else{
			    warning("specification of %s:%s:%s in "
				    "-sectorder file: %s line %lu for "
				    "section (%.16s,%.16s) not used "
				    "(object with that section not in "
				    "loaded objects)", archive_name,
				    object_name, symbol_name,
				    ms->order_filename, line_number,
				    ms->s.segname, ms->s.sectname);
			}
		    }
		    else{
			unused_specifications++;
		    }
		}
		else if(sectorder_detail == TRUE){
		    if(archive_name == NULL){
			warning("specification of %s:%s in -sectorder "
				"file: %s line %lu for section (%.16s,"
				"%.16s) not found in loaded objects",
				object_name, symbol_name,
				ms->order_filename, line_number,
				ms->s.segname, ms->s.sectname);
		    }
		    else{
			warning("specification of %s:%s:%s in "
				"-sectorder file: %s line %lu for "
				"section (%.16s,%.16s) not found in "
				"loaded objects", archive_name,
				object_name, symbol_name,
				ms->order_filename, line_number,
				ms->s.segname, ms->s.sectname);
		    }
		}
		else{
		    unused_specifications++;
		}
	    }
	    i += line_length + 1;
	    line_number++;
	}

	/*
	 * Deallocate the memory for the load order file now that it is
	 * nolonger needed (since the memory has been written on it is
	 * allways deallocated so it won't get written to the swap file
	 * unnecessarily).
	 */
	if((r = vm_deallocate(mach_task_self(), (vm_address_t)
	    ms->order_addr, ms->order_size)) != KERN_SUCCESS)
	    mach_fatal(r, "can't vm_deallocate() memory for -sectorder "
		       "file: %s for section (%.16s,%.16s)",
		       ms->order_filename, ms->s.segname,
		       ms->s.sectname);
	ms->order_addr = NULL;

	/*
	 * For all entries in the load maps that do not have an order
	 * because they were not specified in the load order file
	 * assign them an order.
	 */
	no_specifications = 0;
	for(q = &objects; *q; q = &(object_list->next)){
	    object_list = *q;
	    for(i = 0; i < object_list->used; i++){
		cur_obj = &(object_list->object_files[i]);
		if(cur_obj == base_obj)
		    continue;
		if(cur_obj->dylib)
		    continue;
		if(cur_obj->bundle_loader)
		    continue;
		if(cur_obj->dylinker)
		    continue;
		if(cur_obj->cur_section_map == NULL)
		    continue;
#ifdef DEBUG
		if(debug & (1 << 17))
		    print_load_order(
				cur_obj->cur_section_map->load_orders,
				cur_obj->cur_section_map->nload_orders,
				ms, cur_obj, "file orders assigned");
#endif /* DEBUG */
		load_order = cur_obj->cur_section_map->load_orders;
		n = cur_obj->cur_section_map->nload_orders;

		/*
		 * If there is no orders in this object then cause it to be
		 * treated as if it had a .section_all by default.  This is
		 * done to help the default case work in more cases and not
		 * scatter the object when no symbols were ordered from it.
		 */
		any_order = FALSE;
		for(j = 0; j < n; j++){
		    if(load_order[j].order != 0){
			any_order = TRUE;
			break;
		    }
		}
		if(any_order == FALSE &&
		   cur_obj->cur_section_map->no_load_order == FALSE){
		    cur_obj->cur_section_map->no_load_order = TRUE;
		    output_offset = round(output_offset,
				  (1 << cur_obj->cur_section_map->s->align));
		    cur_obj->cur_section_map->offset = output_offset;
		    output_offset += cur_obj->cur_section_map->s->size;
		    if(sectorder_detail == TRUE){
			if(no_specifications == 0)
			    warning("no specification for the following "
				    "symbols in -sectorder file: %s for "
				    "section (%.16s,%.16s):",
				    ms->order_filename,
				    ms->s.segname, ms->s.sectname);
			for(j = 0; j < n; j++){
			    if(cur_obj->ar_hdr == NULL){
				if(nowarnings == FALSE)
				    print("%s:%s\n", cur_obj->file_name,
					  load_order[j].name);
			    }
			    else{
				if(nowarnings == FALSE)
				    print("%s:%.*s:%s\n", cur_obj->file_name,
					  (int)cur_obj->ar_name_size,
					  cur_obj->ar_name, load_order[j].name);
			    }
			}
		    }
		    no_specifications += n;
		}

		for(j = 0; j < n; j++){
		    if(load_order[j].order == 0){
			if(cur_obj->cur_section_map->no_load_order ==
			   TRUE)
			    continue;
			load_order[j].order = order++;
			output_offset = round(output_offset,
					      (1 << ms->s.align));
			load_order[j].output_offset = output_offset;
			output_offset += load_order[j].input_size;
			if(sectorder_detail == TRUE){
			    if(no_specifications == 0)
				warning("no specification for the following "
					"symbols in -sectorder file: %s for "
					"section (%.16s,%.16s):",
					ms->order_filename,
					ms->s.segname, ms->s.sectname);
			    if(cur_obj->ar_hdr == NULL){
				if(nowarnings == FALSE)
				    print("%s:%s\n", cur_obj->file_name,
					  load_order[j].name);
			    }
			    else{
				if(nowarnings == FALSE)
				    print("%s:%.*s:%s\n", cur_obj->file_name,
					  (int)cur_obj->ar_name_size,
					  cur_obj->ar_name, load_order[j].name);
			    }
			}
			no_specifications++;
		    }
		    else{
			if(cur_obj->cur_section_map->no_load_order ==
			   TRUE){
			    if(cur_obj->ar_hdr == NULL){
				error("specification for both %s:%s "
				      "and %s:%s in -sectorder file: "
				      "%s for section (%.16s,%.16s) "
				      "(not allowed)",
				      cur_obj->file_name,
				      ".section_all",
				      cur_obj->file_name,
				      load_order[j].name,
				      ms->order_filename,
				      ms->s.segname, ms->s.sectname);
			    }
			    else{
				error("specification for both "
				      "%s:%.*s:%s and %s:%.*s:%s "
				      "in -sectorder file: %s for "
				      "section (%.16s,%.16s) "
				      "(not allowed)",
				      cur_obj->file_name,
				      (int)cur_obj->ar_name_size,
				      cur_obj->ar_name,
				      ".section_all",
				      cur_obj->file_name,
				      (int)cur_obj->ar_name_size,
				      cur_obj->ar_name,
				      load_order[j].name,
				      ms->order_filename,
				      ms->s.segname, ms->s.sectname);
			    }
			}
		    }
		}
#ifdef DEBUG
		if(debug & (1 << 18))
		    print_load_order(
				cur_obj->cur_section_map->load_orders,
				cur_obj->cur_section_map->nload_orders,
				ms, cur_obj, "all orders assigned");
#endif /* DEBUG */
	    }
	}
	if(sectorder_detail == FALSE){
	    if(unused_specifications != 0)
		warning("%lu symbols specified in -sectorder file: %s "
			"for section (%.16s,%.16s) not found in "
			"loaded objects", unused_specifications,
			ms->order_filename, ms->s.segname,
			ms->s.sectname);
	    if(no_specifications != 0)
		warning("%lu symbols have no specifications in "
			"-sectorder file: %s for section (%.16s,"
			"%.16s)",no_specifications, ms->order_filename,
			ms->s.segname, ms->s.sectname);
	    if(ambiguous_specifications != 0)
		warning("%lu symbols have ambiguous specifications in "
			"-sectorder file: %s for section (%.16s,"
			"%.16s)", ambiguous_specifications,
			ms->order_filename, ms->s.segname,
			ms->s.sectname);
	}

	/*
	 * There can be seen a ".section_all" and symbol names for the
	 * same object file and these are reported as an error not a
	 * warning.
	 */
	if(errors)
	    return;

	/*
	 * Now the final size of the merged section can be set with all
	 * the contents of the section laid out.
	 */
	ms->s.size = output_offset;

	/*
	 * Finally the fine relocation maps can be allocated and filled
	 * in from the load order maps.
	 */
	for(q = &objects; *q; q = &(object_list->next)){
	    object_list = *q;
	    for(i = 0; i < object_list->used; i++){
		cur_obj = &(object_list->object_files[i]);
		if(cur_obj == base_obj)
		    continue;
		if(cur_obj->dylib)
		    continue;
		if(cur_obj->bundle_loader)
		    continue;
		if(cur_obj->dylinker)
		    continue;
		if(cur_obj->cur_section_map == NULL)
		    continue;
		/*
		 * If this object file has no load orders (a
		 * .section_all for it was specified) then just
		 * create a single fine relocation entry for it
		 * that take care of the whole section.
		 */
		if(cur_obj->cur_section_map->no_load_order == TRUE){
		    fine_relocs = allocate(sizeof(struct fine_reloc));
		    memset(fine_relocs, '\0', sizeof(struct fine_reloc));
		    cur_obj->cur_section_map->fine_relocs = fine_relocs;
		    cur_obj->cur_section_map->nfine_relocs = 1;
		    fine_relocs[0].input_offset = 0;
		    fine_relocs[0].output_offset =
				       cur_obj->cur_section_map->offset;
		    continue;
		}
		n = cur_obj->cur_section_map->nload_orders;
		load_orders = cur_obj->cur_section_map->load_orders;
		fine_relocs = allocate(sizeof(struct fine_reloc) * n);
		memset(fine_relocs, '\0', sizeof(struct fine_reloc) * n);
		cur_obj->cur_section_map->fine_relocs = fine_relocs;
		cur_obj->cur_section_map->nfine_relocs = n;
		for(j = 0; j < n ; j++){
		    fine_relocs[j].input_offset =
					   load_orders[j].input_offset;
		    fine_relocs[j].output_offset =
					   load_orders[j].output_offset;
		}
		/*
		 * Leave the fine relocation map in sorted order by
		 * their input offset so that the pass2 routines can
		 * use them.
		 */
		qsort(fine_relocs,
		      n,
		      sizeof(struct fine_reloc),
		      (int (*)(const void *, const void *))
					 qsort_fine_reloc_input_offset);

		/*
		 * The load order maps are now no longer needed unless
		 * the load map (-M) has been specified.
		 */
		if(load_map == FALSE){
		    free(cur_obj->cur_section_map->load_orders);
		    cur_obj->cur_section_map->load_orders = NULL;
		    cur_obj->cur_section_map->nload_orders = 0;
		}
	    }
	}

	/*
	 * If the load map option (-M) is specified build the
	 * structures to print the map.
	 */
	if(load_map == TRUE)
	    create_order_load_maps(ms, order - 1);
}

/*
 * parse_order_line() parses a load order line into it's archive name, object
 * name and symbol name.  The format for the lines is the following:
 *
 * [<archive name>:]<object name>:<symbol name>
 *
 * If the archive name is not present NULL is returned, if the object name is
 * not present it is set to point at "" and if the symbol name is not present it
 * is set to "".
 */
__private_extern__
void
parse_order_line(
char *line,
char **archive_name,
char **object_name,
char **symbol_name,
struct merged_section *ms,
unsigned long line_number)
{
    unsigned long line_length;
    char *left_bracket;

	/*
	 * The trim has to be done before the checking for objective-C names
	 * syntax because it could have spaces at the end of the line.
	 */ 
	line = trim(line);

	line_length = strlen(line);
	if(line_length == 0){
	    *archive_name = NULL;
	    (*object_name) = "";
	    (*symbol_name) = "";
	    return;
	}

	/*
	 * To allow the objective-C symbol syntax of:
	 * +-[ClassName(CategoryName) Method:Name]
	 * since the method name can have ':'s the brackets
	 * have to be recognized.  This is the only place where
	 * the link editor knows about this.
	 */
	if(line[line_length - 1] == ']'){
	    left_bracket = strrchr(line, '[');
	    if(left_bracket == NULL)
		fatal("format error in -sectorder file: %s line %lu "
		      "for section (%.16s,%.16s) (no matching "
		      "'[' for ending ']' found in symbol name)",
		      ms->order_filename, line_number,
		      ms->s.segname, ms->s.sectname);
	    *left_bracket = '\0';
	    *symbol_name = strrchr(line, ':');
	    *left_bracket = '[';
	}
	/*
	 * A hack for the 3.2 C++ compiler where the symbol name does not end
	 * with a ']' but with the encoded arguments.
	 */
	else if((left_bracket = strrchr(line, '[')) != NULL){
	    *left_bracket = '\0';
	    *symbol_name = strrchr(line, ':');
	    *left_bracket = '[';
	}
	else
	    *symbol_name = strrchr(line, ':');

	if(*symbol_name == NULL){
	    *symbol_name = line;
	    line = "";
	}
	else{
	    **symbol_name = '\0';
	    (*symbol_name)++;
	}

	*object_name = strrchr(line, ':');
	if(*object_name == NULL){
	    *object_name = line;
	    *archive_name = NULL;
	}
	else{
	    **object_name = '\0';
	    (*object_name)++;
	    *archive_name = line;
	}
}

/*
 * create_name_arrays() build the sorted arrays of archive names and object
 * names which along with the load order maps will be use to search for archive,
 * object,symbol name triples from the load order files specified by the user.
 */
static
void
create_name_arrays(void)
{
    unsigned long i;
    long j;
    struct object_list *object_list, **p;
    struct archive_name *ar;
    char *ar_name, *last_slash;

	for(p = &objects; *p; p = &(object_list->next)){
	    object_list = *p;
	    for(i = 0; i < object_list->used; i++){
		cur_obj = &(object_list->object_files[i]);
		if(cur_obj == base_obj)
		    continue;
		if(cur_obj->dylib)
		    continue;
		if(cur_obj->bundle_loader)
		    continue;
		if(cur_obj->dylinker)
		    continue;
		if(cur_obj->command_line)
		    continue;
		if(cur_obj->ar_hdr != NULL){
		    ar = create_archive_name(cur_obj->file_name);
		    ar_name = allocate(cur_obj->ar_name_size + 1);
    		    strncpy(ar_name, cur_obj->ar_name, cur_obj->ar_name_size);
		    ar_name[cur_obj->ar_name_size] = '\0';
		    create_object_name(&(ar->object_names),&(ar->nobject_names),
				       ar_name, strlen(ar_name),
				       cur_obj->file_name);
		}
		else{
		    last_slash = strrchr(cur_obj->file_name, '/');
		    if(last_slash == NULL)
			j = 0;
		    else
			j = last_slash - cur_obj->file_name + 1;
		    create_object_name(&object_names, &nobject_names,
				       cur_obj->file_name, j, NULL);
		}
	    }
	}

	/*
	 * Sort the arrays of names.
	 */
	if(narchive_names != 0){
	    archive_names = reallocate(archive_names, 
				       sizeof(struct archive_name) *
				       narchive_names);
	    qsort(archive_names,
		  narchive_names,
		  sizeof(struct archive_name),
		  (int (*)(const void *, const void *))qsort_archive_names);
	    for(i = 0; i < narchive_names; i++){
		archive_names[i].object_names = reallocate(
						archive_names[i].object_names,
						sizeof(struct object_name) *
						archive_names[i].nobject_names);
		qsort(archive_names[i].object_names,
		      archive_names[i].nobject_names,
		      sizeof(struct object_name),
		      (int (*)(const void *, const void *))qsort_object_names);
	    }
	}
	if(nobject_names != nobjects)
	    object_names = reallocate(object_names,
				  sizeof(struct object_name) * nobject_names);
	qsort(object_names,
	      nobject_names,
	      sizeof(struct object_name),
	      (int (*)(const void *, const void *))qsort_object_names);
}

/*
 * create_archive_name() creates a slot in the archive names array for the name
 * passed to it.  The name may be seen more than once.  The archive name must
 * not have a ':' in it since that is used to delimit names in the -sectorder
 * files.
 */
static
struct archive_name *
create_archive_name(
char *archive_name)
{
    unsigned long i;
    struct archive_name *ar;

	if(strchr(archive_name, ':') != NULL)
	    fatal("archive name: %s has a ':' (it can't when -sectorder "
		  "options are used)", archive_name);
	ar = archive_names;
	for(i = 0; i < narchive_names; i++){
	    if(strcmp(ar->archive_name, archive_name) == 0)
		return(ar);
	    ar++;
	}
	if(archive_names == NULL)
	    archive_names = allocate(sizeof(struct archive_name) * nobjects);
	ar = archive_names + narchive_names;
	narchive_names++;
	ar->archive_name = archive_name;
	ar->object_names = NULL;
	ar->nobject_names = 0;
	return(ar);
}

/*
 * create_object_name() creates a slot in the object names array passed to it
 * for the name passed to it and the current object (cur_obj).  The size of the
 * array is in nobject_names.  Both the object names array and it's size are
 * passed indirectly since it may be allocated to add the name.  The name should
 * not be duplicated in the array.  If this objects array is for an archive
 * the archive_name is passed for error messages and is NULL in not in an
 * archive.  The object name must not have a ':' in it since that is used to
 * delimit names in the -sectorder files.
 */
static
void
create_object_name(
struct object_name **object_names,
unsigned long *nobject_names,
char *object_name,
unsigned long index_length,
char *archive_name)
{
    unsigned long n, i;
    struct object_name *o;

	if(strchr(object_name, ':') != NULL){
	    if(archive_name != NULL)
		fatal("archive member name: %s(%s) has a ':' in it (it can't "
		      "when -sectorder options are used)", archive_name,
		      object_name);
	    else
		fatal("object file name: %s has a ':' in it (it can't when "
		      "-sectorder options are used)", object_name);
	}

	o = *object_names;
	n = *nobject_names;
	for(i = 0; i < n; i++){
	    if(strcmp(o->object_name, object_name) == 0){
		if(archive_name != NULL){
#ifdef notdef
/*
 * Since the 4.4bsd extened format #1 could be used for long member names this
 * warning is now always printed again.
 */
		    struct ar_hdr ar_hdr;
		    /*
		     * The warning is not printed when the name is likely to
		     * have been truncated.  Some tools use the whole ar_name
		     * but ar(1) uses one less so it can put a '\0' in when
		     * in memory.
		     */
		    if(strlen(object_name) != sizeof(ar_hdr.ar_name) &&
		       strlen(object_name) != sizeof(ar_hdr.ar_name) - 1)
#endif
			warning("duplicate archive member name: %s(%s) loaded ("
				"could be ambiguous when -sectorder options "
				"are used)", archive_name, object_name);
		}
		else
		    warning("duplicate object file name: %s loaded (could be "
			    "ambiguous when -sectorder options are used)",
			    object_name);
	    }
	    o++;
	}
	if(*object_names == NULL)
	    *object_names = allocate(sizeof(struct object_name) * nobjects);
	o = *object_names + *nobject_names;
	(*nobject_names)++;
	o->object_name = object_name;
	o->object_file = cur_obj;
	o->index_length = index_length;
}

/*
 * free_name_arrays() frees up the space created for the sorted name arrays.
 */
static
void
free_name_arrays(void)
{
    unsigned long i, j;

	if(archive_names != NULL){
	    for(i = 0; i < narchive_names; i++){
		for(j = 0; j < archive_names[i].nobject_names; j++){
		    free(archive_names[i].object_names[j].object_name);
		}
	    }
	    free(archive_names);
	    archive_names = NULL;
	    narchive_names = 0;
	}
	if(object_names != NULL){
	    free(object_names);
	    object_names = NULL;
	    nobject_names = 0;
	}
}

/*
 * create_load_symbol_hash_table() creates a hash table of all the symbol names
 * in the section for the current section map.  This table is use by
 * lookup_load_order when an exact match for the specification can't be found.
 */
static
void
create_load_symbol_hash_table(
unsigned long nsection_symbols)
{
    unsigned long i, j;

	/* set up the hash table */
	if(load_symbol_hashtable == NULL)
	    load_symbol_hashtable = allocate(sizeof(struct load_symbol *) *
					     LOAD_SYMBOL_HASHTABLE_SIZE);
	memset(load_symbol_hashtable, '\0', sizeof(struct load_symbol *) *
					    LOAD_SYMBOL_HASHTABLE_SIZE);

	/* set up the load_symbols */
	if(nsection_symbols > load_symbols_size){
	    load_symbols_size = nsection_symbols;
	    load_symbols = reallocate(load_symbols, sizeof(struct load_symbol) *
						    load_symbols_size);
	}
	memset(load_symbols, '\0', sizeof(struct load_symbol) *
				   load_symbols_size);
	load_symbols_used = 0;

	for(i = 0; i < narchive_names; i++){
	    for(j = 0; j < archive_names[i].nobject_names; j++){
		if(archive_names[i].object_names[j].object_file->
							cur_section_map != NULL)
		    create_load_symbol_hash_table_for_object(
			    archive_names[i].archive_name,
			    archive_names[i].object_names[j].object_name,
			    archive_names[i].object_names[j].index_length,
			    archive_names[i].object_names[j].object_file->
						 cur_section_map->load_orders,
			    archive_names[i].object_names[j].object_file->
						 cur_section_map->nload_orders);
	    }
	}

	for(j = 0; j < nobject_names; j++){
	    if(object_names[j].object_file->cur_section_map != NULL)
		create_load_symbol_hash_table_for_object(
		    NULL,
		    object_names[j].object_name,
		    object_names[j].index_length,
		    object_names[j].object_file->cur_section_map->load_orders,
		    object_names[j].object_file->cur_section_map->nload_orders);
	}
}

/*
 * free_load_symbol_hash_table() frees up the space used by the symbol hash
 * table.
 */
static
void
free_load_symbol_hash_table(
void)
{
	/* free the hash table */
	if(load_symbol_hashtable != NULL)
	    free(load_symbol_hashtable);
	load_symbol_hashtable = NULL;

	/* free the load_symbols */
	if(load_symbols != NULL)
	    free(load_symbols);
	load_symbols_size = 0;
	load_symbols_used = 0;
}

/*
 * create_load_symbol_hash_table_for_object() is used by
 * create_load_symbol_hash_table() to create the hash table of all the symbol
 * names in the section that is being scatter loaded.  This routine enters all
 * the symbol names in the load_orders in to the hash table for the specified
 * archive_name object_name pair.
 */
static
void
create_load_symbol_hash_table_for_object(
char *archive_name,
char *object_name,
unsigned long index_length,
struct load_order *load_orders,
unsigned long nload_orders)
{
    unsigned long i, hash_index;
    struct load_symbol *load_symbol, *hash_load_symbol, *other_name;

	for(i = 0; i < nload_orders; i++){
	    /*
	     * Get a new load symbol and set the fields for it this load order
	     * entry.
	     */
	    load_symbol = load_symbols + load_symbols_used;
	    load_symbols_used++;
	    load_symbol->symbol_name = load_orders[i].name;
	    load_symbol->object_name = object_name;
	    load_symbol->archive_name = archive_name;
	    load_symbol->index_length = index_length;
	    load_symbol->load_order = &(load_orders[i]);

	    /* find this symbol's place in the hash table */
	    hash_index = hash_string(load_orders[i].name) %
			 LOAD_SYMBOL_HASHTABLE_SIZE;
	    for(hash_load_symbol = load_symbol_hashtable[hash_index]; 
		hash_load_symbol != NULL;
		hash_load_symbol = hash_load_symbol->next){
		if(strcmp(load_orders[i].name,
			  hash_load_symbol->symbol_name) == 0)
		    break;
	    }
	    /* if the symbol was not found in the hash table enter it */
	    if(hash_load_symbol == NULL){
		load_symbol->other_names = NULL;
		load_symbol->next = load_symbol_hashtable[hash_index]; 
		load_symbol_hashtable[hash_index] = load_symbol;
	    }
	    else{
		/*
		 * If the symbol was found in the hash table go through the
		 * other load symbols for the same name checking if their is
		 * another with exactly the same archive and object name and
		 * generate a warning if so.  Then add this load symbol to the
		 * list of other names.
		 */
		for(other_name = hash_load_symbol;
		    other_name != NULL;
		    other_name = other_name->other_names){

		    if(archive_name != NULL){
			if(strcmp(other_name->object_name, object_name) == 0 &&
			   other_name->archive_name != NULL &&
			   strcmp(other_name->archive_name, archive_name) == 0){
			    warning("symbol appears more than once in the same "
				    "file (%s:%s:%s) which is ambiguous when "
				    "using a -sectorder option",
				    other_name->archive_name,
				    other_name->object_name,
				    other_name->symbol_name);
			    break;
			}
		    }
		    else{
			if(strcmp(other_name->object_name, object_name) == 0 &&
			   other_name->archive_name == NULL){
			    warning("symbol appears more than once in the same "
				    "file (%s:%s) which is ambiguous when "
				    "using a -sectorder option",
				    other_name->object_name,
				    other_name->symbol_name);
			    break;
			}
		    }
		}
		load_symbol->other_names = hash_load_symbol->other_names;
		hash_load_symbol->other_names = load_symbol;
		load_symbol->next = NULL;
	    }
	}
}

/*
 * lookup_load_order() is passed an archive, object, symbol name triple and that
 * is looked up in the name arrays and the load order map and returns a pointer
 * to the load order map that matches it.  Only archive_name may be NULL on
 * input.  It returns NULL if not found.
 */
static
struct load_order *
lookup_load_order(
char *archive_name,
char *object_name,
char *symbol_name,
struct merged_section *ms,
unsigned long line_number)
{
    struct archive_name *a;
    struct object_name *o;
    struct load_order *l;
    unsigned long n;

    unsigned long hash_index, number_of_matches;
    struct load_symbol *hash_load_symbol, *other_name, *first_match;
    char *last_slash, *base_name, *archive_base_name;

	if(archive_name != NULL){
	    a = bsearch(archive_name, archive_names, narchive_names,
			sizeof(struct archive_name),
			(int (*)(const void *, const void *))
						  	 bsearch_archive_names);
	    if(a == NULL)
		goto no_exact_match;
	    o = a->object_names;
	    n = a->nobject_names;
	}
	else{
	    o = object_names;
	    n = nobject_names;
	}

	o = bsearch(object_name, o, n, sizeof(struct object_name),
		    (int (*)(const void *, const void *))bsearch_object_names);
	if(o == NULL)
	    goto no_exact_match;
	if(o->object_file->cur_section_map == NULL)
	    goto no_exact_match;

	l = o->object_file->cur_section_map->load_orders;
	n = o->object_file->cur_section_map->nload_orders;
	l = bsearch(symbol_name, l, n, sizeof(struct load_order),
		    (int (*)(const void *, const void *))
						      bsearch_load_order_names);
	if(l == NULL)
	    goto no_exact_match;
	return(l);

no_exact_match:
	/*
	 * To get here an exact match of the archive_name, object_name, and
	 * symbol_name was not found so try to find some load_order for the
	 * symbol_name using the hash table of symbol names.  First thing here
	 * is to strip leading and trailing blanks from the names.
	 */
	archive_name = trim(archive_name);
	object_name = trim(object_name);
	symbol_name = trim(symbol_name);

	/* find this symbol's place in the hash table */
	hash_index = hash_string(symbol_name) % LOAD_SYMBOL_HASHTABLE_SIZE;
	for(hash_load_symbol = load_symbol_hashtable[hash_index]; 
	    hash_load_symbol != NULL;
	    hash_load_symbol = hash_load_symbol->next){
	    if(strcmp(symbol_name, hash_load_symbol->symbol_name) == 0)
		break;
	}
	/* if the symbol was not found then give up */
	if(hash_load_symbol == NULL)
	    return(NULL);

	/* if this symbol is in only one object file then use that */
	if(hash_load_symbol->other_names == NULL)
	    return(hash_load_symbol->load_order);

	/*
	 * Now try to see if their is just one name that has not had an order
	 * specified for it and use that if that is the case.  This ignores both
	 * the archive_name and the object_name.
	 */
	number_of_matches = 0;
	first_match = NULL;
	for(other_name = hash_load_symbol;
	    other_name != NULL;
	    other_name = other_name->other_names){
	    if(other_name->load_order->order == 0){
		if(first_match == NULL)
		    first_match = other_name;
		number_of_matches++;
	    }
	}
	if(number_of_matches == 1)
	    return(first_match->load_order);
	if(number_of_matches == 0)
	    return(NULL);

	/*
	 * Now try to see if their is just one name that the object file name
	 * specified for it matches and use that if that is the case.  Only the
	 * object basename is used and matched against the base name of the
	 * objects or the archive member name that may have been truncated.
	 * This ignores the archive name.
	 */
	last_slash = strrchr(object_name, '/');
	if(last_slash == NULL)
	    base_name = object_name;
	else
	    base_name = last_slash + 1;
	number_of_matches = 0;
	first_match = NULL;
	for(other_name = hash_load_symbol;
	    other_name != NULL;
	    other_name = other_name->other_names){
	    if(other_name->load_order->order == 0){
		if(other_name->archive_name != NULL){
		    if(strncmp(base_name, other_name->object_name,
			       other_name->index_length) == 0){
			if(first_match == NULL)
			    first_match = other_name;
			number_of_matches++;
		    }
		}
		else{
		    if(strcmp(base_name, other_name->object_name +
			      other_name->index_length) == 0){
			if(first_match == NULL)
			    first_match = other_name;
			number_of_matches++;
		    }
		}
	    }
	}
	if(number_of_matches == 1)
	    return(first_match->load_order);

	/*
	 * Now try to see if their is just one name that the base name of the
	 * archive file name specified for it matches and use that if that is
	 * the case.  This ignores the object name.
	 */
	if(archive_name != NULL){
	    last_slash = strrchr(archive_name, '/');
	    if(last_slash == NULL)
		base_name = archive_name;
	    else
		base_name = last_slash + 1;
	    number_of_matches = 0;
	    first_match = NULL;
	    for(other_name = hash_load_symbol;
		other_name != NULL;
		other_name = other_name->other_names){
		if(other_name->load_order->order == 0){
		    if(other_name->archive_name != NULL){
			last_slash = strrchr(other_name->archive_name, '/');
			if(last_slash == NULL)
			    archive_base_name = other_name->archive_name;
			else
			    archive_base_name = last_slash + 1;

			if(strcmp(base_name, archive_base_name) == 0){
			    if(first_match == NULL)
				first_match = other_name;
			    number_of_matches++;
			}
		    }
		}
	    }
	    if(number_of_matches == 1)
		return(first_match->load_order);
	}

	/*
	 * Now we know their is more than one possible match for this symbol
	 * name.  So the first one that does not have an order is picked and
	 * either the ambiguous_specifications count is incremented or warnings
	 * are generated.
	 */
	first_match = NULL;
	for(other_name = hash_load_symbol;
	    other_name != NULL;
	    other_name = other_name->other_names){
	    if(other_name->load_order->order == 0){
		first_match = other_name;
		if(sectorder_detail){
		    if(archive_name != NULL){
			if(other_name->archive_name != NULL)
			    warning("ambiguous specification of %s:%s:%s in "
				    "-sectorder file: %s line %lu for "
				    "section (%.16s,%.16s) using %s:%s:%s",
				    archive_name, object_name, symbol_name,
				    ms->order_filename, line_number,
				    ms->s.segname, ms->s.sectname,
				    other_name->archive_name,
				    other_name->object_name,
				    other_name->symbol_name);
			else
			    warning("ambiguous specification of %s:%s:%s in "
				    "-sectorder file: %s line %lu for "
				    "section (%.16s,%.16s) using %s:%s",
				    archive_name, object_name, symbol_name,
				    ms->order_filename, line_number,
				    ms->s.segname, ms->s.sectname,
				    other_name->object_name,
				    other_name->symbol_name);
		    }
		    else{
			if(other_name->archive_name != NULL)
			    warning("ambiguous specification of %s:%s in "
				    "-sectorder file: %s line %lu for "
				    "section (%.16s,%.16s) using %s:%s:%s",
				    object_name, symbol_name,
				    ms->order_filename, line_number,
				    ms->s.segname, ms->s.sectname,
				    other_name->archive_name,
				    other_name->object_name,
				    other_name->symbol_name);
			else
			    warning("ambiguous specification of %s:%s in "
				    "-sectorder file: %s line %lu for "
				    "section (%.16s,%.16s) using %s:%s",
				    object_name, symbol_name,
				    ms->order_filename, line_number,
				    ms->s.segname, ms->s.sectname,
				    other_name->object_name,
				    other_name->symbol_name);
		    }
		}
		break;
	    }
	}
	if(sectorder_detail == TRUE){
	    for(other_name = hash_load_symbol;
		other_name != NULL;
		other_name = other_name->other_names){
		if(other_name->load_order->order == 0 &&
		   first_match != other_name){
		    if(archive_name != NULL){
			if(other_name->archive_name != NULL)
			    warning("specification %s:%s:%s ambiguous with "
				    "%s:%s:%s", archive_name, object_name,
				    symbol_name, other_name->archive_name,
				    other_name->object_name,
				    other_name->symbol_name);
			else
			    warning("specification %s:%s:%s ambiguous with "
				    "%s:%s", archive_name, object_name,
				    symbol_name, other_name->object_name,
				    other_name->symbol_name);
		    }
		    else{
			if(other_name->archive_name != NULL)
			    warning("specification %s:%s ambiguous with "
				    "%s:%s:%s", object_name, symbol_name,
				    other_name->archive_name,
				    other_name->object_name,
				    other_name->symbol_name);
			else
			    warning("specification %s:%s ambiguous with "
				    "%s:%s", object_name, symbol_name,
				    other_name->object_name,
				    other_name->symbol_name);
		    }
		}
	    }
	}
	else{
	    ambiguous_specifications++;
	}
	return(first_match->load_order);
}

/*
 * trim() is passed a name and trims the spaces off the begining and endding of
 * the name.  It writes '\0' in the spaces at the end of the name.  It returns
 * a pointer into the trimed name.
 */
static
char *
trim(
char *name)
{
    char *p;

	if(name == NULL)
	    return(name);
	
	while(*name != '\0' && *name == ' ')
	    name++;
	if(*name == '\0')
	    return(name);

	p = name;
	while(*p != '\0')
	    p++;
	p--;
	while(p != name && *p == ' ')
	    *p-- = '\0';
	return(name);
}

/*
 * lookup_section_map() is passed an archive, object pair and that is looked up
 * in the name arrays and returns a pointer to the section map that matches it.
 * It returns NULL if not found.
 */
static
struct section_map *
lookup_section_map(
char *archive_name,
char *object_name)
{
    struct archive_name *a;
    struct object_name *o;
    unsigned long n;

	if(archive_name != NULL){
	    a = bsearch(archive_name, archive_names, narchive_names,
			sizeof(struct archive_name),
			(int (*)(const void *, const void *))
						  	 bsearch_archive_names);
	    if(a == NULL)
		return(NULL);
	    o = a->object_names;
	    n = a->nobject_names;
	}
	else{
	    o = object_names;
	    n = nobject_names;
	}

	o = bsearch(object_name, o, n, sizeof(struct object_name),
		    (int (*)(const void *, const void *))bsearch_object_names);
	if(o == NULL)
	    return(NULL);
	return(o->object_file->cur_section_map);
}
#endif /* RLD */

/*
 * Function for qsort to sort load_order structs by their value
 */
__private_extern__
int
qsort_load_order_values(
const struct load_order *load_order1,
const struct load_order *load_order2)
{
	/*
	 * This test is needed to fix an obscure bug where two symbols have
	 * the same value.  This fix makes the load_orders and the fine_relocs
	 * sorted by value end up in the same order even though the load_order
	 * was sorted by name between their sorts by value.  Without this the
	 * blocks for the symbols at the same address get placed in the file
	 * in the wrong place because the subtraction of their input offsets
	 * does not yeild the size of the block in this case.  This is kinda
	 * a funky fix to avoid adding a size field to the fine reloc struct
	 * which would be very expensive in space.
	 */
	if(load_order1->value == load_order2->value)
	    return(strcmp(load_order1->name, load_order2->name));
	else
	    return(load_order1->value - load_order2->value);
}

#ifndef RLD
/*
 * Function for qsort to sort load_order structs by their name
 */
static
int
qsort_load_order_names(
const struct load_order *load_order1,
const struct load_order *load_order2)
{
	return(strcmp(load_order1->name, load_order2->name));
}

/*
 * Function for bsearch to search load_order structs for their name
 */
static
int
bsearch_load_order_names(
char *symbol_name,
const struct load_order *load_order)
{
	return(strcmp(symbol_name, load_order->name));
}

/*
 * Function for qsort for comparing archive names.
 */
static
int
qsort_archive_names(
const struct archive_name *archive_name1,
const struct archive_name *archive_name2)
{
	return(strcmp(archive_name1->archive_name,
		      archive_name2->archive_name));
}

/*
 * Function for bsearch for finding archive names.
 */
static
int
bsearch_archive_names(
const char *name,
const struct archive_name *archive_name)
{
	return(strcmp(name, archive_name->archive_name));
}

/*
 * Function for qsort for comparing object names.
 */
static
int
qsort_object_names(
const struct object_name *object_name1,
const struct object_name *object_name2)
{
	return(strcmp(object_name1->object_name,
		      object_name2->object_name));
}

/*
 * Function for bsearch for finding object names.
 */
static
int
bsearch_object_names(
const char *name,
const struct object_name *object_name)
{
	return(strcmp(name, object_name->object_name));
}

/*
 * Function for qsort to sort fine_reloc structs by their input_offset
 */
static
int
qsort_fine_reloc_input_offset(
const struct fine_reloc *fine_reloc1,
const struct fine_reloc *fine_reloc2)
{
	return(fine_reloc1->input_offset - fine_reloc2->input_offset);
}

/*
 * Function for qsort to sort order_load_map structs by their order.
 */
static
int
qsort_order_load_map_orders(
const struct order_load_map *order_load_map1,
const struct order_load_map *order_load_map2)
{
	return(order_load_map1->order - order_load_map2->order);
}

/*
 * create_order_load_map() creates the structures to be use for printing the
 * load map.
 */
static
void
create_order_load_maps(
struct merged_section *ms,
unsigned long norders)
{
    unsigned long i, j, k, l, m, n;
    struct order_load_map *order_load_maps;
    struct load_order *load_orders;

	order_load_maps = allocate(sizeof(struct order_load_map) * norders);
	ms->order_load_maps = order_load_maps;
	ms->norder_load_maps = norders;
	l = 0;
	for(i = 0; i < narchive_names; i++){
	    for(j = 0; j < archive_names[i].nobject_names; j++){
	        cur_obj = archive_names[i].object_names[j].object_file;
		for(m = 0; m < cur_obj->nsection_maps; m++){
		    if(cur_obj->section_maps[m].output_section != ms)
			continue;
		    if(cur_obj->section_maps[m].no_load_order == TRUE){
			continue;
		    }
		    n = cur_obj->section_maps[m].nload_orders;
		    load_orders = cur_obj->section_maps[m].load_orders;
		    for(k = 0; k < n ; k++){
		       order_load_maps[l].archive_name = 
						  archive_names[i].archive_name;
		       order_load_maps[l].object_name = 
				   archive_names[i].object_names[j].object_name;
		       order_load_maps[l].symbol_name = load_orders[k].name;
		       order_load_maps[l].value = load_orders[k].value;
		       order_load_maps[l].section_map =
						    &(cur_obj->section_maps[m]);
		       order_load_maps[l].size = load_orders[k].input_size;
		       order_load_maps[l].order = load_orders[k].order;
		       l++;
		    }
		    break;
		}
	    }
	}
	for(j = 0; j < nobject_names; j++){
	    cur_obj = object_names[j].object_file;
	    for(m = 0; m < cur_obj->nsection_maps; m++){
		if(cur_obj->section_maps[m].output_section != ms)
		    continue;
		if(cur_obj->section_maps[m].no_load_order == TRUE)
		    continue;
		n = cur_obj->section_maps[m].nload_orders;
		load_orders = cur_obj->section_maps[m].load_orders;
		for(k = 0; k < n ; k++){
		   order_load_maps[l].archive_name = NULL;
		   order_load_maps[l].object_name = object_names[j].object_name;
		   order_load_maps[l].symbol_name = load_orders[k].name;
		   order_load_maps[l].value = load_orders[k].value;
		   order_load_maps[l].section_map = &(cur_obj->section_maps[m]);
		   order_load_maps[l].size = load_orders[k].input_size;
		   order_load_maps[l].order = load_orders[k].order;
		   l++;
		}
	    }
	}

#ifdef DEBUG
	if(debug & (1 << 19)){
	    for(i = 0; i < norders; i++){
		if(order_load_maps[i].archive_name != NULL)
		    print("%s:", order_load_maps[i].archive_name);
		print("%s:%s\n", order_load_maps[i].object_name,
		order_load_maps[i].symbol_name);
	    }
	}
#endif /* DEBUG */

	qsort(order_load_maps,
	      norders,
	      sizeof(struct order_load_map),
	      (int (*)(const void *, const void *))qsort_order_load_map_orders);
}

/*
 * layout_relocs_for_dyld() sets the counts and indexes for the relocation
 * entries that will be in the output file when it is output_for_dyld.
 */
__private_extern__
void
layout_relocs_for_dyld(
void)
{
    unsigned long i, j, section_type, nlocrel, nextrel;
    struct object_list *object_list, **p;
    struct section_map *map;
    struct relocation_info *relocs;

    struct merged_segment **q, *msg;
    struct merged_section **content, *ms;

	/*
	 * For regular and module initialization function pointer sections count
	 * the number of relocation entries that will be in the type of output
	 * file being created.
	 */
	nlocrel = 0;
	nextrel = 0;
	for(p = &objects; *p; p = &(object_list->next)){
	    object_list = *p;
	    for(i = 0; i < object_list->used; i++){
		cur_obj = &(object_list->object_files[i]);
		if(cur_obj == base_obj)
		    continue;
		if(cur_obj->dylib)
		    continue;
		if(cur_obj->bundle_loader)
		    continue;
		if(cur_obj->dylinker)
		    continue;
		cur_obj->ilocrel = nlocrel;
		cur_obj->iextrel = nextrel;
		for(j = 0; j < cur_obj->nsection_maps; j++){
		    section_type = cur_obj->section_maps[j].s->flags &
				   SECTION_TYPE;
		    if(section_type == S_REGULAR ||
		       section_type == S_MOD_INIT_FUNC_POINTERS ||
		       section_type == S_MOD_TERM_FUNC_POINTERS){
			map = cur_obj->section_maps + j;
			relocs = (struct relocation_info *)
				 (cur_obj->obj_addr + map->s->reloff);
			count_relocs(map, relocs, &nlocrel, &nextrel);
		    }
		}
		/*
		 * For merged sections that have external relocation entries
		 * they need to be kept with the object's other external
		 * relocation entries so that in a dynamic library they get
		 * relocated.
		 */
		for(j = 0; j < cur_obj->nsection_maps; j++){
		    section_type = cur_obj->section_maps[j].s->flags &
				   SECTION_TYPE;
		    if(section_type == S_COALESCED){
			map = cur_obj->section_maps + j;
			if(map->nextrel != 0){
			    map->iextrel = nextrel;
			    nextrel += map->nextrel;
			    cur_obj->nextrel += map->nextrel;
			}
			if(map->nlocrel != 0){
			    map->ilocrel = nlocrel;
			    nlocrel += map->nlocrel;
			    cur_obj->nlocrel += map->nlocrel;
			}
		    }
		}
	    }
	}

	/*
	 * For merged sections that could have relocation entries the number
	 * that will be in the type of output file being created was counted
	 * up as the section was merged.  So here just set the indexes into
	 * the local and external relocation entries and add their counts to
	 * the total.
	 */
	q = &merged_segments;
	while(*q){
	    msg = *q;
	    content = &(msg->content_sections);
	    while(*content){
		ms = *content;
		section_type = ms->s.flags & SECTION_TYPE;
		if(section_type == S_LITERAL_POINTERS ||
		   section_type == S_SYMBOL_STUBS ||
		   section_type == S_LAZY_SYMBOL_POINTERS){
		    if(ms->nlocrel != 0){
			ms->ilocrel = nlocrel;
			nlocrel += ms->nlocrel;
			ms->s.flags |= S_ATTR_LOC_RELOC;
		    }
		    /*
		     * It is an error if one of these types of merged sections
		     * has an external relocation entry and the output is a
		     * multi module dynamic library.  As in a multi module dylib
		     * no library module will "own" it and it will never get
		     * used by the dynamic linker and the item relocated.
		     */
		    if(ms->nextrel != 0){
			if(filetype == MH_DYLIB && multi_module_dylib == TRUE)
			    fatal("internal error: layout_relocs_for_dyld() "
			      "called with external relocation entries for "
			      "merged section (%.16s,%.16s) for multi module "
			      "MH_DYLIB output", ms->s.segname, ms->s.sectname);
/* TODO: can this ever get here?  even if not MH_DYLIB? */
			ms->iextrel = nextrel;
			nextrel += ms->nextrel;
			ms->s.flags |= S_ATTR_EXT_RELOC;
		    }
		}
		content = &(ms->next);
	    }
	    q = &(msg->next);
	}

	output_dysymtab_info.dysymtab_command.nlocrel = nlocrel;
	output_dysymtab_info.dysymtab_command.nextrel = nextrel;
}

/*
 * count_relocs() increments the counts of the nlocrel and nextrel for the
 * current object for the specified section based on which relocation entries
 * will be in the output file.
 */
static
void
count_relocs(
struct section_map *map,
struct relocation_info *relocs,
unsigned long *nlocrel,
unsigned long *nextrel)
{
    unsigned long i, j, pair, prev_nlocrel, prev_nextrel;
    struct relocation_info reloc, pair_reloc;
    struct scattered_relocation_info *sreloc;
    unsigned long r_address, r_type, r_extern, r_symbolnum, r_pcrel, r_value,
		  r_length;
    struct undefined_map *undefined_map;
    struct merged_symbol *merged_symbol, **hash_pointer;
    struct nlist *nlists;
    char *strings;
    enum bool defined, pic;
    struct section_map *local_map;
    struct section_map fake_map;
    struct section fake_s;
    char fake_contents[4];
    struct relocation_info fake_relocs[2];

	/* to shut up compiler warning messages "may be used uninitialized" */
	merged_symbol = NULL;
	defined = FALSE;

	prev_nlocrel = cur_obj->nlocrel;
	prev_nextrel = cur_obj->nextrel;
	for(i = 0; i < map->s->nreloc; i++){
	    /*
	     * Note all errors are not flagged here but left for the *_reloc()
	     * routines to flag them.
	     */
	    reloc = relocs[i];
	    if(cur_obj->swapped)
		swap_relocation_info(&reloc, 1, host_byte_sex);
	    /*
	     * Break out the fields of the relocation entry we need here.
	     */
	    if((reloc.r_address & R_SCATTERED) != 0){
		sreloc = (struct scattered_relocation_info *)(&reloc);
		r_address = sreloc->r_address;
		r_pcrel = sreloc->r_pcrel;
		r_type = sreloc->r_type;
		r_length = sreloc->r_length;
		r_extern = 0;
		r_value = sreloc->r_value;
		/* calculate the r_symbolnum (n_sect) from the r_value */
		r_symbolnum = 0;
		for(j = 0; j < cur_obj->nsection_maps; j++){
		    if(r_value >= cur_obj->section_maps[j].s->addr &&
		       r_value < cur_obj->section_maps[j].s->addr +
				 cur_obj->section_maps[j].s->size){
			r_symbolnum = j + 1;
			break;
		    }
		}
		if(r_symbolnum == 0){
		    /*
		     * The edge case where the last address past then end of
		     * of the last section is referenced.
		     */
		    for(j = 0; j < cur_obj->nsection_maps; j++){
			if(r_value == cur_obj->section_maps[j].s->addr +
				      cur_obj->section_maps[j].s->size){
			    r_symbolnum = j + 1;
			    break;
			}
		    }
		    if(r_symbolnum == 0){
			return;
		    }
		}
	    }
	    else{
		r_address = reloc.r_address;
		r_pcrel = reloc.r_pcrel;
		r_type = reloc.r_type;
		r_length = reloc.r_length;
		r_extern = reloc.r_extern;
		r_symbolnum = reloc.r_symbolnum;
	    }
	    if(r_extern){
		if(r_symbolnum >= cur_obj->symtab->nsyms)
		    return;
		undefined_map = bsearch(&r_symbolnum, cur_obj->undefined_maps,
		    cur_obj->nundefineds, sizeof(struct undefined_map),
		    (int (*)(const void *, const void *))undef_bsearch);
		if(undefined_map != NULL)
		    merged_symbol = undefined_map->merged_symbol;
		else{
		    nlists = (struct nlist *)(cur_obj->obj_addr +
					      cur_obj->symtab->symoff);
		    strings = (char *)(cur_obj->obj_addr +
				       cur_obj->symtab->stroff);
		    if((nlists[r_symbolnum].n_type & N_EXT) != N_EXT)
			return;
		    /*
		     * We must allow and create references to defined global
		     * coalesced symbols with external relocation entries so
		     * that the dynamic linker can relocate all references to
		     * the same symbol.
		     */
		    if((nlists[r_symbolnum].n_type & N_TYPE) == N_SECT &&
		       (cur_obj->section_maps[nlists[r_symbolnum].n_sect-1].
			s->flags & SECTION_TYPE) == S_COALESCED){
			hash_pointer = lookup_symbol(strings +
					     nlists[r_symbolnum].n_un.n_strx);
			if(hash_pointer == NULL){
			    fatal("internal error, in count_relocs() failed to "
			          "lookup coalesced symbol %s", strings +
				  nlists[r_symbolnum].n_un.n_strx);
			}
			merged_symbol = *hash_pointer;
		    }
		    else
			return;
		}
		if((merged_symbol->nlist.n_type & N_TYPE) == N_INDR &&
		   merged_symbol->defined_in_dylib == FALSE)
		    merged_symbol = (struct merged_symbol *)
				    merged_symbol->nlist.n_value;
		if(merged_symbol->nlist.n_type == (N_EXT | N_UNDF) ||
		   merged_symbol->nlist.n_type == (N_EXT | N_PBUD) ||
		   (merged_symbol->nlist.n_type == (N_EXT | N_INDR) &&
		    merged_symbol->defined_in_dylib == TRUE)){
		    defined = FALSE;
		}
		else{
		    /*
		     * The symbol is defined but may be a coalesced symbol.
		     * If so and the output is not an executable (does not
		     * have a dynamic linker command) this relocation entry
		     * will remain as an external relocation entry so set
		     * the variable 'defined' to FALSE.
		     */
		    if((merged_symbol->nlist.n_type & N_TYPE) == N_SECT &&
		       (merged_symbol->definition_object->section_maps[
			 merged_symbol->nlist.n_sect-1].
			 s->flags & SECTION_TYPE) == S_COALESCED &&
		       has_dynamic_linker_command == FALSE){
			defined = FALSE;
		    }
		    else{
			defined = TRUE;
		    }
		}
	    }
	    if(reloc_has_pair(arch_flag.cputype, r_type))
		pair = 1;
	    else
		pair = 0;
	    if(r_extern == 0){
		/*
		 * If the r_symbolnum refers to a symbol stub section where
		 * the indirect symbol for what is being reference is now
		 * defined as an N_ABS symbol it will turn r_symbolnum
		 * into NO_SECT.  So what was a pcrel relocation entry refering
		 * to another section now refers to absolute symbol and the
		 * relocation entry is no longer pic and must be kept.
		 */
		if(r_symbolnum > cur_obj->nsection_maps)
		    return;
		local_map = &(cur_obj->section_maps[r_symbolnum - 1]);
		if(r_symbolnum != NO_SECT &&
		   (local_map->s->flags & SECTION_TYPE) == S_SYMBOL_STUBS &&
		   local_map->absolute_indirect_defineds == TRUE &&
		   r_pcrel == 1){
		    /*
		     * So we need to know if r_symbolnum will turn into NO_SECT.
		     * We do this by faking doing the relocation and pick up
		     * the resulting r_symbolnum after relocation.
		     */
		    if(r_address >= map->s->size)
			return;
		    if(pair && i == map->s->nreloc - 1)
			return;
		    /* fake up a section contents using just this item */
		    memcpy(fake_contents,
			   cur_obj->obj_addr + map->s->offset + r_address,
			   1 << r_length);
		    /* fake up relocation entries using just the ones for item*/
		    fake_relocs[0] = reloc;
		    if((reloc.r_address & R_SCATTERED) != 0){
			sreloc = (struct scattered_relocation_info *)
				 (&fake_relocs[0]);
			sreloc->r_address = 0;
		    }
		    else
			fake_relocs[0].r_address = 0;
		    if(pair){
			pair_reloc = relocs[i+1];
			if(cur_obj->swapped)
			    swap_relocation_info(&pair_reloc, 1, host_byte_sex);
			fake_relocs[1] = pair_reloc;
		    }
		    /*
		     * fake up a section map that will cause the correct
		     * r_symbolnum (the relocation may be wrong but we don't
		     * need that).
		     */
		    fake_s = *(map->s);
		    fake_s.nreloc = 1 + pair;
		    fake_s.size = 1 << r_length;
		    fake_s.addr += r_address;
		    fake_map = *map;
		    fake_map.s = &fake_s;
		    fake_map.nfine_relocs = 0;
		    fake_map.fine_relocs = NULL;

		    /* do the fake relocation */
		    if(arch_flag.cputype == CPU_TYPE_MC680x0)
			generic_reloc(fake_contents, fake_relocs, &fake_map,
				      FALSE);
		    else if(arch_flag.cputype == CPU_TYPE_I386)
			generic_reloc(fake_contents, fake_relocs, &fake_map,
				      TRUE);
		    else if(arch_flag.cputype == CPU_TYPE_POWERPC ||
			    arch_flag.cputype == CPU_TYPE_VEO)
			ppc_reloc(fake_contents, fake_relocs, &fake_map);
		    else if(arch_flag.cputype == CPU_TYPE_MC88000)
			m88k_reloc(fake_contents, fake_relocs, &fake_map);
		    else if(arch_flag.cputype == CPU_TYPE_HPPA)
			hppa_reloc(fake_contents, fake_relocs, &fake_map);
		    else if(arch_flag.cputype == CPU_TYPE_SPARC)
			sparc_reloc(fake_contents, fake_relocs, &fake_map);
#ifndef RLD
		    else if(arch_flag.cputype == CPU_TYPE_I860)
			i860_reloc(fake_contents, fake_relocs, map);
#endif /* RLD */

		    /* now pick up the correct resulting r_symbolnum */
		    r_symbolnum = fake_relocs[0].r_symbolnum;
		}
		/*
		 * If this local relocation entry is refering to a coalesced
		 * section and the r_value is that of a global coalesced
		 * symbol then this relocation entry will into a external
		 * relocation entry and the item to be relocated will be
		 * "unrelocated" removing the value of the global coalesced
		 * symbol. 
		 */
		else if(r_symbolnum != NO_SECT &&
		   (local_map->s->flags & SECTION_TYPE) == S_COALESCED){
		    /*
		     * The address of the item being referenced for a scattered
		     * relocation entry is r_address.  But for local relocation
		     * entries the address is in the contents of the item being
		     * relocated (which is architecure/relocation type dependent
		     * to get).
		     *
		     * Once you have that address you have to go through the 
		     * cur_obj's symbol table trying to matching that address.
		     * If you find a match then to need to determine if that
		     * symbols is global in the output file (not a private
		     * extern turned in to a static).
		     *
		     * If all this is true then this relocation entry will
		     * be turned back into an external relocation entry.
		     */
		    ;
		}
		pic = (enum bool)
		       (reloc_is_sectdiff(arch_flag.cputype, r_type) ||
		        (r_pcrel == 1 && r_symbolnum != NO_SECT));
	    }
	    else
		pic = (enum bool)
		       (r_pcrel == 1 &&
		        (merged_symbol->nlist.n_type & N_TYPE) == N_SECT);
	    /*
	     * For output_for_dyld HPPA_RELOC_JBSR's are never put out.
	     */
	    if(arch_flag.cputype == CPU_TYPE_HPPA && r_type == HPPA_RELOC_JBSR){
		i += pair;
		continue;
	    }
	    /*
	     * The number of relocation entries in the output file is based on
	     * one of three different cases:
	     * 	The output file is a multi module dynamic shared library
	     *  The output file has a dynamic linker load command
	     *  The output does not have a dynamic linker load command
	     */
	    if(filetype == MH_DYLIB && multi_module_dylib == TRUE){
		/*
		 * For multi module dynamic shared library files all external
		 * relocations are kept as external relocation entries except
		 * for references to private externs (which are kept as locals) 
		 * and all non-position-independent local relocation entries
		 * are kept. Modules of multi module dylibs are not linked
		 * together and can only be slid keeping all sections relative
		 * to each other the same.
		 */
		if(r_extern && (merged_symbol->nlist.n_type & N_PEXT) == 0){
		    (*nextrel) += 1 + pair;
		    cur_obj->nextrel += 1 + pair;
		}
		else if(pic == FALSE){
		    (*nlocrel) += 1 + pair;
		    cur_obj->nlocrel += 1 + pair;
		}
	    }
	    else if(has_dynamic_linker_command){
		/*
		 * For an file with a dynamic linker load command only external
		 * relocation entries for undefined symbols are kept.  This
		 * output file is a fixed address and can't be moved.
		 */
		if(r_extern){
		    if(defined == FALSE){
			(*nextrel) += 1 + pair;
			cur_obj->nextrel += 1 + pair;
		    }
		}
	    }
	    else{
		/*
		 * For an file without a dynamic linker load command external
		 * relocation entries for undefined symbols are kept and locals
		 * that are non-position-independent are kept.  This file can
		 * only be slid keeping all sections relative to each other the
		 * same.
		 */
		if(r_extern && (merged_symbol->nlist.n_type & N_PEXT) == 0){
		    if(defined == FALSE){
			(*nextrel) += 1 + pair;
			cur_obj->nextrel += 1 + pair;
		    }
		    else if(pic == FALSE){
			(*nlocrel) += 1 + pair;
			cur_obj->nlocrel += 1 + pair;
		    }
		}
		else if(pic == FALSE){
		    (*nlocrel) += 1 + pair;
		    cur_obj->nlocrel += 1 + pair;
		}
	    }
	    i += pair;
	}
	map->nextrel = cur_obj->nextrel - prev_nextrel;
	map->nlocrel = cur_obj->nlocrel - prev_nlocrel;
	if(prev_nextrel != cur_obj->nextrel)
	    map->output_section->s.flags |= S_ATTR_EXT_RELOC;
	if(prev_nlocrel != cur_obj->nlocrel)
	    map->output_section->s.flags |= S_ATTR_LOC_RELOC;
}
#endif /* !defined(RLD) */

/*
 * output_literal_sections() causes each merged literal section to be copied
 * to the output file.  It is called from pass2().
 */
__private_extern__
void
output_literal_sections(void)
{
    struct merged_segment **p, *msg;
    struct merged_section **content, *ms;

	p = &merged_segments;
	while(*p){
	    msg = *p;
	    content = &(msg->content_sections);
	    while(*content){
		ms = *content;
		if((ms->s.flags & SECTION_TYPE) == S_CSTRING_LITERALS ||
		   (ms->s.flags & SECTION_TYPE) == S_4BYTE_LITERALS ||
		   (ms->s.flags & SECTION_TYPE) == S_8BYTE_LITERALS ||
		   (ms->s.flags & SECTION_TYPE) == S_LITERAL_POINTERS)
		    (*ms->literal_output)(ms->literal_data, ms);
		content = &(ms->next);
	    }
	    p = &(msg->next);
	}
}

#ifndef RLD
/*
 * output_sections_from_files() causes each section created from a file to be
 * copied to the output file.  It is called from pass2().
 */
__private_extern__
void
output_sections_from_files(void)
{
    struct merged_segment **p, *msg;
    struct merged_section **content, *ms;
#ifdef DEBUG
    kern_return_t r;
#endif /* DEBUG */

	p = &merged_segments;
	while(*p){
	    msg = *p;
	    content = &(msg->content_sections);
	    while(*content){
		ms = *content;
		if(ms->contents_filename != NULL){
		    memcpy(output_addr + ms->s.offset,
			   ms->file_addr, ms->file_size);
		    /*
		     * The entire section size is flushed (ms->s.size) not just
		     * the size of the file used to create it (ms->filesize) so
		     * to flush the padding due to alignment.
		     */
		    output_flush(ms->s.offset, ms->s.size);
#ifdef DEBUG
		    if((r = vm_deallocate(mach_task_self(), (vm_address_t)
			ms->file_addr, ms->file_size)) != KERN_SUCCESS)
			mach_fatal(r, "can't vm_deallocate() memory for file: "
				   "%s used to create section (%.16s,%.16s)",
				   ms->contents_filename, ms->s.segname,
				   ms->s.sectname);
		    ms->file_addr = NULL;
#endif /* DEBUG */
		}
		content = &(ms->next);
	    }
	    p = &(msg->next);
	}
}
#endif /* !defined(RLD) */

/*
 * output_section() copies the contents of a section and it's relocation entries
 * (if saving relocation entries) into the output file's memory buffer.  Then it
 * calls the appropriate routine specific to the target machine to relocate the
 * section and update the relocation entries (if saving relocation entries).
 */
__private_extern__
void
output_section(
struct section_map *map)
{
    char *contents;
    struct relocation_info *relocs;
#ifndef RLD
    struct relocation_info *output_relocs, *output_locrel, *output_extrel;
    unsigned long nlocrel, nextrel;
    unsigned long nreloc;
#endif

#ifdef DEBUG
	/* The compiler "warning: `output_relocs' may be used uninitialized */
	/* in this function" can safely be ignored */
	output_relocs = NULL;
#endif

	/*
	 * If this section has no contents and no relocation entries just
	 * return.  This can happen a lot with object files that have empty
	 * sections.
	 */ 
	if(map->s->size == 0 && map->s->nreloc == 0)
	    return;

	/*
	 * Copy the contents of the section from the input file into the memory
	 * buffer for the output file.
	 */
	if(map->nfine_relocs != 0)
	    contents = allocate(map->s->size);
	else{
	    /*
	     * This is a hack to pad an i386 pure instructions sections with
	     * nop's (opcode 0x90) to make disassembly cleaner between object's
	     * sections.
	     */
	    if(arch_flag.cputype == CPU_TYPE_I386 &&
	       (map->s->flags & S_ATTR_PURE_INSTRUCTIONS) != 0){
		contents = output_addr + map->output_section->s.offset +
			   map->flush_offset;
		memset(contents, 0x90, map->offset - map->flush_offset);
	    }
	    contents = output_addr + map->output_section->s.offset +map->offset;
	}
	memcpy(contents, cur_obj->obj_addr + map->s->offset, map->s->size);


	/*
	 * If the section has no relocation entries then no relocation is to be
	 * done so just flush the contents and return.
	 */
	if(map->s->nreloc == 0){
#ifndef RLD
	    if(map->nfine_relocs != 0){
		scatter_copy(map, contents);
		free(contents);
	    }
	    else
		output_flush(map->output_section->s.offset + map->flush_offset,
			     map->s->size + (map->offset - map->flush_offset));
#endif /* !defined(RLD) */
	    return;
	}
	else
	    map->output_section->relocated = TRUE;

	/*
	 * Set up the pointer to the relocation entries to be used by the 
	 * relocation routine.  If the relocation entries appear in the file
	 * the relocation routine will update them.  If only some but not all
	 * of the relocation entries will appear in the output file then copy
	 * them from the input file into the memory buffer.  If all of the
	 * relocation entries will appear in the output file then copy them into
	 * the buffer for the output file.  Lastly if no relocation entries will
	 * appear in the output file just used the input files relocation
	 * entries for the relocation routines.
	 */
	if(output_for_dyld){
	    relocs = allocate(map->s->nreloc * sizeof(struct relocation_info));
	    memcpy(relocs,
		   cur_obj->obj_addr + map->s->reloff,
		   map->s->nreloc * sizeof(struct relocation_info));
	}
	else if(save_reloc){
	    /*
	     * For indirect and coalesced sections only those relocation entries
	     * for items in the section used from this object will be saved.  So
	     * allocate a buffer to put them in to use to do the relocation and
	     * later scatter_copy_relocs() will pick out the the relocation
	     * entries to be put in the output file.
	     */
	    if((map->s->flags & SECTION_TYPE) == S_SYMBOL_STUBS ||
	       (map->s->flags & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS ||
	       (map->s->flags & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS ||
	       (map->s->flags & SECTION_TYPE) == S_COALESCED){
		relocs = allocate(map->s->nreloc *
				  sizeof(struct relocation_info));
		memcpy(relocs,
		       cur_obj->obj_addr + map->s->reloff,
		       map->s->nreloc * sizeof(struct relocation_info));
	    }
	    else{
		relocs = (struct relocation_info *)(output_addr +
			 map->output_section->s.reloff +
			 map->output_section->output_nrelocs *
						sizeof(struct relocation_info));
		memcpy(relocs,
		       cur_obj->obj_addr + map->s->reloff,
		       map->s->nreloc * sizeof(struct relocation_info));
	    }
	}
	else{
	    relocs = (struct relocation_info *)(cur_obj->obj_addr +
					        map->s->reloff);
	}
	if(cur_obj->swapped)
	    swap_relocation_info(relocs, map->s->nreloc, host_byte_sex);

	/*
	 * Relocate the contents of the section (based on the target machine)
	 */
	if(arch_flag.cputype == CPU_TYPE_MC680x0)
	    generic_reloc(contents, relocs, map, FALSE);
	else if(arch_flag.cputype == CPU_TYPE_I386)
	    generic_reloc(contents, relocs, map, TRUE);
	else if(arch_flag.cputype == CPU_TYPE_POWERPC ||
		arch_flag.cputype == CPU_TYPE_VEO)
	    ppc_reloc(contents, relocs, map);
	else if(arch_flag.cputype == CPU_TYPE_MC88000)
	    m88k_reloc(contents, relocs, map);
	else if(arch_flag.cputype == CPU_TYPE_HPPA)
	    hppa_reloc(contents, relocs, map);
	else if(arch_flag.cputype == CPU_TYPE_SPARC)
	    sparc_reloc(contents, relocs, map);
#ifndef RLD
	else if(arch_flag.cputype == CPU_TYPE_I860)
	    i860_reloc(contents, relocs, map);
#endif /* RLD */
	else
	    fatal("internal error: output_section() called with unknown "
		  "cputype (%d) set", arch_flag.cputype);
#ifndef RLD

	/*
	 * If the reloc routines caused errors then return as so to not cause
	 * later internal error below.
	 */
	if(errors != 0)
	    return;

	/*
	 * Copy and/or flush the relocated section contents to the output file.
	 */
	if(map->nfine_relocs != 0){
	    scatter_copy(map, contents);
	    free(contents);
	}
	else
	    output_flush(map->output_section->s.offset + map->flush_offset,
			 map->s->size + (map->offset - map->flush_offset));

	/*
	 * If relocation entries will be in the output file copy and/or flush
	 * them to the output file.
	 */
	if(output_for_dyld){
	    /*
	     * Setup pointers in the output file buffer for local and external
	     * relocation entries.
	     */
	    if((map->s->flags & SECTION_TYPE) == S_REGULAR ||
	       (map->s->flags & SECTION_TYPE) == S_MOD_INIT_FUNC_POINTERS ||
	       (map->s->flags & SECTION_TYPE) == S_MOD_TERM_FUNC_POINTERS){
		output_locrel = (struct relocation_info *)(output_addr +
			    output_dysymtab_info.dysymtab_command.locreloff +
			    cur_obj->ilocrel * sizeof(struct relocation_info));
		output_extrel = (struct relocation_info *)(output_addr +
			    output_dysymtab_info.dysymtab_command.extreloff +
			    cur_obj->iextrel * sizeof(struct relocation_info));
	    }
	    else if((map->s->flags & SECTION_TYPE) == S_COALESCED){
		output_locrel = (struct relocation_info *)(output_addr +
			    output_dysymtab_info.dysymtab_command.locreloff +
			    map->ilocrel * sizeof(struct relocation_info));
		output_extrel = (struct relocation_info *)(output_addr +
			    output_dysymtab_info.dysymtab_command.extreloff +
			    map->iextrel * sizeof(struct relocation_info));
	    }
	    else{
		output_locrel = (struct relocation_info *)(output_addr +
			    output_dysymtab_info.dysymtab_command.locreloff +
			    map->output_section->ilocrel *
				sizeof(struct relocation_info));
		output_extrel = (struct relocation_info *)(output_addr +
			    output_dysymtab_info.dysymtab_command.extreloff +
			    map->output_section->iextrel *
				sizeof(struct relocation_info));
	    }
	    /*
	     * Copy out the local and external relocation entries to be kept
	     * for the output file type and adjust the r_address values to be
	     * based on the offset from starting address of the first segment
	     * rather than the offset of the section.
	     */
	    reloc_output_for_dyld(map, relocs, output_locrel, output_extrel,
				  &nlocrel, &nextrel);
	    /*
	     * count_reloc() and coalesced_section_merge() counted and recorded
	     * the number of relocation entries the section was to have in the
	     * output.  This should match what reloc_output_for_dyld() copied
	     * out.
	     */
	    if((map->s->flags & SECTION_TYPE) == S_REGULAR ||
	       (map->s->flags & SECTION_TYPE) == S_MOD_INIT_FUNC_POINTERS ||
	       (map->s->flags & SECTION_TYPE) == S_MOD_TERM_FUNC_POINTERS ||
	       (map->s->flags & SECTION_TYPE) == S_COALESCED){
		if(nextrel != map->nextrel)
		    fatal("internal error: output_section() count of external "
			  "relocation entries does not match\n");
		if(nlocrel != map->nlocrel)
		    fatal("internal error: output_section() count of local "
			  "relocation entries does not match\n");
	    }

	    if(host_byte_sex != target_byte_sex){
		swap_relocation_info(output_locrel, nlocrel, target_byte_sex);
		swap_relocation_info(output_extrel, nextrel, target_byte_sex);
	    }
	    /*
	     * Flush output file buffer's local and external relocation entries
	     * and increment the counts.
	     */
	    if((map->s->flags & SECTION_TYPE) == S_REGULAR ||
	       (map->s->flags & SECTION_TYPE) == S_MOD_INIT_FUNC_POINTERS ||
	       (map->s->flags & SECTION_TYPE) == S_MOD_TERM_FUNC_POINTERS){
		output_flush(output_dysymtab_info.dysymtab_command.locreloff +
			     cur_obj->ilocrel * sizeof(struct relocation_info),
			     nlocrel * sizeof(struct relocation_info));
		cur_obj->ilocrel += nlocrel;
		output_flush(output_dysymtab_info.dysymtab_command.extreloff +
			     cur_obj->iextrel * sizeof(struct relocation_info),
			     nextrel * sizeof(struct relocation_info));
		cur_obj->iextrel += nextrel;
	    }
	    else if((map->s->flags & SECTION_TYPE) == S_COALESCED){
		output_flush(output_dysymtab_info.dysymtab_command.locreloff +
			     map->ilocrel * sizeof(struct relocation_info),
			     nlocrel * sizeof(struct relocation_info));
		/* no increment of map->nlocrel */
		output_flush(output_dysymtab_info.dysymtab_command.extreloff +
			     map->iextrel * sizeof(struct relocation_info),
			     nextrel * sizeof(struct relocation_info));
		/* no increment of map->nextrel */
	    }
	    else{
		output_flush(output_dysymtab_info.dysymtab_command.locreloff +
			     map->output_section->ilocrel *
				sizeof(struct relocation_info),
			     nlocrel * sizeof(struct relocation_info));
		map->output_section->ilocrel += nlocrel;
		output_flush(output_dysymtab_info.dysymtab_command.extreloff +
			     map->output_section->iextrel *
				sizeof(struct relocation_info),
			     nextrel * sizeof(struct relocation_info));
		map->output_section->iextrel += nextrel;
	    }
	    free(relocs);
	}
	else if(save_reloc){
	    if((map->s->flags & SECTION_TYPE) == S_SYMBOL_STUBS ||
	       (map->s->flags & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS ||
	       (map->s->flags & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS ||
	       (map->s->flags & SECTION_TYPE) == S_COALESCED){

		output_relocs = (struct relocation_info *)(output_addr +
			 	 map->output_section->s.reloff +
			 	 map->output_section->output_nrelocs *
						sizeof(struct relocation_info));
		nreloc = scatter_copy_relocs(map, relocs, output_relocs);
		free(relocs);
	    }
	    else{
		nreloc = map->s->nreloc;
		output_relocs = relocs;
	    }
	    if(host_byte_sex != target_byte_sex)
		swap_relocation_info(output_relocs, nreloc, target_byte_sex);
	    output_flush(map->output_section->s.reloff +
			 map->output_section->output_nrelocs *
			 sizeof(struct relocation_info),
			 nreloc * sizeof(struct relocation_info));
	    map->output_section->output_nrelocs += nreloc;
	}
#endif /* !defined(RLD) */
}

#ifndef RLD

/*
 * is_pass2_merged_symbol_coalesced() is passed a merged symbol as it appears
 * in the second pass (that is with its n_sect) set to the output's section
 * number) and returns TRUE if the symbol is in a coalesced section and FALSE
 * otherwise.  This is used by scatter_copy() below to set the value of
 * non-lazy pointers.  This absolutely need to be done by the static linker for
 * private extern coalesced symbols (when -keep_private_extern is not in effect)
 * as their indirect symbol table will be INDIRECT_SYMBOL_LOCAL on output and
 * then the dynamic linker can't fix them up.
 */
static 
enum bool
is_pass2_merged_symbol_coalesced(
struct merged_symbol *merged_symbol)
{
    unsigned long i;

	if(merged_symbol == NULL)
	    return(FALSE);
	if((merged_symbol->nlist.n_type & N_TYPE) != N_SECT)
	    return(FALSE);
	for(i = 0; i < merged_symbol->definition_object->nsection_maps; i++){
	    if(merged_symbol->nlist.n_sect == merged_symbol->definition_object->
			section_maps[i].output_section->output_sectnum)
	    if((merged_symbol->definition_object->section_maps[
		 i].output_section->s.flags & SECTION_TYPE) == S_COALESCED)
		return(TRUE);
	}
	return(FALSE);
}

/*
 * scatter_copy() copies the relocated contents of a section into the output
 * file's memory buffer based on the section's fine relocation maps.
 */
static
void
scatter_copy(
struct section_map *map,
char *contents)
{
    unsigned long i, j;
    struct nlist *nlists;
    unsigned long *indirect_symtab, index, value;
    struct undefined_map *undefined_map;
    struct merged_symbol *merged_symbol;
    char *strings;
    struct section_map *section_map;

	/*
	 * For non-lazy pointer type indirect sections only copy those parts of
	 * the section who's contents are used in the output file and if the
	 * symbol for the non-lazy pointer is defined then use that as instead
	 * of the contents.  This bit of code assumes that all the checks done
	 * when merging the indirect section are valid and so none of them are
	 * done here.  It also assumes that the fine relocation entries each
	 * cover the 4 byte non-lazy pointer.
	 *
	 * If prebinding and this is a lazy pointer section do the same as for
	 * non-lazy pointers.  That is use the value of the indirect symbol.
	 */
	if((map->s->flags & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS ||
	   (prebinding == TRUE &&
	    (map->s->flags & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS)){
	    /* setup pointers to the symbol table and indirect symbol table */
	    nlists = (struct nlist *)(cur_obj->obj_addr +
				      cur_obj->symtab->symoff);
	    indirect_symtab = (unsigned long *)(cur_obj->obj_addr +
					    cur_obj->dysymtab->indirectsymoff);
	    strings = cur_obj->obj_addr + cur_obj->symtab->stroff;
	    for(i = 0; i < map->nfine_relocs - 1; i++){
		if(map->fine_relocs[i].use_contents == TRUE){
		    index = indirect_symtab[map->s->reserved1 + 
			    (map->fine_relocs[i].input_offset / 4)];
		    if(map->fine_relocs[i].indirect_defined == TRUE ||
		       is_pass2_merged_symbol_coalesced(
			    map->fine_relocs[i].merged_symbol) == TRUE ||
		       (prebinding == TRUE && 
			(index != INDIRECT_SYMBOL_LOCAL &&
			 index != INDIRECT_SYMBOL_ABS))){
			if(is_pass2_merged_symbol_coalesced(
			    map->fine_relocs[i].merged_symbol) == TRUE){
    			    value = map->fine_relocs[i].merged_symbol->
				    nlist.n_value;
			}
			else if(map->fine_relocs[i].local_symbol == FALSE){
			    undefined_map = bsearch(&index,
				cur_obj->undefined_maps, cur_obj->nundefineds,
				sizeof(struct undefined_map),
				(int (*)(const void *, const void *))
				undef_bsearch);
			    if(undefined_map == NULL){
				merged_symbol = *(lookup_symbol(strings +
						    nlists[index].n_un.n_strx));
				if(merged_symbol == NULL)
				    fatal("interal error, scatter_copy() failed"
					  " in looking up external symbol");
			    }
			    else
				merged_symbol = undefined_map->merged_symbol;
			    if((merged_symbol->nlist.n_type & N_TYPE) == N_INDR)
				merged_symbol = (struct merged_symbol *)
						merged_symbol->nlist.n_value;
			    value = merged_symbol->nlist.n_value;
			}
			else{
			    if(nlists[index].n_sect == NO_SECT)
				value = nlists[index].n_value;
			    else{
				section_map = &(cur_obj->section_maps[
				    nlists[index].n_sect -1]);
				if(section_map->nfine_relocs == 0)
				    value = nlists[index].n_value -
					   section_map->s->addr +
					   section_map->output_section->s.addr +
					   section_map->offset;
				else
				    value =
					section_map->output_section->s.addr +
					   fine_reloc_output_offset(section_map,
							nlists[index].n_value -
							section_map->s->addr);
			    }
			}
			if(host_byte_sex != target_byte_sex)
			    value = SWAP_LONG(value);
			memcpy(output_addr + map->output_section->s.offset +
			       map->fine_relocs[i].output_offset,
			       &value, sizeof(unsigned long));
		    }
		    else{
			/*
			 * If the indirect symbol table entry is
			 * INDIRECT_SYMBOL_LOCAL the value of the symbol pointer
			 * neededs to be adjusted to where it is in the output.
			 */
			if(index == INDIRECT_SYMBOL_LOCAL){
			    memcpy(&value, contents +
				   map->fine_relocs[i].input_offset, 4);
			    if(cur_obj->swapped)
				value = SWAP_LONG(value);
			    for(j = 0; j < cur_obj->nsection_maps; j++){
				if(value >= cur_obj->section_maps[j].s->addr &&
				   value < cur_obj->section_maps[j].s->addr +
					     cur_obj->section_maps[j].s->size){
				    break;
				}
			    }
			    if(j >= cur_obj->nsection_maps){
				error_with_cur_obj("value of symbol pointer "
				    "(0x%x) in section (%.16s,%.16s) at index "
				    "%ld out of range for an indirect symbol "
				    "table value of INDIRECT_SYMBOL_LOCAL",
				    (unsigned int)value,
				    map->output_section->s.segname,
				    map->output_section->s.sectname, i);
				return;
			    }
			    section_map = &(cur_obj->section_maps[j]);
			    if(section_map->nfine_relocs == 0)
				value = value -
				       section_map->s->addr +
				       section_map->output_section->s.addr +
				       section_map->offset;
			    else
				value =
				    section_map->output_section->s.addr +
				       fine_reloc_output_offset(section_map,
						value - section_map->s->addr);
			    if(host_byte_sex != target_byte_sex)
				value = SWAP_LONG(value);
			    memcpy(output_addr + map->output_section->s.offset +
				   map->fine_relocs[i].output_offset,
				   &value, sizeof(unsigned long));
			}
			else{
			    memcpy(output_addr + map->output_section->s.offset +
					      map->fine_relocs[i].output_offset,
			       contents + map->fine_relocs[i].input_offset,
			       map->fine_relocs[i+1].input_offset -
					      map->fine_relocs[i].input_offset);
			}
		    }
		}
	    }
	    if(map->fine_relocs[i].use_contents == TRUE){
		index = indirect_symtab[map->s->reserved1 + 
			(map->fine_relocs[i].input_offset / 4)];
		if(map->fine_relocs[i].indirect_defined == TRUE ||
		   is_pass2_merged_symbol_coalesced(
			map->fine_relocs[i].merged_symbol) == TRUE ||
		   (prebinding == TRUE && 
		    (index != INDIRECT_SYMBOL_LOCAL &&
		     index != INDIRECT_SYMBOL_ABS))){
		    if(is_pass2_merged_symbol_coalesced(
			map->fine_relocs[i].merged_symbol) == TRUE){
			value = map->fine_relocs[i].merged_symbol->
				nlist.n_value;
		    }
		    else if(map->fine_relocs[i].local_symbol == FALSE){
			undefined_map = bsearch(&index,
			    cur_obj->undefined_maps, cur_obj->nundefineds,
			    sizeof(struct undefined_map),
			    (int (*)(const void *, const void *))
			    undef_bsearch);
			if(undefined_map == NULL){
			    merged_symbol = *(lookup_symbol(strings +
						nlists[index].n_un.n_strx));
			    if(merged_symbol == NULL)
				fatal("interal error, scatter_copy() failed"
				      " in looking up external symbol");
			}
			else
			    merged_symbol = undefined_map->merged_symbol;
			if((merged_symbol->nlist.n_type & N_TYPE) == N_INDR)
			    merged_symbol = (struct merged_symbol *)
					    merged_symbol->nlist.n_value;
			value = merged_symbol->nlist.n_value;
		    }
		    else{
			if(nlists[index].n_sect == NO_SECT)
			    value = nlists[index].n_value;
			else{
			    section_map = &(cur_obj->section_maps[
				nlists[index].n_sect -1]);
			    if(section_map->nfine_relocs == 0)
				value = nlists[index].n_value -
				       section_map->s->addr +
				       section_map->output_section->s.addr +
				       section_map->offset;
			    else
				value =
				    section_map->output_section->s.addr +
				       fine_reloc_output_offset(section_map,
						    nlists[index].n_value -
						    section_map->s->addr);
			}
		    }
		    if(host_byte_sex != target_byte_sex)
			value = SWAP_LONG(value);
		    memcpy(output_addr + map->output_section->s.offset +
			   map->fine_relocs[i].output_offset,
			   &value, sizeof(unsigned long));
		}
		else{
		    /*
		     * If the indirect symbol table entry is
		     * INDIRECT_SYMBOL_LOCAL the value of the symbol pointer
		     * neededs to be adjusted to where it is in the output.
		     */
		    if(index == INDIRECT_SYMBOL_LOCAL){
			memcpy(&value, contents +
			       map->fine_relocs[i].input_offset, 4);
			if(cur_obj->swapped)
			    value = SWAP_LONG(value);
			for(j = 0; j < cur_obj->nsection_maps; j++){
			    if(value >= cur_obj->section_maps[j].s->addr &&
			       value < cur_obj->section_maps[j].s->addr +
					 cur_obj->section_maps[j].s->size){
				break;
			    }
			}
			if(j >= cur_obj->nsection_maps){
			    error_with_cur_obj("value of symbol pointer (0x%x) "
				"in section (%.16s,%.16s) at index %ld out of "
				"range for an indirect symbol table value of "
				"INDIRECT_SYMBOL_LOCAL", (unsigned int)value,
				map->output_section->s.segname,
				map->output_section->s.sectname, i);
			    return;
			}
			section_map = &(cur_obj->section_maps[j]);
			if(section_map->nfine_relocs == 0)
			    value = value -
				   section_map->s->addr +
				   section_map->output_section->s.addr +
				   section_map->offset;
			else
			    value =
				section_map->output_section->s.addr +
				   fine_reloc_output_offset(section_map,
						value - section_map->s->addr);
			if(host_byte_sex != target_byte_sex)
			    value = SWAP_LONG(value);
			memcpy(output_addr + map->output_section->s.offset +
			       map->fine_relocs[i].output_offset,
			       &value, sizeof(unsigned long));
		    }
		    else{
			memcpy(output_addr + map->output_section->s.offset +
					      map->fine_relocs[i].output_offset,
			   contents + map->fine_relocs[i].input_offset,
			   map->s->size - map->fine_relocs[i].input_offset);
		    }
		}
	    }
	}
	/*
	 * For other indirect sections and coalesced sections only copy those
	 * parts of the section who's contents are used in the output file.
	 */
	else if((map->s->flags & SECTION_TYPE) == S_SYMBOL_STUBS ||
	        (map->s->flags & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS ||
	        (map->s->flags & SECTION_TYPE) == S_COALESCED){
	    for(i = 0; i < map->nfine_relocs - 1; i++){
		if(map->fine_relocs[i].use_contents == TRUE){
		    memcpy(output_addr + map->output_section->s.offset +
					      map->fine_relocs[i].output_offset,
			   contents + map->fine_relocs[i].input_offset,
			   map->fine_relocs[i+1].input_offset -
					      map->fine_relocs[i].input_offset);
		}
	    }
	    if(map->fine_relocs[i].use_contents == TRUE){
		memcpy(output_addr + map->output_section->s.offset +
					      map->fine_relocs[i].output_offset,
		       contents + map->fine_relocs[i].input_offset,
		       map->s->size - map->fine_relocs[i].input_offset);
	    }
	}
	else{
	    for(i = 0; i < map->nfine_relocs - 1; i++){
		memcpy(output_addr + map->output_section->s.offset +
					      map->fine_relocs[i].output_offset,
		       contents + map->fine_relocs[i].input_offset,
		       map->fine_relocs[i+1].input_offset -
					      map->fine_relocs[i].input_offset);
	    }
	    memcpy(output_addr + map->output_section->s.offset +
					      map->fine_relocs[i].output_offset,
		   contents + map->fine_relocs[i].input_offset,
		   map->s->size - map->fine_relocs[i].input_offset);
	}
}

/*
 * reloc_output_for_dyld() takes the relocation entries after being processed by
 * a relocation routine and copys the ones to be in the output file for a file
 * the is output for dyld.  It also changes the r_address field of the of the
 * relocation entries to be relative to the first segment's address rather than
 * the section's address.
 */
static
void
reloc_output_for_dyld(
struct section_map *map,
struct relocation_info *relocs,
struct relocation_info *output_locrel,
struct relocation_info *output_extrel,
unsigned long *nlocrel,
unsigned long *nextrel)
{
    unsigned long i, addr_adjust, temp, pair;
    struct relocation_info *reloc;
    struct scattered_relocation_info *sreloc;
    unsigned long r_address, r_extern, r_type, r_scattered, r_pcrel,
		  r_symbolnum, r_value;
    enum bool partial_section, sectdiff, pic, has_sect_diff_relocs;
    enum bool flag_relocs, first_time;

	/* to shut up compiler warning messages "may be used uninitialized" */
	sreloc = NULL;

	/*
	 * If we are flagging relocation entries in read only sections set up
	 * to do that.
	 */
	first_time = TRUE;
	if(read_only_reloc_flag != READ_ONLY_RELOC_SUPPRESS)
	    flag_relocs = is_merged_section_read_only(map->output_section);
	else
	    flag_relocs = FALSE;
	if(flag_relocs == TRUE)
	    clear_read_only_reloc_flags();
	has_sect_diff_relocs = FALSE;

	*nlocrel = 0;
	*nextrel = 0;
	partial_section = (enum bool)
		((map->s->flags & SECTION_TYPE) == S_SYMBOL_STUBS ||
	         (map->s->flags & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS ||
	         (map->s->flags & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS ||
	         (map->s->flags & SECTION_TYPE) == S_COALESCED);
	/*
	 * For MH_SPLIT_SEGS images the r_address is relative to the first
	 * read-write segment and there are no relocation entries allowed in
	 * the read-only segments.  This is needed because the r_address field
	 * is 24 bits which means that the normal split of 265meg wouldn't allow
	 * the use of 24 bits from the address of the first segment which is
	 * what is normally used for outputs for dyld.
	 */
	if(segs_read_only_addr_specified == TRUE)
	    addr_adjust = map->output_section->s.addr - 
	    		  segs_read_write_addr;
	else
	    addr_adjust = map->output_section->s.addr - 
			  merged_segments->sg.vmaddr;
	/*
	 * These relocation entries have been processed by a relocation routine
	 * turning external relocation entries into local relocation entries and
	 * updating the r_address field to be relative to the output section's
	 * address.
	 */
	for(i = 0; i < map->s->nreloc; i++){
	    reloc = relocs + i;
	    /*
	     * Break out the fields of the relocation entry we need here.
	     */
	    if((relocs[i].r_address & R_SCATTERED) != 0){
		sreloc = (struct scattered_relocation_info *)(relocs + i);
		r_scattered = 1;
		r_address = sreloc->r_address;
		r_pcrel = sreloc->r_pcrel;
		r_type = sreloc->r_type;
		r_extern = 0;
		r_value = sreloc->r_value;
		/*
		 * For a scattered relocation entry the r_symbolnum is never
		 * NO_SECT and that is all we need to know in this routine for
		 * a scattered relocation entry.  Calculating the r_symbolnum
		 * (n_sect) from the r_value now that it has been processed by
		 * a relocation routine is not easy as the value could be
		 * in a section with fine relocation entries.  It is doable but
		 * not needed here.  So we fake r_symbolnum to be anything but
		 * NO_SECT (the first section ordinal is used).
		 */
		r_symbolnum = 1;
	    }
	    else{
		r_scattered = 0;
		r_address = reloc->r_address;
		r_pcrel = reloc->r_pcrel;
		r_type = reloc->r_type;
		r_extern = reloc->r_extern;
		r_symbolnum = reloc->r_symbolnum;
	    }
	    if(reloc_has_pair(arch_flag.cputype, r_type))
		pair = 1;
	    else
		pair = 0;
	    if(partial_section){
		if(fine_reloc_offset_in_output_for_output_offset(map,
							r_address) == FALSE){
		    i += pair;
		    continue;
		}
	    }
	    if(r_extern == 0){
		sectdiff = reloc_is_sectdiff(arch_flag.cputype, r_type);
		has_sect_diff_relocs |= sectdiff;
		pic = (enum bool)
		      (sectdiff == TRUE ||
		       (r_pcrel == 1 && r_symbolnum != NO_SECT));
	    }
	    else
		pic = FALSE;
	    /*
	     * For output_for_dyld HPPA_RELOC_JBSR's are never put out.
	     */
	    if(arch_flag.cputype == CPU_TYPE_HPPA && r_type == HPPA_RELOC_JBSR){
		i += pair;
		continue;
	    }
	    /*
	     * The relocation entries in the output file is based on one of
	     * three different cases:
	     * 	The output file is a multi module dynamic shared library
	     *  The output file has a dynamic linker load command
	     *  The output does not have a dynamic linker load command
	     */
	    if(filetype == MH_DYLIB && multi_module_dylib == TRUE){
		/*
		 * For multi module dynamic shared library files all external
		 * relocations are kept as external relocation entries except
		 * for references to private externs (which are have been turned
		 * into locals and kept as locals) and all non-position-
		 * independent local relocation entrie are kept. Modules of
		 * multi module dylibs are not linked together and can only be
		 * slid keeping all sections relative to each other the same.
		 */
		if(r_extern){
		    reloc->r_address += addr_adjust;
		    memcpy(output_extrel + *nextrel, reloc,
			   sizeof(struct relocation_info) * (1 + pair));
		    (*nextrel) += 1 + pair;
		    if(flag_relocs == TRUE)
			flag_read_only_reloc(map->s, r_symbolnum, &first_time);
		}
		else if(pic == FALSE){
		    if(r_scattered)
			sreloc->r_address += addr_adjust;
		    else
			reloc->r_address += addr_adjust;
		    memcpy(output_locrel + *nlocrel, reloc,
			   sizeof(struct relocation_info) * (1 + pair));
		    (*nlocrel) += 1 + pair;
		}
	    }
	    else if(has_dynamic_linker_command){
		/*
		 * For an file with a dynamic linker load command only external
		 * relocation entries for undefined symbols (those that have
		 * not been turned into locals) are kept.  This output file is
		 * at a fixed address and can't be moved.
		 */
		if(r_extern){
		    reloc->r_address += addr_adjust;
		    memcpy(output_extrel + *nextrel, reloc,
			   sizeof(struct relocation_info) * (1 + pair));
		    (*nextrel) += 1 + pair;
		    if(flag_relocs == TRUE)
			flag_read_only_reloc(map->s, r_symbolnum, &first_time);
		}
		/*
		 * Even though the file can't be moved we may be trying to
		 * prebind.  If we are prebinging we need the local
		 * relocation entries for lazy symbol pointers to be saved
		 * so dyld will have the info to undo this if it fails.
		 */
		else if(save_lazy_symbol_pointer_relocs == TRUE &&
			(map->s->flags & SECTION_TYPE) ==
				S_LAZY_SYMBOL_POINTERS){
		    if(r_scattered){
			temp = sreloc->r_address + addr_adjust;
			sreloc->r_address += addr_adjust;
			if(sreloc->r_address != temp)
			    error_with_cur_obj("can't create relocation entry "
				"for prebinding (address of section (%.16s,"
				"%.16s) more than 24-bits away from first "
				"segment, use -noprebind)",
				map->s->segname, map->s->sectname);
		    }
		    else
			reloc->r_address += addr_adjust;
		    memcpy(output_locrel + *nlocrel, reloc,
			   sizeof(struct relocation_info) * (1 + pair));
		    (*nlocrel) += 1 + pair;
		}
	    }
	    else{
		/*
		 * For an file without a dynamic linker load command external
		 * relocation entries for undefined symbols (those that have
		 * not been turned into locals) are kept and locals that are
		 * non-position-independent are kept.  This file can only be
		 * slid keeping all sections relative to each other the same.
		 */
		if(r_extern){
		    reloc->r_address += addr_adjust;
		    memcpy(output_extrel + *nextrel, reloc,
			   sizeof(struct relocation_info) * (1 + pair));
		    (*nextrel) += 1 + pair;
		    if(flag_relocs == TRUE)
			flag_read_only_reloc(map->s, r_symbolnum, &first_time);
		}
		else if(pic == FALSE){
		    if(r_scattered)
			sreloc->r_address += addr_adjust;
		    else
			reloc->r_address += addr_adjust;
		    memcpy(output_locrel + *nlocrel, reloc,
			   sizeof(struct relocation_info) * (1 + pair));
		    (*nlocrel) += 1 + pair;
		}
	    }
	    i += pair;
	}
	if(flag_relocs == TRUE && *nlocrel != 0){
	    if(read_only_reloc_flag == READ_ONLY_RELOC_ERROR)
		error_with_cur_obj("has local relocation entries in "
		    "non-writable section (%.16s,%.16s)",
		    map->s->segname, map->s->sectname);
	    else
		warning_with_cur_obj("has local relocation entries in "
		    "non-writable section (%.16s,%.16s)",
		    map->s->segname, map->s->sectname);
	}
	if(sect_diff_reloc_flag != SECT_DIFF_RELOC_SUPPRESS &&
	   has_sect_diff_relocs == TRUE){
	    if(sect_diff_reloc_flag == SECT_DIFF_RELOC_ERROR)
		error_with_cur_obj("has section difference relocation entries "
		    "in section (%.16s,%.16s)", map->s->segname,
		    map->s->sectname);
	    else
		warning_with_cur_obj("has section difference relocation entries"
		    " in section (%.16s,%.16s)", map->s->segname,
		    map->s->sectname);
	}
}

/*
 * is_merged_section_read_only() returns TRUE if the merged section is in a
 * segment that does not have write permision.  Otherwise it returns FALSE.
 */
static
enum bool
is_merged_section_read_only(
struct merged_section *key)
{
    struct merged_segment **p, *msg;
    struct merged_section **q, *ms;

	p = &merged_segments;
	while(*p){
	    msg = *p;
	    q = &(msg->content_sections);
	    while(*q){
		ms = *q;
		if(ms == key){
		    if((msg->sg.initprot & VM_PROT_WRITE) == 0)
			return(TRUE);
		    else
			return(FALSE);
		}
		q = &(ms->next);
	    }
	    q = &(msg->zerofill_sections);
	    while(*q){
		ms = *q;
		if(ms == key){
		    if((msg->sg.initprot & VM_PROT_WRITE) == 0)
			return(TRUE);
		    else
			return(FALSE);
		}
		q = &(ms->next);
	    }
	    p = &(msg->next);
	}
	fatal("internal error: is_merged_section_read_only() called with "
	      "bad merged section");
	return(FALSE);
}

/*
 * is_merged_symbol_coalesced() is needed by the relocation routines to check
 * for illegal references to coalesced symbols via external relocation routines.
 * The section number in a merged symbol when relocation is done is the section
 * number in the output file so we have to look through the merged sections to
 * find which section this is.  This routine returns TRUE if the symbol is in
 * a coalesced section.
 */
__private_extern__
enum bool
is_merged_symbol_coalesced(
struct merged_symbol *merged_symbol)
{
    struct merged_segment **p, *msg;
    struct merged_section **q, *ms;

	if((merged_symbol->nlist.n_type & N_TYPE) != N_SECT)
	    return(FALSE);

	p = &merged_segments;
	while(*p){
	    msg = *p;
	    q = &(msg->content_sections);
	    while(*q){
		ms = *q;
		if(ms->output_sectnum == merged_symbol->nlist.n_sect){
		    if((ms->s.flags & SECTION_TYPE) == S_COALESCED)
			return(TRUE);
		    else
			return(FALSE);
		}
		q = &(ms->next);
	    }
	    p = &(msg->next);
	}
	fatal("internal error: is_merged_symbol_coalesced() called with "
	      "bad merged symbol");
	return(FALSE);
}

/*
 * scatter_copy_relocs() copies the relocation entries for an indirect section
 * or coalesced section into the output file based on which items in the section
 * are in the output file.  It returns the number of relocation entries that
 * were put in the output file.
 */
static
unsigned long
scatter_copy_relocs(
struct section_map *map,
struct relocation_info *relocs,
struct relocation_info *output_relocs)
{
    unsigned long i, nreloc;
    struct relocation_info *reloc;
    struct scattered_relocation_info *sreloc;
    unsigned long r_address, r_type;

	/*
	 * No checks done here as they were previously done and if there were
	 * errors this will not even get called.
	 */
	nreloc = 0;
	for(i = 0; i < map->s->nreloc; i++){
	    reloc = relocs + i;
	    /*
	     * Break out the fields of the relocation entry we need here.
	     */
	    if((relocs[i].r_address & R_SCATTERED) != 0){
		sreloc = (struct scattered_relocation_info *)(relocs + i);
		r_address = sreloc->r_address;
		r_type = sreloc->r_type;
	    }
	    else{
		r_address = reloc->r_address;
		r_type = reloc->r_type;
	    }
	    if(fine_reloc_offset_in_output_for_output_offset(map, r_address)){
		/* copy reloc into output file */
		memcpy(output_relocs + nreloc, reloc,
		       sizeof(struct relocation_info));
		nreloc++;
		if(reloc_has_pair(arch_flag.cputype, r_type)){
		    /* copy the reloc's pair into output file */
		    memcpy(output_relocs + nreloc, reloc + 1,
			   sizeof(struct relocation_info));
		    nreloc++;
		    i++;
		}
	    }
	    else if(reloc_has_pair(arch_flag.cputype, r_type))
		i++;
	}
	return(nreloc);
}

/*
 * nop_pure_instruction_scattered_sections() is a hack to pad an i386 pure
 * instructions sections with nop's (opcode 0x90) to make disassembly cleaner
 * between scatter loaded symbols.
 */
__private_extern__
void
nop_pure_instruction_scattered_sections(void)
{
    struct merged_segment **p, *msg;
    struct merged_section **content, *ms;
    char *contents;

	p = &merged_segments;
	while(*p){
	    msg = *p;
	    content = &(msg->content_sections);
	    while(*content){
		ms = *content;
		if((ms->order_filename != NULL &&
		    (ms->s.flags & SECTION_TYPE) == S_REGULAR) ||
		   ((ms->s.flags & SECTION_TYPE) == S_SYMBOL_STUBS ||
		    (ms->s.flags & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS||
		    (ms->s.flags & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS ||
		    (ms->s.flags & SECTION_TYPE) == S_COALESCED)){
		    if(arch_flag.cputype == CPU_TYPE_I386 &&
		       (ms->s.flags & S_ATTR_PURE_INSTRUCTIONS) != 0){
			contents = output_addr + ms->s.offset;
			memset(contents, 0x90, ms->s.size);
		    }
		}
		content = &(ms->next);
	    }
	    p = &(msg->next);
	}
}

/*
 * flush_scatter_copied_sections() flushes the entire merged section's output
 * for each merged regular (non-literal) content section that has a load order
 * (and indirect sections).
 */
__private_extern__
void
flush_scatter_copied_sections(void)
{
    struct merged_segment **p, *msg;
    struct merged_section **content, *ms;

	p = &merged_segments;
	while(*p){
	    msg = *p;
	    content = &(msg->content_sections);
	    while(*content){
		ms = *content;
		if((ms->order_filename != NULL &&
		    (ms->s.flags & SECTION_TYPE) == S_REGULAR) ||
		   ((ms->s.flags & SECTION_TYPE) == S_SYMBOL_STUBS ||
		    (ms->s.flags & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS||
		    (ms->s.flags & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS ||
		    (ms->s.flags & SECTION_TYPE) == S_COALESCED)){
		    output_flush(ms->s.offset, ms->s.size);
		}
		content = &(ms->next);
	    }
	    p = &(msg->next);
	}
}
#endif /* !defined(RLD) */

#ifdef RLD
/*
 * reset_merged_sections() is called from rld_load() to place the merged
 * sections back on their merged segment (layout() placed all of them on the
 * object_segment for the MH_OBJECT filetype) and it zeros the size of each the
 * merged section so it can be accumulated for the next rld_load().
 */
__private_extern__
void
reset_merged_sections(void)
{
    struct merged_segment *msg;
    struct merged_section *ms, *prev_ms;

	msg = original_merged_segments;
	if(msg != NULL && merged_segments->content_sections != NULL){
	    ms = merged_segments->content_sections;
	    while(ms != NULL){
		if(strncmp(ms->s.segname, msg->sg.segname,
			   sizeof(msg->sg.segname)) == 0){
		    msg->content_sections = ms;
		    ms->s.size = 0;
		    prev_ms = ms;
		    ms = ms->next;
		    while(ms != NULL && strncmp(ms->s.segname, msg->sg.segname,
			   			sizeof(msg->sg.segname)) == 0){
			ms->s.size = 0;
			prev_ms = ms;
			ms = ms->next;
		    }
		    prev_ms->next = NULL;
		}
		else{
		    msg = msg->next;
		}
	    }
	}

	msg = original_merged_segments;
	if(msg != NULL && merged_segments->zerofill_sections != NULL){
	    ms = merged_segments->zerofill_sections;
	    while(ms != NULL){
		if(strncmp(ms->s.segname, msg->sg.segname,
			   sizeof(msg->sg.segname)) == 0){
		    msg->zerofill_sections = ms;
		    ms->s.size = 0;
		    prev_ms = ms;
		    ms = ms->next;
		    while(ms != NULL && strncmp(ms->s.segname, msg->sg.segname,
			   			sizeof(msg->sg.segname)) == 0){
			ms->s.size = 0;
			prev_ms = ms;
			ms = ms->next;
		    }
		    prev_ms->next = NULL;
		}
		else{
		    msg = msg->next;
		}
	    }
	}
	merged_segments = original_merged_segments;
	original_merged_segments = NULL;
}

/*
 * zero_merged_sections_sizes() is called from rld_load() to zero the size field
 * in the merged sections so the sizes can be accumulated and free the literal
 * data for any literal sections.  Also the alignment of the existing sections
 * is reset to zero.
 */
__private_extern__
void
zero_merged_sections_sizes(void)
{
    struct merged_segment **p, *msg;
    struct merged_section **q, *ms;

	p = &merged_segments;
	while(*p){
	    msg = *p;
	    q = &(msg->content_sections);
	    while(*q){
		ms = *q;
		ms->s.size = 0;
		ms->s.align = 0;
		if(ms->literal_data != NULL){
		    if(ms->literal_free != NULL){
			(*ms->literal_free)(ms->literal_data, ms);
		    }
		}
		q = &(ms->next);
	    }
	    q = &(msg->zerofill_sections);
	    while(*q){
		ms = *q;
		ms->s.size = 0;
		ms->s.align = 0;
		q = &(ms->next);
	    }
	    p = &(msg->next);
	}
}

/*
 * remove_sections() removes the sections and segments that first came from the
 * current set from the merged section list.  The order that sections are
 * merged on to the lists is taken advantaged of here.
 */
__private_extern__
void
remove_merged_sections(void)
{
    struct merged_segment *msg, *prev_msg, *next_msg;
    struct merged_section *ms, *prev_ms, *next_ms;

	/* The compiler "warning: `prev_msg' and `prev_ms' may be used */
	/* uninitialized in this function" can safely be ignored */
	prev_msg = NULL;
	prev_ms = NULL;

	if(original_merged_segments != NULL)
	    reset_merged_sections();

	for(msg = merged_segments; msg != NULL; msg = msg->next){
	    /*
	     * If this segment first comes from the current set then all
	     * remaining segments also come from this set and all of their
	     * sections.  So they are all removed from the list.
	     */
	    if(msg->set_num == cur_set){
		if(msg == merged_segments)
		    merged_segments = NULL;
		else
		    prev_msg->next = NULL;
		while(msg != NULL){
		    ms = msg->content_sections;
		    while(ms != NULL){
			if(ms->literal_data != NULL){
			    if(ms->literal_free != NULL){
				(*ms->literal_free)(ms->literal_data, ms);
				free(ms->literal_data);
				ms->literal_data = NULL;
			    }
			}
			next_ms = ms->next;
			free(ms);
			ms = next_ms;
		    }
		    ms = msg->zerofill_sections;
		    while(ms != NULL){
			next_ms = ms->next;
			free(ms);
			ms = next_ms;
		    }
		    next_msg = msg->next;
		    free(msg);
		    msg = next_msg;
		}
		break;
	    }
	    else{
		/*
		 * This segment first comes from other than the current set
		 * so check to see in any of it's sections from from the 
		 * current set and if so remove them.  Again advantage of the
		 * order is taken so that if a section if found to come from
		 * the current set all remaining sections in that list also come
		 * from that set.
		 */
		for(ms = msg->content_sections; ms != NULL; ms = ms->next){
		    if(ms->set_num == cur_set){
			if(ms == msg->content_sections)
			    msg->content_sections = NULL;
			else
			    prev_ms->next = NULL;
			while(ms != NULL){
			    msg->sg.nsects--;
			    if(ms->literal_data != NULL)
				free(ms->literal_data);
			    next_ms = ms->next;
			    free(ms);
			    ms = next_ms;
			}
			break;
		    }
		    prev_ms = ms;
		}
		for(ms = msg->zerofill_sections; ms != NULL; ms = ms->next){
		    if(ms->set_num == cur_set){
			if(ms == msg->zerofill_sections)
			    msg->zerofill_sections = NULL;
			else
			    prev_ms->next = NULL;
			while(ms != NULL){
			    msg->sg.nsects--;
			    next_ms = ms->next;
			    free(ms);
			    ms = next_ms;
			}
			break;
		    }
		    prev_ms = ms;
		}
	    }
	    prev_msg = msg;
	}
}
#endif /* RLD */

#ifdef DEBUG
/*
 * print_merged_sections() prints the merged section table.  For debugging.
 */
__private_extern__
void
print_merged_sections(
char *string)
{
    struct merged_segment *msg;
    struct merged_section *ms;

	print("Merged section list (%s)\n", string);
	for(msg = merged_segments; msg ; msg = msg->next){
	    print("    Segment %.16s\n", msg->sg.segname);
	    print("\tcmd %lu\n", msg->sg.cmd);
	    print("\tcmdsize %lu\n", msg->sg.cmdsize);
	    print("\tvmaddr 0x%x ", (unsigned int)msg->sg.vmaddr);
	    print("(addr_set %s)\n", msg->addr_set ? "TRUE" : "FALSE");
	    print("\tvmsize 0x%x\n", (unsigned int)msg->sg.vmsize);
	    print("\tfileoff %lu\n", msg->sg.fileoff);
	    print("\tfilesize %lu\n", msg->sg.filesize);
	    print("\tmaxprot ");
	    print_prot(msg->sg.maxprot);
	    print(" (prot_set %s)\n", msg->prot_set ? "TRUE" : "FALSE");
	    print("\tinitprot ");
	    print_prot(msg->sg.initprot);
	    print("\n");
	    print("\tnsects %lu\n", msg->sg.nsects);
	    print("\tflags %lu\n", msg->sg.flags);
#ifdef RLD
	    print("\tset_num %lu\n", msg->set_num);
#endif /* RLD */
	    print("\tfilename %s\n", msg->filename);
	    print("\tcontent_sections\n");
	    for(ms = msg->content_sections; ms ; ms = ms->next){
		print("\t    Section (%.16s,%.16s)\n",
		       ms->s.segname, ms->s.sectname);
		print("\t\taddr 0x%x\n", (unsigned int)ms->s.addr);
		print("\t\tsize %lu\n", ms->s.size);
		print("\t\toffset %lu\n", ms->s.offset);
		print("\t\talign %lu\n", ms->s.align);
		print("\t\tnreloc %lu\n", ms->s.nreloc);
		print("\t\treloff %lu\n", ms->s.reloff);
		print("\t\tflags %s\n",
		      section_flags[ms->s.flags & SECTION_TYPE]);
#ifdef RLD
		print("\t\tset_num %d\n", ms->set_num);
#endif /* RLD */
		if(ms->relocated == TRUE)
    		    print("\t    relocated TRUE\n");
		else
    		    print("\t    relocated FALSE\n");
		if(ms->referenced == TRUE)
    		    print("\t    referenced TRUE\n");
		else
    		    print("\t    referenced FALSE\n");
		if(ms->contents_filename){
		    print("\t    contents_filename %s\n",
			  ms->contents_filename);
		    print("\t    file_addr 0x%x\n",
			  (unsigned int)ms->file_addr);
		    print("\t    file_size %lu\n", ms->file_size);
		}
		if(ms->order_filename){
		    print("\t    order_filename %s\n",
			  ms->order_filename);
		    print("\t    order_addr 0x%x\n",
			  (unsigned int)ms->order_addr);
		    print("\t    order_size %lu\n", ms->order_size);
		}
		if((ms->s.flags & SECTION_TYPE) == S_CSTRING_LITERALS)
		    print_cstring_data(ms->literal_data, "\t    ");
		if((ms->s.flags & SECTION_TYPE) == S_4BYTE_LITERALS)
		    print_literal4_data(ms->literal_data, "\t    ");
		if((ms->s.flags & SECTION_TYPE) == S_8BYTE_LITERALS)
		    print_literal8_data(ms->literal_data, "\t    ");
		if((ms->s.flags & SECTION_TYPE) == S_LITERAL_POINTERS)
		    print_literal_pointer_data(ms->literal_data, "\t    ");
	    }
	    print("\tzerofill_sections\n");
	    for(ms = msg->zerofill_sections; ms ; ms = ms->next){
		print("\t    Section (%.16s,%.16s)\n",
		       ms->s.segname, ms->s.sectname);
		print("\t\taddr 0x%x\n", (unsigned int)ms->s.addr);
		print("\t\tsize %lu\n", ms->s.size);
		print("\t\toffset %lu\n", ms->s.offset);
		print("\t\talign %lu\n", ms->s.align);
		print("\t\tnreloc %lu\n", ms->s.nreloc);
		print("\t\treloff %lu\n", ms->s.reloff);
		print("\t\tflags %s\n",
		      section_flags[ms->s.flags & SECTION_TYPE]);
#ifdef RLD
		print("\t\tset_num %lu\n", ms->set_num);
#endif /* RLD */
	    }
	}
}

/*
 * print_merged_section_stats() prints the stats for the merged sections.
 * For tuning..
 */
__private_extern__
void
print_merged_section_stats(void)
{
    struct merged_segment *msg;
    struct merged_section *ms;

	for(msg = merged_segments; msg ; msg = msg->next){
	    for(ms = msg->content_sections; ms ; ms = ms->next){
		if((ms->s.flags & SECTION_TYPE) == S_LITERAL_POINTERS)
		    literal_pointer_data_stats(ms->literal_data, ms);
		else if((ms->s.flags & SECTION_TYPE) == S_CSTRING_LITERALS)
		    cstring_data_stats(ms->literal_data, ms);
		else if((ms->s.flags & SECTION_TYPE) == S_4BYTE_LITERALS)
		    literal4_data_stats(ms->literal_data, ms);
		else if((ms->s.flags & SECTION_TYPE) == S_8BYTE_LITERALS)
		    literal8_data_stats(ms->literal_data, ms);
	    }
	}
}

/*
 * print_load_order() prints the load_order array passed to it.
 * For debugging.
 */
__private_extern__
void
print_load_order(
struct load_order *load_order,
unsigned long nload_order,
struct merged_section *ms,
struct object_file *object_file,
char *string)
{
    unsigned long i;

	print("Load order 0x%x %lu entries for (%.16s,%.16s) of ",
	      (unsigned int)load_order, nload_order,
	      ms->s.segname, ms->s.sectname);
	print_obj_name(object_file);
	print("(%s)\n", string);
	for(i = 0; i < nload_order; i++){
	    print("entry[%lu]\n", i);
	    print("           name %s\n", load_order[i].name == NULL ? "null" :
		  load_order[i].name);
	    print("          value 0x%08x\n",(unsigned int)load_order[i].value);
	    print("          order %lu\n", load_order[i].order);
	    print("   input_offset %lu\n", load_order[i].input_offset);
	    print("     input_size %lu\n", load_order[i].input_size);
	    print("  output_offset %lu\n", load_order[i].output_offset);
	}
}

/*
 * print_name_arrays() prints the sorted arrays of archive and object names.
 * For debugging.
 */
__private_extern__
void
print_name_arrays(void)
{
    unsigned long i, j;

	print("Sorted archive names:\n");
	for(i = 0; i < narchive_names; i++){
	    print("    archive name %s\n", archive_names[i].archive_name);
	    print("    number of objects %lu\n",archive_names[i].nobject_names);
	    print("    Sorted object names:\n");
	    for(j = 0; j < archive_names[i].nobject_names; j++){
		print("\tobject name %s\n",
		      archive_names[i].object_names[j].object_name);
		print("\tlength %lu\n",
		      archive_names[i].object_names[j].index_length);
		print("\tobject file 0x%x ", (unsigned int)
		      (archive_names[i].object_names[j].object_file));
		print_obj_name(archive_names[i].object_names[j].object_file);
		print("\n");
	    }
	}
	print("Sorted object names:\n");
	for(j = 0; j < nobject_names; j++){
	    print("\tobject name %s\n", object_names[j].object_name);
	    print("\tindex %lu\n", object_names[j].index_length);
	    print("\tobject file 0x%x ",
		  (unsigned int)(object_names[j].object_file));
	    print_obj_name(object_names[j].object_file);
	    print("\n");
	}
}

static
void
print_load_symbol_hash_table(void)
{
    unsigned long i;
    struct load_symbol *load_symbol, *other_name;

	print("load_symbol_hash_table:\n");
	if(load_symbol_hashtable == NULL)
	    return;
	for(i = 0; i < LOAD_SYMBOL_HASHTABLE_SIZE; i++){
	    if(load_symbol_hashtable[i] != NULL)
		print("[%lu]\n", i);
	    for(load_symbol = load_symbol_hashtable[i]; 
	        load_symbol != NULL;
	        load_symbol = load_symbol->next){
		print("load symbol: %lu\n", i);
		if(load_symbol->archive_name != NULL){
		    print("    (%s:%s:%s) length %lu\n",
			  load_symbol->archive_name,
			  load_symbol->object_name,
			  load_symbol->symbol_name,
			  load_symbol->index_length);
		}
		else{
		    print("    (%s:%s) index %lu\n",
			  load_symbol->object_name,
			  load_symbol->symbol_name,
			  load_symbol->index_length);
		}
		print("    load_order 0x%x\n",
		      (unsigned int)(load_symbol->load_order));
		print("    other_names 0x%x\n",
		      (unsigned int)(load_symbol->other_names));
		print("    next 0x%x\n",
		      (unsigned int)(load_symbol->next));
		for(other_name = load_symbol->other_names;
		    other_name != NULL;
		    other_name = other_name->other_names){
		    print("other name\n");
		    if(other_name->archive_name != NULL){
			print("    (%s:%s:%s) length %lu\n",
			      other_name->archive_name,
			      other_name->object_name,
			      other_name->symbol_name,
			      other_name->index_length);
		    }
		    else{
			print("    (%s:%s) index %lu\n",
			      other_name->object_name,
			      other_name->symbol_name,
			      other_name->index_length);
		    }
		    print("    load_order 0x%x\n",
			  (unsigned int)(other_name->load_order));
		    print("    other_names 0x%x\n",
			  (unsigned int)(other_name->other_names));
		    print("    next 0x%x\n",
			  (unsigned int)(load_symbol->next));
		}
	    }
	}
}
#endif /* DEBUG */