You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+ Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */
#include "bfd.h"
#include "sysdep.h"
static void elf_crx_info_to_howto
(bfd *, arelent *, Elf_Internal_Rela *);
static bfd_boolean elf32_crx_relax_delete_bytes
- (bfd *, asection *, bfd_vma, int);
+ (struct bfd_link_info *, bfd *, asection *, bfd_vma, int);
static bfd_reloc_status_type crx_elf_final_link_relocate
(reloc_howto_type *, bfd *, bfd *, asection *,
bfd_byte *, bfd_vma, bfd_vma, bfd_vma,
{BFD_RELOC_CRX_NUM16, R_CRX_NUM16},
{BFD_RELOC_CRX_NUM32, R_CRX_NUM32},
{BFD_RELOC_CRX_IMM16, R_CRX_IMM16},
- {BFD_RELOC_CRX_IMM32, R_CRX_IMM32}
+ {BFD_RELOC_CRX_IMM32, R_CRX_IMM32},
+ {BFD_RELOC_CRX_SWITCH8, R_CRX_SWITCH8},
+ {BFD_RELOC_CRX_SWITCH16, R_CRX_SWITCH16},
+ {BFD_RELOC_CRX_SWITCH32, R_CRX_SWITCH32}
};
static reloc_howto_type crx_elf_howto_table[] =
FALSE, /* partial_inplace */
0xffffffff, /* src_mask */
0xffffffff, /* dst_mask */
- FALSE) /* pcrel_offset */
+ FALSE), /* pcrel_offset */
+
+ /* An 8 bit switch table entry. This is generated for an expression
+ such as ``.byte L1 - L2''. The offset holds the difference
+ between the reloc address and L2. */
+ HOWTO (R_CRX_SWITCH8, /* type */
+ 0, /* rightshift */
+ 0, /* size (0 = byte, 1 = short, 2 = long) */
+ 8, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_unsigned, /* complain_on_overflow */
+ bfd_elf_generic_reloc, /* special_function */
+ "R_CRX_SWITCH8", /* name */
+ FALSE, /* partial_inplace */
+ 0xff, /* src_mask */
+ 0xff, /* dst_mask */
+ TRUE), /* pcrel_offset */
+
+ /* A 16 bit switch table entry. This is generated for an expression
+ such as ``.word L1 - L2''. The offset holds the difference
+ between the reloc address and L2. */
+ HOWTO (R_CRX_SWITCH16, /* type */
+ 0, /* rightshift */
+ 1, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_unsigned, /* complain_on_overflow */
+ bfd_elf_generic_reloc, /* special_function */
+ "R_CRX_SWITCH16", /* name */
+ FALSE, /* partial_inplace */
+ 0xffff, /* src_mask */
+ 0xffff, /* dst_mask */
+ TRUE), /* pcrel_offset */
+
+ /* A 32 bit switch table entry. This is generated for an expression
+ such as ``.long L1 - L2''. The offset holds the difference
+ between the reloc address and L2. */
+ HOWTO (R_CRX_SWITCH32, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_unsigned, /* complain_on_overflow */
+ bfd_elf_generic_reloc, /* special_function */
+ "R_CRX_SWITCH32", /* name */
+ FALSE, /* partial_inplace */
+ 0xffffffff, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ TRUE) /* pcrel_offset */
};
/* Retrieve a howto ptr using a BFD reloc_code. */
return bfd_reloc_ok;
break;
+ case R_CRX_SWITCH8:
+ case R_CRX_SWITCH16:
+ case R_CRX_SWITCH32:
+ /* We only care about the addend, where the difference between
+ expressions is kept. */
+ Rvalue = 0;
+
default:
break;
}
Rvalue |= (((bfd_get_16 (input_bfd, hit_data) << 16) |
bfd_get_16 (input_bfd, hit_data + 2)) & ~howto->dst_mask);
- if (r_type == R_CRX_NUM32)
+ if (r_type == R_CRX_NUM32 || r_type == R_CRX_SWITCH32)
/* Relocation on DATA is purely little-endian, that is, for a
multi-byte datum, the lowest address in memory contains the
little end of the datum, that is, the least significant byte.
/* Delete some bytes from a section while relaxing. */
static bfd_boolean
-elf32_crx_relax_delete_bytes (bfd *abfd, asection *sec,
- bfd_vma addr, int count)
+elf32_crx_relax_delete_bytes (struct bfd_link_info *link_info, bfd *abfd,
+ asection *sec, bfd_vma addr, int count)
{
Elf_Internal_Shdr *symtab_hdr;
unsigned int sec_shndx;
Elf_Internal_Sym *isymend;
struct elf_link_hash_entry **sym_hashes;
struct elf_link_hash_entry **end_hashes;
+ struct elf_link_hash_entry **start_hashes;
unsigned int symcount;
sec_shndx = _bfd_elf_section_from_bfd_section (abfd, sec);
if (isym->st_shndx == sec_shndx
&& isym->st_value > addr
&& isym->st_value < toaddr)
- isym->st_value -= count;
+ {
+ /* Adjust the addend of SWITCH relocations in this section,
+ which reference this local symbol. */
+ for (irel = elf_section_data (sec)->relocs; irel < irelend; irel++)
+ {
+ unsigned long r_symndx;
+ Elf_Internal_Sym *rsym;
+ bfd_vma addsym, subsym;
+
+ /* Skip if not a SWITCH relocation. */
+ if (ELF32_R_TYPE (irel->r_info) != (int) R_CRX_SWITCH8
+ && ELF32_R_TYPE (irel->r_info) != (int) R_CRX_SWITCH16
+ && ELF32_R_TYPE (irel->r_info) != (int) R_CRX_SWITCH32)
+ continue;
+
+ r_symndx = ELF32_R_SYM (irel->r_info);
+ rsym = (Elf_Internal_Sym *) symtab_hdr->contents + r_symndx;
+
+ /* Skip if not the local adjusted symbol. */
+ if (rsym != isym)
+ continue;
+
+ addsym = isym->st_value;
+ subsym = addsym - irel->r_addend;
+
+ /* Fix the addend only when -->> (addsym > addr >= subsym). */
+ if (subsym <= addr)
+ irel->r_addend -= count;
+ else
+ continue;
+ }
+
+ isym->st_value -= count;
+ }
}
/* Now adjust the global symbols defined in this section. */
symcount = (symtab_hdr->sh_size / sizeof (Elf32_External_Sym)
- symtab_hdr->sh_info);
- sym_hashes = elf_sym_hashes (abfd);
+ sym_hashes = start_hashes = elf_sym_hashes (abfd);
end_hashes = sym_hashes + symcount;
for (; sym_hashes < end_hashes; sym_hashes++)
{
struct elf_link_hash_entry *sym_hash = *sym_hashes;
+ /* The '--wrap SYMBOL' option is causing a pain when the object file,
+ containing the definition of __wrap_SYMBOL, includes a direct
+ call to SYMBOL as well. Since both __wrap_SYMBOL and SYMBOL reference
+ the same symbol (which is __wrap_SYMBOL), but still exist as two
+ different symbols in 'sym_hashes', we don't want to adjust
+ the global symbol __wrap_SYMBOL twice.
+ This check is only relevant when symbols are being wrapped. */
+ if (link_info->wrap_hash != NULL)
+ {
+ struct elf_link_hash_entry **cur_sym_hashes;
+
+ /* Loop only over the symbols whom been already checked. */
+ for (cur_sym_hashes = start_hashes; cur_sym_hashes < sym_hashes;
+ cur_sym_hashes++)
+ {
+ /* If the current symbol is identical to 'sym_hash', that means
+ the symbol was already adjusted (or at least checked). */
+ if (*cur_sym_hashes == sym_hash)
+ break;
+ }
+ /* Don't adjust the symbol again. */
+ if (cur_sym_hashes < sym_hashes)
+ continue;
+ }
+
if ((sym_hash->root.type == bfd_link_hash_defined
|| sym_hash->root.type == bfd_link_hash_defweak)
&& sym_hash->root.u.def.section == sec
{
case bfd_reloc_overflow:
if (!((*info->callbacks->reloc_overflow)
- (info, name, howto->name, (bfd_vma) 0,
- input_bfd, input_section, rel->r_offset)))
+ (info, (h ? &h->root : NULL), name, howto->name,
+ (bfd_vma) 0, input_bfd, input_section,
+ rel->r_offset)))
return FALSE;
break;
R_CRX_REL16);
/* Delete two bytes of data. */
- if (!elf32_crx_relax_delete_bytes (abfd, sec,
+ if (!elf32_crx_relax_delete_bytes (link_info, abfd, sec,
irel->r_offset + 2, 2))
goto error_return;
R_CRX_REL8);
/* Delete two bytes of data. */
- if (!elf32_crx_relax_delete_bytes (abfd, sec,
+ if (!elf32_crx_relax_delete_bytes (link_info, abfd, sec,
irel->r_offset + 2, 2))
goto error_return;
/* Verify it's a 'cmp&branch' opcode. */
if ((code & 0xfff0) != 0x3180 && (code & 0xfff0) != 0x3190
&& (code & 0xfff0) != 0x31a0 && (code & 0xfff0) != 0x31c0
- && (code & 0xfff0) != 0x31d0 && (code & 0xfff0) != 0x31e0)
+ && (code & 0xfff0) != 0x31d0 && (code & 0xfff0) != 0x31e0
+ /* Or a Co-processor branch ('bcop'). */
+ && (code & 0xfff0) != 0x3010 && (code & 0xfff0) != 0x3110)
continue;
/* Note that we've changed the relocs, section contents, etc. */
R_CRX_REL8_CMP);
/* Delete two bytes of data. */
- if (!elf32_crx_relax_delete_bytes (abfd, sec,
+ if (!elf32_crx_relax_delete_bytes (link_info, abfd, sec,
irel->r_offset + 4, 2))
goto error_return;
R_CRX_IMM16);
/* Delete two bytes of data. */
- if (!elf32_crx_relax_delete_bytes (abfd, sec,
+ if (!elf32_crx_relax_delete_bytes (link_info, abfd, sec,
irel->r_offset + 2, 2))
goto error_return;