/* * fix_to_relocation_entries() creates the needed relocation entries for the * specified fix structure that is from a section who's address starts at * sect_addr. It returns the number of bytes of relocation_info structs it * placed at riP. */ static uint32_t fix_to_relocation_entries( struct fix *fixP, uint64_t sect_addr, struct relocation_info *riP, uint32_t debug_section) { struct symbol *symbolP; #ifdef ARM struct symbol fake_arm_thumb_symbol; #endif uint32_t count; struct scattered_relocation_info sri; uint32_t sectdiff; #ifdef HPPA uint32_t left21, right14; #endif /* * If fx_addsy is NULL then this fix needs no relocation entry. */ if(fixP->fx_addsy == NULL){ #ifdef ARM if(archflag_cpusubtype != CPU_SUBTYPE_ARM_V7 || (fixP->fx_r_type != ARM_THUMB_32BIT_BRANCH && fixP->fx_r_type != ARM_THUMB_RELOC_BR22)) #endif return(0); } #ifdef TC_VALIDATE_FIX TC_VALIDATE_FIX(fixP, sect_addr, 0); #endif memset(riP, '\0', sizeof(struct relocation_info)); #ifdef ARM if(fixP->fx_addsy == NULL && archflag_cpusubtype == CPU_SUBTYPE_ARM_V7 && (fixP->fx_r_type == ARM_THUMB_32BIT_BRANCH || fixP->fx_r_type == ARM_THUMB_RELOC_BR22)){ memset(&fake_arm_thumb_symbol, '\0', sizeof(struct symbol)); symbolP = &fake_arm_thumb_symbol; symbolP->sy_type = N_ABS; fixP->fx_r_type = ARM_THUMB_32BIT_BRANCH; } else #endif /* ARM */ symbolP = fixP->fx_addsy; switch(fixP->fx_size){ case 1: riP->r_length = 0; break; case 2: riP->r_length = 1; break; case 4: #ifdef PPC if(fixP->fx_r_type == PPC_RELOC_BR14_predicted) riP->r_length = 3; else #endif riP->r_length = 2; break; #if defined(ARCH64) case 8: riP->r_length = 3; break; #endif /* defined(ARCH64) */ default: layout_file = fixP->file; layout_line = fixP->line; as_fatal("Bad fx_size (0x%x) in fix_to_relocation_info()\n", fixP->fx_size); } riP->r_pcrel = fixP->fx_pcrel; riP->r_address = fixP->fx_frag->fr_address + fixP->fx_where - sect_addr; riP->r_type = fixP->fx_r_type; /* * For undefined symbols this will be an external relocation entry. * Or if this is an external coalesced symbol or weak symbol. */ #if defined(I386) && defined(ARCH64) if(fixP->fx_subsy == NULL && (!debug_section || (symbolP->sy_type & N_TYPE) == N_UNDF) && (!is_local_symbol(symbolP) || ((symbolP->sy_type & N_TYPE) == N_SECT && is_section_cstring_literals(symbolP->sy_other)) ) ) { #else if((symbolP->sy_type & N_TYPE) == N_UNDF || ((symbolP->sy_type & N_EXT) == N_EXT && (symbolP->sy_type & N_TYPE) == N_SECT && (is_section_coalesced(symbolP->sy_other) || (symbolP->sy_desc & N_WEAK_DEF) == N_WEAK_DEF) && fixP->fx_subsy == NULL)){ #endif riP->r_extern = 1; riP->r_symbolnum = symbolP->sy_number; } else{ /* * For defined symbols this will be a local relocation entry * (possibly a section difference or a scattered relocation entry). */ riP->r_extern = 0; riP->r_symbolnum = symbolP->sy_other; /* nsect */ /* * Determine if this is left as a local relocation entry or * changed to a SECTDIFF relocation entry. If this comes from a fix * that has a subtract symbol it is a SECTDIFF relocation. Which is * "addsy - subsy + constant" where both symbols are defined in * sections. To encode all this information two scattered * relocation entries are used. The first has the add symbol value * and the second has the subtract symbol value. */ if(fixP->fx_subsy != NULL){ #if defined(I386) && defined(ARCH64) /* Encode fixP->fx_subsy (B) first, then symbolP (fixP->fx_addsy) (A). */ if (is_local_symbol(fixP->fx_subsy)) { riP->r_extern = 0; riP->r_symbolnum = fixP->fx_subsy->sy_other; } else { riP->r_extern = 1; riP->r_symbolnum = fixP->fx_subsy->sy_number; } riP->r_type = X86_64_RELOC_SUBTRACTOR; /* Now write out the unsigned relocation entry. */ riP++; *riP = *(riP - 1); if (is_local_symbol(fixP->fx_addsy)) { riP->r_extern = 0; riP->r_symbolnum = fixP->fx_addsy->sy_other; } else { riP->r_extern = 1; riP->r_symbolnum = fixP->fx_addsy->sy_number; } riP->r_type = X86_64_RELOC_UNSIGNED; return(2 * sizeof(struct relocation_info)); #endif #ifdef PPC if(fixP->fx_r_type == PPC_RELOC_HI16) sectdiff = PPC_RELOC_HI16_SECTDIFF; else if(fixP->fx_r_type == PPC_RELOC_LO16) sectdiff = PPC_RELOC_LO16_SECTDIFF; else if(fixP->fx_r_type == PPC_RELOC_HA16) sectdiff = PPC_RELOC_HA16_SECTDIFF; else if(fixP->fx_r_type == PPC_RELOC_LO14) sectdiff = PPC_RELOC_LO14_SECTDIFF; else #endif #ifdef HPPA if(fixP->fx_r_type == HPPA_RELOC_HI21) sectdiff = HPPA_RELOC_HI21_SECTDIFF; else if(fixP->fx_r_type == HPPA_RELOC_LO14) sectdiff = HPPA_RELOC_LO14_SECTDIFF; else #endif #ifdef SPARC if(fixP->fx_r_type == SPARC_RELOC_HI22) sectdiff = SPARC_RELOC_HI22_SECTDIFF; else if(fixP->fx_r_type == SPARC_RELOC_LO10) sectdiff = SPARC_RELOC_LO10_SECTDIFF; else #endif { if(fixP->fx_r_type != 0){ layout_file = fixP->file; layout_line = fixP->line; as_fatal("Internal error: incorrect fx_r_type (%u) for " "fx_subsy != 0 in fix_to_relocation_info()", fixP->fx_r_type); } if((!(fixP->fx_addsy->sy_type & N_EXT)) && flagseen['k']) sectdiff = RELOC_LOCAL_SECTDIFF; else sectdiff = RELOC_SECTDIFF; } memset(&sri, '\0',sizeof(struct scattered_relocation_info)); sri.r_scattered = 1; sri.r_length = riP->r_length; sri.r_pcrel = riP->r_pcrel; sri.r_address = riP->r_address; sri.r_type = sectdiff; sri.r_value = symbolP->sy_value; *riP = *((struct relocation_info *)&sri); riP++; sri.r_type = RELOC_PAIR; sri.r_value = fixP->fx_subsy->sy_value; if(sectdiff == RELOC_SECTDIFF || sectdiff == RELOC_LOCAL_SECTDIFF) sri.r_address = 0; #ifdef PPC else if(sectdiff == PPC_RELOC_HI16_SECTDIFF || sectdiff == PPC_RELOC_HA16_SECTDIFF){ sri.r_address = (symbolP->sy_value - fixP->fx_subsy->sy_value + fixP->fx_offset) & 0xffff; } else if(sectdiff == PPC_RELOC_LO16_SECTDIFF || sectdiff == PPC_RELOC_LO14_SECTDIFF){ sri.r_address = ((symbolP->sy_value - fixP->fx_subsy->sy_value + fixP->fx_offset) >> 16) & 0xffff; } #endif #ifdef HPPA else if(sectdiff == HPPA_RELOC_HI21_SECTDIFF){ calc_hppa_HILO(symbolP->sy_value - fixP->fx_subsy->sy_value, fixP->fx_offset, &left21, &right14); sri.r_address = right14 & 0x3fff; } else if(sectdiff == HPPA_RELOC_LO14_SECTDIFF){ calc_hppa_HILO(symbolP->sy_value - fixP->fx_subsy->sy_value, fixP->fx_offset, &left21, &right14); sri.r_address = left21 >> 11; } #endif #ifdef SPARC else if(sectdiff == SPARC_RELOC_HI22_SECTDIFF){
/* * fixup_section() does the fixups of the frags and prepares the fixes so * relocation entries can be created from them. The fixups cause the contents * of the frag to have the value for the fixup expression. A fix structure that * ends up with a non-NULL fx_addsy will have a relocation entry created for it. */ static void fixup_section( fixS *fixP, int nsect) { symbolS *add_symbolP; symbolS *sub_symbolP; signed_expr_t value; int size; char *place; int32_t where; char pcrel; fragS *fragP; int add_symbol_N_TYPE; int add_symbol_nsect; #ifndef SPARC int sub_symbol_nsect; #endif /* * The general fix expression is "fx_addsy - fx_subsy + fx_offset". * The goal is to put the result of this expression into the frag at * "place" for size "size". The value of the expression is calculated * in the variable "value" and starts with just the fx_offset. */ for( ; fixP != NULL; fixP = fixP->fx_next){ fragP = fixP->fx_frag; know(fragP); where = fixP->fx_where; place = fragP->fr_literal + where; size = fixP->fx_size; #ifdef TC_FIXUP_SYMBOL fixP->fx_offset += TC_FIXUP_SYMBOL(fixP, nsect, &fixP->fx_addsy); fixP->fx_offset -= TC_FIXUP_SYMBOL(fixP, nsect, &fixP->fx_subsy); #endif #if defined(I386) && defined(ARCH64) if(fixP->fx_addsy == fixP->fx_subsy){ /* * If we've fixed up both symbols to the same location, * we don't need a relocation entry. */ fixP->fx_addsy = NULL; fixP->fx_subsy = NULL; } #endif add_symbolP = fixP->fx_addsy; sub_symbolP = fixP->fx_subsy; value = fixP->fx_offset; pcrel = fixP->fx_pcrel; #if ARM /* If the symbol is defined in this file, the linker won't set the low-order bit for a Thumb symbol, so we have to do it here. */ if(add_symbolP != NULL && add_symbolP->sy_desc & N_ARM_THUMB_DEF && !(sub_symbolP != NULL && sub_symbolP->sy_desc & N_ARM_THUMB_DEF) && !pcrel){ value |= 1; } #endif add_symbol_N_TYPE = 0; add_symbol_nsect = 0; if(add_symbolP != NULL){ add_symbol_N_TYPE = add_symbolP->sy_type & N_TYPE; if(add_symbol_N_TYPE == N_SECT) add_symbol_nsect = add_symbolP->sy_other; } /* * Is there a subtract symbol? */ if(sub_symbolP){ /* is it just -sym ? */ if(add_symbolP == NULL){ if(sub_symbolP->sy_type != N_ABS) as_warn("Negative of non-absolute symbol %s", sub_symbolP->sy_name); #if !(defined(I386) && defined(ARCH64)) /* Symbol offsets are not part of fixups for x86_64. */ value -= sub_symbolP->sy_value; #endif fixP->fx_subsy = NULL; } /* * There are both an add symbol and a subtract symbol at this * point. * * If both symbols are absolute then just calculate the * value of the fix expression and no relocation entry will be * needed. */ else if((sub_symbolP->sy_type & N_TYPE) == N_ABS && (add_symbolP->sy_type & N_TYPE) == N_ABS){ value += add_symbolP->sy_value - sub_symbolP->sy_value; add_symbolP = NULL; fixP->fx_addsy = NULL; /* no relocation entry */ fixP->fx_subsy = NULL; } /* * If both symbols are defined in a section then calculate the * value of the fix expression and let a section difference * relocation entry be created. */ else if((sub_symbolP->sy_type & N_TYPE) == N_SECT && (add_symbolP->sy_type & N_TYPE) == N_SECT){ /* * We are use the new features that are incompatible with * 3.2 then just calculate the value and let this create a * SECTDIFF relocation type. */ #ifdef SPARC /* * Special case dealing with assembler internal relocation * entries SPARC_RELOC_13 and RELOC_22. The can not be * output and must be resolved. */ if((fixP->fx_r_type == SPARC_RELOC_13) || (fixP->fx_r_type == SPARC_RELOC_22)){ if(sub_symbolP->sy_other == add_symbolP->sy_other){ value += add_symbolP->sy_value - sub_symbolP->sy_value; add_symbolP = NULL; fixP->fx_addsy = NULL; /* no relocation entry */ fixP->fx_subsy = NULL; } else{ as_warn("Can't emit reloc type %u {-symbol \"%s\"} " "@ file address %llu (mode?).", fixP->fx_r_type, sub_symbolP->sy_name, fragP->fr_address + where); } } else value += add_symbolP->sy_value - sub_symbolP->sy_value; #else #if !(defined(I386) && defined(ARCH64)) /* * Special case for x86_64. 'value' doesn't include * the difference between the two symbols because * that's handled by the subtractor/vanilla reloc pair. */ value += add_symbolP->sy_value; value -= sub_symbolP->sy_value; #else /* * But for x86_64 expressions in the debug section must * be the actual value of the expression. */ if(is_section_debug(nsect)){ value += add_symbolP->sy_value; value -= sub_symbolP->sy_value; } #endif sub_symbol_nsect = sub_symbolP->sy_other; /* * If we have the special assembly time constant expression * of the difference of two symbols defined in the same * section then divided by exactly 2 adjust the value and * make sure these symbols will produce an assembly time * constant. */ if(fixP->fx_sectdiff_divide_by_two == 1){ value = value / 2; if(is_assembly_time_constant_subtraction_expression( add_symbolP, add_symbol_nsect, sub_symbolP, sub_symbol_nsect) == TRUE){ fixP->fx_addsy = NULL; /* no relocation entry */ goto down; } else{ layout_line = fixP->line; layout_file = fixP->file; as_warn("section difference divide by two " "expression, \"%s\" minus \"%s\" divide by " "2 will not produce an assembly time " "constant", add_symbolP->sy_name, sub_symbolP->sy_name); } } if(is_end_section_address(add_symbol_nsect, add_symbolP->sy_value) || is_end_section_address(sub_symbol_nsect, sub_symbolP->sy_value)){ if(is_assembly_time_constant_subtraction_expression( add_symbolP, add_symbol_nsect, sub_symbolP, sub_symbol_nsect) == TRUE){ fixP->fx_addsy = NULL; /* no relocation entry */ goto down; } if(is_section_debug(nsect) && strcmp(add_symbolP->sy_name, FAKE_LABEL_NAME) == 0 && strcmp(sub_symbolP->sy_name, FAKE_LABEL_NAME) == 0){ fixP->fx_addsy = NULL; /* no relocation entry */ goto down; } layout_line = fixP->line; layout_file = fixP->file; as_warn("section difference relocatable subtraction " "expression, \"%s\" minus \"%s\" using a " "symbol at the end of section will not " "produce an assembly time constant", add_symbolP->sy_name, sub_symbolP->sy_name); as_warn("use a symbol with a constant value created " "with an assignment instead of the expression, " "L_const_sym = %s - %s", add_symbolP->sy_name, sub_symbolP->sy_name); layout_line = 0; layout_file = NULL; } #endif goto down; } /* * If the subtract symbol is absolute subtract it's value from * the fix expression and let a relocation entry get created * that is not a section difference type. */ else if(sub_symbolP->sy_type == N_ABS){ value -= sub_symbolP->sy_value; fixP->fx_subsy = NULL; /* no SECTDIFF relocation entry */ } /* * At this point we have something we can't generate a * relocation entry for (two undefined symbols, etc.). */ else{ layout_line = fixP->line; layout_file = fixP->file; as_bad("non-relocatable subtraction expression, \"%s\" " "minus \"%s\"", add_symbolP->sy_name, sub_symbolP->sy_name); if((add_symbolP->sy_type & N_TYPE) == N_UNDF) as_bad("symbol: \"%s\" can't be undefined in a " "subtraction expression", add_symbolP->sy_name); if((sub_symbolP->sy_type & N_TYPE) == N_UNDF) as_bad("symbol: \"%s\" can't be undefined in a " "subtraction expression", sub_symbolP->sy_name); layout_line = 0; layout_file = NULL; } } /* * If a there is an add symbol in the fixup expression then add * the symbol value into the fixup expression's value. */ if(add_symbolP){ /* * If this symbol is in this section and is pc-relative and we * do not want to force a pc-relative relocation entry (to * support scattered loading) then just calculate the value. */ if(add_symbol_nsect == nsect /* FROM write.c line 2659 */ #ifdef ARM && !TC_FORCE_RELOCATION_LOCAL (fixP) #else && pcrel #endif && !(fixP->fx_pcrel_reloc)){ /* * This fixup was made when the symbol's section was * unknown, but it is now in this section. So we know how * to do the address without relocation. */ value += add_symbolP->sy_value; #ifdef ARM /* FROM write.c line 2667 */ value -= MD_PCREL_FROM_SECTION (fixP, nsect); #else value -= size + where + fragP->fr_address; #endif pcrel = 0; /* Lie. Don't want further pcrel processing. */ fixP->fx_addsy = NULL; /* No relocations please. */ /* * It would be nice to check that the address does not * overflow. * I didn't do this check because: * + It is machine dependent in the general case (eg 32032) * + Compiler output will never need this checking, so why * slow down the usual case? */ } else{ switch(add_symbol_N_TYPE){ case N_ABS: /* * If the value of the symbol was an expression then * now evaluate the expression now. This can happen * when symbols like: * .set x,a-b * are used and the value of x is not known till all * of the symbols are seen and had their values set. */ if(add_symbolP->expression != NULL){ expressionS *exp; exp = (expressionS *)add_symbolP->expression; value += exp->X_add_symbol->sy_value + exp->X_add_number - exp->X_subtract_symbol->sy_value; } else { value += add_symbolP->sy_value; } fixP->fx_addsy = NULL; /* no relocation entry */ add_symbolP = NULL; break; case N_SECT: #if (defined(I386) && defined(ARCH64)) /* * Symbol offsets are not part of fixups for external * symbols for x86_64. */ if((is_section_debug(nsect) && add_symbol_N_TYPE != N_UNDF) || (add_symbol_N_TYPE == N_SECT && is_local_symbol(add_symbolP) && !is_section_cstring_literals(add_symbol_nsect)) ) #else if(((add_symbolP->sy_type & N_EXT) != N_EXT || add_symbol_N_TYPE != N_SECT || !is_section_coalesced(add_symbol_nsect)) && (add_symbolP->sy_desc & N_WEAK_DEF) != N_WEAK_DEF) #endif value += add_symbolP->sy_value; break; case N_UNDF: break; default: BAD_CASE(add_symbol_N_TYPE); break; } } } down: /* * If the fixup expression is pc-relative then the value of the pc * will be added to the expression when the machine executes the * the instruction so we adjust the fixup expression's value by * subtracting off the pc value (where) and adjust for insn size. */ if(pcrel){ #ifdef ARM /* This should work for both */ /* FROM write.c line 2688 */ value -= MD_PCREL_FROM_SECTION (fixP, nsect); #elif !(defined(I386) && defined(ARCH64)) /* Symbol offsets are not part of fixups for x86_64. */ value -= size + where + fragP->fr_address; #endif if(add_symbolP == NULL){ fixP->fx_addsy = &abs_symbol; /* force relocation entry */ } } if((size == 1 && (value & 0xffffff00) && ((value & 0xffffff80) != 0xffffff80)) || (size == 2 && (value & 0xffff0000) && ((value & 0xffff8000) != 0xffff8000))){ layout_line = fixP->line; layout_file = fixP->file; as_bad("Fixup of %lld too large for field width of %d", value, size); layout_line = 0; layout_file = NULL; } /* * Now place the fix expression's value in the place for the size. * And save the fix expression's value to be used when creating * a relocation entry if required. */ md_number_to_imm((unsigned char *)place, value, size, fixP, nsect); fixP->fx_value = value; /* * If this is a non-lazy pointer section and this fix is for a * local symbol without an subtract symbol then cause this not to * generate a relocation entry. This is used with code gen for * fix-n-continue where the compiler generates indirection for * static data references. So the assembly looks like this: * * .non_lazy_symbol_pointer * L_i$non_lazy_ptr: * .indirect_symbol _i * .long _i * * this allows the value of the symbol to be set into the pointer * but not cause the relocation entry to be created. The code in * write_object() then changes the indirect symbol table entry to * INDIRECT_SYMBOL_LOCAL when the symbol is local. This is what * the static and dynamic linkers expect and will then cause the * pointer to be correctly relocated. */ if(is_section_non_lazy_symbol_pointers(nsect) && (add_symbolP->sy_type & N_EXT) != N_EXT && sub_symbolP == NULL){ fixP->fx_addsy = NULL; /* no relocation entry */ } } }
/* * fixup_section() does the fixups of the frags and prepares the fixes so * relocation entries can be created from them. The fixups cause the contents * of the frag to have the value for the fixup expression. A fix structure that * ends up with a non NULL fx_addsy will have a relocation entry created for it. */ static void fixup_section( fixS *fixP, int nsect) { symbolS *add_symbolP; symbolS *sub_symbolP; long value; int size; char *place; long where; char pcrel; fragS *fragP; int add_symbol_N_TYPE; int add_symbol_nsect; #ifndef SPARC int sub_symbol_nsect; #endif /* * The general fix expression is "fx_addsy - fx_subsy + fx_offset". * The goal is to put the result of this expression into the frag at * "place" for size "size". The value of the expression is calculated * in the variable "value" and starts with just the fx_offset. */ for( ; fixP != NULL; fixP = fixP->fx_next){ fragP = fixP->fx_frag; know(fragP); where = fixP->fx_where; place = fragP->fr_literal + where; size = fixP->fx_size; add_symbolP = fixP->fx_addsy; sub_symbolP = fixP->fx_subsy; value = fixP->fx_offset; pcrel = fixP->fx_pcrel; add_symbol_N_TYPE = 0; add_symbol_nsect = 0; if(add_symbolP != NULL){ add_symbol_N_TYPE = add_symbolP->sy_type & N_TYPE; if(add_symbol_N_TYPE == N_SECT) add_symbol_nsect = add_symbolP->sy_other; } /* * Is there a subtract symbol? */ if(sub_symbolP){ /* is it just -sym ? */ if(add_symbolP == NULL){ if(sub_symbolP->sy_type != N_ABS) as_warn("Negative of non-absolute symbol %s", sub_symbolP->sy_name); value -= sub_symbolP->sy_value; fixP->fx_subsy = NULL; } /* * There are both an add symbol and a subtract symbol at this * point. * * If both symbols are absolute then just calculate the * value of the fix expression and no relocation entry will be * needed. */ else if((sub_symbolP->sy_type & N_TYPE) == N_ABS && (add_symbolP->sy_type & N_TYPE) == N_ABS){ value += add_symbolP->sy_value - sub_symbolP->sy_value; add_symbolP = NULL; fixP->fx_addsy = NULL; /* no relocation entry */ fixP->fx_subsy = NULL; } /* * If both symbols are defined in a section then calculate the * value of the fix expression and let a section difference * relocation entry be created. */ else if((sub_symbolP->sy_type & N_TYPE) == N_SECT && (add_symbolP->sy_type & N_TYPE) == N_SECT){ /* * We are use the new features that are incompatible with * 3.2 then just calculate the value and let this create a * SECTDIFF relocation type. */ #ifdef SPARC /* * Special case dealing with assembler internal relocation * entries SPARC_RELOC_13 and RELOC_22. The can not be * output and must be resolved. */ if((fixP->fx_r_type == SPARC_RELOC_13) || (fixP->fx_r_type == SPARC_RELOC_22)){ if(sub_symbolP->sy_other == add_symbolP->sy_other){ value += add_symbolP->sy_value - sub_symbolP->sy_value; add_symbolP = NULL; fixP->fx_addsy = NULL; /* no relocation entry */ fixP->fx_subsy = NULL; } else{ as_warn("Can't emit reloc type %u {-symbol \"%s\"} " "@ file address %ld (mode?).", fixP->fx_r_type, sub_symbolP->sy_name, fragP->fr_address + where); } } else value += add_symbolP->sy_value - sub_symbolP->sy_value; #else value += add_symbolP->sy_value - sub_symbolP->sy_value; sub_symbol_nsect = sub_symbolP->sy_other; if(is_end_section_address(add_symbol_nsect, add_symbolP->sy_value) || is_end_section_address(sub_symbol_nsect, sub_symbolP->sy_value)){ layout_line = fixP->line; layout_file = fixP->file; as_warn("section difference relocatable subtraction " "expression, \"%s\" minus \"%s\" using a " "symbol at the end of section will not " "produce an assembly time constant", add_symbolP->sy_name, sub_symbolP->sy_name); as_warn("use a symbol with a constant value created " "with an assignment instead of the expression, " "L_const_sym = %s - %s", add_symbolP->sy_name, sub_symbolP->sy_name); layout_line = 0; layout_file = NULL; } #endif goto down; } /* * If the subtract symbol is absolute subtract it's value from * the fix expression and let a relocation entry get created * that is not a section difference type. */ else if(sub_symbolP->sy_type == N_ABS){ value -= sub_symbolP->sy_value; fixP->fx_subsy = NULL; /* no SECTDIFF relocation entry */ } /* * At this point we have something we can't generate a * relocation entry for (two undefined symbols, etc.). */ else{ layout_line = fixP->line; layout_file = fixP->file; as_warn("non-relocatable subtraction expression, \"%s\" " "minus \"%s\"", add_symbolP->sy_name, sub_symbolP->sy_name); if((add_symbolP->sy_type & N_TYPE) == N_UNDF) as_warn("symbol: \"%s\" can't be undefined in a " "subtraction expression", add_symbolP->sy_name); if((sub_symbolP->sy_type & N_TYPE) == N_UNDF) as_warn("symbol: \"%s\" can't be undefined in a " "subtraction expression", sub_symbolP->sy_name); layout_line = 0; layout_file = NULL; } } /* * If a there is an add symbol in the fixup expression then add * the symbol value into the fixup expression's value. */ if(add_symbolP){ /* * If this symbol is in this section and is pc-relative and we * do not want to force a pc-relative relocation entry (to * support scattered loading) then just calculate the value. */ if(add_symbol_nsect == nsect && pcrel && !(fixP->fx_pcrel_reloc)){ /* * This fixup was made when the symbol's section was * unknown, but it is now in this section. So we know how * to do the address without relocation. */ value += add_symbolP->sy_value; value -= size + where + fragP->fr_address; pcrel = 0; /* Lie. Don't want further pcrel processing. */ fixP->fx_addsy = NULL; /* No relocations please. */ /* * It would be nice to check that the address does not * overflow. * I didn't do this check because: * + It is machine dependent in the general case (eg 32032) * + Compiler output will never need this checking, so why * slow down the usual case? */ } else{ switch(add_symbol_N_TYPE){ case N_ABS: /* * If the value of the symbol was an expression then * now evaluate the expression now. This can happen * when symbols like: * .set x,a-b * are used and the value of x is not known till all * of the symbols are seen and had their values set. */ if(add_symbolP->expression != NULL){ expressionS *exp; exp = (expressionS *)add_symbolP->expression; value += exp->X_add_symbol->sy_value - exp->X_subtract_symbol->sy_value; } else value += add_symbolP->sy_value; fixP->fx_addsy = NULL; /* no relocation entry */ add_symbolP = NULL; break; case N_SECT: if((add_symbolP->sy_type & N_EXT) != N_EXT || add_symbol_N_TYPE != N_SECT || !is_section_coalesced(add_symbol_nsect)) value += add_symbolP->sy_value; break; case N_UNDF: break; default: BAD_CASE(add_symbol_N_TYPE); break; } } } down: /* * If the fixup expression is pc-relative then the value of the pc * will be added to the expression when the machine executes the * the instruction so we adjust the fixup expression's value by * subtracting off the pc value (where) and adjust for insn size. */ if(pcrel){ value -= size + where + fragP->fr_address; if(add_symbolP == NULL){ fixP->fx_addsy = &abs_symbol; /* force relocation entry */ } } if((size == 1 && (value & 0xffffff00) && ((value & 0xffffff80) != 0xffffff80)) || (size == 2 && (value & 0xffff8000) && ((value & 0xffff8000) != 0xffff8000))) as_warn("Fixup of %ld too large for field width of %d", value, size); /* * Now place the fix expression's value in the place for the size. * And save the fix expression's value to be used when creating * a relocation entry if required. */ md_number_to_imm((unsigned char *)place, value, size, fixP, nsect); fixP->fx_value = value; /* * If this is a non-lazy pointer section and this fix is for a * local symbol without an subtract symbol then cause this not to * generate a relocation entry. This is used with code gen for * fix-n-continue where the compiler generates indirection for * static data references. So the assembly looks like this: * * .non_lazy_symbol_pointer * L_i$non_lazy_ptr: * .indirect_symbol _i * .long _i * * this allows the value of the symbol to be set into the pointer * but not cause the relocation entry to be created. The code in * write_object() then changes the indirect symbol table entry to * INDIRECT_SYMBOL_LOCAL when the symbol is local. This is what * the static and dynamic linkers expect and will then cause the * pointer to be correctly relocated. */ if(is_section_non_lazy_symbol_pointers(nsect) && (add_symbolP->sy_type & N_EXT) != N_EXT && sub_symbolP == NULL){ fixP->fx_addsy = NULL; /* no relocation entry */ } } }