/*
 * Copyright (c) 1999-2003 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 that deal with indirect sections (both
 * lazy and non-lazy symbol pointer sections as well as symbol stub sections). 
 */
#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 "stuff/arch.h"
#include "stuff/reloc.h"

#include "ld.h"
#include "live_refs.h"
#include "objects.h"
#include "sections.h"
#include "pass2.h"
#include "generic_reloc.h"
#include "pass1.h"
#include "symbols.h"
#include "layout.h"
#include "indirect_sections.h"
#include "dylibs.h"

/*
 * The number of indirect symbol table entries in the output file.
 */
__private_extern__ unsigned long nindirectsyms = 0;

/*
 * If we are still attempting to prebind when indirect_section_merge() is
 * called save_lazy_symbol_pointer_relocs will get set in layout.c.
 * Between the time indirect_section_merge() gets called and the time
 * reloc_output_for_dyld() gets called prebinding may be disabled because of
 * various problems.  But the count of relocs can't change after layout so
 * we'll put them out anyway.
 */
__private_extern__ enum bool save_lazy_symbol_pointer_relocs = FALSE;

#ifndef SA_RLD

static unsigned long lookup_indirect_item(
    struct merged_symbol *merged_symbol,
    struct object_file *obj,
    unsigned long index,
    struct indirect_section_data *data, 
    unsigned long stride,
    enum bool *new);

/*
 * indirect_section_merge() merges items from symbol pointers and symbol stub
 * sections from the specified section in the current object file (cur_obj).
 * When redo_live is FALSE it allocates a fine relocation map and sets the
 * fine_relocs field in the section_map to it (as well as the count).
 *
 * When redo_live is FALSE after all the items for this section in this object
 * file have been merged two more things are done.  First the number of
 * relocation entries that will be in the output file is adjusted (incremented)
 * based on which items are used from this object's section.  Second the number
 * of local symbol table entries and the size of the string table is adjusted
 * (decremented) based on the which symbols are in the items from this object's
 * section that will be in the resulting object file.
 *
 * When redo_live is TRUE it re-merges only the live items from symbol pointers
 * and symbol stub sections from the specified section in the current object
 * file (cur_obj).
 */
__private_extern__
void
indirect_section_merge(
struct indirect_section_data *data, 
struct merged_section *ms,
struct section *s, 
struct section_map *section_map,
enum bool redo_live)
{
    unsigned long i, j, stride, section_type, nitems, index;
    struct fine_reloc *fine_relocs;
    struct nlist *nlists;
    unsigned long *indirect_symtab;
    struct undefined_map *undefined_map;
    struct merged_symbol *merged_symbol, *indr_symbol, **hash_pointer;
    enum bool new;

    struct relocation_info *relocs, reloc;
    struct scattered_relocation_info *sreloc, *spair_reloc;
    unsigned long r_address, r_pcrel, r_length, r_type, pair_r_type, r_extern,
		  r_symbolnum, r_scattered, r_value, pair;
#ifndef RLD
    enum bool pic;
#endif
    enum bool defined, force_extern_reloc;
    unsigned long nsect;
    char *strings;

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

	if(s->size == 0)
	    return;

	stride = 0;
	pair_r_type = 0;
	section_type = s->flags & SECTION_TYPE;
	if(section_type == S_LAZY_SYMBOL_POINTERS ||
	   section_type == S_NON_LAZY_SYMBOL_POINTERS){
	    stride = 4;
	}
	else if(section_type == S_SYMBOL_STUBS)
	    stride = s->reserved2;
	else
	    fatal("internal error, in indirect_section_merge() section type "
		  "(%lu) is not correct for an indirect section", section_type);
	nitems = s->size / stride;

	if(s->size % stride != 0){
	    error_with_cur_obj("malformed object (section (%.16s,%.16s) size "
		"is not a multiple of %lu bytes)", s->segname, s->sectname,
		stride);
	    return;
	}
	if(cur_obj->dysymtab == NULL){
	    error_with_cur_obj("malformed object (file has indirect section "
		"(%.16s,%.16s) but no dysymtab_command)", s->segname,
		s->sectname);
	    return;
	}
	if(s->reserved1 > cur_obj->dysymtab->nindirectsyms){
	    error_with_cur_obj("malformed object (index into indirect symbol "
		"table (reserved1 field) for indirect section (%.16s,%.16s) "
		"past the end of the table)", s->segname, s->sectname);
	    return;
	}
	if(s->reserved1 + nitems > cur_obj->dysymtab->nindirectsyms){
	    error_with_cur_obj("malformed object (indirect symbol table entries"
		"for section (%.16s,%.16s) extends past the end of the table)",
		s->segname, s->sectname);
	    return;
	}
	if(s->size == 0){
	    if(redo_live == FALSE){
		section_map->fine_relocs = NULL;
		section_map->nfine_relocs = 0;
	    }
	    return;
	}
#ifdef DEBUG
	if(redo_live == FALSE){
	    data->nfiles++;
	    data->nitems += nitems;
	}
#endif /* DEBUG */

	/*
	 * First deal with the contents of the indirect section and determine
	 * based on the indirect symbol for each item in the section if the
	 * contents 1) will in the output and used from this object, 2) used
	 * from a previous merged object or 3) the value of the indirect symbol
	 * will be used instead of the contents of this item.  This information
	 * is encoded into the fine_reloc structures for each item.
	 */
	/* setup pointers to the symbol, indirect symbol and string tables */
	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;
	if(redo_live == FALSE){
	    fine_relocs = allocate(nitems * sizeof(struct fine_reloc));
	    memset(fine_relocs, '\0', nitems * sizeof(struct fine_reloc));
	}
	else{
	    fine_relocs = section_map->fine_relocs;
	    nitems = section_map->nfine_relocs;
	}
	for(i = 0; i < nitems; i++){
	    /* get the indirect symbol table entry for this item */
	    index = indirect_symtab[s->reserved1 + i];
	    /*
	     * With strip(1), nmedit(l) or and "ld(1) -r with private externs
	     * index could be INDIRECT_SYMBOL_LOCAL or INDIRECT_SYMBOL_ABS.
	     */
	    if(index == INDIRECT_SYMBOL_LOCAL ||
	       index == INDIRECT_SYMBOL_ABS){
		if(redo_live == FALSE){
		    fine_relocs[i].local_symbol = TRUE;
		    fine_relocs[i].indirect_defined = FALSE;
		    fine_relocs[i].use_contents = TRUE;
		    fine_relocs[i].input_offset = i * stride;
		    fine_relocs[i].merged_symbol = NULL;
		    if(index == INDIRECT_SYMBOL_LOCAL)
			fine_relocs[i].indirect_symbol_local = TRUE;
		}
		if(redo_live == FALSE || fine_relocs[i].live == TRUE){
		    fine_relocs[i].output_offset = lookup_indirect_item(
				NULL, cur_obj, index, data, stride, &new);
		}
		else if(redo_live == TRUE && fine_relocs[i].live == FALSE){
		    fine_relocs[i].use_contents = FALSE;
		}
		goto account_for_size;
	    }

	    /*
	     * Since the indirect symbol table entry should be for an undefined
	     * symbol use the current object's undefined symbol map to get the
	     * merged symbol.
	     */
	    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 = undefined_map->merged_symbol;
		/*
		 * If the indirect symbol table entry is for a private extern
		 * it is an error.
		 */
		if((merged_symbol->nlist.n_type & N_PEXT) == N_PEXT &&
		   (merged_symbol->nlist.n_type & N_EXT) != N_EXT &&
		   section_type == S_NON_LAZY_SYMBOL_POINTERS &&
		   output_for_dyld == FALSE)
		    fatal("indirect symbol:%s can not be a private extern",
			  strings + nlists[index].n_un.n_strx);
	    }
	    else if((nlists[index].n_type & N_EXT) == N_EXT){
		/*
		 * The indirect symbol table entry was not for an undefined but
		 * it is an external symbol so get the merged_symbol for this
		 * external symbol by looking it up by name.
		 */
		merged_symbol = *(lookup_symbol(strings +
						nlists[index].n_un.n_strx));
		if(merged_symbol == NULL)
		    fatal("interal error, indirect_section_merge() failed in "
			  "looking up external symbol");
		/*
		 * If this is an indirect symbol resolve indirection (all chains
		 * of indirect symbols have been resolved so that they point at
		 * a symbol that is not an indirect symbol).
		 */
		if((merged_symbol->nlist.n_type & N_TYPE) == N_INDR)
		    merged_symbol = (struct merged_symbol *)
				    merged_symbol->nlist.n_value;
		/*
		 * If the indirect symbol table entry is for a private extern
		 * it is an error.
		 */
		if((merged_symbol->nlist.n_type & N_PEXT) == N_PEXT &&
		   (merged_symbol->nlist.n_type & N_EXT) != N_EXT &&
		   section_type == S_NON_LAZY_SYMBOL_POINTERS &&
		   output_for_dyld == FALSE)
		    fatal("indirect symbol:%s can not be a private extern",
			  strings + nlists[index].n_un.n_strx);
	    }
	    else if((nlists[index].n_type & N_PEXT) == N_PEXT &&
		    (nlists[index].n_type & N_EXT) != N_EXT &&
		    section_type == S_NON_LAZY_SYMBOL_POINTERS &&
		    output_for_dyld == FALSE)
		/*
		 * If the indirect symbol table entry is for a private extern
		 * it is an error.
		 */
		fatal("indirect symbol:%s can not be a private extern",
		      strings + nlists[index].n_un.n_strx);
	    else if((nlists[index].n_type & N_STAB) == 0){
		/*
		 * The indirect symbol table entry was not even an external
		 * symbol but it is a local defined symbol.  So the symbol table
		 * index will be use in place of the merged_symbol.  This is
		 * allowed on input only for stub sections and won't be created
		 * on output.
		 */
		merged_symbol = NULL;
	    }
	    else{
		/*
		 * The indirect symbol table entry was a stab, this is an error.
		 */
		error_with_cur_obj("malformed object (bad indirect symbol "
		    "table entry (%ld) (symbol at index %lu is a stab)",
		    s->reserved1 + i, index);
		return;
	    }
	    /*
	     * Now set the values in the fine relocation entry for this item
	     * based on the type of section and the merged symbol for this
	     * item's indirect symbol and if the output file is a dynamic
	     * shared library file.
	     */
	    if(redo_live == FALSE)
		fine_relocs[i].input_offset = i * stride;
	    if(merged_symbol == NULL){
		/*
		 * Indirect table entries which refer to local defined symbols
		 * are allowed only in symbol stub and lazy pointer sections so
		 * they can always be removed and never created on output.
		 */
		if(section_type == S_NON_LAZY_SYMBOL_POINTERS){
		    if((nlists[index].n_type & N_PEXT) == 0){
			error_with_cur_obj("malformed object (bad indirect "
			    "symbol table entry (%ld) (symbol at index %lu is "
			    "not external)", s->reserved1 + i, index);
			return;
		    }
		    /*
		     * A non-lazy symbol pointer for a private extern that no
		     * longer is external.
		     */
		    if(redo_live == FALSE){
			fine_relocs[i].local_symbol = TRUE;
			fine_relocs[i].indirect_defined = TRUE;
			fine_relocs[i].use_contents = TRUE;
			fine_relocs[i].merged_symbol = NULL;
		    }
		    if(redo_live == FALSE || fine_relocs[i].live == TRUE){
			fine_relocs[i].output_offset = lookup_indirect_item(
				    NULL, cur_obj, index, data, stride, &new);
		    }
		    else if(redo_live == TRUE && fine_relocs[i].live == FALSE){
			fine_relocs[i].use_contents = FALSE;
		    }
		}
		else{
		    if(redo_live == FALSE){
			fine_relocs[i].local_symbol = TRUE;
			fine_relocs[i].indirect_defined = TRUE;
			fine_relocs[i].use_contents = FALSE;
			fine_relocs[i].output_offset = index;
			fine_relocs[i].merged_symbol = NULL;
		    }
		}
	    }
	    else{
		/*
		 * If this is an indirect symbol resolve indirection (all chains
		 * of indirect symbols have been resolved so that they point at
		 * a symbol that is not an indirect symbol).
		 */
		if((merged_symbol->nlist.n_type & N_TYPE) == N_INDR &&
		   merged_symbol->defined_in_dylib == FALSE){
		    /*
		     * If this N_INDR symbol was in a chain has symbols both
		     * from dylib and not from dylibs get then there was a
		     * recorded a pair for the merged symbol and the first in
		     * the chain defined in a dylib for the indr_symbol to be
		     * used.  If not then merged_symbol->nlist.n_value can be
		     * used.
		     */
		    indr_symbol = NULL;
		    for(j = 0; j < nindr_symbol_pairs; j++){
			if(indr_symbol_pairs[j].merged_symbol == merged_symbol)
			    indr_symbol = indr_symbol_pairs[j].indr_symbol;
		    }
		    if(indr_symbol == NULL)
			indr_symbol = (struct merged_symbol *)
				(merged_symbol->nlist.n_value);
		    merged_symbol = indr_symbol;
		}

		if(redo_live == FALSE)
		    fine_relocs[i].local_symbol = FALSE;

		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)){
		    if(redo_live == FALSE){
			fine_relocs[i].indirect_defined = FALSE;
		    }
		}
		else 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 &&
			filetype != MH_DYLINKER){
		    if((output_for_dyld &&
		       (has_dynamic_linker_command || filetype == MH_BUNDLE) &&
		       (((merged_symbol->nlist.n_desc & N_WEAK_DEF) !=
			 N_WEAK_DEF) ||
		       ((merged_symbol->nlist.n_type & N_PEXT) == N_PEXT &&
			keep_private_externs == FALSE) ) ) ||
		       (filetype == MH_DYLIB && multi_module_dylib == FALSE &&
			(merged_symbol->nlist.n_type & N_PEXT) == N_PEXT) ){
			if(redo_live == FALSE)
			    fine_relocs[i].indirect_defined = TRUE;
		    }
		    else{
			if(redo_live == FALSE)
			    fine_relocs[i].indirect_defined = FALSE;
		    }
		}
		else{
		    if(redo_live == FALSE)
			fine_relocs[i].indirect_defined = TRUE;
		}

		if((merged_symbol->nlist.n_type & N_TYPE) == N_ABS)
		    section_map->absolute_indirect_defineds = TRUE;

		if((filetype == MH_DYLIB && multi_module_dylib == TRUE) ||
		   section_type == S_NON_LAZY_SYMBOL_POINTERS ||
		   fine_relocs[i].indirect_defined == FALSE){
		    if(redo_live == FALSE || fine_relocs[i].live == TRUE){
			fine_relocs[i].output_offset = lookup_indirect_item(
				    merged_symbol, NULL, 0, data, stride, &new);
		    }
		    else if(redo_live == TRUE && fine_relocs[i].live == FALSE){
			new = FALSE;
		    }
		    fine_relocs[i].use_contents = new;
		    if(redo_live == FALSE)
			fine_relocs[i].merged_symbol = merged_symbol;
		}
		else{
		    if(redo_live == FALSE){
			fine_relocs[i].use_contents = FALSE;
			fine_relocs[i].merged_symbol = merged_symbol;
		    }
		}
	    }

account_for_size:
	    /*
	     * If this item's contents will be used in the output file account
	     * for it's size in the merged section and it's entry in the
	     * indirect symbol table.
	     */
	    if(fine_relocs[i].use_contents){
		/*
		 * When -dead_strip is specified this will be called a second
		 * time and redo_live will be TRUE.  If so only account for this
		 * item if it is live.
		 */
		if(redo_live == FALSE || fine_relocs[i].live == TRUE){
		    ms->s.size += stride;
		    nindirectsyms++;
		}
	    }
	}
	if(redo_live == FALSE){
	    section_map->fine_relocs = fine_relocs;
	    section_map->nfine_relocs = nitems;
	}

	/*
	 * Second deal with the relocation entries for the section in this
	 * object file.  Now that it has been determined for which items the 
	 * contents will be used from this object file.
	 */
	/*
 	 * A lazy symbol pointer section must have one relocation entry for each
	 * pointer.  A non-lazy symbol pointer section can't have any relocation
	 * entries (this was checked in check_cur_obj() before getting here).
	 * And a symbol stub section may have many relocation entries.
	 */
	if(section_type == S_LAZY_SYMBOL_POINTERS && s->nreloc != nitems){
	    error_with_cur_obj("malformed object (lazy symbol pointer section "
		"(%.16s,%.16s) does not have is exactly one relocation entry "
		"for each pointer)", s->segname, s->sectname);
	    return;
	}
	/*
	 * This loop loops through the relocation entries and using the
	 * use_contents field (via a call to fine_reloc_offset_in_output())
	 * of the fine_relocs just created determines how many relocation
	 * entries will be in the output for this section of this object file.
	 */
	relocs = (struct relocation_info *)(cur_obj->obj_addr + s->reloff);
	for(i = 0; i < s->nreloc; i++){
	    reloc = relocs[i];
	    if(cur_obj->swapped &&
	       section_map->input_relocs_already_swapped == FALSE)
		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_scattered = 1;
		r_address = sreloc->r_address;
		r_pcrel = sreloc->r_pcrel;
		r_length = sreloc->r_length;
		r_value = sreloc->r_value;
		r_type = sreloc->r_type;
		r_extern = 0;

		/* 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){
			error_with_cur_obj("r_value (0x%x) field of relocation "
			    "entry %lu in section (%.16s,%.16s) out of range",
			    (unsigned int)r_value, i, section_map->s->segname,
			    section_map->s->sectname);
			return;
		    }
		}
	    }
	    else{
		r_scattered = 0;
		r_address = reloc.r_address;
		r_pcrel = reloc.r_pcrel;
		r_length = reloc.r_length;
		r_type = reloc.r_type;
		r_extern = reloc.r_extern;
		r_symbolnum = reloc.r_symbolnum;
	    }
	    /*
	     * Make sure that this is not a stray PAIR relocation entry.
	     */
	    if(r_type == reloc_pair_r_type(arch_flag.cputype)){
		error_with_cur_obj("malformed object (stray relocation PAIR "
		    "entry (%lu) in section (%.16s,%.16s))", i, s->segname,
		    s->sectname);
		continue;
	    }
	    /*
	     * The r_address field is really an offset into the contents of the
	     * section and must reference something inside the section.
	     */
	    if(r_address >= s->size){
		error_with_cur_obj("malformed object (r_address (0x%x) field "
		    "of relocation entry %ld in section (%.16s,%.16s) out of "
		    "range)",(unsigned int)r_address, i,s->segname,s->sectname);
		continue;
	    }
	    /*
	     * If this relocation entry is suppose to have a PAIR make sure it
	     * does.
	     */
	    if(reloc_has_pair(arch_flag.cputype, r_type)){
		if(i + 1 < s->nreloc){
		    reloc = relocs[i + 1];
		    if(cur_obj->swapped &&
		       section_map->input_relocs_already_swapped == FALSE)
			swap_relocation_info(&reloc, 1, host_byte_sex);
		    if((reloc.r_address & R_SCATTERED) != 0){
			spair_reloc = (struct scattered_relocation_info *)
                                      &reloc;
                        pair_r_type = spair_reloc->r_type;
		    }
		    else{
                        pair_r_type = reloc.r_type;
		    }
		}
		if(i + 1 >= s->nreloc ||
		   pair_r_type != reloc_pair_r_type(arch_flag.cputype)){
		    error_with_cur_obj("malformed object (relocation entry "
			"(%lu) in section (%.16s,%.16s) missing following "
			"associated PAIR entry)", i, s->segname, s->sectname);
                    continue;
                }
	    }
	    /*
	     * For a lazy symbol pointer section all relocation entries must be
	     * for one of the pointers or an external relocation entry and
	     * therefore the offset must be a multiple of 4 (the size of a
	     * pointer), have an r_length field of 2 (long) and a r_pcrel field
	     * of 0 (FALSE).
	     */
	    if(section_type == S_LAZY_SYMBOL_POINTERS){
		if(r_address % 4 != 0){
		    error_with_cur_obj("malformed object, illegal reference "
			"(r_address (0x%x) field of relocation entry %lu in "
			"lazy symbol pointer section (%.16s,%.16s) is not a "
			"multiple of 4)", (unsigned int)r_address, i,
			s->segname, s->sectname);
		    continue;
		}
		if(r_length != 2){
		    error_with_cur_obj("malformed object, illegal reference "
			"(r_length (0x%x) field of relocation entry %lu in "
			"lazy symbol pointer section (%.16s,%.16s) is not 2 "
			"(long))", (unsigned int)r_length, i, s->segname,
			s->sectname);
		    continue;
		}
		if(r_pcrel != 0){
		    error_with_cur_obj("malformed object, illegal reference "
			"(r_pcrel (0x%x) field of relocation entry %lu in lazy "
			"symbol pointer section (%.16s,%.16s) is not 0 "
			"(FALSE))", (unsigned int)r_pcrel, i, s->segname,
			s->sectname);
		    continue;
		}
		if(r_type != 0){
		    error_with_cur_obj("malformed object, illegal reference "
			"(r_type (0x%x) field of relocation entry %lu in lazy "
			"symbol pointer section (%.16s,%.16s) is not 0 "
			"(VANILLA))", (unsigned int)r_type, i, s->segname,
			s->sectname);
		    continue;
		}
		if(r_scattered != 0){
		    error_with_cur_obj("malformed object, illegal reference "
			"(relocation entry %lu in lazy symbol pointer section "
			"(%.16s,%.16s) is a scattered type)", i, s->segname,
			s->sectname);
		    continue;
		}
		if(r_extern == 0 && r_symbolnum == R_ABS){
	    	    error_with_cur_obj("malformed object, illegal reference "
			"(reference from a lazy symbol pointer section (%.16s,"
			"%.16s) relocation entry (%lu) which is not to a "
			"symbol stub section)", s->segname, s->sectname, i);
		    continue;
		}
	    }
	    /*
	     * Assumed the symbol for this relocation entry is defined (always
	     * true for local relocation entries).  Then reset the variable
	     * "defined" correctly if this is an external relocation entry based
	     * on if the symbol is defined, where it is defined and the output
	     * file type.
	     */
	    defined = TRUE;
	    force_extern_reloc = FALSE;
	    if(output_for_dyld && r_extern){
		/*
		 * This is an external relocation entry.  So the value to be
		 * added to the item to be relocated is the value of the symbol.
		 * r_symbolnum is an index into the input file's symbol table
		 * of the symbol being refered to.  The symbol must be an
		 * undefined or coalesced symbol to be used in an external
		 * relocation entry.
		 */
		if(r_symbolnum >= cur_obj->symtab->nsyms){
		    error_with_cur_obj("r_symbolnum (%lu) field of external "
			"relocation entry %lu in section (%.16s,%.16s) out of "
			"range", r_symbolnum, i, s->segname, s->sectname);
		    continue;
		}
		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{
		    if((nlists[r_symbolnum].n_type & N_EXT) != N_EXT){
			error_with_cur_obj("r_symbolnum (%lu) field of "
			    "external relocation entry %lu in section "
			    "(%.16s,%.16s) refers to a non-external symbol",
			     r_symbolnum, i, s->segname, s->sectname);
			continue;
		    }
		    /*
		     * 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 indirect_section_merge() "
				  "failed to lookup coalesced symbol %s",
				  strings + nlists[r_symbolnum].n_un.n_strx);
			}
			merged_symbol = *hash_pointer;
			if(((merged_symbol->nlist.n_type & N_PEXT) == N_PEXT &&
			    keep_private_externs == FALSE) ||
			   dynamic == FALSE ||
			   (output_for_dyld && has_dynamic_linker_command))
			    force_extern_reloc = FALSE;
			else
			    force_extern_reloc = TRUE;
		    }
		    else{
			if(nlists[r_symbolnum].n_type != (N_EXT | N_UNDF)){
			    error_with_cur_obj("r_symbolnum (%lu) field of "
				"external relocation entry %lu in section "
				"(%.16s,%.16s) refers to a non-undefined "
				"symbol", r_symbolnum, i,
				section_map->s->segname,
				section_map->s->sectname);
			    return;
			}
			print_obj_name(cur_obj);
			fatal("internal error, in indirect_section_merge()"
			    " symbol index %lu in above file not in "
			    "undefined map", r_symbolnum);
		    }
		}
		/*
		 * If this is an indirect symbol resolve indirection (all chains
		 * of indirect symbols have been resolved so that they point at
		 * a symbol that is not an indirect symbol).
		 */
		if((merged_symbol->nlist.n_type & N_TYPE) == N_INDR)
		    merged_symbol = (struct merged_symbol *)
				    merged_symbol->nlist.n_value;
		/*
		 * For multi module dynamic shared library format files the
		 * merged sections that could have had external relocation
		 * entries must be resolved to private extern symbols.  This is
		 * because for multi module MH_DYLIB files all modules share the
		 * merged sections and the entire section gets relocated when
		 * the library is mapped in. So the above restriction assures
		 * the merged section will get relocated properly and can be
		 * shared amoung library modules.
		 */
		if(filetype == MH_DYLIB && multi_module_dylib == TRUE){
		    /*
		     * If the symbol is undefined or not a private extern it is
		     * an error for in this section for a MH_DYLIB file.
		     */
		    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)){
			if(merged_symbol->error_flagged_for_dylib == 0){
			    error_with_cur_obj("illegal undefined reference "
				"for multi module MH_DYLIB output file to "
				"symbol: %s from section (%.16s,%.16s) "
				"relocation entry: %lu",
				merged_symbol->nlist.n_un.n_name, s->segname,
				s->sectname, i);
			    merged_symbol->error_flagged_for_dylib = 1;
			}
		    }
		    else if((merged_symbol->nlist.n_type & N_PEXT) != N_PEXT){
			if(merged_symbol->error_flagged_for_dylib == 0){
			    error_with_cur_obj("illegal external reference for "
				"multi module MH_DYLIB output file to symbol: "
				"%s (not a private extern symbol) from section "
				"(%.16s,%.16s) relocation entry: %lu",
				merged_symbol->nlist.n_un.n_name,
				s->segname, s->sectname, i);
			    merged_symbol->error_flagged_for_dylib = 1;
			}
		    }
		}
		else{
		    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
			defined = TRUE;
		}
	    }
	    if(reloc_has_pair(arch_flag.cputype, r_type))
		pair = 1;
	    else
		pair = 0;
#ifndef RLD
	    /*
	     * If saving relocation entries see if this relocation entry is for 
	     * an item that is going to be in the output file and if so count it
	     * as one of the output relocation entries.
	     */
	    if(output_for_dyld &&
	       fine_reloc_offset_in_output(section_map, r_address)){
		/*
		 * Mark this section as being relocated (staticly).
		 */
		if(dead_strip == FALSE || redo_live == TRUE)
		    ms->relocated = TRUE;
		if(r_extern == 0)
		    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);
		/*
		 * 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 there are
		     * no external relocation entries that will be left as
		     * external as checked above.  Only 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(pic == FALSE)
			ms->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)
			    ms->nextrel += 1 + pair;
			/*
			 * As of the PowerPC port, relocation entries for
			 * lazy symbol pointers can be external so when the
			 * the symbol is defined if we are doing prebinding and
			 * this is for a lazy symbol pointer then this will
			 * turn into a local relocation entry and we save it
			 * so we can undo the prebinding if needed.
			 */
			else if(save_lazy_symbol_pointer_relocs == TRUE &&
				section_type == S_LAZY_SYMBOL_POINTERS)
			    ms->nlocrel += 1 + pair;
		    }
		    /*
		     * 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 &&
			    section_type == S_LAZY_SYMBOL_POINTERS){
			ms->nlocrel += 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){
			if(defined == FALSE || force_extern_reloc == TRUE)
			    ms->nextrel += 1 + pair;
			else if(pic == FALSE)
			    ms->nlocrel += 1 + pair;
		    }
		    else if(pic == FALSE)
			ms->nlocrel += 1 + pair;
		}
	    }
	    else if(save_reloc &&
	            fine_reloc_offset_in_output(section_map, r_address)){
		ms->s.nreloc += 1 + pair;
		nreloc += 1 + pair;
	    }
#endif /* !defined(RLD) */
	    i += pair;
	}
	/*
	 * If the the number of relocation entries is not zero mark this section
	 * as being relocated (staticly).
	 */
	if(ms->s.nreloc != 0){
	    if(dead_strip == FALSE || redo_live == TRUE)
		ms->relocated = TRUE;
	}

	/*
	 * Third deal with the symbol table entries for local symbols and N_STAB
	 * symbols in this section in this object file.  Now that it has been
	 * determined for which items the contents will be used from this
	 * object file.  If when -dead_strip is specified we have to wait to
	 * do this until we are called a second time when redo_live is TRUE and
	 * all the fine_relocs have had their live field set.
	 */
	if(dead_strip == FALSE || redo_live == TRUE){
	    /* determine the section number this has in this object */
	    for(nsect = 0; nsect < cur_obj->nsection_maps; nsect++)
		if(s == cur_obj->section_maps[nsect].s)
		    break;
	    nsect++; /* section numbers start at 1 (not zero) */
	    /* set up a pointer to the string table */
	    strings = (char *)(cur_obj->obj_addr + cur_obj->symtab->stroff);
	    discard_local_symbols_for_section(nsect, nlists, strings, s,
					      section_map);
	}
}

static
unsigned long
lookup_indirect_item(
struct merged_symbol *merged_symbol,
struct object_file *obj,
unsigned long index,
struct indirect_section_data *data, 
unsigned long stride,
enum bool *new)
{
    unsigned long hashval, output_offset;
    struct indirect_item_block **p, *indirect_item_block;
    struct indirect_item *indirect_item;
    struct indirect_item_bucket *bp;

	if(data->hashtable == NULL){
	    data->hashtable = allocate(sizeof(struct indirect_item_bucket *) *
						  INDIRECT_SECTION_HASHSIZE);
	    memset(data->hashtable, '\0',
		   sizeof(struct indirect_item_bucket *) *
						  INDIRECT_SECTION_HASHSIZE);
	}
#if defined(DEBUG) && defined(PROBE_COUNT)
	data->nprobes++;
#endif
	hashval = ((unsigned long)merged_symbol) % INDIRECT_SECTION_HASHSIZE;
	for(bp = data->hashtable[hashval]; bp; bp = bp->next){
#if defined(DEBUG) && defined(PROBE_COUNT)
	    data->nprobes++;
#endif
	    if(bp->indirect_item->merged_symbol == merged_symbol &&
	       merged_symbol != NULL){
		*new = FALSE;
		return(bp->output_offset);
	    }
	}

	bp = allocate(sizeof(struct indirect_item_bucket));
	output_offset = 0;
	for(p = &(data->indirect_item_blocks);
	    *p ;
	    p = &(indirect_item_block->next)){

	    indirect_item_block = *p;
	    if(indirect_item_block->used != INDIRECT_SECTION_BLOCK_SIZE){
		indirect_item = indirect_item_block->indirect_items +
				  indirect_item_block->used;
		indirect_item->merged_symbol = merged_symbol;
		indirect_item->obj = obj;
		indirect_item->index = index;

		bp->indirect_item = indirect_item;
		bp->output_offset = output_offset +
				    indirect_item_block->used * stride;
		bp->next = data->hashtable[hashval];
		data->hashtable[hashval] = bp;

		indirect_item_block->used++;
		*new = TRUE;
		return(bp->output_offset);
	    }
	    output_offset += indirect_item_block->used * stride;
	}
	*p = allocate(sizeof(struct indirect_item_block));
	indirect_item_block = *p;
	indirect_item = indirect_item_block->indirect_items;
	indirect_item->merged_symbol = merged_symbol;
	indirect_item->obj = obj;
	indirect_item->index = index;
	indirect_item_block->used = 1;
	indirect_item_block->next = NULL;

	bp->indirect_item = indirect_item;
	bp->output_offset = output_offset;
	bp->next = data->hashtable[hashval];
	data->hashtable[hashval] = bp;

	*new = TRUE;
	return(bp->output_offset);
}

/*
 * indirect_section_order() enters indirect symbols from the order_file from the
 * merged section structure.  Since this is called before any call to
 * indirect_section_merge() and it enters the indirect symbols in the order of
 * the file it causes the section to be ordered.
 */
__private_extern__
void
indirect_section_order(
struct indirect_section_data *data, 
struct merged_section *ms)
{
#ifndef RLD
    kern_return_t r;
#ifdef __MWERKS__
    struct indirect_section_data *dummy1;
    struct merged_section *dummy2;
        dummy1 = data;
        dummy2 = ms;
#endif

	warning("section ordering for indirect sections not supported ("
		"-sectorder %s %s %s ignored)", ms->s.segname, ms->s.sectname,
		ms->order_filename);
	/*
	 * Deallocate the memory for the load order file now that it is
	 * nolonger needed.
	 */
	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;
#else /* RLD */
#ifdef __MWERKS__
    struct indirect_section_data *dummy1;
    struct merged_section *dummy2;
        dummy1 = data;
        dummy2 = ms;
#endif
#endif /* RLD */
}

/*
 * indirect_section_reset_live() is called when -dead_strip is specified after
 * the indirect sections the input objects are merged. It clears out
 * the indirect_section_data so the live indirect items can be re-merged (by
 * later calling indirect_section_merge() with redo_live == TRUE.
 */
__private_extern__
void
indirect_section_reset_live(
struct indirect_section_data *data, 
struct merged_section *ms)
{
	/*
	 * reset the total number of indirect symbol table entries in the
	 * output file.  Really needs to happen only once.  But placing it
	 * here it will get reset once for each merged section.
	 */
	nindirectsyms = 0;

	/* reset the merge section size back to zero */
	ms->s.size = 0;

	/* reset the count of relocation entries for this merged section */
	if(output_for_dyld){
	    ms->nlocrel = 0;
	    ms->nextrel = 0;
	}
	else if(save_reloc){
	    nreloc -= ms->s.nreloc;
	    ms->s.nreloc = 0;
	}

	/* clear out the previously merged data */
	indirect_section_free(data);
}

/*
 * indirect_live_ref() is called by walk_references() as part of the live
 * marking pass when -dead_strip is specified to get the live_ref when a
 * indirect section is referenced.  The reference is specified by fine_reloc,
 * map and obj.  This routine sets the live_ref struct passed to it for the
 * reference.
 */
__private_extern__
void
indirect_live_ref(
struct fine_reloc *fine_reloc,
struct section_map *map,
struct object_file *obj,
struct live_ref *ref)
{
    unsigned long index, value;
    struct nlist *nlists;
    char *contents;

	if((map->s->flags & SECTION_TYPE) != S_SYMBOL_STUBS &&
	   (map->s->flags & SECTION_TYPE) != S_LAZY_SYMBOL_POINTERS &&
	   (map->s->flags & SECTION_TYPE) != S_NON_LAZY_SYMBOL_POINTERS)
	    fatal("internal error: indirect_live_ref() called with map not for "
		  "indirect section");

	/*
	 * Note: that fine_relocs for indirect symbol which are
	 * INDIRECT_SYMBOL_LOCAL and INDIRECT_SYMBOL_ABS have local_symbol set
	 * to TRUE.
	 */
	ref->ref_type = LIVE_REF_NONE;
	ref->value = 0;
	ref->merged_symbol = NULL;

	if(fine_reloc->local_symbol == TRUE){
	    /*
	     * Indirect table entries which refer to local defined symbols are
	     * allowed only in symbol stub and lazy pointer sections so they can
	     * always be removed and never created on output.  When this happens
	     * the fine_reloc has use_contents set to FALSE and the symbol index
	     * of the local symbol stored in output_offset.
	     */
	    if(fine_reloc->use_contents == FALSE){
		if((map->s->flags & SECTION_TYPE) ==
		   S_NON_LAZY_SYMBOL_POINTERS){
		    fatal("internal error: indirect_live_ref() called "
			  "with fine_reloc for non-lazy pointer with "
			  "local_symbol == TRUE and use_contents == "
			  "FALSE");
		}
		index = fine_reloc->output_offset;
		nlists = (struct nlist *)(obj->obj_addr +
					  obj->symtab->symoff);
		ref->ref_type = LIVE_REF_VALUE;
		ref->value = nlists[index].n_value;
	    }
	    else{
		/*
		 * Both local_symbol is TRUE and use_contents is TRUE.  This
		 * happens for INDIRECT_SYMBOL_LOCAL and INDIRECT_SYMBOL_ABS.
		 * In this case we need to get the reference from the indirect
		 * symbol table entry (the section contents).  The contents of
		 * the non-lazy pointer has the address of the item being
		 * referenced.  If it is INDIRECT_SYMBOL_LOCAL then it will be
		 * in this object.  If it is INDIRECT_SYMBOL_ABS we will not
		 * return a reference.  If we have a INDIRECT_SYMBOL_ABS then
		 * we will return the ref_type set to LIVE_REF_NONE.
		 */
		if((map->s->flags & SECTION_TYPE) == S_SYMBOL_STUBS ||
		   (map->s->flags & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS)
		    fatal("internal error: indirect_live_ref() called "
			  "with fine_reloc for stub or lazy pointer "
			  "with local_symbol == TRUE and use_contents "
			  "== TRUE");
		if(fine_reloc->indirect_symbol_local == TRUE){
		    contents = obj->obj_addr + map->s->offset;
		    memcpy(&value, contents + fine_reloc->input_offset, 4);
		    if(obj->swapped)
			value = SWAP_LONG(value);
		    ref->ref_type = LIVE_REF_VALUE;
		    ref->value = value;
		}
	    }
	}
	else{
	    /*
	     * Mark the external symbols being referenced live.
	     */
	    ref->ref_type = LIVE_REF_SYMBOL;
	    ref->merged_symbol = fine_reloc->merged_symbol;
	}
}

/*
 * indirect_section_free() free()'s up all space used by the data block except 
 * the data block itself.
 */
__private_extern__
void
indirect_section_free(
struct indirect_section_data *data)
{
    unsigned long i;
    struct indirect_item_bucket *bp, *next_bp;
    struct indirect_item_block *indirect_item_block,
			       *next_indirect_item_block;

	/*
	 * Free all data for this block.
	 */
	if(data->hashtable != NULL){
	    for(i = 0; i < INDIRECT_SECTION_HASHSIZE; i++){
		for(bp = data->hashtable[i]; bp; ){
		    next_bp = bp->next;
		    free(bp);
		    bp = next_bp;
		}
	    }
	    free(data->hashtable);
	    data->hashtable = NULL;
	}
	for(indirect_item_block = data->indirect_item_blocks;
	    indirect_item_block;
	    indirect_item_block = next_indirect_item_block){

	    next_indirect_item_block = indirect_item_block->next;
	    free(indirect_item_block);
	}
	data->indirect_item_blocks = NULL;
}
#endif /* !defined(SA_RLD) */

/*
 * legal_reference() determines if the specified reference is legal with respect
 * to the correct usage of symbol stub sections and lazy symbol pointer sections
 * (as parts of these section can be removed by the link editor and we must make
 * sure that these things are correctly being referenced so removing them will
 * produce a correct object file).  The specified reference comes from a
 * relocation entry and is specified as section maps and offsets FROM the place
 * of reference to the item being refered TO and the relocation entry index
 * causing the reference.  It returns TRUE if the reference is legal if not it
 * prints the appropate error message and returns FALSE.
 */
__private_extern__
enum bool
legal_reference(
struct section_map *from_map,
unsigned long from_offset,
struct section_map *to_map,
unsigned long to_offset,
unsigned long from_reloc_index,
enum bool sectdiff_reloc)
{
#ifndef SA_RLD
    unsigned long to_section_type, from_section_type;
    unsigned long *indirect_symtab;
    struct fine_reloc *to_fine_reloc, *from_fine_reloc;
    struct merged_symbol *merged_symbol;
    unsigned long stride;

	from_section_type = from_map->s->flags & SECTION_TYPE;
	/*
	 * If we are not using the block where this reference is coming from
	 * then we don't care if this is an illegal reference or not.
	 */
	if(from_section_type == S_COALESCED){
	    from_fine_reloc =
		fine_reloc_for_input_offset(from_map, from_offset);
	    if(from_fine_reloc->use_contents == FALSE)
		return(TRUE);
	}

	to_section_type = to_map->s->flags & SECTION_TYPE;
	/*
	 * If this is a coalesced section then the reference may not be to this
	 * coalesced section if the referenced block's contents is not used,
	 * because the block could have been for a weak definition symbol.
	 */
	if(to_section_type == S_COALESCED){
	    to_fine_reloc = fine_reloc_for_input_offset(to_map, to_offset);
	    /*
	     * If this reference is to a local symbol then it is ok to reference
	     * this coalesced symbol directly from anywhere.
	     */
	    if(to_fine_reloc->local_symbol == TRUE)
		return(TRUE);
	    if(to_fine_reloc->use_contents == FALSE){
		merged_symbol = to_fine_reloc->merged_symbol;
		if(merged_symbol->defined_in_dylib == TRUE){
		    if(sectdiff_reloc == TRUE && dynamic == TRUE){
			error_with_cur_obj("illegal reference for -dynamic "
			    "code (section difference reference from section "
			    "(%.16s,%.16s) relocation entry (%lu) "
			    "to symbol: %s defined in dylib: %s)",
			    from_map->s->segname, from_map->s->sectname,
			    from_reloc_index, merged_symbol->nlist.n_un.n_name,
			    merged_symbol->definition_object->file_name);
			return(FALSE);
		    }
		    to_section_type = S_REGULAR;
		}
		else if((merged_symbol->nlist.n_type & N_TYPE) == N_SECT){
		    if(merged_symbols_relocated == FALSE)
			to_section_type = merged_symbol->definition_object->
			    section_maps[merged_symbol->nlist.n_sect - 1].s->
			    flags & SECTION_TYPE;
		    else
			to_section_type =
			    pass2_nsect_merged_symbol_section_type(
				merged_symbol);
		}
		else{
		    if(sectdiff_reloc == TRUE && dynamic == TRUE){
			if((merged_symbol->nlist.n_type & N_TYPE) == N_ABS){
			    error_with_cur_obj("illegal reference for -dynamic "
				"code (section difference reference from "
				"section (%.16s,%.16s) relocation entry (%lu) "
				"to absolute symbol: %s)",
				from_map->s->segname, from_map->s->sectname,
				from_reloc_index,
				merged_symbol->nlist.n_un.n_name);
			    return(FALSE);
			}
		    }
		    to_section_type = S_REGULAR;
		}
	    }
	}

	/*
	 * To allow the dynamic linker to use the same coalesced symbol through
	 * out the program all references to coalesced symbols must be
	 * relocatable to the same coalesced symbol.  Most references are done
	 * indirectly through symbol stubs and non-lazy pointers, static
	 * initialization of data references are done directly.  The only type
	 * direct reference that can't be relocated by the dynamic linker is
	 * a reference through a section difference relocation entry because
	 * it is pic and does not make it the output_for_dyld files.  This is
	 * bad code generation except for the case the reference is TO the
	 * same block the reference is from or to a block with a local symbol.
	 * The first is usually the picbase of a routine in a coalesced section
	 * and the second allows for unwind tables to reference private extern
	 * coalesced symbols.
	 */
	if(dynamic == TRUE && to_section_type == S_COALESCED &&
	   sectdiff_reloc == TRUE && (from_map != to_map ||
	   fine_reloc_for_input_offset(from_map, from_offset) != 
	   fine_reloc_for_input_offset(to_map, to_offset)) &&
	   fine_reloc_for_input_offset(to_map, to_offset)->local_symbol ==
		FALSE){
	    error_with_cur_obj("malformed object, illegal reference for "
		"-dynamic code (reference to a coalesced section (%.16s,"
		"%.16s) from section (%.16s,%.16s) relocation entry (%lu))",
		to_map->s->segname, to_map->s->sectname, from_map->s->segname,
		from_map->s->sectname, from_reloc_index);
	    return(FALSE);
	}

	/*
	 * If the reference is not to or from a symbol stub section or a
	 * lazy symbol pointer section it is legal as far as used of these
	 * sections goes.
	 */
	if(to_section_type != S_SYMBOL_STUBS &&
	   to_section_type != S_LAZY_SYMBOL_POINTERS &&
	   from_section_type != S_SYMBOL_STUBS &&
	   from_section_type != S_LAZY_SYMBOL_POINTERS)
	    return(TRUE);

	/*
	 * See if this reference is to a symbol stub section.
	 */
	if(to_section_type == S_SYMBOL_STUBS){
	    /*
	     * For references to a symbol stub section that are made from any
	     * thing but a lazy pointer section the reference must be made to
	     * the start of a symbol stub in that section.
	     */
	    if(from_section_type != S_LAZY_SYMBOL_POINTERS){
		if((to_offset % to_map->s->reserved2) != 0){
		    /*
		     * A reference to a symbol stub section may be made from the
		     * same symbol stub section from the same stub which is what
		     * happens for a pic style symbol stub.
		     */
		    if(from_section_type == S_SYMBOL_STUBS &&
		       from_map == to_map &&
		       from_offset / from_map->s->reserved2 ==
		       to_offset / to_map->s->reserved2)
			return(TRUE);
		    error_with_cur_obj("malformed object, illegal reference "
			"(reference to a symbol stub not at the start of the "
			"stub from section (%.16s,%.16s) relocation entry "
			"(%lu))", from_map->s->segname, from_map->s->sectname,
			from_reloc_index);
		    return(FALSE);
		}
		else
		    return(TRUE);
	    }
	    else{
		/*
		 * This reference is to a symbol stub section from a lazy
		 * pointer section.  The stub being referenced and the pointer
		 * must both have the same indirect symbol.  By same in this
		 * case we check for the same indirect symbol table entry
		 * (that is the same index into the symbol table).
		 */
		indirect_symtab = (unsigned long *)(cur_obj->obj_addr +
					    cur_obj->dysymtab->indirectsymoff);
		stride = 4;
		if(indirect_symtab[from_map->s->reserved1 + from_offset /
							stride] !=
		   indirect_symtab[to_map->s->reserved1 + to_offset /
							to_map->s->reserved2]){
		    error_with_cur_obj("malformed object, illegal reference "
			"(reference to symbol stub in section (%.16s,%.16s) "
			"at address 0x%x from lazy pointer section "
			"(%.16s,%.16s) relocation entry (%lu) with non-"
			"matching indirect symbols)", to_map->s->segname,
			to_map->s->sectname,
			(unsigned int)(to_map->s->addr + to_offset),
			from_map->s->segname, from_map->s->sectname,
			from_reloc_index);
		    return(FALSE);
		}
		else
		    return(TRUE);
	    }
	}

	/*
	 * See if this reference is to a lazy symbol pointer section.
	 */
	if(to_section_type == S_LAZY_SYMBOL_POINTERS){
	    /*
	     * Legal references to lazy pointer sections can only be made from a
	     * symbol stub section.
	     */
	    if(from_section_type != S_SYMBOL_STUBS){
		error_with_cur_obj("malformed object, illegal reference "
		    "(reference to a lazy symbol pointer section from section "
		    "(%.16s,%.16s) relocation entry (%lu) which is not a "
		    "symbol stub section)", from_map->s->segname,
		    from_map->s->sectname, from_reloc_index);
		return(FALSE);
	    }
	    else{
		/*
		 * This reference is to a lazy pointer section from a symbol
		 * stub section.  The pointer being referenced and the stub
		 * must both have the same indirect symbol.  By same in this
		 * case we check for the same indirect symbol table entry
		 * (that is the same index into the symbol table).
		 */
		indirect_symtab = (unsigned long *)(cur_obj->obj_addr +
					    cur_obj->dysymtab->indirectsymoff);
		stride = (arch_flag.cputype == CPU_TYPE_POWERPC64 ? 8 : 4);
		if(indirect_symtab[from_map->s->reserved1 + from_offset /
				   from_map->s->reserved2] !=
		   indirect_symtab[to_map->s->reserved1 + to_offset / stride]){
		    error_with_cur_obj("malformed object, illegal reference "
			"(reference to lazy symbol pointer section "
			"(%.16s,%.16s) at address 0x%x from symbol stub "
			"section (%.16s,%.16s) relocation entry (%lu) with "
			"non-matching indirect symbols)",
			to_map->s->segname, to_map->s->sectname,
			(unsigned int)(to_map->s->addr + to_offset),
			from_map->s->segname, from_map->s->sectname,
			from_reloc_index);
		    return(FALSE);
		}
		else
		    return(TRUE);
	    }
	}

	/*
	 * See if this reference is from a symbol stub section.
	 */
	if(from_section_type == S_SYMBOL_STUBS){
	    /*
	     * At this point we know this reference from a symbol stub section
	     * is not to lazy symbol pointer section.  The only thing else this
	     * section should be referencing is the stub binding helper routine.
	     */
	    return(TRUE);
	}
#endif /* !defined(SA_RLD) */
	return(TRUE);
}

#ifndef SA_RLD
/*
 * output_indirect_symbols() copies the indirect symbol table into the output
 * file.
 */
__private_extern__
void
output_indirect_symbols(
void)
{
    unsigned long i, *indirect_symbols, nindirect_symbols;
    struct merged_segment **p, *msg;
    struct merged_section **content, *ms;
    struct indirect_section_data *data;
    struct indirect_item_block **q, *indirect_item_block;
    struct indirect_item *indirect_item;

	if(nindirectsyms == 0)
	    return;
	if(strip_level == STRIP_ALL)
	    fatal("can't use -s with input files containing indirect symbols "
		  "(output file must contain at least global symbols, for "
		  "maximum stripping use -x)");
	indirect_symbols = (unsigned long *)(output_addr +
			output_dysymtab_info.dysymtab_command.indirectsymoff);
	nindirect_symbols = 0;
	p = &merged_segments;
	while(*p){
	    msg = *p;
	    content = &(msg->content_sections);
	    while(*content){
		ms = *content;
		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){
		    data = (struct indirect_section_data *)ms->literal_data;
		    ms->s.reserved1 = nindirect_symbols;
		    for(q = &(data->indirect_item_blocks);
			*q ;
			q = &(indirect_item_block->next)){

			indirect_item_block = *q;
			for(i = 0; i < indirect_item_block->used; i++){
			    indirect_item = indirect_item_block->
					    indirect_items + i;
			    if(indirect_item->merged_symbol != NULL){
				/*
				 * If this is a non-lazy symbol pointer section
				 * and the symbol is a private extern then
				 * change the indirect symbol to
				 * INDIRECT_SYMBOL_LOCAL or
				 * INDIRECT_SYMBOL_ABS.
				 */
				if((ms->s.flags & SECTION_TYPE) ==
				   S_NON_LAZY_SYMBOL_POINTERS &&
				   (indirect_item->merged_symbol->
					nlist.n_type & N_PEXT) == N_PEXT &&
				    keep_private_externs == FALSE){
				    if((indirect_item->merged_symbol->
					nlist.n_type & N_TYPE) == N_ABS)
					indirect_symbols[nindirect_symbols++] =
						INDIRECT_SYMBOL_ABS;
				    else
					indirect_symbols[nindirect_symbols++] =
						INDIRECT_SYMBOL_LOCAL;
				}
				else{
				    indirect_symbols[nindirect_symbols++] =
					merged_symbol_output_index(
					    indirect_item->merged_symbol);
				}
			    }
			    else{
				if(indirect_item->index ==
				       INDIRECT_SYMBOL_LOCAL ||
				   indirect_item->index ==
				       INDIRECT_SYMBOL_ABS){
				    indirect_symbols[nindirect_symbols++] =
					indirect_item->index;
				}
				else{
				    indirect_symbols[nindirect_symbols++] =
					local_symbol_output_index(
					    indirect_item->obj,
					    indirect_item->index);
				}
			    }
			}
		    }
		}
		content = &(ms->next);
	    }
	    p = &(msg->next);
	}
	if(nindirect_symbols != nindirectsyms)
	    fatal("internal error, nindirect_symbols != nindirectsyms in "
		   "output_indirect_symbols()");
	if(host_byte_sex != target_byte_sex)
	    swap_indirect_symbols(indirect_symbols, nindirect_symbols,
		target_byte_sex);
#ifndef RLD
	output_flush(output_dysymtab_info.dysymtab_command.indirectsymoff,
		     nindirect_symbols * sizeof(unsigned long));
#endif /* !defined(RLD) */
}
#endif /* !defined(SA_RLD) */
