/* * 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 */ } } }