diff options
Diffstat (limited to 'bfd')
-rw-r--r-- | bfd/ChangeLog | 57 | ||||
-rw-r--r-- | bfd/bfd-in2.h | 5 | ||||
-rw-r--r-- | bfd/elf32-mips.c | 34 | ||||
-rw-r--r-- | bfd/elf64-mips.c | 66 | ||||
-rw-r--r-- | bfd/elfn32-mips.c | 66 | ||||
-rw-r--r-- | bfd/elfxx-mips.c | 348 | ||||
-rw-r--r-- | bfd/libbfd.h | 2 | ||||
-rw-r--r-- | bfd/reloc.c | 7 |
8 files changed, 488 insertions, 97 deletions
diff --git a/bfd/ChangeLog b/bfd/ChangeLog index 1bcbc40..ead15cd 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,3 +1,60 @@ +2008-08-06 Richard Sandiford <rdsandiford@googlemail.com> + + * reloc.c (BFD_RELOC_MIPS16_GOT16, BFD_RELOC_MIPS16_CALL16): Declare. + * libbfd.h, bfd-in2.h: Regenerate. + * elf32-mips.c (elf_mips16_howto_table_rel): Fill in reserved + R_MIPS16_GOT16 and R_MIPS16_CALL16 entries. + (mips16_reloc_map): Add mappings. + * elf64-mips.c (mips16_elf64_howto_table_rel): Fill in reserved + R_MIPS16_GOT16 and R_MIPS16_CALL16 entries. + (mips16_elf64_howto_table_rela): Likewise. + (mips16_reloc_map): Add mappings. + * elfn32-mips.c (elf_mips16_howto_table_rel): Fill in reserved + R_MIPS16_GOT16 and R_MIPS16_CALL16 entries. + (elf_mips16_howto_table_rela): Likewise. + (mips16_reloc_map): Add mappings. + * elfxx-mips.c (mips_elf_create_shadow_symbol): New function. + (section_allows_mips16_refs_p): Likewise. + (mips16_stub_symndx): Likewise. + (mips_elf_check_mips16_stubs): Treat the data argument as a + bfd_link_info. Mark every dynamic symbol as needing MIPS16 stubs + and create a "shadow" symbol for the original MIPS16 definition. + (mips16_reloc_p, got16_reloc_p, call16_reloc_p, hi16_reloc_p) + (lo16_reloc_p, mips16_call_reloc_p): New functions. + (_bfd_mips16_elf_reloc_unshuffle): Use mips16_reloc_p to generalize + relocation checks. + (_bfd_mips16_elf_reloc_shuffle): Likewise. + (_bfd_mips_elf_lo16_reloc): Handle R_MIPS16_GOT16. + (mips_elf_got16_entry): Add comment. + (mips_elf_calculate_relocation): Use hi16_reloc_p, + lo16_reloc_p, mips16_call_reloc_p, call16_reloc_p and got16_reloc_p + to generalize relocation checks. Use section_allows_mips16_refs_p + instead of mips16_stub_section_p. Handle R_MIPS16_CALL16 and + R_MIPS16_GOT16, allowing the former to refer directly to a + MIPS16 function if its stub is not needed. + (mips16_stub_section_p): Delete. + (_bfd_mips_elf_symbol_processing): Convert odd-valued function + symbols into even MIPS16 symbols. + (mips_elf_add_lo16_rel_addend): Use mips16_reloc_p to generalize + a relocation check. + (_bfd_mips_elf_check_relocs): Calculate "bed" and "rel_end" + earlier in the function. Use mips16_stub_symndx to identify + the target function. Avoid out-of-bounds accesses when the + stub has no relocations; report an error instead. Use + section_allows_mips16_refs_p instead of mips16_stub_section_p. + Use mips16_call_reloc_p and got16_reloc_p to generalize relocation + checks. Handle R_MIPS16_CALL16 and R_MIPS16_GOT16. Don't create + dynamic relocations for absolute references to __gnu_local_gp. + (_bfd_mips_elf_always_size_sections): Pass a bfd_link_info as + the argument to mips_elf_check_mips16_stubs. Generalize comment. + (_bfd_mips_elf_relocate_section): Use hi16_reloc_p and got16_reloc_p + to generalize relocation checks. + (_bfd_mips_elf_finish_dynamic_symbol): If a dynamic MIPS16 function + symbol has a non-MIPS16 stub, redirect the symbol to the stub. + Fix an overly long line. Don't give dynamic symbols type STO_MIPS16. + (_bfd_mips_elf_gc_sweep_hook): Handle R_MIPS16_CALL16 and + R_MIPS16_GOT16. + 2008-08-06 Alan Modra <amodra@bigpond.net.au> * elf32-ppc.c (ppc_elf_relax_section): Clear R_PPC_PLTREL24 addend. diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h index f8e34ac..edd0e2a 100644 --- a/bfd/bfd-in2.h +++ b/bfd/bfd-in2.h @@ -2604,6 +2604,11 @@ to compensate for the borrow when the low bits are added. */ /* Low 16 bits of pc-relative value */ BFD_RELOC_LO16_PCREL, +/* Equivalent of BFD_RELOC_MIPS_*, but with the MIPS16 layout of +16-bit immediate fields */ + BFD_RELOC_MIPS16_GOT16, + BFD_RELOC_MIPS16_CALL16, + /* MIPS16 high 16 bits of 32-bit value. */ BFD_RELOC_MIPS16_HI16, diff --git a/bfd/elf32-mips.c b/bfd/elf32-mips.c index 3d39dc9..8169991 100644 --- a/bfd/elf32-mips.c +++ b/bfd/elf32-mips.c @@ -771,11 +771,35 @@ static reloc_howto_type elf_mips16_howto_table_rel[] = 0x0000ffff, /* dst_mask */ FALSE), /* pcrel_offset */ - /* A placeholder for MIPS16 reference to global offset table. */ - EMPTY_HOWTO (R_MIPS16_GOT16), + /* A MIPS16 reference to the global offset table. */ + HOWTO (R_MIPS16_GOT16, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont, /* complain_on_overflow */ + _bfd_mips_elf_got16_reloc, /* special_function */ + "R_MIPS16_GOT16", /* name */ + TRUE, /* partial_inplace */ + 0x0000ffff, /* src_mask */ + 0x0000ffff, /* dst_mask */ + FALSE), /* pcrel_offset */ - /* A placeholder for MIPS16 16 bit call through global offset table. */ - EMPTY_HOWTO (R_MIPS16_CALL16), + /* A MIPS16 call through the global offset table. */ + HOWTO (R_MIPS16_CALL16, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont, /* complain_on_overflow */ + _bfd_mips_elf_generic_reloc, /* special_function */ + "R_MIPS16_CALL16", /* name */ + TRUE, /* partial_inplace */ + 0x0000ffff, /* src_mask */ + 0x0000ffff, /* dst_mask */ + FALSE), /* pcrel_offset */ /* MIPS16 high 16 bits of symbol value. */ HOWTO (R_MIPS16_HI16, /* type */ @@ -1224,6 +1248,8 @@ static const struct elf_reloc_map mips16_reloc_map[] = { { BFD_RELOC_MIPS16_JMP, R_MIPS16_26 - R_MIPS16_min }, { BFD_RELOC_MIPS16_GPREL, R_MIPS16_GPREL - R_MIPS16_min }, + { BFD_RELOC_MIPS16_GOT16, R_MIPS16_GOT16 - R_MIPS16_min }, + { BFD_RELOC_MIPS16_CALL16, R_MIPS16_CALL16 - R_MIPS16_min }, { BFD_RELOC_MIPS16_HI16_S, R_MIPS16_HI16 - R_MIPS16_min }, { BFD_RELOC_MIPS16_LO16, R_MIPS16_LO16 - R_MIPS16_min }, }; diff --git a/bfd/elf64-mips.c b/bfd/elf64-mips.c index e8e00fb..520fafe 100644 --- a/bfd/elf64-mips.c +++ b/bfd/elf64-mips.c @@ -1503,11 +1503,35 @@ static reloc_howto_type mips16_elf64_howto_table_rel[] = 0x0000ffff, /* dst_mask */ FALSE), /* pcrel_offset */ - /* A placeholder for MIPS16 reference to global offset table. */ - EMPTY_HOWTO (R_MIPS16_GOT16), + /* A MIPS16 reference to the global offset table. */ + HOWTO (R_MIPS16_GOT16, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont, /* complain_on_overflow */ + _bfd_mips_elf_got16_reloc, /* special_function */ + "R_MIPS16_GOT16", /* name */ + TRUE, /* partial_inplace */ + 0x0000ffff, /* src_mask */ + 0x0000ffff, /* dst_mask */ + FALSE), /* pcrel_offset */ - /* A placeholder for MIPS16 16 bit call through global offset table. */ - EMPTY_HOWTO (R_MIPS16_CALL16), + /* A MIPS16 call through the global offset table. */ + HOWTO (R_MIPS16_CALL16, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont, /* complain_on_overflow */ + _bfd_mips_elf_generic_reloc, /* special_function */ + "R_MIPS16_CALL16", /* name */ + TRUE, /* partial_inplace */ + 0x0000ffff, /* src_mask */ + 0x0000ffff, /* dst_mask */ + FALSE), /* pcrel_offset */ /* MIPS16 high 16 bits of symbol value. */ HOWTO (R_MIPS16_HI16, /* type */ @@ -1575,11 +1599,35 @@ static reloc_howto_type mips16_elf64_howto_table_rela[] = 0x0000ffff, /* dst_mask */ FALSE), /* pcrel_offset */ - /* A placeholder for MIPS16 reference to global offset table. */ - EMPTY_HOWTO (R_MIPS16_GOT16), + /* A MIPS16 reference to the global offset table. */ + HOWTO (R_MIPS16_GOT16, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont, /* complain_on_overflow */ + _bfd_mips_elf_got16_reloc, /* special_function */ + "R_MIPS16_GOT16", /* name */ + FALSE, /* partial_inplace */ + 0x0000ffff, /* src_mask */ + 0x0000ffff, /* dst_mask */ + FALSE), /* pcrel_offset */ - /* A placeholder for MIPS16 16 bit call through global offset table. */ - EMPTY_HOWTO (R_MIPS16_CALL16), + /* A MIPS16 call through the global offset table. */ + HOWTO (R_MIPS16_CALL16, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont, /* complain_on_overflow */ + _bfd_mips_elf_generic_reloc, /* special_function */ + "R_MIPS16_CALL16", /* name */ + FALSE, /* partial_inplace */ + 0x0000ffff, /* src_mask */ + 0x0000ffff, /* dst_mask */ + FALSE), /* pcrel_offset */ /* MIPS16 high 16 bits of symbol value. */ HOWTO (R_MIPS16_HI16, /* type */ @@ -2197,6 +2245,8 @@ static const struct elf_reloc_map mips16_reloc_map[] = { { BFD_RELOC_MIPS16_JMP, R_MIPS16_26 - R_MIPS16_min }, { BFD_RELOC_MIPS16_GPREL, R_MIPS16_GPREL - R_MIPS16_min }, + { BFD_RELOC_MIPS16_GOT16, R_MIPS16_GOT16 - R_MIPS16_min }, + { BFD_RELOC_MIPS16_CALL16, R_MIPS16_CALL16 - R_MIPS16_min }, { BFD_RELOC_MIPS16_HI16_S, R_MIPS16_HI16 - R_MIPS16_min }, { BFD_RELOC_MIPS16_LO16, R_MIPS16_LO16 - R_MIPS16_min }, }; diff --git a/bfd/elfn32-mips.c b/bfd/elfn32-mips.c index 74133c5..2a2d59e 100644 --- a/bfd/elfn32-mips.c +++ b/bfd/elfn32-mips.c @@ -1496,11 +1496,35 @@ static reloc_howto_type elf_mips16_howto_table_rel[] = 0x0000ffff, /* dst_mask */ FALSE), /* pcrel_offset */ - /* A placeholder for MIPS16 reference to global offset table. */ - EMPTY_HOWTO (R_MIPS16_GOT16), + /* A MIPS16 reference to the global offset table. */ + HOWTO (R_MIPS16_GOT16, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont, /* complain_on_overflow */ + _bfd_mips_elf_got16_reloc, /* special_function */ + "R_MIPS16_GOT16", /* name */ + TRUE, /* partial_inplace */ + 0x0000ffff, /* src_mask */ + 0x0000ffff, /* dst_mask */ + FALSE), /* pcrel_offset */ - /* A placeholder for MIPS16 16 bit call through global offset table. */ - EMPTY_HOWTO (R_MIPS16_CALL16), + /* A MIPS16 call through the global offset table. */ + HOWTO (R_MIPS16_CALL16, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont, /* complain_on_overflow */ + _bfd_mips_elf_generic_reloc, /* special_function */ + "R_MIPS16_CALL16", /* name */ + TRUE, /* partial_inplace */ + 0x0000ffff, /* src_mask */ + 0x0000ffff, /* dst_mask */ + FALSE), /* pcrel_offset */ /* MIPS16 high 16 bits of symbol value. */ HOWTO (R_MIPS16_HI16, /* type */ @@ -1568,11 +1592,35 @@ static reloc_howto_type elf_mips16_howto_table_rela[] = 0x0000ffff, /* dst_mask */ FALSE), /* pcrel_offset */ - /* A placeholder for MIPS16 reference to global offset table. */ - EMPTY_HOWTO (R_MIPS16_GOT16), + /* A MIPS16 reference to the global offset table. */ + HOWTO (R_MIPS16_GOT16, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont, /* complain_on_overflow */ + _bfd_mips_elf_got16_reloc, /* special_function */ + "R_MIPS16_GOT16", /* name */ + FALSE, /* partial_inplace */ + 0x0000ffff, /* src_mask */ + 0x0000ffff, /* dst_mask */ + FALSE), /* pcrel_offset */ - /* A placeholder for MIPS16 16 bit call through global offset table. */ - EMPTY_HOWTO (R_MIPS16_CALL16), + /* A MIPS16 call through the global offset table. */ + HOWTO (R_MIPS16_CALL16, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont, /* complain_on_overflow */ + _bfd_mips_elf_generic_reloc, /* special_function */ + "R_MIPS16_CALL16", /* name */ + FALSE, /* partial_inplace */ + 0x0000ffff, /* src_mask */ + 0x0000ffff, /* dst_mask */ + FALSE), /* pcrel_offset */ /* MIPS16 high 16 bits of symbol value. */ HOWTO (R_MIPS16_HI16, /* type */ @@ -2041,6 +2089,8 @@ static const struct elf_reloc_map mips16_reloc_map[] = { { BFD_RELOC_MIPS16_JMP, R_MIPS16_26 - R_MIPS16_min }, { BFD_RELOC_MIPS16_GPREL, R_MIPS16_GPREL - R_MIPS16_min }, + { BFD_RELOC_MIPS16_GOT16, R_MIPS16_GOT16 - R_MIPS16_min }, + { BFD_RELOC_MIPS16_CALL16, R_MIPS16_CALL16 - R_MIPS16_min }, { BFD_RELOC_MIPS16_HI16_S, R_MIPS16_HI16 - R_MIPS16_min }, { BFD_RELOC_MIPS16_LO16, R_MIPS16_LO16 - R_MIPS16_min }, }; diff --git a/bfd/elfxx-mips.c b/bfd/elfxx-mips.c index 827915c..00f33bd 100644 --- a/bfd/elfxx-mips.c +++ b/bfd/elfxx-mips.c @@ -525,8 +525,6 @@ static bfd_boolean mips_elf_sort_hash_table_f (struct mips_elf_link_hash_entry *, void *); static bfd_vma mips_elf_high (bfd_vma); -static bfd_boolean mips16_stub_section_p - (bfd *, asection *); static bfd_boolean mips_elf_create_dynamic_relocation (bfd *, struct bfd_link_info *, const Elf_Internal_Rela *, struct mips_elf_link_hash_entry *, asection *, bfd_vma, @@ -1148,17 +1146,104 @@ mips_elf_create_procedure_table (void *handle, bfd *abfd, free (sv); return FALSE; } + +/* We're about to redefine H. Create a symbol to represent H's + current value and size, to help make the disassembly easier + to read. */ + +static bfd_boolean +mips_elf_create_shadow_symbol (struct bfd_link_info *info, + struct mips_elf_link_hash_entry *h, + const char *prefix) +{ + struct bfd_link_hash_entry *bh; + struct elf_link_hash_entry *elfh; + const char *name; + asection *s; + bfd_vma value; + + /* Read the symbol's value. */ + BFD_ASSERT (h->root.root.type == bfd_link_hash_defined + || h->root.root.type == bfd_link_hash_defweak); + s = h->root.root.u.def.section; + value = h->root.root.u.def.value; + + /* Create a new symbol. */ + name = ACONCAT ((prefix, h->root.root.root.string, NULL)); + bh = NULL; + if (!_bfd_generic_link_add_one_symbol (info, s->owner, name, + BSF_LOCAL, s, value, NULL, + TRUE, FALSE, &bh)) + return FALSE; + + /* Make it local and copy the other attributes from H. */ + elfh = (struct elf_link_hash_entry *) bh; + elfh->type = ELF_ST_INFO (STB_LOCAL, ELF_ST_TYPE (h->root.type)); + elfh->other = h->root.other; + elfh->size = h->root.size; + elfh->forced_local = 1; + return TRUE; +} + +/* Return TRUE if relocations in SECTION can refer directly to a MIPS16 + function rather than to a hard-float stub. */ + +static bfd_boolean +section_allows_mips16_refs_p (asection *section) +{ + const char *name; + + name = bfd_get_section_name (section->owner, section); + return (FN_STUB_P (name) + || CALL_STUB_P (name) + || CALL_FP_STUB_P (name) + || strcmp (name, ".pdr") == 0); +} + +/* [RELOCS, RELEND) are the relocations against SEC, which is a MIPS16 + stub section of some kind. Return the R_SYMNDX of the target + function, or 0 if we can't decide which function that is. */ + +static unsigned long +mips16_stub_symndx (asection *sec, const Elf_Internal_Rela *relocs, + const Elf_Internal_Rela *relend) +{ + const Elf_Internal_Rela *rel; + + /* Trust the first R_MIPS_NONE relocation, if any. */ + for (rel = relocs; rel < relend; rel++) + if (ELF_R_TYPE (sec->owner, rel->r_info) == R_MIPS_NONE) + return ELF_R_SYM (sec->owner, rel->r_info); + + /* Otherwise trust the first relocation, whatever its kind. This is + the traditional behavior. */ + if (relocs < relend) + return ELF_R_SYM (sec->owner, relocs->r_info); + + return 0; +} /* Check the mips16 stubs for a particular symbol, and see if we can discard them. */ static bfd_boolean -mips_elf_check_mips16_stubs (struct mips_elf_link_hash_entry *h, - void *data ATTRIBUTE_UNUSED) +mips_elf_check_mips16_stubs (struct mips_elf_link_hash_entry *h, void *data) { + struct bfd_link_info *info; + + info = (struct bfd_link_info *) data; if (h->root.root.type == bfd_link_hash_warning) h = (struct mips_elf_link_hash_entry *) h->root.root.u.i.link; + /* Dynamic symbols must use the standard call interface, in case other + objects try to call them. */ + if (h->fn_stub != NULL + && h->root.dynindx != -1) + { + mips_elf_create_shadow_symbol (info, h, ".mips16."); + h->need_fn_stub = TRUE; + } + if (h->fn_stub != NULL && ! h->need_fn_stub) { @@ -1257,8 +1342,18 @@ mips_elf_check_mips16_stubs (struct mips_elf_link_hash_entry *h, let R = (((A < 2) | ((P + 4) & 0xf0000000) + S) >> 2) ((R & 0x1f0000) << 5) | ((R & 0x3e00000) >> 5) | (R & 0xffff) - R_MIPS16_GPREL is used for GP-relative addressing in mips16 - mode. A typical instruction will have a format like this: + The table below lists the other MIPS16 instruction relocations. + Each one is calculated in the same way as the non-MIPS16 relocation + given on the right, but using the extended MIPS16 layout of 16-bit + immediate fields: + + R_MIPS16_GPREL R_MIPS_GPREL16 + R_MIPS16_GOT16 R_MIPS_GOT16 + R_MIPS16_CALL16 R_MIPS_CALL16 + R_MIPS16_HI16 R_MIPS_HI16 + R_MIPS16_LO16 R_MIPS_LO16 + + A typical instruction will have a format like this: +--------------+--------------------------------+ | EXTEND | Imm 10:5 | Imm 15:11 | @@ -1269,28 +1364,65 @@ mips_elf_check_mips16_stubs (struct mips_elf_link_hash_entry *h, EXTEND is the five bit value 11110. Major is the instruction opcode. - This is handled exactly like R_MIPS_GPREL16, except that the - addend is retrieved and stored as shown in this diagram; that - is, the Imm fields above replace the V-rel16 field. + All we need to do here is shuffle the bits appropriately. + As above, the two 16-bit halves must be swapped on a + little-endian system. */ + +static inline bfd_boolean +mips16_reloc_p (int r_type) +{ + switch (r_type) + { + case R_MIPS16_26: + case R_MIPS16_GPREL: + case R_MIPS16_GOT16: + case R_MIPS16_CALL16: + case R_MIPS16_HI16: + case R_MIPS16_LO16: + return TRUE; + + default: + return FALSE; + } +} + +static inline bfd_boolean +got16_reloc_p (int r_type) +{ + return r_type == R_MIPS_GOT16 || r_type == R_MIPS16_GOT16; +} + +static inline bfd_boolean +call16_reloc_p (int r_type) +{ + return r_type == R_MIPS_CALL16 || r_type == R_MIPS16_CALL16; +} + +static inline bfd_boolean +hi16_reloc_p (int r_type) +{ + return r_type == R_MIPS_HI16 || r_type == R_MIPS16_HI16; +} - All we need to do here is shuffle the bits appropriately. As - above, the two 16-bit halves must be swapped on a - little-endian system. +static inline bfd_boolean +lo16_reloc_p (int r_type) +{ + return r_type == R_MIPS_LO16 || r_type == R_MIPS16_LO16; +} + +static inline bfd_boolean +mips16_call_reloc_p (int r_type) +{ + return r_type == R_MIPS16_26 || r_type == R_MIPS16_CALL16; +} - R_MIPS16_HI16 and R_MIPS16_LO16 are used in mips16 mode to - access data when neither GP-relative nor PC-relative addressing - can be used. They are handled like R_MIPS_HI16 and R_MIPS_LO16, - except that the addend is retrieved and stored as shown above - for R_MIPS16_GPREL. - */ void _bfd_mips16_elf_reloc_unshuffle (bfd *abfd, int r_type, bfd_boolean jal_shuffle, bfd_byte *data) { bfd_vma extend, insn, val; - if (r_type != R_MIPS16_26 && r_type != R_MIPS16_GPREL - && r_type != R_MIPS16_HI16 && r_type != R_MIPS16_LO16) + if (!mips16_reloc_p (r_type)) return; /* Pick up the mips16 extend instruction and the real instruction. */ @@ -1316,8 +1448,7 @@ _bfd_mips16_elf_reloc_shuffle (bfd *abfd, int r_type, { bfd_vma extend, insn, val; - if (r_type != R_MIPS16_26 && r_type != R_MIPS16_GPREL - && r_type != R_MIPS16_HI16 && r_type != R_MIPS16_LO16) + if (!mips16_reloc_p (r_type)) return; val = bfd_get_32 (abfd, data); @@ -1446,7 +1577,7 @@ _bfd_mips_elf_hi16_reloc (bfd *abfd ATTRIBUTE_UNUSED, arelent *reloc_entry, return bfd_reloc_ok; } -/* A howto special_function for REL R_MIPS_GOT16 relocations. This is just +/* A howto special_function for REL R_MIPS*_GOT16 relocations. This is just like any other 16-bit relocation when applied to global symbols, but is treated in the same as R_MIPS_HI16 when applied to local symbols. */ @@ -1495,13 +1626,15 @@ _bfd_mips_elf_lo16_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol, hi = mips_hi16_list; - /* R_MIPS_GOT16 relocations are something of a special case. We - want to install the addend in the same way as for a R_MIPS_HI16 + /* R_MIPS*_GOT16 relocations are something of a special case. We + want to install the addend in the same way as for a R_MIPS*_HI16 relocation (with a rightshift of 16). However, since GOT16 relocations can also be used with global symbols, their howto has a rightshift of 0. */ if (hi->rel.howto->type == R_MIPS_GOT16) hi->rel.howto = MIPS_ELF_RTYPE_TO_HOWTO (abfd, R_MIPS_HI16, FALSE); + else if (hi->rel.howto->type == R_MIPS16_GOT16) + hi->rel.howto = MIPS_ELF_RTYPE_TO_HOWTO (abfd, R_MIPS16_HI16, FALSE); /* VALLO is a signed 16-bit number. Bias it by 0x8000 so that any carry or borrow will induce a change of +1 or -1 in the high part. */ @@ -2620,7 +2753,7 @@ mips_elf_got_page (bfd *abfd, bfd *ibfd, struct bfd_link_info *info, return index; } -/* Find a local GOT entry for an R_MIPS_GOT16 relocation against VALUE. +/* Find a local GOT entry for an R_MIPS*_GOT16 relocation against VALUE. EXTERNAL is true if the relocation was against a global symbol that has been forced local. */ @@ -2641,6 +2774,9 @@ mips_elf_got16_entry (bfd *abfd, bfd *ibfd, struct bfd_link_info *info, g = mips_elf_got_info (elf_hash_table (info)->dynobj, &sgot); + /* It doesn't matter whether the original relocation was R_MIPS_GOT16, + R_MIPS16_GOT16, R_MIPS_CALL16, etc. The format of the entry is the + same in all cases. */ entry = mips_elf_create_local_got_entry (abfd, info, ibfd, g, sgot, value, 0, NULL, R_MIPS_GOT16); if (entry) @@ -4216,8 +4352,7 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd, { /* Relocations against _gp_disp are permitted only with R_MIPS_HI16 and R_MIPS_LO16 relocations. */ - if (r_type != R_MIPS_HI16 && r_type != R_MIPS_LO16 - && r_type != R_MIPS16_HI16 && r_type != R_MIPS16_LO16) + if (!hi16_reloc_p (r_type) && !lo16_reloc_p (r_type)) return bfd_reloc_notsupported; gp_disp_p = TRUE; @@ -4291,15 +4426,24 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd, target_is_16_bit_code_p = ELF_ST_IS_MIPS16 (h->root.other); } - /* If this is a 32- or 64-bit call to a 16-bit function with a stub, we - need to redirect the call to the stub, unless we're already *in* - a stub. */ - if (r_type != R_MIPS16_26 && !info->relocatable - && ((h != NULL && h->fn_stub != NULL) + /* If this is a reference to a 16-bit function with a stub, we need + to redirect the relocation to the stub unless: + + (a) the relocation is for a MIPS16 JAL; + + (b) the relocation is for a MIPS16 PIC call, and there are no + non-MIPS16 uses of the GOT slot; or + + (c) the section allows direct references to MIPS16 functions. */ + if (r_type != R_MIPS16_26 + && !info->relocatable + && ((h != NULL + && h->fn_stub != NULL + && (r_type != R_MIPS16_CALL16 || h->need_fn_stub)) || (local_p && elf_tdata (input_bfd)->local_stubs != NULL && elf_tdata (input_bfd)->local_stubs[r_symndx] != NULL)) - && !mips16_stub_section_p (input_bfd, input_section)) + && !section_allows_mips16_refs_p (input_section)) { /* This is a 32- or 64-bit call to a 16-bit function. We should have already noticed that we were going to need the @@ -4317,7 +4461,9 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd, target_is_16_bit_code_p = FALSE; } /* If this is a 16-bit call to a 32- or 64-bit function with a stub, we - need to redirect the call to the stub. */ + need to redirect the call to the stub. Note that we specifically + exclude R_MIPS16_CALL16 from this behavior; indirect calls should + use an indirect stub instead. */ else if (r_type == R_MIPS16_26 && !info->relocatable && ((h != NULL && (h->call_stub != NULL || h->call_fp_stub != NULL)) || (local_p @@ -4389,6 +4535,8 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd, break; /* Fall through. */ + case R_MIPS16_CALL16: + case R_MIPS16_GOT16: case R_MIPS_CALL16: case R_MIPS_GOT16: case R_MIPS_GOT_DISP: @@ -4414,7 +4562,7 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd, if (htab->is_vxworks && (r_type == R_MIPS_CALL_HI16 || r_type == R_MIPS_CALL_LO16 - || r_type == R_MIPS_CALL16)) + || call16_reloc_p (r_type))) { BFD_ASSERT (addend == 0); BFD_ASSERT (h->root.needs_plt); @@ -4444,7 +4592,7 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd, } } else if (!htab->is_vxworks - && (r_type == R_MIPS_CALL16 || (r_type == R_MIPS_GOT16))) + && (call16_reloc_p (r_type) || got16_reloc_p (r_type))) /* The calculation below does not involve "g". */ break; else @@ -4674,10 +4822,12 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd, overflowed_p = mips_elf_overflow_p (value, 16); break; + case R_MIPS16_GOT16: + case R_MIPS16_CALL16: case R_MIPS_GOT16: case R_MIPS_CALL16: /* VxWorks does not have separate local and global semantics for - R_MIPS_GOT16; every relocation evaluates to "G". */ + R_MIPS*_GOT16; every relocation evaluates to "G". */ if (!htab->is_vxworks && local_p) { bfd_boolean forced; @@ -4921,16 +5071,6 @@ mips_elf_perform_relocation (struct bfd_link_info *info, return TRUE; } - -/* Returns TRUE if SECTION is a MIPS16 stub section. */ - -static bfd_boolean -mips16_stub_section_p (bfd *abfd ATTRIBUTE_UNUSED, asection *section) -{ - const char *name = bfd_get_section_name (abfd, section); - - return FN_STUB_P (name) || CALL_STUB_P (name) || CALL_FP_STUB_P (name); -} /* Add room for N relocations to the .rel(a).dyn section in ABFD. */ @@ -5312,14 +5452,14 @@ static asection mips_elf_acom_section; static asymbol mips_elf_acom_symbol; static asymbol *mips_elf_acom_symbol_ptr; -/* Handle the special MIPS section numbers that a symbol may use. - This is used for both the 32-bit and the 64-bit ABI. */ +/* This is used for both the 32-bit and the 64-bit ABI. */ void _bfd_mips_elf_symbol_processing (bfd *abfd, asymbol *asym) { elf_symbol_type *elfsym; + /* Handle the special MIPS section numbers that a symbol may use. */ elfsym = (elf_symbol_type *) asym; switch (elfsym->internal_elf_sym.st_shndx) { @@ -5407,6 +5547,15 @@ _bfd_mips_elf_symbol_processing (bfd *abfd, asymbol *asym) } break; } + + /* If this is an odd-valued function symbol, assume it's a MIPS16 one. */ + if (ELF_ST_TYPE (elfsym->internal_elf_sym.st_info) == STT_FUNC + && (asym->value & 1) != 0) + { + asym->value--; + elfsym->internal_elf_sym.st_other + = ELF_ST_SET_MIPS16 (elfsym->internal_elf_sym.st_other); + } } /* Implement elf_backend_eh_frame_address_size. This differs from @@ -6401,7 +6550,7 @@ mips_elf_add_lo16_rel_addend (bfd *abfd, bfd_vma l; r_type = ELF_R_TYPE (abfd, rel->r_info); - if (r_type == R_MIPS16_HI16) + if (mips16_reloc_p (r_type)) lo16_type = R_MIPS16_LO16; else lo16_type = R_MIPS_LO16; @@ -6491,6 +6640,9 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, sym_hashes = elf_sym_hashes (abfd); extsymoff = (elf_bad_symtab (abfd)) ? 0 : symtab_hdr->sh_info; + bed = get_elf_backend_data (abfd); + rel_end = relocs + sec->reloc_count * bed->s->int_rels_per_ext_rel; + /* Check for the mips16 stub sections. */ name = bfd_get_section_name (abfd, sec); @@ -6501,7 +6653,16 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, /* Look at the relocation information to figure out which symbol this is for. */ - r_symndx = ELF_R_SYM (abfd, relocs->r_info); + r_symndx = mips16_stub_symndx (sec, relocs, rel_end); + if (r_symndx == 0) + { + (*_bfd_error_handler) + (_("%B: Warning: cannot determine the target function for" + " stub section `%s'"), + abfd, name); + bfd_set_error (bfd_error_bad_value); + return FALSE; + } if (r_symndx < extsymoff || sym_hashes[r_symndx - extsymoff] == NULL) @@ -6519,7 +6680,7 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, /* We can ignore stub sections when looking for relocs. */ if ((o->flags & SEC_RELOC) == 0 || o->reloc_count == 0 - || mips16_stub_section_p (abfd, o)) + || section_allows_mips16_refs_p (o)) continue; sec_relocs @@ -6531,7 +6692,7 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, rend = sec_relocs + o->reloc_count; for (r = sec_relocs; r < rend; r++) if (ELF_R_SYM (abfd, r->r_info) == r_symndx - && ELF_R_TYPE (abfd, r->r_info) != R_MIPS16_26) + && !mips16_call_reloc_p (ELF_R_TYPE (abfd, r->r_info))) break; if (elf_section_data (o)->relocs != sec_relocs) @@ -6617,7 +6778,16 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, /* Look at the relocation information to figure out which symbol this is for. */ - r_symndx = ELF_R_SYM (abfd, relocs->r_info); + r_symndx = mips16_stub_symndx (sec, relocs, rel_end); + if (r_symndx == 0) + { + (*_bfd_error_handler) + (_("%B: Warning: cannot determine the target function for" + " stub section `%s'"), + abfd, name); + bfd_set_error (bfd_error_bad_value); + return FALSE; + } if (r_symndx < extsymoff || sym_hashes[r_symndx - extsymoff] == NULL) @@ -6635,7 +6805,7 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, /* We can ignore stub sections when looking for relocs. */ if ((o->flags & SEC_RELOC) == 0 || o->reloc_count == 0 - || mips16_stub_section_p (abfd, o)) + || section_allows_mips16_refs_p (o)) continue; sec_relocs @@ -6743,8 +6913,6 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, } sreloc = NULL; - bed = get_elf_backend_data (abfd); - rel_end = relocs + sec->reloc_count * bed->s->int_rels_per_ext_rel; contents = NULL; for (rel = relocs; rel < rel_end; ++rel) { @@ -6782,6 +6950,8 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, { switch (r_type) { + case R_MIPS16_GOT16: + case R_MIPS16_CALL16: case R_MIPS_GOT16: case R_MIPS_CALL16: case R_MIPS_CALL_HI16: @@ -6851,13 +7021,13 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, else if (r_type == R_MIPS_CALL_LO16 || r_type == R_MIPS_GOT_LO16 || r_type == R_MIPS_GOT_DISP - || (r_type == R_MIPS_GOT16 && htab->is_vxworks)) + || (got16_reloc_p (r_type) && htab->is_vxworks)) { /* We may need a local GOT entry for this relocation. We don't count R_MIPS_GOT_PAGE because we can estimate the maximum number of pages needed by looking at the size of - the segment. Similar comments apply to R_MIPS_GOT16 and - R_MIPS_CALL16, except on VxWorks, where GOT relocations + the segment. Similar comments apply to R_MIPS*_GOT16 and + R_MIPS*_CALL16, except on VxWorks, where GOT relocations always evaluate to "G". We don't count R_MIPS_GOT_HI16, or R_MIPS_CALL_HI16 because these are always followed by an R_MIPS_GOT_LO16 or R_MIPS_CALL_LO16. */ @@ -6869,6 +7039,7 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, switch (r_type) { case R_MIPS_CALL16: + case R_MIPS16_CALL16: if (h == NULL) { (*_bfd_error_handler) @@ -6919,6 +7090,7 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, } /* Fall through. */ + case R_MIPS16_GOT16: case R_MIPS_GOT16: case R_MIPS_GOT_HI16: case R_MIPS_GOT_LO16: @@ -7003,6 +7175,7 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, are handled using copy relocs or PLT stubs, so there's no need to add a .rela.dyn entry for this relocation. */ if ((info->shared || (h != NULL && !htab->is_vxworks)) + && !(h && strcmp (h->root.root.string, "__gnu_local_gp") == 0) && (sec->flags & SEC_ALLOC) != 0) { if (sreloc == NULL) @@ -7116,6 +7289,7 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, default: ((struct mips_elf_link_hash_entry *) h)->no_fn_stub = TRUE; break; + case R_MIPS16_CALL16: case R_MIPS_CALL16: case R_MIPS_CALL_HI16: case R_MIPS_CALL_LO16: @@ -7123,12 +7297,13 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, break; } - /* If this reloc is not a 16 bit call, and it has a global - symbol, then we will need the fn_stub if there is one. - References from a stub section do not count. */ + /* See if this reloc would need to refer to a MIPS16 hard-float stub, + if there is one. We only need to handle global symbols here; + we decide whether to keep or delete stubs for local symbols + when processing the stub's relocations. */ if (h != NULL - && r_type != R_MIPS16_26 - && !mips16_stub_section_p (abfd, sec)) + && !mips16_call_reloc_p (r_type) + && !section_allows_mips16_refs_p (sec)) { struct mips_elf_link_hash_entry *mh; @@ -7638,7 +7813,7 @@ _bfd_mips_elf_always_size_sections (bfd *output_bfd, if (! (info->relocatable || ! mips_elf_hash_table (info)->mips16_stubs_seen)) mips_elf_link_hash_traverse (mips_elf_hash_table (info), - mips_elf_check_mips16_stubs, NULL); + mips_elf_check_mips16_stubs, info); dynobj = elf_hash_table (info)->dynobj; if (dynobj == NULL) @@ -7700,7 +7875,7 @@ _bfd_mips_elf_always_size_sections (bfd *output_bfd, loadable_size += htab->function_stub_size * (i + 1); if (htab->is_vxworks) - /* There's no need to allocate page entries for VxWorks; R_MIPS_GOT16 + /* There's no need to allocate page entries for VxWorks; R_MIPS*_GOT16 relocations against local symbols evaluate to "G", and the EABI does not include R_MIPS_GOT_PAGE. */ page_gotno = 0; @@ -8248,9 +8423,8 @@ _bfd_mips_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info, rela_relocation_p = FALSE; addend = mips_elf_read_rel_addend (input_bfd, rel, howto, contents); - if (r_type == R_MIPS_HI16 - || r_type == R_MIPS16_HI16 - || (r_type == R_MIPS_GOT16 + if (hi16_reloc_p (r_type) + || (got16_reloc_p (r_type) && mips_elf_local_relocation_p (input_bfd, rel, local_sections, FALSE))) { @@ -8289,8 +8463,7 @@ _bfd_mips_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info, if (!rela_relocation_p && rel->r_addend) { addend += rel->r_addend; - if (r_type == R_MIPS_HI16 - || r_type == R_MIPS_GOT16) + if (hi16_reloc_p (r_type) || got16_reloc_p (r_type)) addend = mips_elf_high (addend); else if (r_type == R_MIPS_HIGHER) addend = mips_elf_higher (addend); @@ -8555,9 +8728,11 @@ _bfd_mips_elf_finish_dynamic_symbol (bfd *output_bfd, const char *name; int idx; struct mips_elf_link_hash_table *htab; + struct mips_elf_link_hash_entry *hmips; htab = mips_elf_hash_table (info); dynobj = elf_hash_table (info)->dynobj; + hmips = (struct mips_elf_link_hash_entry *) h; if (h->plt.offset != MINUS_ONE) { @@ -8620,6 +8795,18 @@ _bfd_mips_elf_finish_dynamic_symbol (bfd *output_bfd, + h->plt.offset); } + /* If we have a MIPS16 function with a stub, the dynamic symbol must + refer to the stub, since only the stub uses the standard calling + conventions. */ + if (h->dynindx != -1 && hmips->fn_stub != NULL) + { + BFD_ASSERT (hmips->need_fn_stub); + sym->st_value = (hmips->fn_stub->output_section->vma + + hmips->fn_stub->output_offset); + sym->st_size = hmips->fn_stub->size; + sym->st_other = ELF_ST_VISIBILITY (sym->st_other); + } + BFD_ASSERT (h->dynindx != -1 || h->forced_local); @@ -8638,7 +8825,8 @@ _bfd_mips_elf_finish_dynamic_symbol (bfd *output_bfd, bfd_vma value; value = sym->st_value; - offset = mips_elf_global_got_index (dynobj, output_bfd, h, R_MIPS_GOT16, info); + offset = mips_elf_global_got_index (dynobj, output_bfd, h, + R_MIPS_GOT16, info); MIPS_ELF_PUT_WORD (output_bfd, value, sgot->contents + offset); } @@ -8652,7 +8840,7 @@ _bfd_mips_elf_finish_dynamic_symbol (bfd *output_bfd, e.abfd = output_bfd; e.symndx = -1; - e.d.h = (struct mips_elf_link_hash_entry *)h; + e.d.h = hmips; e.tls_type = 0; for (g = g->next; g->next != gg; g = g->next) @@ -8768,9 +8956,13 @@ _bfd_mips_elf_finish_dynamic_symbol (bfd *output_bfd, } } - /* If this is a mips16 symbol, force the value to be even. */ + /* Keep dynamic MIPS16 symbols odd. This allows the dynamic linker to + treat MIPS16 symbols like any other. */ if (ELF_ST_IS_MIPS16 (sym->st_other)) - sym->st_value &= ~1; + { + BFD_ASSERT (sym->st_value & 1); + sym->st_other -= STO_MIPS16; + } return TRUE; } @@ -9998,6 +10190,8 @@ _bfd_mips_elf_gc_sweep_hook (bfd *abfd ATTRIBUTE_UNUSED, for (rel = relocs; rel < relend; rel++) switch (ELF_R_TYPE (abfd, rel->r_info)) { + case R_MIPS16_GOT16: + case R_MIPS16_CALL16: case R_MIPS_GOT16: case R_MIPS_CALL16: case R_MIPS_CALL_HI16: diff --git a/bfd/libbfd.h b/bfd/libbfd.h index 2c97498..640b284 100644 --- a/bfd/libbfd.h +++ b/bfd/libbfd.h @@ -964,6 +964,8 @@ static const char *const bfd_reloc_code_real_names[] = { "@@uninitialized@@", "BFD_RELOC_HI16_PCREL", "BFD_RELOC_HI16_S_PCREL", "BFD_RELOC_LO16_PCREL", + "BFD_RELOC_MIPS16_GOT16", + "BFD_RELOC_MIPS16_CALL16", "BFD_RELOC_MIPS16_HI16", "BFD_RELOC_MIPS16_HI16_S", "BFD_RELOC_MIPS16_LO16", diff --git a/bfd/reloc.c b/bfd/reloc.c index 03f0f9f..8881f8f 100644 --- a/bfd/reloc.c +++ b/bfd/reloc.c @@ -2154,6 +2154,13 @@ ENUMDOC Low 16 bits of pc-relative value ENUM + BFD_RELOC_MIPS16_GOT16 +ENUMX + BFD_RELOC_MIPS16_CALL16 +ENUMDOC + Equivalent of BFD_RELOC_MIPS_*, but with the MIPS16 layout of + 16-bit immediate fields +ENUM BFD_RELOC_MIPS16_HI16 ENUMDOC MIPS16 high 16 bits of 32-bit value. |