📄 tc-sh.c
字号:
struct sh_count_relocs{ /* Symbol we are looking for. */ symbolS *sym; /* Count of relocs found. */ int count;};/* Count the number of fixups in a section which refer to a particular symbol. When using BFD_ASSEMBLER, this is called via bfd_map_over_sections. */static voidsh_count_relocs (abfd, sec, data) bfd *abfd ATTRIBUTE_UNUSED; segT sec; PTR data;{ struct sh_count_relocs *info = (struct sh_count_relocs *) data; segment_info_type *seginfo; symbolS *sym; fixS *fix; seginfo = seg_info (sec); if (seginfo == NULL) return; sym = info->sym; for (fix = seginfo->fix_root; fix != NULL; fix = fix->fx_next) { if (fix->fx_addsy == sym) { ++info->count; fix->fx_tcbit = 1; } }}/* Handle the count relocs for a particular section. When using BFD_ASSEMBLER, this is called via bfd_map_over_sections. */static voidsh_frob_section (abfd, sec, ignore) bfd *abfd ATTRIBUTE_UNUSED; segT sec; PTR ignore ATTRIBUTE_UNUSED;{ segment_info_type *seginfo; fixS *fix; seginfo = seg_info (sec); if (seginfo == NULL) return; for (fix = seginfo->fix_root; fix != NULL; fix = fix->fx_next) { symbolS *sym; bfd_vma val; fixS *fscan; struct sh_count_relocs info; if (fix->fx_r_type != BFD_RELOC_SH_USES) continue; /* The BFD_RELOC_SH_USES reloc should refer to a defined local symbol in the same section. */ sym = fix->fx_addsy; if (sym == NULL || fix->fx_subsy != NULL || fix->fx_addnumber != 0 || S_GET_SEGMENT (sym) != sec#if ! defined (BFD_ASSEMBLER) && defined (OBJ_COFF) || S_GET_STORAGE_CLASS (sym) == C_EXT#endif || S_IS_EXTERNAL (sym)) { as_warn_where (fix->fx_file, fix->fx_line, _(".uses does not refer to a local symbol in the same section")); continue; } /* Look through the fixups again, this time looking for one at the same location as sym. */ val = S_GET_VALUE (sym); for (fscan = seginfo->fix_root; fscan != NULL; fscan = fscan->fx_next) if (val == fscan->fx_frag->fr_address + fscan->fx_where && fscan->fx_r_type != BFD_RELOC_SH_ALIGN && fscan->fx_r_type != BFD_RELOC_SH_CODE && fscan->fx_r_type != BFD_RELOC_SH_DATA && fscan->fx_r_type != BFD_RELOC_SH_LABEL) break; if (fscan == NULL) { as_warn_where (fix->fx_file, fix->fx_line, _("can't find fixup pointed to by .uses")); continue; } if (fscan->fx_tcbit) { /* We've already done this one. */ continue; } /* The variable fscan should also be a fixup to a local symbol in the same section. */ sym = fscan->fx_addsy; if (sym == NULL || fscan->fx_subsy != NULL || fscan->fx_addnumber != 0 || S_GET_SEGMENT (sym) != sec#if ! defined (BFD_ASSEMBLER) && defined (OBJ_COFF) || S_GET_STORAGE_CLASS (sym) == C_EXT#endif || S_IS_EXTERNAL (sym)) { as_warn_where (fix->fx_file, fix->fx_line, _(".uses target does not refer to a local symbol in the same section")); continue; } /* Now we look through all the fixups of all the sections, counting the number of times we find a reference to sym. */ info.sym = sym; info.count = 0;#ifdef BFD_ASSEMBLER bfd_map_over_sections (stdoutput, sh_count_relocs, (PTR) &info);#else { int iscan; for (iscan = SEG_E0; iscan < SEG_UNKNOWN; iscan++) sh_count_relocs ((bfd *) NULL, iscan, (PTR) &info); }#endif if (info.count < 1) abort (); /* Generate a BFD_RELOC_SH_COUNT fixup at the location of sym. We have already adjusted the value of sym to include the fragment address, so we undo that adjustment here. */ subseg_change (sec, 0); fix_new (symbol_get_frag (sym), S_GET_VALUE (sym) - symbol_get_frag (sym)->fr_address, 4, &abs_symbol, info.count, 0, BFD_RELOC_SH_COUNT); }}/* This function is called after the symbol table has been completed, but before the relocs or section contents have been written out. If we have seen any .uses pseudo-ops, they point to an instruction which loads a register with the address of a function. We look through the fixups to find where the function address is being loaded from. We then generate a COUNT reloc giving the number of times that function address is referred to. The linker uses this information when doing relaxing, to decide when it can eliminate the stored function address entirely. */voidsh_frob_file (){ if (! sh_relax) return;#ifdef BFD_ASSEMBLER bfd_map_over_sections (stdoutput, sh_frob_section, (PTR) NULL);#else { int iseg; for (iseg = SEG_E0; iseg < SEG_UNKNOWN; iseg++) sh_frob_section ((bfd *) NULL, iseg, (PTR) NULL); }#endif}/* Called after relaxing. Set the correct sizes of the fragments, and create relocs so that md_apply_fix will fill in the correct values. */voidmd_convert_frag (headers, seg, fragP)#ifdef BFD_ASSEMBLER bfd *headers ATTRIBUTE_UNUSED;#else object_headers *headers;#endif segT seg; fragS *fragP;{ int donerelax = 0; switch (fragP->fr_subtype) { case C (COND_JUMP, COND8): case C (COND_JUMP_DELAY, COND8): subseg_change (seg, 0); fix_new (fragP, fragP->fr_fix, 2, fragP->fr_symbol, fragP->fr_offset, 1, BFD_RELOC_SH_PCDISP8BY2); fragP->fr_fix += 2; fragP->fr_var = 0; break; case C (UNCOND_JUMP, UNCOND12): subseg_change (seg, 0); fix_new (fragP, fragP->fr_fix, 2, fragP->fr_symbol, fragP->fr_offset, 1, BFD_RELOC_SH_PCDISP12BY2); fragP->fr_fix += 2; fragP->fr_var = 0; break; case C (UNCOND_JUMP, UNCOND32): case C (UNCOND_JUMP, UNDEF_WORD_DISP): if (fragP->fr_symbol == NULL) as_bad_where (fragP->fr_file, fragP->fr_line, _("displacement overflows 12-bit field")); else if (S_IS_DEFINED (fragP->fr_symbol)) as_bad_where (fragP->fr_file, fragP->fr_line, _("displacement to defined symbol %s overflows 12-bit field"), S_GET_NAME (fragP->fr_symbol)); else as_bad_where (fragP->fr_file, fragP->fr_line, _("displacement to undefined symbol %s overflows 12-bit field"), S_GET_NAME (fragP->fr_symbol)); /* Stabilize this frag, so we don't trip an assert. */ fragP->fr_fix += fragP->fr_var; fragP->fr_var = 0; break; case C (COND_JUMP, COND12): case C (COND_JUMP_DELAY, COND12): /* A bcond won't fit, so turn it into a b!cond; bra disp; nop. */ /* I found that a relax failure for gcc.c-torture/execute/930628-1.c was due to gas incorrectly relaxing an out-of-range conditional branch with delay slot. It turned: bf.s L6 (slot mov.l r12,@(44,r0)) into:2c: 8f 01 a0 8b bf.s 32 <_main+32> (slot bra L6)30: 00 09 nop32: 10 cb mov.l r12,@(44,r0) Therefore, branches with delay slots have to be handled differently from ones without delay slots. */ { unsigned char *buffer = (unsigned char *) (fragP->fr_fix + fragP->fr_literal); int highbyte = target_big_endian ? 0 : 1; int lowbyte = target_big_endian ? 1 : 0; int delay = fragP->fr_subtype == C (COND_JUMP_DELAY, COND12); /* Toggle the true/false bit of the bcond. */ buffer[highbyte] ^= 0x2; /* If this is a dalayed branch, we may not put the the bra in the slot. So we change it to a non-delayed branch, like that: b! cond slot_label; bra disp; slot_label: slot_insn ??? We should try if swapping the conditional branch and its delay-slot insn already makes the branch reach. */ /* Build a relocation to six / four bytes farther on. */ subseg_change (seg, 0); fix_new (fragP, fragP->fr_fix, 2,#ifdef BFD_ASSEMBLER section_symbol (seg),#else seg_info (seg)->dot,#endif fragP->fr_address + fragP->fr_fix + (delay ? 4 : 6), 1, BFD_RELOC_SH_PCDISP8BY2); /* Set up a jump instruction. */ buffer[highbyte + 2] = 0xa0; buffer[lowbyte + 2] = 0; fix_new (fragP, fragP->fr_fix + 2, 2, fragP->fr_symbol, fragP->fr_offset, 1, BFD_RELOC_SH_PCDISP12BY2); if (delay) { buffer[highbyte] &= ~0x4; /* Removes delay slot from branch. */ fragP->fr_fix += 4; } else { /* Fill in a NOP instruction. */ buffer[highbyte + 4] = 0x0; buffer[lowbyte + 4] = 0x9; fragP->fr_fix += 6; } fragP->fr_var = 0; donerelax = 1; } break; case C (COND_JUMP, COND32): case C (COND_JUMP_DELAY, COND32): case C (COND_JUMP, UNDEF_WORD_DISP): case C (COND_JUMP_DELAY, UNDEF_WORD_DISP): if (fragP->fr_symbol == NULL) as_bad_where (fragP->fr_file, fragP->fr_line, _("displacement overflows 8-bit field")); else if (S_IS_DEFINED (fragP->fr_symbol)) as_bad_where (fragP->fr_file, fragP->fr_line, _("displacement to defined symbol %s overflows 8-bit field"), S_GET_NAME (fragP->fr_symbol)); else as_bad_where (fragP->fr_file, fragP->fr_line, _("displacement to undefined symbol %s overflows 8-bit field "), S_GET_NAME (fragP->fr_symbol)); /* Stabilize this frag, so we don't trip an assert. */ fragP->fr_fix += fragP->fr_var; fragP->fr_var = 0; break; default: abort (); } if (donerelax && !sh_relax) as_warn_where (fragP->fr_file, fragP->fr_line, _("overflow in branch to %s; converted into longer instruction sequence"), (fragP->fr_symbol != NULL ? S_GET_NAME (fragP->fr_symbol) : ""));}valueTmd_section_align (seg, size) segT seg ATTRIBUTE_UNUSED; valueT size;{#ifdef BFD_ASSEMBLER#ifdef OBJ_ELF return size;#else /* ! OBJ_ELF */ return ((size + (1 << bfd_get_section_alignment (stdoutput, seg)) - 1) & (-1 << bfd_get_section_alignment (stdoutput, seg)));#endif /* ! OBJ_ELF */#else /* ! BFD_ASSEMBLER */ return ((size + (1 << section_alignment[(int) seg]) - 1) & (-1 << section_alignment[(int) seg]));#endif /* ! BFD_ASSEMBLER */}/* This static variable is set by s_uacons to tell sh_cons_align that the expession does not need to be aligned. */static int sh_no_align_cons = 0;/* This handles the unaligned space allocation pseudo-ops, such as .uaword. .uaword is just like .word, but the value does not need to be aligned. */static voids_uacons (bytes) int bytes;{ /* Tell sh_cons_align not to align this value. */ sh_no_align_cons = 1; cons (bytes);}/* If a .word, et. al., pseud-op is seen, warn if the value is not aligned correctly. Note that this can cause warnings to be issued when assembling initialized structured which were declared with the packed attribute. FIXME: Perhaps we should require an option to enable this warning? */voidsh_cons_align (nbytes) int nbytes;{ int nalign; char *p; if (sh_no_align_cons) { /* This is an unaligned pseudo-op. */ sh_no_align_cons = 0; return; } nalign = 0; while ((nbytes & 1) == 0) { ++nalign; nbytes >>= 1; } if (nalign == 0) return; if (now_seg == absolute_section) { if ((abs_section_offset & ((1 << nalign) - 1)) != 0) as_warn (_("misaligned data")); return; } p = frag_var (rs_align_test, 1, 1, (relax_substateT) 0, (symbolS *) NULL, (offsetT) nalign, (char *) NULL); record_alignment (now_seg, nalign);}/* When relaxing, we need to output a reloc for any .align directive that requests alignment to a four byte boundary or larger. This is also where we check for misaligned data. */voidsh_handle_align (frag) fragS *frag;{ int bytes = frag->fr_next->fr_address - frag->fr_address - frag->fr_fix; if (frag->fr_type == rs_align_code) { static const unsigned char big_nop_pattern[] = { 0x00, 0x09 }; static const unsigned char little_nop_pattern[] = { 0x09, 0x00 }; char *p = frag->fr_literal + frag->fr_fix; if (bytes & 1) { *p++ = 0; bytes--; frag->fr_fix += 1; } if (target_big_endian) { memcpy (p, big_nop_pattern, sizeof big_nop_pattern); frag->fr_var = sizeof big_nop_pattern; } else { memcpy (p, little_nop_pattern, sizeof little_nop_pattern); frag->fr_var = sizeof little_nop_pattern; } } else if (frag->fr_type == rs_align_test) { if (bytes != 0) as_warn_where (frag->fr_file, frag->fr_line, _("misaligned data")); } if (sh_relax && (frag->fr_type == rs_align || frag->fr_type == rs_align_code) && frag->fr_address + frag->fr_fix > 0 && frag->fr_offset > 1 && now_seg != bss_section) fix_new (frag, frag->fr_fix, 2, &abs_symbol, frag->fr_offset, 0, BFD_RELOC_SH_ALIGN);}/* This macro decides whether a particular reloc is an entry in a switch table. It is used when relaxing, because the linker needs to know about all such entries so that it can adjust them if necessary. */#ifdef BFD_ASSEMBLER#define SWITCH_TABLE_CONS(fix) (0)#else#define SWITCH_TABLE_CONS(fix) \ ((fix)->fx_r_type == 0 \ && ((fix)->fx_size == 2 \ || (fix)->fx_size == 1 \ || (fix)->fx_size == 4))#endif#define SWITCH_TABLE(fix) \ ((fix)->fx_addsy != NULL \ && (fix)->fx_subsy != NULL \ && S_GET_SEGMENT ((fix)->fx_addsy) == text_section \ && S_GET_SEGMENT ((fix)->fx_subsy) == text_section \ && ((fix)->fx_r_type == BFD_RELOC_32 \ || (fix)->fx_r_type == BFD_RELOC_16 \ || (fix)->fx_r_type == BFD_RELOC_8 \ || SWITCH_TABLE_CONS (fix)))/* See whether we need to force a relocation into the output file. This is used to force out switch and PC relative relocations when relaxing. */intsh_force_relocation (fix) fixS *fix;{ if (fix->fx_r_type == BFD_RELOC_VTABLE_INHERIT || fix->fx_r_type == BFD_RELOC_VTABLE_ENTRY || fix->fx_r_type == BFD_RELOC_SH_LOOP_START || fix->fx_r_type == BFD_RELOC_SH_LOOP_END) return 1; if (! sh_relax) return 0; return (fix->fx_pcrel || SWITCH_TABLE (fix) || fix->fx_r_type == BFD_RELOC_SH_COUNT || fix->fx_r_type == BFD_RELOC_SH_ALIGN || fix->fx_r_type == BFD_RELOC_SH_CODE || fix->fx_r_type == BFD_RELOC_SH_DATA || fix->fx_r_type == BFD_RELOC_SH_LABEL);}#ifdef OBJ_ELFbooleansh_fix_adjustable (fixP) fixS *fixP;{ if (fixP->fx_addsy == NULL) return 1; if (fixP->fx_r_type == BFD_RELOC_SH_PCDISP8BY2 || fixP->fx_r_type == BFD_RELOC_SH_PCDISP12BY2 || fixP->fx_r_type == BFD_RELOC_SH_PCRELIMM8BY2 || fixP->fx_r_type == BFD_RELOC_SH_PCRELIMM8BY4 || fixP->fx_r_type == BFD_RELOC_8_PCREL || fixP->fx_r_type == BFD_RELOC_SH_SWITCH16 || fixP->fx_r_type == BFD_RELOC_SH_SWITCH32) return 1; if (! TC_RELOC_RTSYM_LOC_FIXUP (fixP) || fixP->fx_r_type == BFD_RELOC_32_GOTOFF || fixP->fx_r_type == BFD_RELOC_RVA) return 0; /* We need the symbol name for the VTABLE entries */ if (fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY) return 0; return 1;}voidsh_elf_final_processing (){ int val; /* Set file-specific f
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -