📄 tc-arc.c
字号:
break; default: *sizeP = 0; return "bad call to md_atof"; } t = atof_ieee (input_line_pointer, type, words); if (t) input_line_pointer = t; *sizeP = prec * sizeof (LITTLENUM_TYPE); for (wordP = words; prec--;) { md_number_to_chars (litP, (valueT) (*wordP++), sizeof (LITTLENUM_TYPE)); litP += sizeof (LITTLENUM_TYPE); } return NULL;}/* Write a value out to the object file, using the appropriate endianness. */voidmd_number_to_chars (buf, val, n) char *buf; valueT val; int n;{ if (target_big_endian) number_to_chars_bigendian (buf, val, n); else number_to_chars_littleendian (buf, val, n);}/* Round up a section size to the appropriate boundary. */valueTmd_section_align (segment, size) segT segment; valueT size;{ int align = bfd_get_section_alignment (stdoutput, segment); return ((size + (1 << align) - 1) & (-1 << align));}/* We don't have any form of relaxing. */intmd_estimate_size_before_relax (fragp, seg) fragS *fragp ATTRIBUTE_UNUSED; asection *seg ATTRIBUTE_UNUSED;{ as_fatal (_("md_estimate_size_before_relax\n")); return 1;}/* Convert a machine dependent frag. We never generate these. */voidmd_convert_frag (abfd, sec, fragp) bfd *abfd ATTRIBUTE_UNUSED; asection *sec ATTRIBUTE_UNUSED; fragS *fragp ATTRIBUTE_UNUSED;{ as_fatal (_("md_convert_frag\n"));}voidarc_code_symbol (expressionP) expressionS *expressionP;{ if (expressionP->X_op == O_symbol && expressionP->X_add_number == 0 /* I think this test is unnecessary but just as a sanity check... */ && expressionP->X_op_symbol == NULL) { expressionS two; expressionP->X_op = O_right_shift; expressionP->X_add_symbol->sy_value.X_op = O_constant; two.X_op = O_constant; two.X_add_symbol = two.X_op_symbol = NULL; two.X_add_number = 2; expressionP->X_op_symbol = make_expr_symbol (&two); } /* Allow %st(sym1-sym2) */ else if (expressionP->X_op == O_subtract && expressionP->X_add_symbol != NULL && expressionP->X_op_symbol != NULL && expressionP->X_add_number == 0) { expressionS two; expressionP->X_add_symbol = make_expr_symbol (expressionP); expressionP->X_op = O_right_shift; two.X_op = O_constant; two.X_add_symbol = two.X_op_symbol = NULL; two.X_add_number = 2; expressionP->X_op_symbol = make_expr_symbol (&two); } else { as_bad ("expression too complex code symbol"); return; }}/* Parse an operand that is machine-specific. The ARC has a special %-op to adjust addresses so they're usable in branches. The "st" is short for the STatus register. ??? Later expand this to take a flags value too. ??? We can't create new expression types so we map the %-op's onto the existing syntax. This means that the user could use the chosen syntax to achieve the same effect. */voidmd_operand (expressionP) expressionS *expressionP;{ char *p = input_line_pointer; if (*p == '%') if (strncmp (p, "%st(", 4) == 0) { input_line_pointer += 4; expression (expressionP); if (*input_line_pointer != ')') { as_bad ("missing ')' in %%-op"); return; } ++input_line_pointer; arc_code_symbol (expressionP); } else { /* It could be a register. */ int i, l; struct arc_ext_operand_value *ext_oper = arc_ext_operands; p++; while (ext_oper) { l = strlen (ext_oper->operand.name); if (!strncmp (p, ext_oper->operand.name, l) && !isalnum(*(p + l))) { input_line_pointer += l + 1; expressionP->X_op = O_register; expressionP->X_add_number = (int) &ext_oper->operand; return; } ext_oper = ext_oper->next; } for (i = 0; i < arc_reg_names_count; i++) { l = strlen (arc_reg_names[i].name); if (!strncmp (p, arc_reg_names[i].name, l) && !isalnum (*(p + l))) { input_line_pointer += l + 1; expressionP->X_op = O_register; expressionP->X_add_number = (int) &arc_reg_names[i]; break; } } }}/* We have no need to default values of symbols. We could catch register names here, but that is handled by inserting them all in the symbol table to begin with. */symbolS *md_undefined_symbol (name) char *name ATTRIBUTE_UNUSED;{ return 0;}/* Functions concerning expressions. *//* Parse a .byte, .word, etc. expression. Values for the status register are specified with %st(label). `label' will be right shifted by 2. */voidarc_parse_cons_expression (exp, nbytes) expressionS *exp; unsigned int nbytes ATTRIBUTE_UNUSED;{ char *p = input_line_pointer; int code_symbol_fix = 0; for (; ! is_end_of_line[(unsigned char) *p]; p++) if (*p == '@' && !strncmp (p, "@h30", 4)) { code_symbol_fix = 1; strcpy (p, "; "); } expr (0, exp); if (code_symbol_fix) { arc_code_symbol (exp); input_line_pointer = p; }}/* Record a fixup for a cons expression. */voidarc_cons_fix_new (frag, where, nbytes, exp) fragS *frag; int where; int nbytes; expressionS *exp;{ if (nbytes == 4) { int reloc_type; expressionS exptmp; /* This may be a special ARC reloc (eg: %st()). */ reloc_type = get_arc_exp_reloc_type (1, BFD_RELOC_32, exp, &exptmp); fix_new_exp (frag, where, nbytes, &exptmp, 0, reloc_type); } else { fix_new_exp (frag, where, nbytes, exp, 0, nbytes == 2 ? BFD_RELOC_16 : nbytes == 8 ? BFD_RELOC_64 : BFD_RELOC_32); }}/* Functions concerning relocs. *//* The location from which a PC relative jump should be calculated, given a PC relative reloc. */longmd_pcrel_from (fixP) fixS *fixP;{ if (fixP->fx_addsy != (symbolS *) NULL && ! S_IS_DEFINED (fixP->fx_addsy)) { /* The symbol is undefined. Let the linker figure it out. */ return 0; } /* Return the address of the delay slot. */ return fixP->fx_frag->fr_address + fixP->fx_where + fixP->fx_size;}/* Compute the reloc type of an expression. The possibly modified expression is stored in EXPNEW. This is used to convert the expressions generated by the %-op's into the appropriate operand type. It is called for both data in instructions (operands) and data outside instructions (variables, debugging info, etc.). Currently supported %-ops: %st(symbol): represented as "symbol >> 2" "st" is short for STatus as in the status register (pc) DEFAULT_TYPE is the type to use if no special processing is required. DATA_P is non-zero for data or limm values, zero for insn operands. Remember that the opcode "insertion fns" cannot be used on data, they're only for inserting operands into insns. They also can't be used for limm values as the insertion routines don't handle limm values. When called for insns we return fudged reloc types (real_value - BFD_RELOC_UNUSED). When called for data or limm values we use real reloc types. */static intget_arc_exp_reloc_type (data_p, default_type, exp, expnew) int data_p; int default_type; expressionS *exp; expressionS *expnew;{ /* If the expression is "symbol >> 2" we must change it to just "symbol", as fix_new_exp can't handle it. Similarily for (symbol - symbol) >> 2. That's ok though. What's really going on here is that we're using ">> 2" as a special syntax for specifying BFD_RELOC_ARC_B26. */ if (exp->X_op == O_right_shift && exp->X_op_symbol != NULL && exp->X_op_symbol->sy_value.X_op == O_constant && exp->X_op_symbol->sy_value.X_add_number == 2 && exp->X_add_number == 0) { if (exp->X_add_symbol != NULL && (exp->X_add_symbol->sy_value.X_op == O_constant || exp->X_add_symbol->sy_value.X_op == O_symbol)) { *expnew = *exp; expnew->X_op = O_symbol; expnew->X_op_symbol = NULL; return data_p ? BFD_RELOC_ARC_B26 : arc_operand_map['J']; } else if (exp->X_add_symbol != NULL && exp->X_add_symbol->sy_value.X_op == O_subtract) { *expnew = exp->X_add_symbol->sy_value; return data_p ? BFD_RELOC_ARC_B26 : arc_operand_map['J']; } } *expnew = *exp; return default_type;}/* Apply a fixup to the object code. This is called for all the fixups we generated by the call to fix_new_exp, above. In the call above we used a reloc code which was the largest legal reloc code plus the operand index. Here we undo that to recover the operand index. At this point all symbol values should be fully resolved, and we attempt to completely resolve the reloc. If we can not do that, we determine the correct reloc code and put it back in the fixup. */intmd_apply_fix3 (fixP, valueP, seg) fixS *fixP; valueT *valueP; segT seg;{#if 0 char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;#endif valueT value; /* FIXME FIXME FIXME: The value we are passed in *valueP includes the symbol values. Since we are using BFD_ASSEMBLER, if we are doing this relocation the code in write.c is going to call bfd_perform_relocation, which is also going to use the symbol value. That means that if the reloc is fully resolved we want to use *valueP since bfd_perform_relocation is not being used. However, if the reloc is not fully resolved we do not want to use *valueP, and must use fx_offset instead. However, if the reloc is PC relative, we do want to use *valueP since it includes the result of md_pcrel_from. This is confusing. */ if (fixP->fx_addsy == (symbolS *) NULL) { value = *valueP; fixP->fx_done = 1; } else if (fixP->fx_pcrel) { value = *valueP; /* ELF relocations are against symbols. If this symbol is in a different section then we need to leave it for the linker to deal with. Unfortunately, md_pcrel_from can't tell, so we have to undo it's effects here. */ if (S_IS_DEFINED (fixP->fx_addsy) && S_GET_SEGMENT (fixP->fx_addsy) != seg) value += md_pcrel_from (fixP); } else { value = fixP->fx_offset; if (fixP->fx_subsy != (symbolS *) NULL) { if (S_GET_SEGMENT (fixP->fx_subsy) == absolute_section) value -= S_GET_VALUE (fixP->fx_subsy); else { /* We can't actually support subtracting a symbol. */ as_bad_where (fixP->fx_file, fixP->fx_line, "expression too complex"); } } } if ((int) fixP->fx_r_type >= (int) BFD_RELOC_UNUSED) { int opindex; const struct arc_operand *operand; char *where; arc_insn insn; opindex = (int) fixP->fx_r_type - (int) BFD_RELOC_UNUSED; operand = &arc_operands[opindex]; /* Fetch the instruction, insert the fully resolved operand value, and stuff the instruction back again. */ where = fixP->fx_frag->fr_literal + fixP->fx_where; if (target_big_endian) insn = bfd_getb32 ((unsigned char *) where); else insn = bfd_getl32 ((unsigned char *) where); insn = arc_insert_operand (insn, operand, -1, NULL, (offsetT) value, fixP->fx_file, fixP->fx_line); if (target_big_endian) bfd_putb32 ((bfd_vma) insn, (unsigned char *) where); else bfd_putl32 ((bfd_vma) insn, (unsigned char *) where); if (fixP->fx_done) { /* Nothing else to do here. */ return 1; } /* Determine a BFD reloc value based on the operand information. We are only prepared to turn a few of the operands into relocs. !!! Note that we can't handle limm values here. Since we're using implicit addends the addend must be inserted into the instruction, however, the opcode insertion routines currently do nothing with limm values. */ if (operand->fmt == 'B') { assert ((operand->flags & ARC_OPERAND_RELATIVE_BRANCH) != 0 && operand->bits == 20 && operand->shift == 7); fixP->fx_r_type = BFD_RELOC_ARC_B22_PCREL; } else if (operand->fmt == 'J') { assert ((operand->flags & ARC_OPERAND_ABSOLUTE_BRANCH) != 0 && operand->bits == 24 && operand->shift == 32); fixP->fx_r_type = BFD_RELOC_ARC_B26; } else if (operand->fmt == 'L') { assert ((operand->flags & ARC_OPERAND_LIMM) != 0 && operand->bits == 32 && operand->shift == 32); fixP->fx_r_type = BFD_RELOC_32; } else { as_bad_where (fixP->fx_file, fixP->fx_line, "unresolved expression that must be resolved"); fixP->fx_done = 1; return 1; } } else { switch (fixP->fx_r_type) { case BFD_RELOC_8: md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where, value, 1); break; case BFD_RELOC_16: md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where, value, 2); break; case BFD_RELOC_32: md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where, value, 4); break;#if 0 case BFD_RELOC_64: md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where, value, 8); break;#endif case BFD_RELOC_ARC_B26: /* If !fixP->fx_done then `value' is an implicit addend. We must shift it right by 2 in this case as well because the linker performs the relocation and then adds this in (as opposed to adding this in and then shifting right by 2). */ value >>= 2; md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where, value, 4); break; default: abort (); } } fixP->fx_addnumber = value; return 1;}/* Translate internal representation of relocation info to BFD target format. */arelent *tc_gen_reloc (section, fixP) asection *section ATTRIBUTE_UNUSED; fixS *fixP;{ arelent *reloc; reloc = (arelent *) xmalloc (sizeof (arelent)); reloc->sym_ptr_ptr = &fixP->fx_addsy->bsym; reloc->address = fixP->fx_frag->fr_address + fixP->fx_where; reloc->howto = bfd_reloc_type_lookup (stdoutput, fixP->fx_r_type); if (reloc->howto == (reloc_howto_type *) NULL) { as_bad_where (fixP->fx_file, fixP->fx_line, "internal error: can't export reloc type %d (`%s')", fixP->fx_r_type, bfd_get_reloc_code_name (fixP->fx_r_type)); return NULL; } assert (!fixP->fx_pcrel == !reloc->howto->pc_relative); /* Set addend to account for PC being advanced one insn before the target address is computed, drop fx_addnumber as it is handled elsewhere mlm */ reloc->addend = (fixP->fx_pcrel ? -4 : 0); return reloc;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -