📄 tc-cris.c
字号:
and offsets to varying parts. */ symbolS *symbolP; unsigned long var_part_offset; /* Where, in file space, is _var of *fragP? */ unsigned long address_of_var_part = 0; /* Where, in file space, does addr point? */ unsigned long target_address; know (fragP->fr_type == rs_machine_dependent); length_code = fragP->fr_subtype & STATE_LENGTH_MASK; know (length_code >= 0 && length_code < STATE_MAX_LENGTH); var_part_offset = fragP->fr_fix; var_partp = fragP->fr_literal + var_part_offset; opcodep = fragP->fr_opcode; symbolP = fragP->fr_symbol; target_address = (symbolP ? S_GET_VALUE (symbolP) + symbol_get_frag(fragP->fr_symbol)->fr_address : 0 ) + fragP->fr_offset; address_of_var_part = fragP->fr_address + var_part_offset; switch (fragP->fr_subtype) { case ENCODE_RELAX (STATE_CONDITIONAL_BRANCH, STATE_BYTE): opcodep[0] = branch_disp ((target_address - address_of_var_part)); var_part_size = 0; break; case ENCODE_RELAX (STATE_CONDITIONAL_BRANCH, STATE_WORD): /* We had a quick immediate branch, now turn it into a word one i.e. a PC autoincrement. */ opcodep[0] = BRANCH_PC_LOW; opcodep[1] &= 0xF0; opcodep[1] |= BRANCH_INCR_HIGH; md_number_to_chars (var_partp, (long) (target_address - (address_of_var_part + 2)), 2); var_part_size = 2; break; case ENCODE_RELAX (STATE_CONDITIONAL_BRANCH, STATE_DWORD): gen_cond_branch_32 (fragP->fr_opcode, var_partp, fragP, fragP->fr_symbol, (symbolS *) NULL, fragP->fr_offset); /* Ten bytes added: a branch, nop and a jump. */ var_part_size = 2 + 2 + 4 + 2; break; case ENCODE_RELAX (STATE_BASE_PLUS_DISP_PREFIX, STATE_BYTE): var_partp[0] = target_address - (address_of_var_part + 1); var_part_size = 0; break; case ENCODE_RELAX (STATE_BASE_PLUS_DISP_PREFIX, STATE_WORD): /* We had a BDAP 8-bit "quick immediate", now turn it into a 16-bit one that uses PC autoincrement. */ opcodep[0] = BDAP_PC_LOW + (1 << 4); opcodep[1] &= 0xF0; opcodep[1] |= BDAP_INCR_HIGH; md_number_to_chars (var_partp, (long) (target_address), 2); var_part_size = 2; break; case ENCODE_RELAX (STATE_BASE_PLUS_DISP_PREFIX, STATE_DWORD): /* We had a BDAP 16-bit "word", change the offset to a dword. */ opcodep[0] = BDAP_PC_LOW + (2 << 4); opcodep[1] &= 0xF0; opcodep[1] |= BDAP_INCR_HIGH; if (fragP->fr_symbol == NULL) md_number_to_chars (var_partp, fragP->fr_offset, 4); else fix_new (fragP, var_partp - fragP->fr_literal, 4, fragP->fr_symbol, fragP->fr_offset, 0, BFD_RELOC_32); var_part_size = 4; break; default: BAD_CASE (fragP->fr_subtype); break; } fragP->fr_fix += var_part_size;}/* Generate a short jump around a secondary jump table. Used by md_create_long_jump. This used to be md_create_short_jump, but is now called from md_create_long_jump instead, when sufficient. since the sizes of the jumps are the same. It used to be brittle, making possibilities for creating bad code. */static voidcris_create_short_jump (storep, from_addr, to_addr, fragP, to_symbol) char *storep; addressT from_addr; addressT to_addr; fragS *fragP ATTRIBUTE_UNUSED; symbolS *to_symbol ATTRIBUTE_UNUSED;{ long int distance; distance = to_addr - from_addr; if (-254 <= distance && distance <= 256) { /* Create a "short" short jump: "BA distance - 2". */ storep[0] = branch_disp (distance - 2); storep[1] = BA_QUICK_HIGH; /* A nop for the delay slot. */ md_number_to_chars (storep + 2, NOP_OPCODE, 2); /* The extra word should be filled with something sane too. Make it a nop to keep disassembly sane. */ md_number_to_chars (storep + 4, NOP_OPCODE, 2); } else { /* Make it a "long" short jump: "BA (PC+)". */ md_number_to_chars (storep, BA_PC_INCR_OPCODE, 2); /* ".WORD distance - 4". */ md_number_to_chars (storep + 2, (long) (distance - 4), 2); /* A nop for the delay slot. */ md_number_to_chars (storep + 4, NOP_OPCODE, 2); }}/* Generate a long jump in a secondary jump table. storep Where to store the jump instruction. from_addr Address of the jump instruction. to_addr Destination address of the jump. fragP Which frag the destination address operand lies in. to_symbol Destination symbol. */voidmd_create_long_jump (storep, from_addr, to_addr, fragP, to_symbol) char *storep; addressT from_addr; addressT to_addr; fragS *fragP; symbolS *to_symbol;{ long int distance; distance = to_addr - from_addr; if (-32763 <= distance && distance <= 32772) { /* Then make it a "short" long jump. */ cris_create_short_jump (storep, from_addr, to_addr, fragP, to_symbol); } else { /* We have a "long" long jump: "JUMP (PC+)". */ md_number_to_chars (storep, JUMP_PC_INCR_OPCODE, 2); /* Follow with a ".DWORD to_addr". */ fix_new (fragP, storep + 2 - fragP->fr_literal, 4, to_symbol, 0, 0, BFD_RELOC_32); }}/* Allocate space for the first piece of an insn, and mark it as the start of the insn for debug-format use. */static char *cris_insn_first_word_frag (){ char *insnp = frag_more (2); /* We need to mark the start of the insn by passing dwarf2_emit_insn the offset from the current fragment position. This must be done after the first fragment is created but before any other fragments (fixed or varying) are created. Note that the offset only corresponds to the "size" of the insn for a fixed-size, non-expanded insn. */ if (OUTPUT_FLAVOR == bfd_target_elf_flavour) dwarf2_emit_insn (2); return insnp;}/* Port-specific assembler initialization. */voidmd_begin (){ const char *hashret = NULL; int i = 0; /* Set up a hash table for the instructions. */ op_hash = hash_new (); if (op_hash == NULL) as_fatal (_("Virtual memory exhausted")); while (cris_opcodes[i].name != NULL) { const char *name = cris_opcodes[i].name; hashret = hash_insert (op_hash, name, (PTR) &cris_opcodes[i]); if (hashret != NULL && *hashret != '\0') as_fatal (_("Can't hash `%s': %s\n"), cris_opcodes[i].name, *hashret == 0 ? _("(unknown reason)") : hashret); do { if (cris_opcodes[i].match & cris_opcodes[i].lose) as_fatal (_("Buggy opcode: `%s' \"%s\"\n"), cris_opcodes[i].name, cris_opcodes[i].args); ++i; } while (cris_opcodes[i].name != NULL && strcmp (cris_opcodes[i].name, name) == 0); }}/* Assemble a source line. */voidmd_assemble (str) char *str;{ struct cris_instruction output_instruction; struct cris_prefix prefix; char *opcodep; char *p; know (str); /* Do the low-level grunt - assemble to bits and split up into a prefix and ordinary insn. */ cris_process_instruction (str, &output_instruction, &prefix); /* Handle any prefixes to the instruction. */ switch (prefix.kind) { case PREFIX_NONE: break; /* When the expression is unknown for a BDAP, it can need 0, 2 or 4 extra bytes, so we handle it separately. */ case PREFIX_BDAP_IMM: gen_bdap (prefix.base_reg_number, &prefix.expr); break; case PREFIX_BDAP: case PREFIX_BIAP: case PREFIX_DIP: opcodep = cris_insn_first_word_frag (); /* Output the prefix opcode. */ md_number_to_chars (opcodep, (long) prefix.opcode, 2); /* This only happens for DIP, but is ok for the others as they have no reloc. */ if (prefix.reloc != BFD_RELOC_NONE) { /* Output an absolute mode address. */ p = frag_more (4); fix_new_exp (frag_now, (p - frag_now->fr_literal), 4, &prefix.expr, 0, prefix.reloc); } break; case PREFIX_PUSH: opcodep = cris_insn_first_word_frag (); /* Output the prefix opcode. Being a "push", we add the negative size of the register to "sp". */ if (output_instruction.spec_reg != NULL) { /* Special register. */ opcodep[0] = -output_instruction.spec_reg->reg_size; } else { /* General register. */ opcodep[0] = -4; } opcodep[1] = (REG_SP << 4) + (BDAP_QUICK_OPCODE >> 8); break; default: BAD_CASE (prefix.kind); } /* If we only had a prefix insn, we're done. */ if (output_instruction.insn_type == CRIS_INSN_NONE) return; /* Done with the prefix. Continue with the main instruction. */ if (prefix.kind == PREFIX_NONE) opcodep = cris_insn_first_word_frag (); else opcodep = frag_more (2); /* Output the instruction opcode. */ md_number_to_chars (opcodep, (long) (output_instruction.opcode), 2); /* Output the symbol-dependent instruction stuff. */ if (output_instruction.insn_type == CRIS_INSN_BRANCH) { segT to_seg = absolute_section; int is_undefined = 0; int length_code; if (output_instruction.expr.X_op != O_constant) { to_seg = S_GET_SEGMENT (output_instruction.expr.X_add_symbol); if (to_seg == undefined_section) is_undefined = 1; } if (output_instruction.expr.X_op == O_constant || to_seg == now_seg || is_undefined) { /* If is_undefined, then the expression may BECOME now_seg. */ length_code = is_undefined ? STATE_UNDF : STATE_BYTE; /* Make room for max ten bytes of variable length. */ frag_var (rs_machine_dependent, 10, 0, ENCODE_RELAX (STATE_CONDITIONAL_BRANCH, length_code), output_instruction.expr.X_add_symbol, output_instruction.expr.X_add_number, opcodep); } else { /* We have: to_seg != now_seg && to_seg != undefined_section. This means it is a branch to a known symbol in another section. Code in data? Weird but valid. Emit a 32-bit branch. */ gen_cond_branch_32 (opcodep, frag_more (10), frag_now, output_instruction.expr.X_add_symbol, (symbolS *) NULL, output_instruction.expr.X_add_number); } } else { if (output_instruction.imm_oprnd_size > 0) { /* The intruction has an immediate operand. */ enum bfd_reloc_code_real reloc = 0; switch (output_instruction.imm_oprnd_size) { /* Any byte-size immediate constants are treated as word-size. FIXME: Thus overflow check does not work correctly. */ case 2: reloc = BFD_RELOC_16; break; case 4: reloc = BFD_RELOC_32; break; default: BAD_CASE (output_instruction.imm_oprnd_size); } p = frag_more (output_instruction.imm_oprnd_size); fix_new_exp (frag_now, (p - frag_now->fr_literal), output_instruction.imm_oprnd_size, &output_instruction.expr, 0, reloc); } else if (output_instruction.reloc != BFD_RELOC_NONE) { /* An immediate operand that has a relocation and needs to be processed further. */ /* It is important to use fix_new_exp here and everywhere else (and not fix_new), as fix_new_exp can handle "difference expressions" - where the expression contains a difference of two symbols in the same segment. */ fix_new_exp (frag_now, (opcodep - frag_now->fr_literal), 2, &output_instruction.expr, 0, output_instruction.reloc); } }}/* Low level text-to-bits assembly. */static voidcris_process_instruction (insn_text, out_insnp, prefixp) char *insn_text; struct cris_instruction *out_insnp; struct cris_prefix *prefixp;{ char *s; char modified_char = 0; const char *args; struct cris_opcode *instruction; char *operands; int match = 0; int mode; int regno; int size_bits; /* Reset these fields to a harmless state in case we need to return in error. */ prefixp->kind = PREFIX_NONE; prefixp->reloc = BFD_RELOC_NONE; out_insnp->insn_type = CRIS_INSN_NORMAL; out_insnp->imm_oprnd_size = 0; /* Find the end of the opcode mnemonic. We assume (true in 2.9.1) that the caller has translated the opcode to lower-case, up to the first non-letter. */ for (operands = insn_text; islower (*operands); ++operands) ; /* Terminate the opcode after letters, but save the character there if it was of significance. */ switch (*operands) { case '\0': break; case '.': /* Put back the modified character later. */ modified_char = *operands; /* Fall through. */ case ' ': /* Consume the character after the mnemonic and replace it with '\0'. */ *operands++ = '\0'; break; default: as_bad (_("Unknown opcode: `%s'"), insn_text); return; } /* Find the instruction. */ instruction = (struct cris_opcode *) hash_find (op_hash, insn_text); if (instruction == NULL) { as_bad (_("Unknown opcode: `%s'"), insn_text); return; } /* Put back the modified character. */ switch (modified_char) { case 0: break; default: *--operands = modified_char; } /* Try to match an opcode table slot. */ for (s = operands;;) { int imm_expr_found; /* Initialize *prefixp, perhaps after being modified for a "near match". */ prefixp->kind = PREFIX_NONE; prefixp->reloc = BFD_RELOC_NONE; /* Initialize *out_insnp. */ memset (out_insnp, 0, sizeof (*out_insnp));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -