From: Ian Lance Taylor Date: Thu, 27 Feb 1997 23:38:19 +0000 (+0000) Subject: * elf32-mips.c (struct mips_got_info): Add assigned_gotno field. X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=85d6f0b476fd84375e4c50d02b02bf34321d0546;p=binutils-gdb.git * elf32-mips.c (struct mips_got_info): Add assigned_gotno field. (mips_elf_relocate_got_local): Change return type to boolean. Don't assume that the first zero entry is unassigned; instead, use assigned_gotno. (mips_elf_relocate_section): Check return value of mips_elf_relocate_got_local. (mips_elf_create_got_section): Initialize assigned_gotno field. --- diff --git a/bfd/ChangeLog b/bfd/ChangeLog index f38632321cb..36d7204d353 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,3 +1,13 @@ +Thu Feb 27 18:36:23 1997 Ian Lance Taylor + + * elf32-mips.c (struct mips_got_info): Add assigned_gotno field. + (mips_elf_relocate_got_local): Change return type to boolean. + Don't assume that the first zero entry is unassigned; instead, use + assigned_gotno. + (mips_elf_relocate_section): Check return value of + mips_elf_relocate_got_local. + (mips_elf_create_got_section): Initialize assigned_gotno field. + start-sanitize-d30v Wed Feb 26 15:19:51 1997 Martin M. Hunt diff --git a/bfd/elf32-mips.c b/bfd/elf32-mips.c index ec7d483e6a0..c45197fab00 100644 --- a/bfd/elf32-mips.c +++ b/bfd/elf32-mips.c @@ -75,13 +75,15 @@ static boolean mips_elf_final_link static void mips_elf_relocate_hi16 PARAMS ((bfd *, Elf_Internal_Rela *, Elf_Internal_Rela *, bfd_byte *, bfd_vma)); -static void mips_elf_relocate_got_local +static boolean mips_elf_relocate_got_local PARAMS ((bfd *, bfd *, asection *, Elf_Internal_Rela *, Elf_Internal_Rela *, bfd_byte *, bfd_vma)); static void mips_elf_relocate_global_got PARAMS ((bfd *, Elf_Internal_Rela *, bfd_byte *, bfd_vma)); static bfd_reloc_status_type mips16_jump_reloc PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **)); +static bfd_reloc_status_type mips16_gprel_reloc + PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **)); static boolean mips_elf_adjust_dynindx PARAMS ((struct elf_link_hash_entry *, PTR)); static boolean mips_elf_relocate_section @@ -134,6 +136,8 @@ struct mips_got_info unsigned long global_gotsym; /* The number of local .got entries. */ unsigned int local_gotno; + /* The number of local .got entries we have used. */ + unsigned int assigned_gotno; }; /* The number of local .got entries we reserve. */ @@ -306,8 +310,9 @@ enum reloc_type R_MIPS_HIGHER, R_MIPS_HIGHEST, R_MIPS_CALL_HI16, R_MIPS_CALL_LO16, R_MIPS_max, - /* This reloc is used for the mips16. */ - R_MIPS16_26 = 100 + /* These relocs are used for the mips16. */ + R_MIPS16_26 = 100, + R_MIPS16_GPREL = 101 }; static reloc_howto_type elf_mips_howto_table[] = @@ -712,6 +717,25 @@ static reloc_howto_type elf_mips16_jump_howto = 0x3ffffff, /* dst_mask */ false); /* pcrel_offset */ +/* The reloc used for the mips16 gprel instruction. The src_mask and + dsk_mask for this howto do not reflect the actual instruction, in + which the value is not contiguous; the masks are for the + convenience of the relocate_section routine. */ +static reloc_howto_type elf_mips16_gprel_howto = + HOWTO (R_MIPS16_GPREL, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + false, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_signed, /* complain_on_overflow */ + mips16_gprel_reloc, /* special_function */ + "R_MIPS16_GPREL", /* name */ + true, /* partial_inplace */ + 0xffff, /* src_mask */ + 0xffff, /* dst_mask */ + false); /* pcrel_offset */ + /* Do a R_MIPS_HI16 relocation. This has to be done in combination with a R_MIPS_LO16 reloc, because there is a carry from the LO16 to the HI16. Here we just save the information we need; we do the @@ -1343,6 +1367,82 @@ mips16_jump_reloc (abfd, reloc_entry, symbol, data, input_section, abort (); } +/* Handle a mips16 GP relative reloc. */ + +static bfd_reloc_status_type +mips16_gprel_reloc (abfd, reloc_entry, symbol, data, input_section, + output_bfd, error_message) + bfd *abfd; + arelent *reloc_entry; + asymbol *symbol; + PTR data; + asection *input_section; + bfd *output_bfd; + char **error_message; +{ + boolean relocateable; + bfd_reloc_status_type ret; + bfd_vma gp; + unsigned short extend, insn; + unsigned long final; + + /* If we're relocating, and this is an external symbol with no + addend, we don't want to change anything. We will only have an + addend if this is a newly created reloc, not read from an ELF + file. */ + if (output_bfd != NULL + && (symbol->flags & BSF_SECTION_SYM) == 0 + && reloc_entry->addend == 0) + { + reloc_entry->address += input_section->output_offset; + return bfd_reloc_ok; + } + + if (output_bfd != NULL) + relocateable = true; + else + { + relocateable = false; + output_bfd = symbol->section->output_section->owner; + } + + ret = mips_elf_final_gp (output_bfd, symbol, relocateable, error_message, + &gp); + if (ret != bfd_reloc_ok) + return ret; + + if (reloc_entry->address > input_section->_cooked_size) + return bfd_reloc_outofrange; + + /* Pick up the mips16 extend instruction and the real instruction. */ + extend = bfd_get_16 (abfd, (bfd_byte *) data + reloc_entry->address); + insn = bfd_get_16 (abfd, (bfd_byte *) data + reloc_entry->address + 2); + + /* Stuff the current addend back as a 32 bit value, do the usual + relocation, and then clean up. */ + bfd_put_32 (abfd, + (((extend & 0x1f) << 11) + | (extend & 0x7e0) + | (insn & 0x1f)), + (bfd_byte *) data + reloc_entry->address); + + ret = gprel16_with_gp (abfd, symbol, reloc_entry, input_section, + relocateable, data, gp); + + final = bfd_get_32 (abfd, (bfd_byte *) data + reloc_entry->address); + bfd_put_16 (abfd, + ((extend & 0xf800) + | ((final >> 11) & 0x1f) + | (final & 0x7e0)), + (bfd_byte *) data + reloc_entry->address); + bfd_put_16 (abfd, + ((insn & 0xffe0) + | (final & 0x1f)), + (bfd_byte *) data + reloc_entry->address + 2); + + return ret; +} + /* A mapping from BFD reloc types to MIPS ELF reloc types. */ struct elf_reloc_map { @@ -1387,10 +1487,12 @@ bfd_elf32_bfd_reloc_type_lookup (abfd, code) return &elf_mips_howto_table[(int) mips_reloc_map[i].elf_reloc_val]; } - /* Special handling for the MIPS16 jump, since it is a made up reloc - type with a large value. */ + /* Special handling for the MIPS16 relocs, since they are made up + reloc types with a large value. */ if (code == BFD_RELOC_MIPS16_JMP) return &elf_mips16_jump_howto; + else if (code == BFD_RELOC_MIPS16_GPREL) + return &elf_mips16_gprel_howto; return NULL; } @@ -1408,6 +1510,8 @@ mips_info_to_howto_rel (abfd, cache_ptr, dst) r_type = ELF32_R_TYPE (dst->r_info); if (r_type == R_MIPS16_26) cache_ptr->howto = &elf_mips16_jump_howto; + else if (r_type == R_MIPS16_GPREL) + cache_ptr->howto = &elf_mips16_gprel_howto; else { BFD_ASSERT (r_type < (unsigned int) R_MIPS_max); @@ -4299,7 +4403,7 @@ mips_elf_relocate_hi16 (input_bfd, relhi, rello, contents, addend) /* Handle a MIPS ELF local GOT16 reloc. */ -static void +static boolean mips_elf_relocate_got_local (output_bfd, input_bfd, sgot, relhi, rello, contents, addend) bfd *output_bfd; @@ -4310,8 +4414,8 @@ mips_elf_relocate_got_local (output_bfd, input_bfd, sgot, relhi, rello, bfd_byte *contents; bfd_vma addend; { - int local_gotno; - int i; + unsigned int assigned_gotno; + unsigned int i; bfd_vma insn; bfd_vma addlo; bfd_vma address; @@ -4336,33 +4440,36 @@ mips_elf_relocate_got_local (output_bfd, input_bfd, sgot, relhi, rello, g = (struct mips_got_info *) elf_section_data (sgot)->tdata; BFD_ASSERT (g != NULL); - local_gotno = g->local_gotno; + assigned_gotno = g->assigned_gotno; got_contents = sgot->contents; hipage = addend & 0xffff0000; - for (i = MIPS_RESERVED_GOTNO; i < local_gotno; i++) + for (i = MIPS_RESERVED_GOTNO; i < assigned_gotno; i++) { address = bfd_get_32 (input_bfd, got_contents + i * 4); if (hipage == (address & 0xffff0000)) break; - if (address == (bfd_vma) 0) + } + + if (i == assigned_gotno) + { + if (assigned_gotno >= g->local_gotno) { - bfd_put_32 (input_bfd, hipage, got_contents + i * 4); - break; + (*_bfd_error_handler) + ("more got entries are needed for hipage relocations"); + bfd_set_error (bfd_error_bad_value); + return false; } - } - BFD_ASSERT (i < local_gotno); -#if 1 - if (i == local_gotno) - (*_bfd_error_handler) - ("ELF MIPS linker: more got entries are needed for hipage: %x", - hipage); -#endif + bfd_put_32 (input_bfd, hipage, got_contents + assigned_gotno * 4); + ++g->assigned_gotno; + } i = - ELF_MIPS_GP_OFFSET (output_bfd) + i * 4; bfd_put_32 (input_bfd, (insn & 0xffff0000) | (i & 0xffff), contents + relhi->r_offset); + + return true; } /* Handle MIPS ELF CALL16 reloc and global GOT16 reloc. */ @@ -4446,15 +4553,19 @@ mips_elf_relocate_section (output_bfd, info, input_bfd, input_section, bfd_reloc_status_type r; r_type = ELF32_R_TYPE (rel->r_info); - if ((r_type < 0 || r_type >= (int) R_MIPS_max) && r_type != R_MIPS16_26) + if ((r_type < 0 || r_type >= (int) R_MIPS_max) + && r_type != R_MIPS16_26 + && r_type != R_MIPS16_GPREL) { bfd_set_error (bfd_error_bad_value); return false; } - if (r_type != R_MIPS16_26) - howto = elf_mips_howto_table + r_type; - else + if (r_type == R_MIPS16_26) howto = &elf_mips16_jump_howto; + else if (r_type == R_MIPS16_GPREL) + howto = &elf_mips16_gprel_howto; + else + howto = elf_mips_howto_table + r_type; if (dynobj != NULL && (r_type == R_MIPS_CALL16 @@ -4480,7 +4591,8 @@ mips_elf_relocate_section (output_bfd, info, input_bfd, input_section, /* Mix in the change in GP address for a GP relative reloc. */ if (r_type != R_MIPS_GPREL16 && r_type != R_MIPS_LITERAL - && r_type != R_MIPS_GPREL32) + && r_type != R_MIPS_GPREL32 + && r_type != R_MIPS16_GPREL) addend = 0; else { @@ -4833,10 +4945,11 @@ mips_elf_relocate_section (output_bfd, info, input_bfd, input_section, if ((rel + 1) < relend && ELF32_R_TYPE ((rel + 1)->r_info) == R_MIPS_LO16) { - mips_elf_relocate_got_local (output_bfd, input_bfd, sgot, - rel, rel + 1, - contents, - relocation + addend); + if (! mips_elf_relocate_got_local (output_bfd, input_bfd, + sgot, rel, rel + 1, + contents, + relocation + addend)) + return false; r = bfd_reloc_ok; } else @@ -5071,11 +5184,68 @@ mips_elf_relocate_section (output_bfd, info, input_bfd, input_section, bfd_put_16 (input_bfd, insn, contents + rel->r_offset); } } + else if (r_type == R_MIPS16_GPREL) + { + unsigned short extend, insn; + bfd_byte buf[4]; + unsigned long final; + + /* Extract the addend into buf, run the regular reloc, + and stuff the resulting value back into the + instructions. */ + if (rel->r_offset > input_section->_raw_size) + r = bfd_reloc_outofrange; + else + { + extend = bfd_get_16 (input_bfd, contents + rel->r_offset); + insn = bfd_get_16 (input_bfd, contents + rel->r_offset + 2); + bfd_put_32 (input_bfd, + (((extend & 0x1f) << 11) + | (extend & 0x7e0) + | (insn & 0x1f)), + buf); + r = _bfd_final_link_relocate (howto, input_bfd, + input_section, buf, + (bfd_vma) 0, relocation, + addend); + final = bfd_get_32 (input_bfd, buf); + bfd_put_16 (input_bfd, + ((extend & 0xf800) + | ((final >> 11) & 0x1f) + | (final & 0x7e0)), + contents + rel->r_offset); + bfd_put_16 (input_bfd, + ((insn & 0xffe0) + | (final & 0x1f)), + contents + rel->r_offset + 2); + } + } else r = _bfd_final_link_relocate (howto, input_bfd, input_section, contents, rel->r_offset, relocation, addend); + /* The jal instruction can only jump to an address which is + divisible by 4, and it can only jump to an address with + the same upper 4 bits as the PC. */ + if (r == bfd_reloc_ok + && (r_type == R_MIPS16_26 || r_type == R_MIPS_26)) + { + bfd_vma addr; + + addr = relocation; + if (other == STO_MIPS16) + addr &= ~ (bfd_vma) 1; + addr += addend; + if ((addr & 3) != 0 + || ((addr & 0xf0000000) + != ((input_section->output_section->vma + + input_section->output_offset + + rel->r_offset) + & 0xf0000000))) + r = bfd_reloc_overflow; + } + if (SGI_COMPAT (abfd) && scpt != NULL && (input_section->flags & SEC_ALLOC) != 0) @@ -5396,6 +5566,7 @@ mips_elf_create_got_section (abfd, info) return false; g->global_gotsym = 0; g->local_gotno = MIPS_RESERVED_GOTNO; + g->assigned_gotno = MIPS_RESERVED_GOTNO; if (elf_section_data (s) == NULL) { s->used_by_bfd =