diff options
Diffstat (limited to 'bfd/elf32-sh.c')
-rw-r--r-- | bfd/elf32-sh.c | 201 |
1 files changed, 180 insertions, 21 deletions
diff --git a/bfd/elf32-sh.c b/bfd/elf32-sh.c index ad60e07..6913fa4 100644 --- a/bfd/elf32-sh.c +++ b/bfd/elf32-sh.c @@ -66,7 +66,7 @@ static reloc_howto_type sh_elf_howto_table[] = false, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ - sh_elf_reloc, /* special_function */ + sh_elf_ignore_reloc, /* special_function */ "R_SH_NONE", /* name */ false, /* partial_inplace */ 0, /* src_mask */ @@ -97,7 +97,7 @@ static reloc_howto_type sh_elf_howto_table[] = true, /* pc_relative */ 0, /* bitpos */ complain_overflow_signed, /* complain_on_overflow */ - sh_elf_reloc, /* special_function */ + sh_elf_ignore_reloc, /* special_function */ "R_SH_REL32", /* name */ false, /* partial_inplace */ 0, /* src_mask */ @@ -112,7 +112,7 @@ static reloc_howto_type sh_elf_howto_table[] = true, /* pc_relative */ 0, /* bitpos */ complain_overflow_signed, /* complain_on_overflow */ - sh_elf_reloc, /* special_function */ + sh_elf_ignore_reloc, /* special_function */ "R_SH_DIR8WPN", /* name */ true, /* partial_inplace */ 0xff, /* src_mask */ @@ -142,7 +142,7 @@ static reloc_howto_type sh_elf_howto_table[] = true, /* pc_relative */ 0, /* bitpos */ complain_overflow_unsigned, /* complain_on_overflow */ - sh_elf_reloc, /* special_function */ + sh_elf_ignore_reloc, /* special_function */ "R_SH_DIR8WPL", /* name */ true, /* partial_inplace */ 0xff, /* src_mask */ @@ -157,7 +157,7 @@ static reloc_howto_type sh_elf_howto_table[] = true, /* pc_relative */ 0, /* bitpos */ complain_overflow_unsigned, /* complain_on_overflow */ - sh_elf_reloc, /* special_function */ + sh_elf_ignore_reloc, /* special_function */ "R_SH_DIR8WPZ", /* name */ true, /* partial_inplace */ 0xff, /* src_mask */ @@ -174,7 +174,7 @@ static reloc_howto_type sh_elf_howto_table[] = false, /* pc_relative */ 0, /* bitpos */ complain_overflow_unsigned, /* complain_on_overflow */ - sh_elf_reloc, /* special_function */ + sh_elf_ignore_reloc, /* special_function */ "R_SH_DIR8BP", /* name */ false, /* partial_inplace */ 0, /* src_mask */ @@ -191,7 +191,7 @@ static reloc_howto_type sh_elf_howto_table[] = false, /* pc_relative */ 0, /* bitpos */ complain_overflow_unsigned, /* complain_on_overflow */ - sh_elf_reloc, /* special_function */ + sh_elf_ignore_reloc, /* special_function */ "R_SH_DIR8W", /* name */ false, /* partial_inplace */ 0, /* src_mask */ @@ -208,7 +208,7 @@ static reloc_howto_type sh_elf_howto_table[] = false, /* pc_relative */ 0, /* bitpos */ complain_overflow_unsigned, /* complain_on_overflow */ - sh_elf_reloc, /* special_function */ + sh_elf_ignore_reloc, /* special_function */ "R_SH_DIR8L", /* name */ false, /* partial_inplace */ 0, /* src_mask */ @@ -420,9 +420,147 @@ static reloc_howto_type sh_elf_howto_table[] = 0, /* dst_mask */ false), /* pcrel_offset */ + /* 8 bit PC relative divided by 2 - but specified in a very odd way. */ + HOWTO (R_SH_LOOP_START, /* type */ + 1, /* rightshift */ + 1, /* size (0 = byte, 1 = short, 2 = long) */ + 8, /* bitsize */ + false, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_signed, /* complain_on_overflow */ + sh_elf_ignore_reloc, /* special_function */ + "R_SH_LOOP_START", /* name */ + true, /* partial_inplace */ + 0xff, /* src_mask */ + 0xff, /* dst_mask */ + true), /* pcrel_offset */ + + /* 8 bit PC relative divided by 2 - but specified in a very odd way. */ + HOWTO (R_SH_LOOP_END, /* type */ + 1, /* rightshift */ + 1, /* size (0 = byte, 1 = short, 2 = long) */ + 8, /* bitsize */ + false, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_signed, /* complain_on_overflow */ + sh_elf_ignore_reloc, /* special_function */ + "R_SH_LOOP_END", /* name */ + true, /* partial_inplace */ + 0xff, /* src_mask */ + 0xff, /* dst_mask */ + true), /* pcrel_offset */ + }; -/* This function is used for normal relocs. This is like the COFF +static bfd_reloc_status_type +sh_elf_reloc_loop (r_type, input_bfd, input_section, contents, addr, + symbol_section, start, end) + int r_type; + bfd *input_bfd; + asection *input_section; + bfd_byte *contents; + bfd_vma addr; + asection *symbol_section; + bfd_vma start, end; +{ + static bfd_vma last_addr; + asection *last_symbol_section; + bfd_byte *free_contents = NULL; + bfd_byte *start_ptr, *ptr, *last_ptr; + int diff, cum_diff; + bfd_signed_vma x; + int insn; + + /* Sanity check the address. */ + if (addr > input_section->_raw_size) + return bfd_reloc_outofrange; + + /* We require the start and end relocations to be processed consecutively - + although we allow then to be processed forwards or backwards. */ + if (! last_addr) + { + last_addr = addr; + last_symbol_section = symbol_section; + return bfd_reloc_ok; + } + if (last_addr != addr) + abort (); + last_addr = 0; + + if (! symbol_section || last_symbol_section != symbol_section || end < start) + return bfd_reloc_outofrange; + + /* Get the symbol_section contents. */ + if (symbol_section != input_section) + { + if (elf_section_data (symbol_section)->this_hdr.contents != NULL) + contents = elf_section_data (symbol_section)->this_hdr.contents; + else + { + free_contents = contents + = (bfd_byte *) bfd_malloc (symbol_section->_raw_size); + if (contents == NULL) + return bfd_reloc_outofrange; + if (! bfd_get_section_contents (input_bfd, symbol_section, contents, + (file_ptr) 0, + symbol_section->_raw_size)) + { + free (contents); + return bfd_reloc_outofrange; + } + } + } +#define IS_PPI(PTR) ((bfd_get_16 (input_bfd, (PTR)) & 0xfc00) == 0xf800) + start_ptr = contents + start; + for (cum_diff = -6, ptr = contents + end; cum_diff < 0 && ptr > start_ptr;) + { + for (last_ptr = ptr, ptr -= 4; ptr >= start_ptr && IS_PPI (ptr);) + ptr -= 2; + ptr += 2; + diff = last_ptr - ptr >> 1; + cum_diff += diff & 1; + cum_diff += diff; + } + /* Calculate the start / end values to load into rs / re minus four - + so that will cancel out the four we would otherwise have to add to + addr to get the value to subtract in order to get relative addressing. */ + if (cum_diff >= 0) + { + start -= 4; + end = (ptr + cum_diff * 2) - contents; + } + else + { + bfd_vma start0 = start - 4; + + while (start0 >= 0 && IS_PPI (contents + start0)) + start0 -= 2; + start0 = start - 2 - ((start - start0) & 2); + start = start0 - cum_diff - 2; + end = start0; + } + + if (free_contents) + free (free_contents); + + insn = bfd_get_16 (input_bfd, contents + addr); + + x = (insn & 0x200 ? end : start) - addr; + if (input_section != symbol_section) + x += ((symbol_section->output_section->vma + symbol_section->output_offset) + - (input_section->output_section->vma + + input_section->output_offset)); + x >>= 1; + if (x < -128 || x > 127) + return bfd_reloc_overflow; + + x = insn & ~0xff | x & 0xff; + bfd_put_16 (input_bfd, x, contents + addr); + + return bfd_reloc_ok; +} + +/* This function is used for normal relocs. This used to be like the COFF function, and is almost certainly incorrect for other ELF targets. */ static bfd_reloc_status_type @@ -453,9 +591,7 @@ sh_elf_reloc (abfd, reloc_entry, symbol_in, data, input_section, output_bfd, /* Almost all relocs have to do with relaxing. If any work must be done for them, it has been done in sh_relax_section. */ - if (r_type != R_SH_DIR32 - && (r_type != R_SH_IND12W - || (symbol_in->flags & BSF_LOCAL) != 0)) + if (r_type == R_SH_IND12W && (symbol_in->flags & BSF_LOCAL) != 0) return bfd_reloc_ok; if (symbol_in != NULL @@ -549,6 +685,8 @@ static const struct elf_reloc_map sh_reloc_map[] = { BFD_RELOC_SH_LABEL, R_SH_LABEL }, { BFD_RELOC_VTABLE_INHERIT, R_SH_GNU_VTINHERIT }, { BFD_RELOC_VTABLE_ENTRY, R_SH_GNU_VTENTRY }, + { BFD_RELOC_SH_LOOP_START, R_SH_LOOP_START }, + { BFD_RELOC_SH_LOOP_END, R_SH_LOOP_END }, }; /* Given a BFD reloc code, return the howto structure for the @@ -1734,11 +1872,13 @@ sh_elf_relocate_section (output_bfd, info, input_bfd, input_section, /* Many of the relocs are only used for relaxing, and are handled entirely by the relaxation code. */ - if (r_type > (int) R_SH_LAST_INVALID_RELOC) + if (r_type > (int) R_SH_LAST_INVALID_RELOC + && r_type < (int) R_SH_LOOP_START) continue; if (r_type < 0 - || r_type >= (int) R_SH_FIRST_INVALID_RELOC) + || (r_type >= (int) R_SH_FIRST_INVALID_RELOC + && r_type <= (int) R_SH_LAST_INVALID_RELOC)) { bfd_set_error (bfd_error_bad_value); return false; @@ -1747,7 +1887,9 @@ sh_elf_relocate_section (output_bfd, info, input_bfd, input_section, /* FIXME: This is certainly incorrect. However, it is how the COFF linker works. */ if (r_type != (int) R_SH_DIR32 - && r_type != (int) R_SH_IND12W) + && r_type != (int) R_SH_IND12W + && r_type != (int) R_SH_LOOP_START + && r_type != (int) R_SH_LOOP_END) continue; howto = sh_elf_howto_table + r_type; @@ -1803,15 +1945,32 @@ sh_elf_relocate_section (output_bfd, info, input_bfd, input_section, { case (int)R_SH_DIR32: addend = rel->r_addend; + /* Fall through. */ + default: + /* COFF relocs don't use the addend. The addend is used for + R_SH_DIR32 to be compatible with other compilers. */ + r = _bfd_final_link_relocate (howto, input_bfd, input_section, + contents, rel->r_offset, + relocation, addend); break; + case R_SH_LOOP_START: + { + static bfd_vma start, end; + + start = (relocation + rel->r_addend + - (sec->output_section->vma + sec->output_offset)); + r = sh_elf_reloc_loop (r_type, input_bfd, input_section, contents, + rel->r_offset, sec, start, end); + break; + case R_SH_LOOP_END: + end = (relocation + rel->r_addend + - (sec->output_section->vma + sec->output_offset)); + r = sh_elf_reloc_loop (r_type, input_bfd, input_section, contents, + rel->r_offset, sec, start, end); + break; + } } - /* COFF relocs don't use the addend. The addend is used for R_SH_DIR32 - to be compatible with other compilers. */ - r = _bfd_final_link_relocate (howto, input_bfd, input_section, - contents, rel->r_offset, - relocation, addend); - if (r != bfd_reloc_ok) { switch (r) |