objects.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 table of object files to be
 * loaded.
 */
#include <stdlib.h>
#if !(defined(KLD) && defined(__STATIC__))
#include <stdio.h>
#include <limits.h>
#include <mach/mach.h>
#else /* defined(KLD) && defined(__STATIC__) */
#include <mach/mach.h>
#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 <ar.h>
#include "stuff/bool.h"
#include "stuff/bytesex.h"

#include "ld.h"
#include "objects.h"
#include "sections.h"
#include "pass1.h"
#include "symbols.h"
#include "sets.h"

/*
 * The head of the object file list and the total count of all object files
 * in the list.  The number objects is only used in main() to tell if there
 * had been any objects loaded into the output file.
 */
__private_extern__ struct object_list *objects = NULL;
__private_extern__ unsigned long nobjects = 0;

/*
 * A pointer to the current object being processed in pass1 or pass2.
 */
__private_extern__ struct object_file *cur_obj = NULL;

/*
 * A pointer to the base object for an incremental link if not NULL.
 */
__private_extern__ struct object_file *base_obj = NULL;

/*
 * new_object_file() returns a pointer to the next available object_file
 * structrure.  The object_file structure is allways zeroed.
 */
__private_extern__
struct object_file *
new_object_file(void)
{
    struct object_list *object_list, **p;
    struct object_file *object_file;

	for(p = &objects; *p; p = &(object_list->next)){
	    object_list = *p;
	    if(object_list->used == NOBJECTS)
		continue;
	    object_file = &(object_list->object_files[object_list->used]);
	    object_list->used++;
	    nobjects++;
#ifdef RLD
	    object_file->set_num = cur_set;
#endif /* RLD */
	    return(object_file);
	}
	*p = allocate(sizeof(struct object_list));
	object_list = *p;
	memset(object_list, '\0', sizeof(struct object_list));
	object_file = &(object_list->object_files[object_list->used]);
	object_list->used++;
	nobjects++;
#ifdef RLD
	object_file->set_num = cur_set;
#endif /* RLD */
	return(object_file);
}

#ifndef RLD
/*
 * object_index() returns the index into the module table for a object file
 * structure.  It is only used in the creation of the table of contents entries
 * in a multi module MH_DYLIB file.
 */
__private_extern__
unsigned long
object_index(
struct object_file *obj)
{
    unsigned long index, i;
    struct object_list *object_list, **p;
    struct object_file *cmp_obj;

	if(multi_module_dylib == FALSE)
	    return(0);
	index = 0;
	for(p = &objects; *p; p = &(object_list->next)){
	    object_list = *p;
	    for(i = 0; i < object_list->used; i++){
		cmp_obj = &(object_list->object_files[i]);
		if(cmp_obj->dylib == TRUE)
		    continue;
		if(cmp_obj->bundle_loader == TRUE)
		    continue;
		if(cmp_obj == obj)
		    return(index);
		index++;
	    }
	}
	fatal("internal error: object_index() called with bad object file "
	      "pointer");
	return(0); /* to prevent warning from compiler */
}
#endif /* !defined(RLD) */

/*
 * add_last_object_file() adds the specified object file to the end of the
 * object file list.
 */
__private_extern__
struct object_file *
add_last_object_file(
struct object_file *new_object)
{
    struct object_file *last_object;

	last_object = new_object_file();
	*last_object = *new_object;
	return(last_object);
}

/*
 * remove_last_object_file() removes the specified object file from the end of
 * the object file list.
 */
__private_extern__
void
remove_last_object_file(
struct object_file *last_object)
{
    struct object_list *object_list, **p;
    struct object_file *object_file;

	object_file = NULL;
	object_list = NULL;
	for(p = &objects; *p; p = &(object_list->next)){
	    object_list = *p;
	    object_file = &(object_list->object_files[object_list->used - 1]);
	    if(object_list->used == NOBJECTS)
		continue;
	}
	if(object_file == NULL || object_file != last_object)
	    fatal("internal error: remove_last_object_file() called with "
		  "object file that was not the last object file");
	memset(object_file, '\0', sizeof(struct object_file));
	object_list->used--;
}

/*
 * Print the name of the specified object structure in the form: "filename ",
 * "archive(member) " or "dylib(member).
 */
__private_extern__
void
print_obj_name(
struct object_file *obj)
{
    char *strings;

	if(obj->ar_hdr != NULL){
	    print("%s(%.*s) ", obj->file_name, (int)obj->ar_name_size,
		  obj->ar_name);
	}
	else if(obj->dylib_module != NULL){
	    strings = obj->obj_addr + obj->symtab->stroff;
	    print("%s(%s) ", obj->file_name,
		  strings + obj->dylib_module->module_name);
	}
	else
	    print("%s ", obj->file_name);
}

__private_extern__
unsigned long
size_ar_name(
struct ar_hdr *ar_hdr)
{
    long i;

	i = sizeof(ar_hdr->ar_name) - 1;
	if(ar_hdr->ar_name[i] == ' '){
	    do{
		if(ar_hdr->ar_name[i] != ' ')
		    break;
		i--;
	    }while(i > 0);
	}
	return(i + 1);
}

/*
 * print_whatsloaded() prints which object files are loaded.  This has to be
 * called after pass1 to get the correct result.
 */
__private_extern__
void
print_whatsloaded(void)
{
    unsigned long i;
    struct object_list *object_list, **p;
    struct object_file *obj;
    char *strings;

	for(p = &objects; *p; p = &(object_list->next)){
	    object_list = *p;
	    for(i = 0; i < object_list->used; i++){
		obj = &(object_list->object_files[i]);
		if(obj->dylib && obj->dylib_module == NULL)
		    continue;
		if(obj->bundle_loader)
		    continue;
		if(obj->dylinker)
		    continue;
		if(obj->ar_hdr){
		    print("%s(%.*s)\n",obj->file_name,
			  (int)obj->ar_name_size, obj->ar_name);
		}
		else if(obj->dylib_module != NULL){
		    strings = obj->obj_addr + obj->symtab->stroff;
		    print("%s(%s)\n", obj->file_name,
			  strings + obj->dylib_module->module_name);
		}
		else
		    print("%s\n", obj->file_name);
	    }
	}
}

/*
 * is_dylib_module_loaded() returns TRUE is the specified dynamic library module
 * is already loaded and FALSE otherwise.  It is used by search_dynamic_libs()
 * to make sure the module that defines the library initialization routine is
 * loaded.
 */
__private_extern__
enum bool
is_dylib_module_loaded(
struct dylib_module *dylib_module)
{
    unsigned long i;
    struct object_list *object_list, **p;
    struct object_file *obj;

	for(p = &objects; *p; p = &(object_list->next)){
	    object_list = *p;
	    for(i = 0; i < object_list->used; i++){
		obj = &(object_list->object_files[i]);
		if(obj->dylib && obj->dylib_module != NULL){
		    if(obj->dylib_module == dylib_module)
			return(TRUE);
		}
	    }
	}
	return(FALSE);
}

/*
 * fine_reloc_output_offset() returns the output offset for the specified 
 * input offset and the section map using the fine relocation entries.
 */
__private_extern__
unsigned long
fine_reloc_output_offset(
struct section_map *map,
unsigned long input_offset)
{
    struct fine_reloc *fine_reloc;
    unsigned long section_type;

	fine_reloc = fine_reloc_for_input_offset(map, input_offset);

	/*
	 * This routine is used to set the r_address field of relocation
	 * entries.  For entries that are part of a item who's contents is
	 * not used in the output file the return value here for r_address is
	 * to set it past the end of the section in the output file.  This is
	 * then checked for in fine_reloc_offset_in_output_for_output_offset()
	 * to know if the r_address (output_offet) is not in the output.
	 */
	section_type = map->s->flags & SECTION_TYPE;
	if((section_type == S_LAZY_SYMBOL_POINTERS ||
	    section_type == S_NON_LAZY_SYMBOL_POINTERS ||
	    section_type == S_SYMBOL_STUBS ||
	    section_type == S_COALESCED) &&
	   fine_reloc->use_contents == FALSE)
	    return(map->output_section->s.size +
	       input_offset - fine_reloc->input_offset);

	return(fine_reloc->output_offset +
	       input_offset - fine_reloc->input_offset);
}

/*
 * fine_reloc_output_address() returns the output_address for the input_offset
 * in the section with the specified section map.  This can return one of two
 * things depending on the fine relocation entry for the input_offset.  If the
 * section is a symbol stub section and the fine_relocation entry's indirect
 * symbol is define then the value of the symbol is used.  Otherwise the output
 * offset for the fine relocation entry is added to the specified
 * output_base_address.
 */
__private_extern__
unsigned long
fine_reloc_output_address(
struct section_map *map,
unsigned long input_offset,
unsigned long output_base_address)
{
    struct fine_reloc *fine_reloc;
    struct merged_symbol *merged_symbol;
    unsigned long index;
    struct nlist *nlists;
    struct section_map *section_map;

	fine_reloc = fine_reloc_for_input_offset(map, input_offset);
	if(fine_reloc->local_symbol == TRUE){
	    if((map->s->flags & SECTION_TYPE) == S_SYMBOL_STUBS){
		index = fine_reloc->output_offset;
		nlists = (struct nlist *)(cur_obj->obj_addr +
					  cur_obj->symtab->symoff);
		if(nlists[index].n_sect == NO_SECT)
		    return(nlists[index].n_value);
		section_map = &(cur_obj->section_maps[nlists[index].n_sect -1]);
		if(section_map->nfine_relocs == 0)
		    return(nlists[index].n_value - section_map->s->addr +
			   section_map->output_section->s.addr +
			   section_map->offset);
		else
		    return(section_map->output_section->s.addr +
			   fine_reloc_output_offset(section_map,
						    nlists[index].n_value -
						    section_map->s->addr));
	    }
	    else if((map->s->flags & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS ||
		    (map->s->flags & SECTION_TYPE) ==
						  S_NON_LAZY_SYMBOL_POINTERS){
		return(output_base_address +
		       fine_reloc->output_offset +
		       input_offset - fine_reloc->input_offset);
	    }
	    else if((map->s->flags & SECTION_TYPE) == S_COALESCED){
		if(fine_reloc->use_contents == TRUE){
		    return(output_base_address +
			   fine_reloc->output_offset +
			   input_offset - fine_reloc->input_offset);
		}
		else{
		    merged_symbol =
			(struct merged_symbol *)fine_reloc->output_offset;
		    if((merged_symbol->nlist.n_type & N_TYPE) == N_INDR)
			merged_symbol = (struct merged_symbol *)
					merged_symbol->nlist.n_value;
		    return(merged_symbol->nlist.n_value);
		}
	    }
	    else{
		fatal("internal error, fine_reloc_output_address() called with "
		      "an input_offset which maps to a fine_reloc where "
		      "local_symbol is TRUE but it's not in a S_SYMBOL_STUBS, "
		      "S_LAZY_SYMBOL_POINTERS or S_COALESCED section");
		return(0);
	    }
	}
	else if((map->s->flags & SECTION_TYPE) == S_COALESCED){
	    if(fine_reloc->use_contents == TRUE){
		return(output_base_address +
		       fine_reloc->output_offset +
		       input_offset - fine_reloc->input_offset);
	    }
	    else{
		merged_symbol =
		    (struct merged_symbol *)fine_reloc->output_offset;
		if((merged_symbol->nlist.n_type & N_TYPE) == N_INDR)
		    merged_symbol = (struct merged_symbol *)
				    merged_symbol->nlist.n_value;
		return(merged_symbol->nlist.n_value);
	    }
	}
	else if((map->s->flags & SECTION_TYPE) == S_SYMBOL_STUBS &&
	        fine_reloc->indirect_defined == TRUE){
	    if(filetype != MH_DYLIB ||
	       (filetype == MH_DYLIB && multi_module_dylib == FALSE) ||
	       (cur_obj == fine_reloc->merged_symbol->definition_object &&
		input_offset - fine_reloc->input_offset == 0)){
		if(cur_obj == fine_reloc->merged_symbol->definition_object)
		    merged_symbol = fine_reloc->merged_symbol;
		else
		    merged_symbol = (struct merged_symbol *)
				    fine_reloc->output_offset;
		if((merged_symbol->nlist.n_type & N_TYPE) == N_INDR)
		    merged_symbol = (struct merged_symbol *)
				    merged_symbol->nlist.n_value;
		return(merged_symbol->nlist.n_value);
	    }
	    else{
		return(output_base_address +
		       fine_reloc->output_offset +
		       input_offset - fine_reloc->input_offset);
	    }
	}
	else{
	    return(output_base_address +
		   fine_reloc->output_offset +
		   input_offset - fine_reloc->input_offset);
	}
}

/*
 * fine_reloc_offset_in_output() returns TRUE if the input offset is part of a
 * range that will be in the output file.  This is used in processing the parts
 * of sections (contents, reloccation entries, symbols) that get removed when
 * a section is a symbol stub section or lazy pointer section.
 */
__private_extern__
enum bool
fine_reloc_offset_in_output(
struct section_map *map,
unsigned long input_offset)
{
    struct fine_reloc *fine_reloc;

	fine_reloc = fine_reloc_for_input_offset(map, input_offset);
	return((enum bool)(fine_reloc->use_contents));
}

/*
 * fine_reloc_offset_in_output_for_output_offset() does the same as
 * fine_reloc_offset_in_output() but using the output_offset instead of the
 * input offset and returns TRUE if the output offset is part of a range that
 * will be in the output file.  This is used in reloc_output_for_dyld() in
 * determing if a relocation entry will be in the output file and is needed
 * because the r_address of a relocation entry was modified by the reloc
 * routine.
 */
__private_extern__
enum bool
fine_reloc_offset_in_output_for_output_offset(
struct section_map *map,
unsigned long output_offset)
{
    unsigned long section_type;

	if(map->nfine_relocs == 0)
	    fatal("internal error, fine_reloc_offset_in_output_for_output_"
		  "offset() called with a map->nfine_relocs == 0");

	section_type = map->s->flags & SECTION_TYPE;
	if(section_type != S_LAZY_SYMBOL_POINTERS &&
	   section_type != S_NON_LAZY_SYMBOL_POINTERS &&
	   section_type != S_SYMBOL_STUBS &&
	   section_type != S_COALESCED)
	    fatal("internal error, fine_reloc_offset_in_output_for_output_"
		  "offset() called with a section map for a non-indirect and "
		  "non-coalesced section");

	/*
	 * For relocation entries that were part of a item who's contents is
	 * not used in the output file fine_reloc_output_offset() was used to
	 * set it's r_address to past the end of the section in the output file.
	 * So in here we check for that and if that is the case we say the
	 * output_offset is not in the output otherwise it is in the output.
	 */
	if(output_offset >= map->output_section->s.size)
	    return(FALSE);
	else
	    return(TRUE);
}

/*
 * fine_reloc_output_sectnum() returns the section number in the output file
 * being referenced for the input_offset in the section with the specified
 * section map.  This can return one of two things depending on the fine
 * relocation entry for the input_offset.  If the section being referenced is
 * a symbol stub section and the fine_relocation entry's indirect symbol is
 * define then the section number of the merged symbol is used.  Otherwise the
 * section number of the referenced section has in the output is returned.
 */
__private_extern__
unsigned long
fine_reloc_output_sectnum(
struct section_map *map,
unsigned long input_offset)
{
    struct fine_reloc *fine_reloc;
    struct merged_symbol *merged_symbol;
    unsigned long index;
    unsigned long *indirect_symtab;
    struct nlist *nlists;
    struct section_map *section_map;

	fine_reloc = fine_reloc_for_input_offset(map, input_offset);
	if(fine_reloc->local_symbol == TRUE){
	    if((map->s->flags & SECTION_TYPE) == S_COALESCED){
		return(map->output_section->output_sectnum);
	    }
	    else if((map->s->flags & SECTION_TYPE) == S_SYMBOL_STUBS){
		index = fine_reloc->output_offset;
	    }
	    else if((map->s->flags & SECTION_TYPE) ==
		     S_NON_LAZY_SYMBOL_POINTERS ||
	    	    (map->s->flags & SECTION_TYPE) ==
		     S_LAZY_SYMBOL_POINTERS){
		indirect_symtab = (unsigned long *)(cur_obj->obj_addr +
					    cur_obj->dysymtab->indirectsymoff);
		index = indirect_symtab[map->s->reserved1 + 
			(fine_reloc->input_offset / 4)];
	    }
	    else{
		fatal("internal error, fine_reloc_output_sectnum() called with "
		      "an input_offset which maps to a fine_reloc where "
		      "local_symbol is TRUE but it's not in a S_SYMBOL_STUBS, "
		      "S_NON_LAZY_SYMBOL_POINTERS, S_LAZY_SYMBOL_POINTERS or "
		      "S_COALESCED section");
		index = 0;
	    }
	    nlists = (struct nlist *)(cur_obj->obj_addr +
				      cur_obj->symtab->symoff);
	    if(nlists[index].n_sect == NO_SECT)
		return(NO_SECT);
	    section_map = &(cur_obj->section_maps[nlists[index].n_sect - 1]);
	    return(section_map->output_section->output_sectnum);
	}
	else if((map->s->flags & SECTION_TYPE) == S_SYMBOL_STUBS &&
	        fine_reloc->indirect_defined == TRUE &&
	        (filetype != MH_DYLIB || multi_module_dylib == FALSE)){
	    merged_symbol = (struct merged_symbol *)fine_reloc->output_offset;
	    if((merged_symbol->nlist.n_type & N_TYPE) == N_INDR)
		merged_symbol = (struct merged_symbol *)
				merged_symbol->nlist.n_value;
	    return(merged_symbol->nlist.n_sect);
	}
	else{
	    return(map->output_section->output_sectnum);
	}
}

/*
 * fine_reloc_for_input_offset() returns the fine relocation entry for the
 * specified input offset and the section map.
 */
__private_extern__
struct fine_reloc *
fine_reloc_for_input_offset(
struct section_map *map,
unsigned long input_offset)
{
    int l = 0;
    int u = map->nfine_relocs - 1;
    int m;
    int r;

	if(map->nfine_relocs == 0)
	    fatal("internal error, fine_reloc_for_input_offset() called with a "
		  "section_map->nfine_relocs == 0");
	l = 0;
	m = 0;
	u = map->nfine_relocs - 1;
	while(l <= u){
	    m = (l + u) / 2;
	    if((r = (input_offset - map->fine_relocs[m].input_offset)) == 0)
		return(map->fine_relocs + m);
	    else if (r < 0)
		u = m - 1;
	    else
		l = m + 1;
	}
	if(m == 0 || input_offset > map->fine_relocs[m].input_offset)
	    return(map->fine_relocs + m);
	else
	    return(map->fine_relocs + (m - 1));
}

#ifdef RLD
/*
 * clean_objects() does two things.  For each object file in the current set
 * it first it deallocates the memory used for the object file.  Then it sets
 * the pointer to the section in each section map to point at the merged section
 * so it still can be used by trace_symbol() on future rld_load()'s (again only
 * for object files in the current set).
 */
__private_extern__
void
clean_objects(void)
{
    unsigned long i, j;
    struct object_list *object_list, **p;
    struct object_file *object_file;
#ifndef SA_RLD
    kern_return_t r;
#endif /* !defined(SA_RLD) */

	for(p = &objects; *p; p = &(object_list->next)){
	    object_list = *p;
	    for(i = 0; i < object_list->used; i++){
		object_file = &(object_list->object_files[i]);
		if(object_file->set_num != cur_set)
		    continue;
		if(object_file->ar_hdr == NULL &&
		   object_file->from_fat_file == FALSE &&
		   object_file->obj_size != 0 &&
		   object_file->user_obj_addr == FALSE){
#ifndef SA_RLD
		    if((r = vm_deallocate(mach_task_self(),
				  (vm_address_t)object_file->obj_addr,
				  object_file->obj_size)) != KERN_SUCCESS)
			mach_fatal(r, "can't vm_deallocate() memory for "
				   "mapped file %s",object_file->file_name);
#ifdef RLD_VM_ALLOC_DEBUG
		    print("rld() vm_deallocate: addr = 0x%0x size = 0x%x\n",
			  (unsigned int)object_file->obj_addr,
			  (unsigned int)object_file->obj_size);
#endif /* RLD_VM_ALLOC_DEBUG */
#endif /* !defined(SA_RLD) */
		}
		object_file->obj_addr = NULL;
		object_file->obj_size = 0;
	        object_file->user_obj_addr = FALSE;

		/*
		 * Since the tracing of symbols and the creation of the common
		 * section both use the section's segname and sectname feilds
		 * these need to still be valid after the memory for the file
		 * has been deallocated.  So just set the pointer to point at
		 * the merged section.
		 */
		if(object_file->section_maps != NULL){
		    for(j = 0; j < object_file->nsection_maps; j++){
			object_file->section_maps[j].s = 
			    &(object_file->section_maps[j].output_section->s);
		    }
		}
	    }
	}
}

/*
 * remove_objects() removes the object structures that are from the
 * current object file set.  This takes advantage of the fact
 * that objects from the current set come after the previous set.
 */
__private_extern__
void
remove_objects(void)
{
    unsigned long i, removed;
    /* The compiler "warning: `prev_object_list' may be used uninitialized in */
    /* this function" can safely be ignored */
    struct object_list *object_list, *prev_object_list, *next_object_list;
    struct object_file *object_file;

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

	for(object_list = objects;
	    object_list != NULL;
	    object_list = object_list->next){
	    removed = 0;
	    for(i = 0; i < object_list->used; i++){
		object_file = &(object_list->object_files[i]);
		if(object_file->set_num == cur_set){
		    if(cur_set != -1)
			free(object_file->file_name);
		    if(object_file->section_maps != NULL)
			free(object_file->section_maps);
		    if(object_file->undefined_maps != NULL)
			free(object_file->undefined_maps);
		    memset(object_file, '\0', sizeof(struct object_file));
		    removed++;
		}
	    }
	    object_list->used -= removed;
	    nobjects -= removed;
	}
	/*
	 * Find the first object list that now has 0 entries used.
	 */
	for(object_list = objects;
	    object_list != NULL;
	    object_list = object_list->next){
	    if(object_list->used == 0)
		break;
	    prev_object_list = object_list;
	}
	/*
	 * If there are any object lists with 0 entries used free them.
	 */
	if(object_list != NULL && object_list->used == 0){
	    /*
	     * First set the pointer to this list in the previous list to
	     * NULL.
	     */
	    if(object_list == objects)
		objects = NULL;
	    else
		prev_object_list->next = NULL;
	    /*
	     * Now free this list and do the same for all remaining lists.
	     */
	    do {
		next_object_list = object_list->next;
		free(object_list);
		object_list = next_object_list;
	    }while(object_list != NULL);
	}
}
#endif /* RLD */

#ifdef DEBUG
/*
 * print_object_list() prints the object table.  Used for debugging.
 */
__private_extern__
void
print_object_list(void)
{
    unsigned long i, j, k;
    struct object_list *object_list, **p;
    struct object_file *object_file;
    struct fine_reloc *fine_relocs;

	print("Object file list\n");
	for(p = &objects; *p; p = &(object_list->next)){
	    object_list = *p;
	    print("    object_list 0x%x\n", (unsigned int)object_list);
	    print("    used %lu\n", object_list->used);
	    print("    next 0x%x\n", (unsigned int)object_list->next);
	    for(i = 0; i < object_list->used; i++){
		object_file = &(object_list->object_files[i]);
		print("\tfile_name %s\n", object_file->file_name);
		print("\tobj_addr 0x%x\n", (unsigned int)object_file->obj_addr);
		print("\tobj_size %lu\n", object_file->obj_size);
    		print("\tar_hdr 0x%x", (unsigned int)object_file->ar_hdr);
    		if(object_file->ar_hdr != NULL)
		    print(" (%.12s)\n", (char *)object_file->ar_hdr);
		else
		    print("\n");
    		print("\tnsection_maps %lu\n", object_file->nsection_maps);
		for(j = 0; j < object_file->nsection_maps; j++){
		    print("\t    (%s,%s)\n",
			   object_file->section_maps[j].s->segname,
			   object_file->section_maps[j].s->sectname);
		    print("\t    offset 0x%x\n",
			   (unsigned int)object_file->section_maps[j].offset);
		    print("\t    fine_relocs 0x%x\n",
		       (unsigned int)object_file->section_maps[j].fine_relocs);
		    print("\t    nfine_relocs %lu\n",
			   object_file->section_maps[j].nfine_relocs);
		    fine_relocs = object_file->section_maps[j].fine_relocs;
		    for(k = 0;
			k < object_file->section_maps[j].nfine_relocs;
			k++){
			print("\t\t%-6u %-6lu\n",
			       fine_relocs[k].input_offset,
			       fine_relocs[k].output_offset);
		    }
		}
    		print("\tnundefineds %lu\n", object_file->nundefineds);
		for(j = 0; j < object_file->nundefineds; j++){
		    print("\t    (%lu,%s)\n",
			   object_file->undefined_maps[j].index,
			   object_file->undefined_maps[j].merged_symbol->nlist.
								n_un.n_name);
		}
#ifdef RLD
		print("\tset_num = %d\n", object_file->set_num);
#endif /* RLD */
	    }
	}
}
#endif /* DEBUG */