Example #1
0
/*
 * 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 */
	    }
	}
}
Example #2
0
/*
 * 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){