/* * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * Portions Copyright (c) 1999 Apple Computer, Inc. All Rights * Reserved. This file contains Original Code and/or Modifications of * Original Code as defined in and that are subject to the Apple Public * Source License Version 1.1 (the "License"). You may not use this file * except in compliance with the License. Please obtain a copy of the * License at http://www.apple.com/publicsource and read it before using * this file. * * The Original Code and all software distributed under the License are * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE OR NON- INFRINGEMENT. Please see the * License for the specific language governing rights and limitations * under the License. * * @APPLE_LICENSE_HEADER_END@ */ #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 #if !(defined(KLD) && defined(__STATIC__)) #include #include #else /* defined(KLD) && defined(__STATIC__) */ #include #endif /* !(defined(KLD) && defined(__STATIC__)) */ #include #include #include "stuff/openstep_mach.h" #include #include #include #include #include #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: * * [:]: * * 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. */ __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; 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; 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 */