/* * literal8_merge() merges 8 byte literals from the specified section in the * current object file (cur_obj). It allocates a fine relocation map and * sets the fine_relocs field in the section_map to it (as well as the count). */ __private_extern__ void literal8_merge( struct literal8_data *data, struct merged_section *ms, struct section *s, struct section_map *section_map) { unsigned long nliteral8s, i; struct literal8 *literal8s; struct fine_reloc *fine_relocs; if(s->size == 0){ section_map->fine_relocs = NULL; section_map->nfine_relocs = 0; return; } /* * Calcualte the number of literals so the size of the fine relocation * structures can be allocated. */ if(s->size % 8 != 0){ error_with_cur_obj("8 byte literal section (%.16s,%.16s) size is " "not a multiple of 8 bytes", ms->s.segname, ms->s.sectname); return; } nliteral8s = s->size / 8; #ifdef DEBUG data->nfiles++; data->nliterals += nliteral8s; #endif /* DEBUG */ fine_relocs = allocate(nliteral8s * sizeof(struct fine_reloc)); memset(fine_relocs, '\0', nliteral8s * sizeof(struct fine_reloc)); /* * lookup and enter each C string in the section and record the offsets * in the input file and in the output file. */ literal8s = (struct literal8 *)(cur_obj->obj_addr + s->offset); for(i = 0; i < nliteral8s; i++){ fine_relocs[i].input_offset = i * 8; fine_relocs[i].output_offset = lookup_literal8(literal8s[i], data, ms); } section_map->fine_relocs = fine_relocs; section_map->nfine_relocs = nliteral8s; }
/* * literal_pointer_merge() merges literal pointers from the specified section * in the current object file (cur_obj). It allocates a fine relocation map and * sets the fine_relocs field in the section_map to it (as well as the count). */ __private_extern__ void literal_pointer_merge( struct literal_pointer_data *data, struct merged_section *ms, struct section *s, struct section_map *section_map) { long i; unsigned long nliterals, j; char *literals; struct fine_reloc *fine_relocs; struct relocation_info *relocs; struct relocation_info *reloc; struct scattered_relocation_info *sreloc; unsigned long r_address, r_symbolnum, r_pcrel, r_length, r_extern, r_scattered, r_value; struct undefined_map *undefined_map; struct nlist *nlists; char *strings; enum bool defined, new; struct merged_symbol *merged_symbol, **hash_pointer; struct section_map *literal_map; struct merged_section *literal_ms; struct section *literal_s; unsigned long section_value, input_section_offset, merged_section_offset, offset; if(s->size == 0){ section_map->fine_relocs = NULL; section_map->nfine_relocs = 0; return; } if(s->size % 4 != 0){ error_with_cur_obj("literal pointer section (%.16s,%.16s) size is " "not a multiple of 4 bytes",s->segname, s->sectname); return; } nliterals = s->size / 4; if(s->nreloc != nliterals){ error_with_cur_obj("literal pointer section (%.16s,%.16s) does not " "have is exactly one relocation entry for each " "pointer\n", s->segname, s->sectname); return; } #ifdef DEBUG data->nfiles++; data->nliterals += nliterals; #endif /* DEBUG */ /* * The size is not zero an it has as many relocation entries as literals so * this section is being relocated. */ ms->relocated = TRUE; /* * Set the output_offset to -1 here so that when going through the * relocation entries to merge the literals the error of having more than * one relocation entry for each literal can be caught. */ fine_relocs = allocate(nliterals * sizeof(struct fine_reloc)); memset(fine_relocs, '\0', nliterals * sizeof(struct fine_reloc)); for(j = 0; j < nliterals; j++){ fine_relocs[j].output_offset = -1; } section_map->fine_relocs = fine_relocs; section_map->nfine_relocs = nliterals; /* * Because at this point it is known that their are exactly as many literals * in the section as relocation entries either could be used to merge the * the literals themselves. The loop is driven off the relocation entries * for two reasons: first if it looped throught the literals themselfs a * costly search would result in trying to find the relocation entry for it * and by doing it the way it is done here the r_address (really an offset) * can be used directly to get the literal, secondly by looping through the * relocation entries checking for the error case of more than one * relocation entry refering to the same literal can be caught easily. So * if everything goes without error then their must have been exactly one * relocation entry for each literal pointer in this section. The reason * that this loop runs backwards through the relocation entries is to get * this implementation of merging literal pointers to match the previous * one so a binary compare can be done (the previous implemention went * through the literals and the current assembler puts out the relocation * entries in reverse order so these two implementions just happen to get * the exact same result). */ relocs = (struct relocation_info *)(cur_obj->obj_addr + s->reloff); literals = (char *)(cur_obj->obj_addr + s->offset); if(cur_obj->swapped) swap_relocation_info(relocs, s->nreloc, host_byte_sex); merged_symbol = NULL; for(i = s->nreloc - 1; i >= 0 ; i--){ /* * Break out the fields of the relocation entry. */ if((relocs[i].r_address & R_SCATTERED) != 0){ sreloc = (struct scattered_relocation_info *)(relocs + i); reloc = NULL; r_scattered = 1; r_address = sreloc->r_address; r_pcrel = sreloc->r_pcrel; r_length = sreloc->r_length; r_value = sreloc->r_value; r_extern = 0; /* calculate the r_symbolnum (n_sect) from the r_value */ r_symbolnum = 0; for(j = 0; j < cur_obj->nsection_maps; j++){ if(r_value >= cur_obj->section_maps[j].s->addr && r_value < cur_obj->section_maps[j].s->addr + cur_obj->section_maps[j].s->size){ r_symbolnum = j + 1; break; } } if(r_symbolnum == 0){ /* * The edge case where the last address past then end of * of the last section is referenced. */ for(j = 0; j < cur_obj->nsection_maps; j++){ if(r_value == cur_obj->section_maps[j].s->addr + cur_obj->section_maps[j].s->size){ r_symbolnum = j + 1; break; } } if(r_symbolnum == 0){ error_with_cur_obj("r_value (0x%x) field of relocation " "entry %lu in section (%.16s,%.16s) out of range", (unsigned int)r_value, i, s->segname, s->sectname); continue; } } } else{ reloc = relocs + i; sreloc = NULL; r_scattered = 0; r_address = reloc->r_address; r_pcrel = reloc->r_pcrel; r_length = reloc->r_length; r_extern = reloc->r_extern; r_symbolnum = reloc->r_symbolnum; r_value = 0; } /* * The r_address field is really an offset into the contents of the * section and must reference something inside the section. */ if(r_address >= s->size){ error_with_cur_obj("r_address (0x%x) field of relocation entry " "%ld in section (%.16s,%.16s) out of range", (unsigned int)r_address, i, s->segname, s->sectname); continue; } /* * For a literal pointer section all relocation entries must be for one * of the pointers and therefore the offset must be a multiple of 4, * have an r_length field of 2 (long) and a r_pcrel field of 0 (FALSE). */ if(r_address % 4 != 0){ error_with_cur_obj("r_address (0x%x) field of relocation entry " "%ld in literal pointer section (%.16s,%.16s) is not a " "multiple of 4", (unsigned int)r_address, i, s->segname, s->sectname); continue; } if(r_length != 2){ error_with_cur_obj("r_length (0x%x) field of relocation entry " "%ld in literal pointer section (%.16s,%.16s) is not 2 (long)", (unsigned int)r_length, i, s->segname, s->sectname); continue; } if(r_pcrel != 0){ error_with_cur_obj("r_pcrel (0x%x) field of relocation entry " "%ld in literal pointer section (%.16s,%.16s) is not 0 " "(FALSE)", (unsigned int)r_pcrel, i, s->segname, s->sectname); continue; } defined = TRUE; /* * If r_extern is set this relocation entry is an external entry * else it is a local entry (or scattered entry). */ if(r_extern){ /* * This is an external relocation entry. So the value to be * added to the item to be relocated is the value of the symbol. * r_symbolnum is an index into the input file's symbol table * of the symbol being refered to. The symbol must be an * undefined symbol to be used in an external relocation entry. */ if(r_symbolnum >= cur_obj->symtab->nsyms){ error_with_cur_obj("r_symbolnum (%lu) field of external " "relocation entry %ld in section (%.16s,%.16s) out of " "range", r_symbolnum, i, s->segname, s->sectname); continue; } undefined_map = bsearch(&r_symbolnum, cur_obj->undefined_maps, cur_obj->nundefineds, sizeof(struct undefined_map), (int (*)(const void *, const void *))undef_bsearch); if(undefined_map != NULL){ merged_symbol = undefined_map->merged_symbol; } else{ nlists = (struct nlist *)(cur_obj->obj_addr + cur_obj->symtab->symoff); strings = cur_obj->obj_addr + cur_obj->symtab->stroff; if((nlists[r_symbolnum].n_type & N_EXT) != N_EXT){ error_with_cur_obj("r_symbolnum (%lu) field of external " "relocation entry %lu in section (%.16s,%.16s) refers " "to a non-external symbol", r_symbolnum, i, s->segname, s->sectname); continue; } /* * We must correctly catch the errors of a literal pointer * refering defined global coalesced symbols with external * relocation entries. */ 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 literal_pointer_merge() " "failed to lookup coalesced symbol %s", strings + nlists[r_symbolnum].n_un.n_strx); } merged_symbol = *hash_pointer; error_with_cur_obj("exteral symbol (%s) for external " "relocation entry %ld in section (%.16s,%.16s) refers " "a symbol not defined in a literal section", merged_symbol->nlist.n_un.n_name, i, s->segname, s->sectname); continue; } else{ if(nlists[r_symbolnum].n_type != (N_EXT | N_UNDF)){ error_with_cur_obj("r_symbolnum (%lu) field of " "external relocation entry %lu in section " "(%.16s,%.16s) refers to a non-undefined symbol", r_symbolnum, i, s->segname, s->sectname); continue; } print_obj_name(cur_obj); fatal("internal error, in literal_pointer_merge() symbol " "index %lu in above file not in undefined map", r_symbolnum); } } /* * If this is an indirect symbol resolve indirection (all chains * of indirect symbols have been resolved so that they point at * a symbol that is not an indirect symbol). */ if((merged_symbol->nlist.n_type & N_TYPE) == N_INDR) merged_symbol = (struct merged_symbol *) merged_symbol->nlist.n_value; /* * If the symbol is a common symbol it is an error * because it not a pointer to a literal. */ if(merged_symbol->nlist.n_type == (N_EXT | N_UNDF) && merged_symbol->nlist.n_value != 0){ error_with_cur_obj("r_symbolnum (%lu) field of external " "relocation entry %ld in section (%.16s,%.16s) refers to " "a common symbol (%s) and is not in a literal section", r_symbolnum, i, s->segname, s->sectname, merged_symbol->nlist.n_un.n_name); continue; } /* * If the symbol is an absolute symbol it is treated as an error * because it is not known to be a pointer to a literal. */ if((merged_symbol->nlist.n_type & N_TYPE) == N_ABS){ error_with_cur_obj("r_symbolnum (%lu) field of external " "relocation entry %ld in section (%.16s,%.16s) refers to " "an absolue symbol (%s) and is not known to be in a " "literal section", r_symbolnum, i, s->segname, s->sectname, merged_symbol->nlist.n_un.n_name); continue; } /* * For multi module dynamic shared library format files the * merged sections that could have had external relocation * entries must be resolved to private extern symbols. This is * because for multi module MH_DYLIB files all modules share the * merged sections and the entire section gets relocated when * the library is mapped in. So the above restriction assures * the merged section will get relocated properly and can be * shared amoung library modules. */ if(filetype == MH_DYLIB && multi_module_dylib == TRUE){ /* * If the symbol is undefined or not a private extern it is an * error for in this section for a MH_DYLIB file. */ if(merged_symbol->nlist.n_type == (N_EXT | N_UNDF)){ if(merged_symbol->error_flagged_for_dylib == 0){ error_with_cur_obj("illegal undefined reference for " "multi module MH_DYLIB output file to symbol: %s " "from a literal pointer section (section (%.16s," "%.16s) relocation entry: %lu)", merged_symbol->nlist.n_un.n_name, s->segname, s->sectname, i); merged_symbol->error_flagged_for_dylib = 1; } } else if((merged_symbol->nlist.n_type & N_PEXT) != N_PEXT){ if(merged_symbol->error_flagged_for_dylib == 0){ error_with_cur_obj("illegal external reference for " "multi module MH_DYLIB output file to symbol: %s " "(not a private extern symbol) from a literal " "pointer section (section (%.16s,%.16s) relocation " "entry: %lu)", merged_symbol->nlist.n_un.n_name, s->segname, s->sectname, i); merged_symbol->error_flagged_for_dylib = 1; } } } /* * If the symbol is an undefined symbol then the literal is an * offset to be added to the value of the symbol. */ if(merged_symbol->nlist.n_type == (N_EXT | N_UNDF)){ literal_ms = NULL; merged_section_offset = 0; offset = get_long((long *)(literals + r_address)); defined = FALSE; } else { /* * All other types of symbol of symbol have been handled so this * symbol must be defined in a section. The section that the * symbol is defined in must be a literal section or else it * is an error. Since this is an external relocation entry and * the symbol is defined this means it is defined in some other * object than this one. */ if((merged_symbol->nlist.n_type & N_TYPE) != N_SECT || (merged_symbol->nlist.n_type & N_EXT) != N_EXT){ fatal("internal error, in merge_literal_pointers() merged " "symbol %s does not have a type of N_EXT|N_SECT", merged_symbol->nlist.n_un.n_name); } literal_map = &(merged_symbol->definition_object-> section_maps[merged_symbol->nlist.n_sect - 1]); literal_ms = literal_map->output_section; if((literal_ms->s.flags & SECTION_TYPE) != S_CSTRING_LITERALS && (literal_ms->s.flags & SECTION_TYPE) != S_4BYTE_LITERALS && (literal_ms->s.flags & SECTION_TYPE) != S_8BYTE_LITERALS){ error_with_cur_obj("exteral symbol (%s) for external " "relocation entry %ld in section (%.16s,%.16s) refers " "a symbol not defined in a literal section", merged_symbol->nlist.n_un.n_name, i, s->segname, s->sectname); continue; } section_value = merged_symbol->nlist.n_value; literal_s = literal_map->s; if(section_value < literal_s->addr || section_value > literal_s->addr + literal_s->size){ error_with_cur_obj("exteral symbol's (%s) address not in " "the section (%.16s,%.16s) it is defined in", merged_symbol->nlist.n_un.n_name, literal_s->segname, literal_s->sectname); continue; } input_section_offset = section_value - literal_s->addr; /* * At this point it is known that the merged section the * literal is defined in is a literal section. The checking * for an internal error if the section does not have fine_reloc * entry is left to fine_reloc_output_offset(); */ merged_section_offset = fine_reloc_output_offset(literal_map, input_section_offset); /* * since this was an external relocation entry the value of the * literal pointer is symbol+offset and the relocation is done * based on only the symbol's value without the offset added. * That's why offset is NOT added to input_section_offset above. * Also if the offset is not zero that is need to be known so * that a scattered relocation entry can be created on output. */ offset = get_long((long *)(literals + r_address)); /* * merged_symbol is set to NULL for the call to * lookup_literal_pointer because this symbol is not undefined. */ merged_symbol = NULL; /* mark the section this symbol is in as referenced */ literal_map->output_section->referenced = TRUE; } } else{ /* * This is a local relocation entry (the value to which the item * to be relocated is refering to is defined in section number * r_symbolnum in this object file). Check that r_symbolnum is not * R_ABS so it can be used to directly index the section map. * For scattered relocation entries r_value was previously checked * to be in the section refered to by r_symbolnum. */ if(r_symbolnum == R_ABS){ error_with_cur_obj("r_symbolnum (0x%x) field of relocation " "entry %ld in literal pointer section (%.16s,%.16s) is " "R_ABS (not correct for a literal pointer section)", (unsigned int)r_symbolnum, i, s->segname, s->sectname); continue; } merged_symbol = NULL; literal_map = &(cur_obj->section_maps[r_symbolnum - 1]); literal_s = literal_map->s; literal_ms = literal_map->output_section; if(r_scattered == 0){ offset = 0; section_value = get_long((long *)(literals + r_address)); if(section_value < literal_s->addr || section_value > literal_s->addr + literal_s->size){ error_with_cur_obj("literal pointer (0x%x) in section " "(%.16s,%.16s) at address 0x%x does not point into " "its (%.16s,%.16s) section as refered to by its " "r_symbolnum (0x%x) field in relocation entry %ld as " "it should", (unsigned int)section_value, s->segname, s->sectname, (unsigned int)(s->addr + r_address), literal_s->segname, literal_s->sectname, (unsigned int)r_symbolnum, i); continue; } if((literal_ms->s.flags & SECTION_TYPE) != S_CSTRING_LITERALS && (literal_ms->s.flags & SECTION_TYPE) != S_4BYTE_LITERALS && (literal_ms->s.flags & SECTION_TYPE) != S_8BYTE_LITERALS){ error_with_cur_obj("r_symbolnum field (0x%x) in relocation " "entry %ld in literal pointer section (%.16s,%.16s) " "refers to section (%.16s,%.16s) which is not a " "literal section", (unsigned int)r_symbolnum, i, s->segname, s->sectname, literal_ms->s.segname, literal_ms->s.sectname); continue; } } else{ offset = get_long((long *)(literals + r_address)) - r_value; section_value = r_value; if((literal_ms->s.flags & SECTION_TYPE) != S_CSTRING_LITERALS && (literal_ms->s.flags & SECTION_TYPE) != S_4BYTE_LITERALS && (literal_ms->s.flags & SECTION_TYPE) != S_8BYTE_LITERALS){ error_with_cur_obj("r_value field (0x%x) in relocation " "entry %ld in literal pointer section (%.16s,%.16s) " "refers to section (%.16s,%.16s) which is not a " "literal section", (unsigned int)r_value, i, s->segname, s->sectname, literal_ms->s.segname, literal_ms->s.sectname); continue; } } input_section_offset = section_value - literal_s->addr; /* * At this point it is known that the merged section the * literal is defined in is a literal section. The checking * for an internal error if the section does not have fine_reloc * entry is left to fine_reloc_output_offset(); */ merged_section_offset = fine_reloc_output_offset(literal_map, input_section_offset); /* mark the section this literal is in as referenced */ literal_map->output_section->referenced = TRUE; } /* * Since all the output_offset field of all the fine reloc entries were * set to -1 before merging the literals and there must be only one * relocation entry for each literal pointer if the relocation entry * for this literal does not have an output_offset of -1 it is an error * because we have seen it before. */ if((int)(fine_relocs[r_address/4].output_offset) != -1){ error_with_cur_obj("more than one relocation entry for literal " "pointer at address 0x%x (r_address 0x%x) in section " "(%.16s,%.16s)", (unsigned int)(s->addr + r_address), (unsigned int)r_address, s->segname, s->sectname); continue; } /* * Now at long last the literal pointer can be merged and the fine * relocation entries for it can be built. */ fine_relocs[r_address/4].input_offset = r_address; fine_relocs[r_address/4].output_offset = lookup_literal_pointer(merged_symbol, literal_ms, merged_section_offset, offset, data, ms, &new); count_reloc(ms, new, r_extern, defined); } }
/* * merge_fvmlibs() handles the things relelated to fixed VM libraries. It does * two things. First any object file that has a LC_LOADFVMLIB command in it * indicated that library must be loaded in the final output. So this routine * collects all the load fixed VM library load commands into a list. Second * for the link editor to do overlap checking of the segments in a fixed VM * library with all the segments in the output file the program mkshlib(l) * builds an object file with segments marked with SG_FVMLIB and one * LC_LOADFVMLIB command for the library those segments are in. So this routine * also collects a list of these segments so the overlap checking can be done. */ __private_extern__ void merge_fvmlibs(void) { unsigned long i, nload_fvmlibs, nid_fvmlibs, nfvmlib_segments; struct mach_header *mh; struct load_command *lc; struct segment_command *sg; struct fvmlib_command *fl; struct merged_fvmlib *mfl; #ifdef DEBUG /* The compiler "warnings: `fl' and `mfl' may be used uninitialized */ /* in this function" can safely be ignored */ fl = NULL;; mfl = NULL;; #endif /* DEBUG */ /* * First process all the load commands for the fixed VM libraries * loaded. This needs to be done first so that the fvmlib segments * can be associated with the library they are for. */ nload_fvmlibs = 0; nid_fvmlibs = 0; nfvmlib_segments = 0; mh = (struct mach_header *)cur_obj->obj_addr; lc = (struct load_command *)((char *)cur_obj->obj_addr + sizeof(struct mach_header)); for(i = 0; i < mh->ncmds; i++){ if(lc->cmd == LC_LOADFVMLIB){ fl = (struct fvmlib_command *)lc; mfl = lookup_merged_fvmlib(fl); nload_fvmlibs++; } if(lc->cmd == LC_IDFVMLIB){ fl = (struct fvmlib_command *)lc; mfl = lookup_merged_fvmlib(fl); nid_fvmlibs++; } else if(lc->cmd == LC_SEGMENT){ sg = (struct segment_command *)lc; if(sg->flags == SG_FVMLIB) nfvmlib_segments++; } lc = (struct load_command *)((char *)lc + lc->cmdsize); } /* * If the output file type is MH_FVMLIB then there should be one * LC_IDFVMLIB command seen in all the input files (that check is done * layout_segments() in layout.c. No LC_LOADFVMLIB commands should be * seen in the input files for the MH_FVMLIB output format and this is * checked in check_cur_obj() in pass1.c. */ if(filetype == MH_FVMLIB){ if(nid_fvmlibs > 1){ error_with_cur_obj("contains more than one LC_IDFVMLIB load " "command"); return; } } /* * For output file types that are not MH_FVMLIB then. There must be * exactly one LC_LOADFVMLIB command seen in the object file which is * the fixed VM library associated with these segments (if it has any * of these segments). There should not be any LC_IDFVMLIB commands * seen in the input files if the output format is not MH_FVMLIB and * this is checked in check_cur_obj() in pass1.c. */ else{ if(nfvmlib_segments == 0) return; if(nload_fvmlibs == 0){ error_with_cur_obj("contains SG_FVMLIB segments but no " "LC_LOADFVMLIB load command"); return; } if(nload_fvmlibs > 1){ error_with_cur_obj("contains SG_FVMLIB segments and more than " "one LC_LOADFVMLIB load command"); return; } if(mfl->multiple) return; lc = (struct load_command *)((char *)cur_obj->obj_addr + sizeof(struct mach_header)); for(i = 0; i < mh->ncmds; i++){ if(lc->cmd == LC_SEGMENT){ sg = (struct segment_command *)lc; if(sg->flags == SG_FVMLIB) add_fvmlib_segment(sg, (char *)fl + fl->fvmlib.name.offset); } lc = (struct load_command *)((char *)lc + lc->cmdsize); } } }