Index: bfd/elf32-nios2.c |
diff --git a/bfd/elf32-nios2.c b/bfd/elf32-nios2.c |
new file mode 100644 |
index 0000000000000000000000000000000000000000..82e55164c8b9d71c3c194961787028c43d68b969 |
--- /dev/null |
+++ b/bfd/elf32-nios2.c |
@@ -0,0 +1,4135 @@ |
+/* 32-bit ELF support for Nios II. |
+ Copyright (C) 2012, 2013 Free Software Foundation, Inc. |
+ Contributed by Nigel Gray (ngray@altera.com). |
+ Contributed by Mentor Graphics, Inc. |
+ |
+ This file is part of BFD, the Binary File Descriptor library. |
+ |
+ This program is free software; you can redistribute it and/or modify |
+ it under the terms of the GNU General Public License as published by |
+ the Free Software Foundation; either version 3 of the License, or |
+ (at your option) any later version. |
+ |
+ This program is distributed in the hope that it will be useful, |
+ but WITHOUT ANY WARRANTY; without even the implied warranty of |
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
+ GNU General Public License for more details. |
+ |
+ 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., 51 Franklin Street - Fifth Floor, Boston, |
+ MA 02110-1301, USA. */ |
+ |
+/* This file handles Altera Nios II ELF targets. */ |
+ |
+#include "sysdep.h" |
+#include "bfd.h" |
+#include "libbfd.h" |
+#include "bfdlink.h" |
+#include "genlink.h" |
+#include "elf-bfd.h" |
+#include "elf/nios2.h" |
+#include "opcode/nios2.h" |
+ |
+/* Use RELA relocations. */ |
+#ifndef USE_RELA |
+#define USE_RELA |
+#endif |
+ |
+#ifdef USE_REL |
+#undef USE_REL |
+#endif |
+ |
+/* Forward declarations. */ |
+static bfd_reloc_status_type nios2_elf32_ignore_reloc |
+ (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); |
+static bfd_reloc_status_type nios2_elf32_hi16_relocate |
+ (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); |
+static bfd_reloc_status_type nios2_elf32_lo16_relocate |
+ (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); |
+static bfd_reloc_status_type nios2_elf32_hiadj16_relocate |
+ (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); |
+static bfd_reloc_status_type nios2_elf32_pcrel_lo16_relocate |
+ (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); |
+static bfd_reloc_status_type nios2_elf32_pcrel_hiadj16_relocate |
+ (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); |
+static bfd_reloc_status_type nios2_elf32_pcrel16_relocate |
+ (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); |
+static bfd_reloc_status_type nios2_elf32_call26_relocate |
+ (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); |
+static bfd_reloc_status_type nios2_elf32_gprel_relocate |
+ (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); |
+static bfd_reloc_status_type nios2_elf32_ujmp_relocate |
+ (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); |
+static bfd_reloc_status_type nios2_elf32_cjmp_relocate |
+ (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); |
+static bfd_reloc_status_type nios2_elf32_callr_relocate |
+ (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); |
+ |
+/* Target vector. */ |
+extern const bfd_target bfd_elf32_littlenios2_vec; |
+extern const bfd_target bfd_elf32_bignios2_vec; |
+ |
+/* Offset of tp and dtp pointers from start of TLS block. */ |
+#define TP_OFFSET 0x7000 |
+#define DTP_OFFSET 0x8000 |
+ |
+/* The relocation table used for SHT_REL sections. */ |
+static reloc_howto_type elf_nios2_howto_table_rel[] = { |
+ /* No relocation. */ |
+ HOWTO (R_NIOS2_NONE, /* type */ |
+ 0, /* rightshift */ |
+ 0, /* size (0 = byte, 1 = short, 2 = long) */ |
+ 0, /* bitsize */ |
+ FALSE, /* pc_relative */ |
+ 0, /* bitpos */ |
+ complain_overflow_dont, /* complain_on_overflow */ |
+ bfd_elf_generic_reloc, /* special_function */ |
+ "R_NIOS2_NONE", /* name */ |
+ FALSE, /* partial_inplace */ |
+ 0, /* src_mask */ |
+ 0, /* dst_mask */ |
+ FALSE), /* pcrel_offset */ |
+ |
+ /* 16-bit signed immediate relocation. */ |
+ HOWTO (R_NIOS2_S16, /* type */ |
+ 0, /* rightshift */ |
+ 2, /* size (0 = byte, 1 = short, 2 = long) */ |
+ 16, /* bitsize */ |
+ FALSE, /* pc_relative */ |
+ 6, /* bitpos */ |
+ complain_overflow_signed, /* complain on overflow */ |
+ bfd_elf_generic_reloc, /* special function */ |
+ "R_NIOS2_S16", /* name */ |
+ FALSE, /* partial_inplace */ |
+ 0x003fffc0, /* src_mask */ |
+ 0x003fffc0, /* dest_mask */ |
+ FALSE), /* pcrel_offset */ |
+ |
+ /* 16-bit unsigned immediate relocation. */ |
+ HOWTO (R_NIOS2_U16, /* type */ |
+ 0, /* rightshift */ |
+ 2, /* size (0 = byte, 1 = short, 2 = long) */ |
+ 16, /* bitsize */ |
+ FALSE, /* pc_relative */ |
+ 6, /* bitpos */ |
+ complain_overflow_unsigned, /* complain on overflow */ |
+ bfd_elf_generic_reloc, /* special function */ |
+ "R_NIOS2_U16", /* name */ |
+ FALSE, /* partial_inplace */ |
+ 0x003fffc0, /* src_mask */ |
+ 0x003fffc0, /* dest_mask */ |
+ FALSE), /* pcrel_offset */ |
+ |
+ HOWTO (R_NIOS2_PCREL16, /* type */ |
+ 0, /* rightshift */ |
+ 2, /* size (0 = byte, 1 = short, 2 = long) */ |
+ 16, /* bitsize */ |
+ TRUE, /* pc_relative */ |
+ 6, /* bitpos */ |
+ complain_overflow_signed, /* complain on overflow */ |
+ nios2_elf32_pcrel16_relocate, /* special function */ |
+ "R_NIOS2_PCREL16", /* name */ |
+ FALSE, /* partial_inplace */ |
+ 0x003fffc0, /* src_mask */ |
+ 0x003fffc0, /* dest_mask */ |
+ TRUE), /* pcrel_offset */ |
+ |
+ HOWTO (R_NIOS2_CALL26, /* type */ |
+ 2, /* rightshift */ |
+ 2, /* size (0 = byte, 1 = short, 2 = long) */ |
+ 26, /* bitsize */ |
+ FALSE, /* pc_relative */ |
+ 6, /* bitpos */ |
+ complain_overflow_dont, /* complain on overflow */ |
+ nios2_elf32_call26_relocate, /* special function */ |
+ "R_NIOS2_CALL26", /* name */ |
+ FALSE, /* partial_inplace */ |
+ 0xffffffc0, /* src_mask */ |
+ 0xffffffc0, /* dst_mask */ |
+ FALSE), /* pcrel_offset */ |
+ |
+ HOWTO (R_NIOS2_IMM5, |
+ 0, |
+ 2, |
+ 5, |
+ FALSE, |
+ 6, |
+ complain_overflow_bitfield, |
+ bfd_elf_generic_reloc, |
+ "R_NIOS2_IMM5", |
+ FALSE, |
+ 0x000007c0, |
+ 0x000007c0, |
+ FALSE), |
+ |
+ HOWTO (R_NIOS2_CACHE_OPX, |
+ 0, |
+ 2, |
+ 5, |
+ FALSE, |
+ 22, |
+ complain_overflow_bitfield, |
+ bfd_elf_generic_reloc, |
+ "R_NIOS2_CACHE_OPX", |
+ FALSE, |
+ 0x07c00000, |
+ 0x07c00000, |
+ FALSE), |
+ |
+ HOWTO (R_NIOS2_IMM6, |
+ 0, |
+ 2, |
+ 6, |
+ FALSE, |
+ 6, |
+ complain_overflow_bitfield, |
+ bfd_elf_generic_reloc, |
+ "R_NIOS2_IMM6", |
+ FALSE, |
+ 0x00000fc0, |
+ 0x00000fc0, |
+ FALSE), |
+ |
+ HOWTO (R_NIOS2_IMM8, |
+ 0, |
+ 2, |
+ 8, |
+ FALSE, |
+ 6, |
+ complain_overflow_bitfield, |
+ bfd_elf_generic_reloc, |
+ "R_NIOS2_IMM8", |
+ FALSE, |
+ 0x00003fc0, |
+ 0x00003fc0, |
+ FALSE), |
+ |
+ HOWTO (R_NIOS2_HI16, |
+ 0, |
+ 2, |
+ 32, |
+ FALSE, |
+ 6, |
+ complain_overflow_dont, |
+ nios2_elf32_hi16_relocate, |
+ "R_NIOS2_HI16", |
+ FALSE, |
+ 0x003fffc0, |
+ 0x003fffc0, |
+ FALSE), |
+ |
+ HOWTO (R_NIOS2_LO16, |
+ 0, |
+ 2, |
+ 32, |
+ FALSE, |
+ 6, |
+ complain_overflow_dont, |
+ nios2_elf32_lo16_relocate, |
+ "R_NIOS2_LO16", |
+ FALSE, |
+ 0x003fffc0, |
+ 0x003fffc0, |
+ FALSE), |
+ |
+ HOWTO (R_NIOS2_HIADJ16, |
+ 0, |
+ 2, |
+ 32, |
+ FALSE, |
+ 6, |
+ complain_overflow_dont, |
+ nios2_elf32_hiadj16_relocate, |
+ "R_NIOS2_HIADJ16", |
+ FALSE, |
+ 0x003fffc0, |
+ 0x003fffc0, |
+ FALSE), |
+ |
+ HOWTO (R_NIOS2_BFD_RELOC_32, |
+ 0, |
+ 2, /* long */ |
+ 32, |
+ FALSE, |
+ 0, |
+ complain_overflow_dont, |
+ bfd_elf_generic_reloc, |
+ "R_NIOS2_BFD_RELOC32", |
+ FALSE, |
+ 0xffffffff, |
+ 0xffffffff, |
+ FALSE), |
+ |
+ HOWTO (R_NIOS2_BFD_RELOC_16, |
+ 0, |
+ 1, /* short */ |
+ 16, |
+ FALSE, |
+ 0, |
+ complain_overflow_bitfield, |
+ bfd_elf_generic_reloc, |
+ "R_NIOS2_BFD_RELOC16", |
+ FALSE, |
+ 0x0000ffff, |
+ 0x0000ffff, |
+ FALSE), |
+ |
+ HOWTO (R_NIOS2_BFD_RELOC_8, |
+ 0, |
+ 0, /* byte */ |
+ 8, |
+ FALSE, |
+ 0, |
+ complain_overflow_bitfield, |
+ bfd_elf_generic_reloc, |
+ "R_NIOS2_BFD_RELOC8", |
+ FALSE, |
+ 0x000000ff, |
+ 0x000000ff, |
+ FALSE), |
+ |
+ HOWTO (R_NIOS2_GPREL, |
+ 0, |
+ 2, |
+ 32, |
+ FALSE, |
+ 6, |
+ complain_overflow_dont, |
+ nios2_elf32_gprel_relocate, |
+ "R_NIOS2_GPREL", |
+ FALSE, |
+ 0x003fffc0, |
+ 0x003fffc0, |
+ FALSE), |
+ |
+ HOWTO (R_NIOS2_GNU_VTINHERIT, |
+ 0, |
+ 2, /* short */ |
+ 0, |
+ FALSE, |
+ 0, |
+ complain_overflow_dont, |
+ NULL, |
+ "R_NIOS2_GNU_VTINHERIT", |
+ FALSE, |
+ 0, |
+ 0, |
+ FALSE), |
+ |
+ HOWTO (R_NIOS2_GNU_VTENTRY, |
+ 0, |
+ 2, /* byte */ |
+ 0, |
+ FALSE, |
+ 0, |
+ complain_overflow_dont, |
+ _bfd_elf_rel_vtable_reloc_fn, |
+ "R_NIOS2_GNU_VTENTRY", |
+ FALSE, |
+ 0, |
+ 0, |
+ FALSE), |
+ |
+ HOWTO (R_NIOS2_UJMP, |
+ 0, |
+ 2, |
+ 32, |
+ FALSE, |
+ 6, |
+ complain_overflow_dont, |
+ nios2_elf32_ujmp_relocate, |
+ "R_NIOS2_UJMP", |
+ FALSE, |
+ 0x003fffc0, |
+ 0x003fffc0, |
+ FALSE), |
+ |
+ HOWTO (R_NIOS2_CJMP, |
+ 0, |
+ 2, |
+ 32, |
+ FALSE, |
+ 6, |
+ complain_overflow_dont, |
+ nios2_elf32_cjmp_relocate, |
+ "R_NIOS2_CJMP", |
+ FALSE, |
+ 0x003fffc0, |
+ 0x003fffc0, |
+ FALSE), |
+ |
+ HOWTO (R_NIOS2_CALLR, |
+ 0, |
+ 2, |
+ 32, |
+ FALSE, |
+ 6, |
+ complain_overflow_dont, |
+ nios2_elf32_callr_relocate, |
+ "R_NIOS2_CALLR", |
+ FALSE, |
+ 0x003fffc0, |
+ 0x003fffc0, |
+ FALSE), |
+ |
+ HOWTO (R_NIOS2_ALIGN, |
+ 0, |
+ 2, |
+ 0, |
+ FALSE, |
+ 0, |
+ complain_overflow_dont, |
+ nios2_elf32_ignore_reloc, |
+ "R_NIOS2_ALIGN", |
+ FALSE, |
+ 0, |
+ 0, |
+ TRUE), |
+ |
+ |
+ HOWTO (R_NIOS2_GOT16, |
+ 0, |
+ 2, |
+ 16, |
+ FALSE, |
+ 6, |
+ complain_overflow_bitfield, |
+ bfd_elf_generic_reloc, |
+ "R_NIOS2_GOT16", |
+ FALSE, |
+ 0x003fffc0, |
+ 0x003fffc0, |
+ FALSE), |
+ |
+ HOWTO (R_NIOS2_CALL16, |
+ 0, |
+ 2, |
+ 16, |
+ FALSE, |
+ 6, |
+ complain_overflow_bitfield, |
+ bfd_elf_generic_reloc, |
+ "R_NIOS2_CALL16", |
+ FALSE, |
+ 0x003fffc0, |
+ 0x003fffc0, |
+ FALSE), |
+ |
+ HOWTO (R_NIOS2_GOTOFF_LO, |
+ 0, |
+ 2, |
+ 16, |
+ FALSE, |
+ 6, |
+ complain_overflow_dont, |
+ bfd_elf_generic_reloc, |
+ "R_NIOS2_GOTOFF_LO", |
+ FALSE, |
+ 0x003fffc0, |
+ 0x003fffc0, |
+ FALSE), |
+ |
+ HOWTO (R_NIOS2_GOTOFF_HA, |
+ 0, |
+ 2, |
+ 16, |
+ FALSE, |
+ 6, |
+ complain_overflow_dont, |
+ bfd_elf_generic_reloc, |
+ "R_NIOS2_GOTOFF_HA", |
+ FALSE, |
+ 0x003fffc0, |
+ 0x003fffc0, |
+ FALSE), |
+ |
+ HOWTO (R_NIOS2_PCREL_LO, |
+ 0, |
+ 2, |
+ 16, |
+ TRUE, |
+ 6, |
+ complain_overflow_dont, |
+ nios2_elf32_pcrel_lo16_relocate, |
+ "R_NIOS2_PCREL_LO", |
+ FALSE, |
+ 0x003fffc0, |
+ 0x003fffc0, |
+ TRUE), |
+ |
+ HOWTO (R_NIOS2_PCREL_HA, |
+ 0, |
+ 2, |
+ 16, |
+ FALSE, /* This is a PC-relative relocation, but we need to subtract |
+ PC ourselves before the HIADJ. */ |
+ 6, |
+ complain_overflow_dont, |
+ nios2_elf32_pcrel_hiadj16_relocate, |
+ "R_NIOS2_PCREL_HA", |
+ FALSE, |
+ 0x003fffc0, |
+ 0x003fffc0, |
+ TRUE), |
+ |
+ HOWTO (R_NIOS2_TLS_GD16, |
+ 0, |
+ 2, |
+ 16, |
+ FALSE, |
+ 6, |
+ complain_overflow_bitfield, |
+ bfd_elf_generic_reloc, |
+ "R_NIOS2_TLS_GD16", |
+ FALSE, |
+ 0x003fffc0, |
+ 0x003fffc0, |
+ FALSE), |
+ |
+ HOWTO (R_NIOS2_TLS_LDM16, |
+ 0, |
+ 2, |
+ 16, |
+ FALSE, |
+ 6, |
+ complain_overflow_bitfield, |
+ bfd_elf_generic_reloc, |
+ "R_NIOS2_TLS_LDM16", |
+ FALSE, |
+ 0x003fffc0, |
+ 0x003fffc0, |
+ FALSE), |
+ |
+ HOWTO (R_NIOS2_TLS_LDO16, |
+ 0, |
+ 2, |
+ 16, |
+ FALSE, |
+ 6, |
+ complain_overflow_bitfield, |
+ bfd_elf_generic_reloc, |
+ "R_NIOS2_TLS_LDO16", |
+ FALSE, |
+ 0x003fffc0, |
+ 0x003fffc0, |
+ FALSE), |
+ |
+ HOWTO (R_NIOS2_TLS_IE16, |
+ 0, |
+ 2, |
+ 16, |
+ FALSE, |
+ 6, |
+ complain_overflow_bitfield, |
+ bfd_elf_generic_reloc, |
+ "R_NIOS2_TLS_IE16", |
+ FALSE, |
+ 0x003fffc0, |
+ 0x003fffc0, |
+ FALSE), |
+ |
+ HOWTO (R_NIOS2_TLS_LE16, |
+ 0, |
+ 2, |
+ 16, |
+ FALSE, |
+ 6, |
+ complain_overflow_bitfield, |
+ bfd_elf_generic_reloc, |
+ "R_NIOS2_TLS_LE16", |
+ FALSE, |
+ 0x003fffc0, |
+ 0x003fffc0, |
+ FALSE), |
+ |
+ HOWTO (R_NIOS2_TLS_DTPMOD, |
+ 0, |
+ 2, |
+ 32, |
+ FALSE, |
+ 0, |
+ complain_overflow_dont, |
+ bfd_elf_generic_reloc, |
+ "R_NIOS2_TLS_DTPMOD", |
+ FALSE, |
+ 0xffffffff, |
+ 0xffffffff, |
+ FALSE), |
+ |
+ HOWTO (R_NIOS2_TLS_DTPREL, |
+ 0, |
+ 2, |
+ 32, |
+ FALSE, |
+ 0, |
+ complain_overflow_dont, |
+ bfd_elf_generic_reloc, |
+ "R_NIOS2_TLS_DTPREL", |
+ FALSE, |
+ 0xffffffff, |
+ 0xffffffff, |
+ FALSE), |
+ |
+ HOWTO (R_NIOS2_TLS_TPREL, |
+ 0, |
+ 2, |
+ 32, |
+ FALSE, |
+ 0, |
+ complain_overflow_dont, |
+ bfd_elf_generic_reloc, |
+ "R_NIOS2_TLS_TPREL", |
+ FALSE, |
+ 0xffffffff, |
+ 0xffffffff, |
+ FALSE), |
+ |
+ HOWTO (R_NIOS2_COPY, |
+ 0, |
+ 2, |
+ 32, |
+ FALSE, |
+ 0, |
+ complain_overflow_dont, |
+ bfd_elf_generic_reloc, |
+ "R_NIOS2_COPY", |
+ FALSE, |
+ 0, |
+ 0, |
+ FALSE), |
+ |
+ HOWTO (R_NIOS2_GLOB_DAT, |
+ 0, |
+ 2, |
+ 32, |
+ FALSE, |
+ 0, |
+ complain_overflow_dont, |
+ bfd_elf_generic_reloc, |
+ "R_NIOS2_GLOB_DAT", |
+ FALSE, |
+ 0xffffffff, |
+ 0xffffffff, |
+ FALSE), |
+ |
+ HOWTO (R_NIOS2_JUMP_SLOT, |
+ 0, |
+ 2, |
+ 32, |
+ FALSE, |
+ 0, |
+ complain_overflow_dont, |
+ bfd_elf_generic_reloc, |
+ "R_NIOS2_JUMP_SLOT", |
+ FALSE, |
+ 0xffffffff, |
+ 0xffffffff, |
+ FALSE), |
+ |
+ HOWTO (R_NIOS2_RELATIVE, |
+ 0, |
+ 2, |
+ 32, |
+ FALSE, |
+ 0, |
+ complain_overflow_dont, |
+ bfd_elf_generic_reloc, |
+ "R_NIOS2_RELATIVE", |
+ FALSE, |
+ 0xffffffff, |
+ 0xffffffff, |
+ FALSE), |
+ |
+ HOWTO (R_NIOS2_GOTOFF, |
+ 0, |
+ 2, |
+ 32, |
+ FALSE, |
+ 0, |
+ complain_overflow_dont, |
+ bfd_elf_generic_reloc, |
+ "R_NIOS2_GOTOFF", |
+ FALSE, |
+ 0xffffffff, |
+ 0xffffffff, |
+ FALSE), |
+ |
+/* Add other relocations here. */ |
+}; |
+ |
+static unsigned char elf_code_to_howto_index[R_NIOS2_ILLEGAL + 1]; |
+ |
+/* Return the howto for relocation RTYPE. */ |
+static reloc_howto_type * |
+lookup_howto (unsigned int rtype) |
+{ |
+ static int initialized = 0; |
+ int i; |
+ int howto_tbl_size = (int) (sizeof (elf_nios2_howto_table_rel) |
+ / sizeof (elf_nios2_howto_table_rel[0])); |
+ |
+ if (!initialized) |
+ { |
+ initialized = 1; |
+ memset (elf_code_to_howto_index, 0xff, |
+ sizeof (elf_code_to_howto_index)); |
+ for (i = 0; i < howto_tbl_size; i++) |
+ elf_code_to_howto_index[elf_nios2_howto_table_rel[i].type] = i; |
+ } |
+ |
+ BFD_ASSERT (rtype <= R_NIOS2_ILLEGAL); |
+ i = elf_code_to_howto_index[rtype]; |
+ if (i >= howto_tbl_size) |
+ return 0; |
+ return elf_nios2_howto_table_rel + i; |
+} |
+ |
+/* Map for converting BFD reloc types to Nios II reloc types. */ |
+struct elf_reloc_map |
+{ |
+ bfd_reloc_code_real_type bfd_val; |
+ enum elf_nios2_reloc_type elf_val; |
+}; |
+ |
+static const struct elf_reloc_map nios2_reloc_map[] = { |
+ {BFD_RELOC_NIOS2_S16, R_NIOS2_S16}, |
+ {BFD_RELOC_NIOS2_U16, R_NIOS2_U16}, |
+ {BFD_RELOC_16_PCREL, R_NIOS2_PCREL16}, |
+ {BFD_RELOC_NIOS2_CALL26, R_NIOS2_CALL26}, |
+ {BFD_RELOC_NIOS2_IMM5, R_NIOS2_IMM5}, |
+ {BFD_RELOC_NIOS2_CACHE_OPX, R_NIOS2_CACHE_OPX}, |
+ {BFD_RELOC_NIOS2_IMM6, R_NIOS2_IMM6}, |
+ {BFD_RELOC_NIOS2_IMM8, R_NIOS2_IMM8}, |
+ {BFD_RELOC_NIOS2_HI16, R_NIOS2_HI16}, |
+ {BFD_RELOC_NIOS2_LO16, R_NIOS2_LO16}, |
+ {BFD_RELOC_NIOS2_HIADJ16, R_NIOS2_HIADJ16}, |
+ {BFD_RELOC_32, R_NIOS2_BFD_RELOC_32}, |
+ {BFD_RELOC_16, R_NIOS2_BFD_RELOC_16}, |
+ {BFD_RELOC_8, R_NIOS2_BFD_RELOC_8}, |
+ {BFD_RELOC_NIOS2_GPREL, R_NIOS2_GPREL}, |
+ {BFD_RELOC_VTABLE_INHERIT, R_NIOS2_GNU_VTINHERIT}, |
+ {BFD_RELOC_VTABLE_ENTRY, R_NIOS2_GNU_VTENTRY}, |
+ {BFD_RELOC_NIOS2_UJMP, R_NIOS2_UJMP}, |
+ {BFD_RELOC_NIOS2_CJMP, R_NIOS2_CJMP}, |
+ {BFD_RELOC_NIOS2_CALLR, R_NIOS2_CALLR}, |
+ {BFD_RELOC_NIOS2_ALIGN, R_NIOS2_ALIGN}, |
+ {BFD_RELOC_NIOS2_GOT16, R_NIOS2_GOT16}, |
+ {BFD_RELOC_NIOS2_CALL16, R_NIOS2_CALL16}, |
+ {BFD_RELOC_NIOS2_GOTOFF_LO, R_NIOS2_GOTOFF_LO}, |
+ {BFD_RELOC_NIOS2_GOTOFF_HA, R_NIOS2_GOTOFF_HA}, |
+ {BFD_RELOC_NIOS2_PCREL_LO, R_NIOS2_PCREL_LO}, |
+ {BFD_RELOC_NIOS2_PCREL_HA, R_NIOS2_PCREL_HA}, |
+ {BFD_RELOC_NIOS2_TLS_GD16, R_NIOS2_TLS_GD16}, |
+ {BFD_RELOC_NIOS2_TLS_LDM16, R_NIOS2_TLS_LDM16}, |
+ {BFD_RELOC_NIOS2_TLS_LDO16, R_NIOS2_TLS_LDO16}, |
+ {BFD_RELOC_NIOS2_TLS_IE16, R_NIOS2_TLS_IE16}, |
+ {BFD_RELOC_NIOS2_TLS_LE16, R_NIOS2_TLS_LE16}, |
+ {BFD_RELOC_NIOS2_TLS_DTPMOD, R_NIOS2_TLS_DTPMOD}, |
+ {BFD_RELOC_NIOS2_TLS_DTPREL, R_NIOS2_TLS_DTPREL}, |
+ {BFD_RELOC_NIOS2_TLS_TPREL, R_NIOS2_TLS_TPREL}, |
+ {BFD_RELOC_NIOS2_COPY, R_NIOS2_COPY}, |
+ {BFD_RELOC_NIOS2_GLOB_DAT, R_NIOS2_GLOB_DAT}, |
+ {BFD_RELOC_NIOS2_JUMP_SLOT, R_NIOS2_JUMP_SLOT}, |
+ {BFD_RELOC_NIOS2_RELATIVE, R_NIOS2_RELATIVE}, |
+ {BFD_RELOC_NIOS2_GOTOFF, R_NIOS2_GOTOFF} |
+}; |
+ |
+/* The Nios II linker needs to keep track of the number of relocs that it |
+ decides to copy as dynamic relocs in check_relocs for each symbol. |
+ This is so that it can later discard them if they are found to be |
+ unnecessary. We store the information in a field extending the |
+ regular ELF linker hash table. */ |
+ |
+struct elf32_nios2_dyn_relocs |
+{ |
+ struct elf32_nios2_dyn_relocs *next; |
+ |
+ /* The input section of the reloc. */ |
+ asection *sec; |
+ |
+ /* Total number of relocs copied for the input section. */ |
+ bfd_size_type count; |
+ |
+ /* Number of pc-relative relocs copied for the input section. */ |
+ bfd_size_type pc_count; |
+}; |
+ |
+/* Nios II ELF linker hash entry. */ |
+ |
+struct elf32_nios2_link_hash_entry |
+{ |
+ struct elf_link_hash_entry root; |
+ |
+ /* Track dynamic relocs copied for this symbol. */ |
+ struct elf32_nios2_dyn_relocs *dyn_relocs; |
+ |
+#define GOT_UNKNOWN 0 |
+#define GOT_NORMAL 1 |
+#define GOT_TLS_GD 2 |
+#define GOT_TLS_IE 4 |
+ unsigned char tls_type; |
+ |
+ /* We need to detect and take special action for symbols which are only |
+ referenced with %call() and not with %got(). Such symbols do not need |
+ a dynamic GOT reloc in shared objects, only a dynamic PLT reloc. Lazy |
+ linking will not work if the dynamic GOT reloc exists. |
+ To check for this condition efficiently, we compare got_types_used against |
+ CALL16_USED, meaning |
+ (got_types_used & (GOT16_USED | CALL16_USED)) == CALL16_USED. */ |
+#define GOT16_USED 1 |
+#define CALL16_USED 2 |
+ unsigned char got_types_used; |
+}; |
+ |
+#define elf32_nios2_hash_entry(ent) \ |
+ ((struct elf32_nios2_link_hash_entry *) (ent)) |
+ |
+/* Get the Nios II elf linker hash table from a link_info structure. */ |
+#define elf32_nios2_hash_table(info) \ |
+ ((struct elf32_nios2_link_hash_table *) ((info)->hash)) |
+ |
+/* Nios II ELF linker hash table. */ |
+struct elf32_nios2_link_hash_table |
+ { |
+ /* The main hash table. */ |
+ struct elf_link_hash_table root; |
+ |
+ /* Short-cuts to get to dynamic linker sections. */ |
+ asection *sdynbss; |
+ asection *srelbss; |
+ asection *sbss; |
+ |
+ union { |
+ bfd_signed_vma refcount; |
+ bfd_vma offset; |
+ } tls_ldm_got; |
+ |
+ /* Small local sym cache. */ |
+ struct sym_cache sym_cache; |
+ |
+ bfd_vma res_n_size; |
+ }; |
+ |
+struct nios2_elf32_obj_tdata |
+{ |
+ struct elf_obj_tdata root; |
+ |
+ /* tls_type for each local got entry. */ |
+ char *local_got_tls_type; |
+ |
+ /* TRUE if TLS GD relocs have been seen for this object. */ |
+ bfd_boolean has_tlsgd; |
+}; |
+ |
+#define elf32_nios2_tdata(abfd) \ |
+ ((struct nios2_elf32_obj_tdata *) (abfd)->tdata.any) |
+ |
+#define elf32_nios2_local_got_tls_type(abfd) \ |
+ (elf32_nios2_tdata (abfd)->local_got_tls_type) |
+ |
+/* The name of the dynamic interpreter. This is put in the .interp |
+ section. */ |
+#define ELF_DYNAMIC_INTERPRETER "/lib/ld.so.1" |
+ |
+/* PLT implementation for position-dependent code. */ |
+static const bfd_vma nios2_plt_entry[] = { /* .PLTn: */ |
+ 0x03c00034, /* movhi r15, %hiadj(plt_got_slot_address) */ |
+ 0x7bc00017, /* ldw r15, %lo(plt_got_slot_address)(r15) */ |
+ 0x7800683a /* jmp r15 */ |
+}; |
+ |
+static const bfd_vma nios2_plt0_entry[] = { /* .PLTresolve */ |
+ 0x03800034, /* movhi r14, %hiadj(res_0) */ |
+ 0x73800004, /* addi r14, r14, %lo(res_0) */ |
+ 0x7b9fc83a, /* sub r15, r15, r14 */ |
+ 0x03400034, /* movhi r13, %hiadj(_GLOBAL_OFFSET_TABLE_) */ |
+ 0x6b800017, /* ldw r14, %lo(_GLOBAL_OFFSET_TABLE_+4)(r13) */ |
+ 0x6b400017, /* ldw r13, %lo(_GLOBAL_OFFSET_TABLE_+8)(r13) */ |
+ 0x6800683a /* jmp r13 */ |
+}; |
+ |
+/* PLT implementation for position-independent code. */ |
+static const bfd_vma nios2_so_plt_entry[] = { /* .PLTn */ |
+ 0x03c00034, /* movhi r15, %hiadj(index * 4) */ |
+ 0x7bc00004, /* addi r15, r15, %lo(index * 4) */ |
+ 0x00000006 /* br .PLTresolve */ |
+}; |
+ |
+static const bfd_vma nios2_so_plt0_entry[] = { /* .PLTresolve */ |
+ 0x001ce03a, /* nextpc r14 */ |
+ 0x03400034, /* movhi r13, %hiadj(_GLOBAL_OFFSET_TABLE_) */ |
+ 0x6b9b883a, /* add r13, r13, r14 */ |
+ 0x6b800017, /* ldw r14, %lo(_GLOBAL_OFFSET_TABLE_+4)(r13) */ |
+ 0x6b400017, /* ldw r13, %lo(_GLOBAL_OFFSET_TABLE_+8)(r13) */ |
+ 0x6800683a /* jmp r13 */ |
+}; |
+ |
+/* Implement elf_backend_grok_prstatus: |
+ Support for core dump NOTE sections. */ |
+static bfd_boolean |
+nios2_grok_prstatus (bfd *abfd, Elf_Internal_Note *note) |
+{ |
+ int offset; |
+ size_t size; |
+ |
+ switch (note->descsz) |
+ { |
+ default: |
+ return FALSE; |
+ |
+ case 212: /* Linux/Nios II */ |
+ /* pr_cursig */ |
+ elf_tdata (abfd)->core->signal = bfd_get_16 (abfd, note->descdata + 12); |
+ |
+ /* pr_pid */ |
+ elf_tdata (abfd)->core->pid = bfd_get_32 (abfd, note->descdata + 24); |
+ |
+ /* pr_reg */ |
+ offset = 72; |
+ size = 136; |
+ |
+ break; |
+ } |
+ |
+ /* Make a ".reg/999" section. */ |
+ return _bfd_elfcore_make_pseudosection (abfd, ".reg", |
+ size, note->descpos + offset); |
+} |
+ |
+/* Implement elf_backend_grok_psinfo. */ |
+static bfd_boolean |
+nios2_grok_psinfo (bfd *abfd, Elf_Internal_Note *note) |
+{ |
+ switch (note->descsz) |
+ { |
+ default: |
+ return FALSE; |
+ |
+ case 124: /* Linux/Nios II elf_prpsinfo */ |
+ elf_tdata (abfd)->core->program |
+ = _bfd_elfcore_strndup (abfd, note->descdata + 28, 16); |
+ elf_tdata (abfd)->core->command |
+ = _bfd_elfcore_strndup (abfd, note->descdata + 44, 80); |
+ } |
+ |
+ /* Note that for some reason, a spurious space is tacked |
+ onto the end of the args in some (at least one anyway) |
+ implementations, so strip it off if it exists. */ |
+ |
+ { |
+ char *command = elf_tdata (abfd)->core->command; |
+ int n = strlen (command); |
+ |
+ if (0 < n && command[n - 1] == ' ') |
+ command[n - 1] = '\0'; |
+ } |
+ |
+ return TRUE; |
+} |
+ |
+/* Create an entry in a Nios II ELF linker hash table. */ |
+static struct bfd_hash_entry * |
+link_hash_newfunc (struct bfd_hash_entry *entry, |
+ struct bfd_hash_table *table, const char *string) |
+{ |
+ /* Allocate the structure if it has not already been allocated by a |
+ subclass. */ |
+ if (entry == NULL) |
+ { |
+ entry = bfd_hash_allocate (table, |
+ sizeof (struct elf32_nios2_link_hash_entry)); |
+ if (entry == NULL) |
+ return entry; |
+ } |
+ |
+ /* Call the allocation method of the superclass. */ |
+ entry = _bfd_elf_link_hash_newfunc (entry, table, string); |
+ if (entry) |
+ { |
+ struct elf32_nios2_link_hash_entry *eh; |
+ |
+ eh = (struct elf32_nios2_link_hash_entry *) entry; |
+ eh->dyn_relocs = NULL; |
+ eh->tls_type = GOT_UNKNOWN; |
+ eh->got_types_used = 0; |
+ } |
+ |
+ return entry; |
+} |
+ |
+/* Implement bfd_elf32_bfd_reloc_type_lookup: |
+ Given a BFD reloc type, return a howto structure. */ |
+static reloc_howto_type * |
+nios2_elf32_bfd_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED, |
+ bfd_reloc_code_real_type code) |
+{ |
+ int i; |
+ for (i = 0; |
+ i < (int) (sizeof (nios2_reloc_map) / sizeof (struct elf_reloc_map)); |
+ ++i) |
+ if (nios2_reloc_map[i].bfd_val == code) |
+ return &elf_nios2_howto_table_rel[(int) nios2_reloc_map[i].elf_val]; |
+ return NULL; |
+} |
+ |
+/* Implement bfd_elf32_bfd_reloc_name_lookup: |
+ Given a reloc name, return a howto structure. */ |
+static reloc_howto_type * |
+nios2_elf32_bfd_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED, |
+ const char *r_name) |
+{ |
+ unsigned int i; |
+ for (i = 0; |
+ i < (sizeof (elf_nios2_howto_table_rel) |
+ / sizeof (elf_nios2_howto_table_rel[0])); |
+ i++) |
+ if (elf_nios2_howto_table_rel[i].name |
+ && strcasecmp (elf_nios2_howto_table_rel[i].name, r_name) == 0) |
+ return &elf_nios2_howto_table_rel[i]; |
+ |
+ return NULL; |
+} |
+ |
+/* Implement elf_info_to_howto: |
+ Given a ELF32 relocation, fill in a arelent structure. */ |
+static void |
+nios2_elf32_info_to_howto (bfd *abfd ATTRIBUTE_UNUSED, arelent *cache_ptr, |
+ Elf_Internal_Rela *dst) |
+{ |
+ unsigned int r_type; |
+ |
+ r_type = ELF32_R_TYPE (dst->r_info); |
+ BFD_ASSERT (r_type < R_NIOS2_ILLEGAL); |
+ cache_ptr->howto = &elf_nios2_howto_table_rel[r_type]; |
+} |
+ |
+/* Return the base VMA address which should be subtracted from real addresses |
+ when resolving @dtpoff relocation. |
+ This is PT_TLS segment p_vaddr. */ |
+static bfd_vma |
+dtpoff_base (struct bfd_link_info *info) |
+{ |
+ /* If tls_sec is NULL, we should have signalled an error already. */ |
+ if (elf_hash_table (info)->tls_sec == NULL) |
+ return 0; |
+ return elf_hash_table (info)->tls_sec->vma; |
+} |
+ |
+/* Return the relocation value for @tpoff relocation |
+ if STT_TLS virtual address is ADDRESS. */ |
+static bfd_vma |
+tpoff (struct bfd_link_info *info, bfd_vma address) |
+{ |
+ struct elf_link_hash_table *htab = elf_hash_table (info); |
+ |
+ /* If tls_sec is NULL, we should have signalled an error already. */ |
+ if (htab->tls_sec == NULL) |
+ return 0; |
+ return address - htab->tls_sec->vma; |
+} |
+ |
+/* Set the GP value for OUTPUT_BFD. Returns FALSE if this is a |
+ dangerous relocation. */ |
+static bfd_boolean |
+nios2_elf_assign_gp (bfd *output_bfd, bfd_vma *pgp, struct bfd_link_info *info) |
+{ |
+ |
+ bfd_boolean gp_found; |
+ struct bfd_hash_entry *h; |
+ struct bfd_link_hash_entry *lh; |
+ |
+ /* If we've already figured out what GP will be, just return it. */ |
+ *pgp = _bfd_get_gp_value (output_bfd); |
+ if (*pgp) |
+ return TRUE; |
+ |
+ h = bfd_hash_lookup (&info->hash->table, "_gp", FALSE, FALSE); |
+ lh = (struct bfd_link_hash_entry *) h; |
+lookup: |
+ if (lh) |
+ { |
+ switch (lh->type) |
+ { |
+ case bfd_link_hash_undefined: |
+ case bfd_link_hash_undefweak: |
+ case bfd_link_hash_common: |
+ gp_found = FALSE; |
+ break; |
+ case bfd_link_hash_defined: |
+ case bfd_link_hash_defweak: |
+ gp_found = TRUE; |
+ *pgp = lh->u.def.value; |
+ break; |
+ case bfd_link_hash_indirect: |
+ case bfd_link_hash_warning: |
+ lh = lh->u.i.link; |
+ /* @@FIXME ignoring warning for now */ |
+ goto lookup; |
+ case bfd_link_hash_new: |
+ default: |
+ abort (); |
+ } |
+ } |
+ else |
+ gp_found = FALSE; |
+ |
+ if (!gp_found) |
+ { |
+ /* Only get the error once. */ |
+ *pgp = 4; |
+ _bfd_set_gp_value (output_bfd, *pgp); |
+ return FALSE; |
+ } |
+ |
+ _bfd_set_gp_value (output_bfd, *pgp); |
+ |
+ return TRUE; |
+} |
+ |
+/* Retrieve the previously cached _gp pointer, returning bfd_reloc_dangerous |
+ if it's not available as we don't have a link_info pointer available here |
+ to look it up in the output symbol table. We don't need to adjust the |
+ symbol value for an external symbol if we are producing relocatable |
+ output. */ |
+static bfd_reloc_status_type |
+nios2_elf_final_gp (bfd *output_bfd, asymbol *symbol, bfd_boolean relocatable, |
+ char **error_message, bfd_vma *pgp) |
+{ |
+ if (bfd_is_und_section (symbol->section) && !relocatable) |
+ { |
+ *pgp = 0; |
+ return bfd_reloc_undefined; |
+ } |
+ |
+ *pgp = _bfd_get_gp_value (output_bfd); |
+ if (*pgp == 0 && (!relocatable || (symbol->flags & BSF_SECTION_SYM) != 0)) |
+ { |
+ if (relocatable) |
+ { |
+ /* Make up a value. */ |
+ *pgp = symbol->section->output_section->vma + 0x4000; |
+ _bfd_set_gp_value (output_bfd, *pgp); |
+ } |
+ else |
+ { |
+ *error_message |
+ = (char *) _("global pointer relative relocation when _gp not defined"); |
+ return bfd_reloc_dangerous; |
+ } |
+ } |
+ |
+ return bfd_reloc_ok; |
+} |
+ |
+/* The usual way of loading a 32-bit constant into a Nios II register is to |
+ load the high 16 bits in one instruction and then add the low 16 bits with |
+ a signed add. This means that the high halfword needs to be adjusted to |
+ compensate for the sign bit of the low halfword. This function returns the |
+ adjusted high halfword for a given 32-bit constant. */ |
+static |
+bfd_vma hiadj (bfd_vma symbol_value) |
+{ |
+ return ((symbol_value + 0x8000) >> 16) & 0xffff; |
+} |
+ |
+/* Do the relocations that require special handling. */ |
+static bfd_reloc_status_type |
+nios2_elf32_do_hi16_relocate (bfd *abfd, reloc_howto_type *howto, |
+ asection *input_section, |
+ bfd_byte *data, bfd_vma offset, |
+ bfd_vma symbol_value, bfd_vma addend) |
+{ |
+ symbol_value = symbol_value + addend; |
+ addend = 0; |
+ symbol_value = (symbol_value >> 16) & 0xffff; |
+ return _bfd_final_link_relocate (howto, abfd, input_section, |
+ data, offset, symbol_value, addend); |
+} |
+ |
+static bfd_reloc_status_type |
+nios2_elf32_do_lo16_relocate (bfd *abfd, reloc_howto_type *howto, |
+ asection *input_section, |
+ bfd_byte *data, bfd_vma offset, |
+ bfd_vma symbol_value, bfd_vma addend) |
+{ |
+ symbol_value = symbol_value + addend; |
+ addend = 0; |
+ symbol_value = symbol_value & 0xffff; |
+ return _bfd_final_link_relocate (howto, abfd, input_section, |
+ data, offset, symbol_value, addend); |
+} |
+ |
+static bfd_reloc_status_type |
+nios2_elf32_do_hiadj16_relocate (bfd *abfd, reloc_howto_type *howto, |
+ asection *input_section, |
+ bfd_byte *data, bfd_vma offset, |
+ bfd_vma symbol_value, bfd_vma addend) |
+{ |
+ symbol_value = symbol_value + addend; |
+ addend = 0; |
+ symbol_value = hiadj(symbol_value); |
+ return _bfd_final_link_relocate (howto, abfd, input_section, data, offset, |
+ symbol_value, addend); |
+} |
+ |
+static bfd_reloc_status_type |
+nios2_elf32_do_pcrel_lo16_relocate (bfd *abfd, reloc_howto_type *howto, |
+ asection *input_section, |
+ bfd_byte *data, bfd_vma offset, |
+ bfd_vma symbol_value, bfd_vma addend) |
+{ |
+ symbol_value = symbol_value + addend; |
+ addend = 0; |
+ symbol_value = symbol_value & 0xffff; |
+ return _bfd_final_link_relocate (howto, abfd, input_section, |
+ data, offset, symbol_value, addend); |
+} |
+ |
+static bfd_reloc_status_type |
+nios2_elf32_do_pcrel_hiadj16_relocate (bfd *abfd, reloc_howto_type *howto, |
+ asection *input_section, |
+ bfd_byte *data, bfd_vma offset, |
+ bfd_vma symbol_value, bfd_vma addend) |
+{ |
+ symbol_value = symbol_value + addend; |
+ symbol_value -= (input_section->output_section->vma |
+ + input_section->output_offset); |
+ symbol_value -= offset; |
+ addend = 0; |
+ symbol_value = hiadj(symbol_value); |
+ return _bfd_final_link_relocate (howto, abfd, input_section, data, offset, |
+ symbol_value, addend); |
+} |
+ |
+static bfd_reloc_status_type |
+nios2_elf32_do_pcrel16_relocate (bfd *abfd, reloc_howto_type *howto, |
+ asection *input_section, |
+ bfd_byte *data, bfd_vma offset, |
+ bfd_vma symbol_value, bfd_vma addend) |
+{ |
+ /* NIOS2 pc relative relocations are relative to the next 32-bit instruction |
+ so we need to subtract 4 before doing a final_link_relocate. */ |
+ symbol_value = symbol_value + addend - 4; |
+ addend = 0; |
+ return _bfd_final_link_relocate (howto, abfd, input_section, |
+ data, offset, symbol_value, addend); |
+} |
+ |
+static bfd_reloc_status_type |
+nios2_elf32_do_call26_relocate (bfd *abfd, reloc_howto_type *howto, |
+ asection *input_section, |
+ bfd_byte *data, bfd_vma offset, |
+ bfd_vma symbol_value, bfd_vma addend) |
+{ |
+ /* Check that the relocation is in the same page as the current address. */ |
+ if (((symbol_value + addend) & 0xf0000000) |
+ != ((input_section->output_section->vma + offset) & 0xf0000000)) |
+ return bfd_reloc_overflow; |
+ |
+ return _bfd_final_link_relocate (howto, abfd, input_section, |
+ data, offset, symbol_value, addend); |
+} |
+ |
+static bfd_reloc_status_type |
+nios2_elf32_do_gprel_relocate (bfd *abfd, reloc_howto_type *howto, |
+ asection *input_section, |
+ bfd_byte *data, bfd_vma offset, |
+ bfd_vma symbol_value, bfd_vma addend) |
+{ |
+ /* Because we need the output_bfd, the special handling is done |
+ in nios2_elf32_relocate_section or in nios2_elf32_gprel_relocate. */ |
+ return _bfd_final_link_relocate (howto, abfd, input_section, |
+ data, offset, symbol_value, addend); |
+} |
+ |
+static bfd_reloc_status_type |
+nios2_elf32_do_ujmp_relocate (bfd *abfd, reloc_howto_type *howto, |
+ asection *input_section, |
+ bfd_byte *data, bfd_vma offset, |
+ bfd_vma symbol_value, bfd_vma addend) |
+{ |
+ bfd_vma symbol_lo16, symbol_hi16; |
+ bfd_reloc_status_type r; |
+ symbol_value = symbol_value + addend; |
+ addend = 0; |
+ symbol_hi16 = (symbol_value >> 16) & 0xffff; |
+ symbol_lo16 = symbol_value & 0xffff; |
+ |
+ r = _bfd_final_link_relocate (howto, abfd, input_section, |
+ data, offset, symbol_hi16, addend); |
+ |
+ if (r == bfd_reloc_ok) |
+ return _bfd_final_link_relocate (howto, abfd, input_section, |
+ data, offset + 4, symbol_lo16, addend); |
+ |
+ return r; |
+} |
+ |
+static bfd_reloc_status_type |
+nios2_elf32_do_cjmp_relocate (bfd *abfd, reloc_howto_type *howto, |
+ asection *input_section, |
+ bfd_byte *data, bfd_vma offset, |
+ bfd_vma symbol_value, bfd_vma addend) |
+{ |
+ bfd_vma symbol_lo16, symbol_hi16; |
+ bfd_reloc_status_type r; |
+ symbol_value = symbol_value + addend; |
+ addend = 0; |
+ symbol_hi16 = (symbol_value >> 16) & 0xffff; |
+ symbol_lo16 = symbol_value & 0xffff; |
+ |
+ r = _bfd_final_link_relocate (howto, abfd, input_section, |
+ data, offset, symbol_hi16, addend); |
+ |
+ if (r == bfd_reloc_ok) |
+ return _bfd_final_link_relocate (howto, abfd, input_section, |
+ data, offset + 4, symbol_lo16, addend); |
+ |
+ return r; |
+} |
+ |
+static bfd_reloc_status_type |
+nios2_elf32_do_callr_relocate (bfd *abfd, reloc_howto_type *howto, |
+ asection *input_section, |
+ bfd_byte *data, bfd_vma offset, |
+ bfd_vma symbol_value, bfd_vma addend) |
+{ |
+ bfd_vma symbol_lo16, symbol_hi16; |
+ bfd_reloc_status_type r; |
+ symbol_value = symbol_value + addend; |
+ addend = 0; |
+ symbol_hi16 = (symbol_value >> 16) & 0xffff; |
+ symbol_lo16 = symbol_value & 0xffff; |
+ |
+ r = _bfd_final_link_relocate (howto, abfd, input_section, |
+ data, offset, symbol_hi16, addend); |
+ |
+ if (r == bfd_reloc_ok) |
+ return _bfd_final_link_relocate (howto, abfd, input_section, |
+ data, offset + 4, symbol_lo16, addend); |
+ |
+ return r; |
+} |
+ |
+/* HOWTO handlers for relocations that require special handling. */ |
+ |
+/* This is for relocations used only when relaxing to ensure |
+ changes in size of section don't screw up .align. */ |
+static bfd_reloc_status_type |
+nios2_elf32_ignore_reloc (bfd *abfd ATTRIBUTE_UNUSED, arelent *reloc_entry, |
+ asymbol *symbol ATTRIBUTE_UNUSED, |
+ void *data ATTRIBUTE_UNUSED, asection *input_section, |
+ bfd *output_bfd, |
+ char **error_message ATTRIBUTE_UNUSED) |
+{ |
+ if (output_bfd != NULL) |
+ reloc_entry->address += input_section->output_offset; |
+ return bfd_reloc_ok; |
+} |
+ |
+static bfd_reloc_status_type |
+nios2_elf32_hi16_relocate (bfd *abfd, arelent *reloc_entry, asymbol *symbol, |
+ void *data, asection *input_section, |
+ bfd *output_bfd, |
+ char **error_message ATTRIBUTE_UNUSED) |
+{ |
+ /* This part is from bfd_elf_generic_reloc. */ |
+ if (output_bfd != NULL |
+ && (symbol->flags & BSF_SECTION_SYM) == 0 |
+ && (!reloc_entry->howto->partial_inplace || reloc_entry->addend == 0)) |
+ { |
+ reloc_entry->address += input_section->output_offset; |
+ return bfd_reloc_ok; |
+ } |
+ |
+ if (output_bfd != NULL) |
+ /* FIXME: See bfd_perform_relocation. Is this right? */ |
+ return bfd_reloc_continue; |
+ |
+ return nios2_elf32_do_hi16_relocate (abfd, reloc_entry->howto, |
+ input_section, |
+ data, reloc_entry->address, |
+ (symbol->value |
+ + symbol->section->output_section->vma |
+ + symbol->section->output_offset), |
+ reloc_entry->addend); |
+} |
+ |
+static bfd_reloc_status_type |
+nios2_elf32_lo16_relocate (bfd *abfd, arelent *reloc_entry, asymbol *symbol, |
+ void *data, asection *input_section, |
+ bfd *output_bfd, |
+ char **error_message ATTRIBUTE_UNUSED) |
+{ |
+ /* This part is from bfd_elf_generic_reloc. */ |
+ if (output_bfd != NULL |
+ && (symbol->flags & BSF_SECTION_SYM) == 0 |
+ && (!reloc_entry->howto->partial_inplace || reloc_entry->addend == 0)) |
+ { |
+ reloc_entry->address += input_section->output_offset; |
+ return bfd_reloc_ok; |
+ } |
+ |
+ if (output_bfd != NULL) |
+ /* FIXME: See bfd_perform_relocation. Is this right? */ |
+ return bfd_reloc_continue; |
+ |
+ return nios2_elf32_do_lo16_relocate (abfd, reloc_entry->howto, |
+ input_section, |
+ data, reloc_entry->address, |
+ (symbol->value |
+ + symbol->section->output_section->vma |
+ + symbol->section->output_offset), |
+ reloc_entry->addend); |
+} |
+ |
+static bfd_reloc_status_type |
+nios2_elf32_hiadj16_relocate (bfd *abfd, arelent *reloc_entry, asymbol *symbol, |
+ void *data, asection *input_section, |
+ bfd *output_bfd, |
+ char **error_message ATTRIBUTE_UNUSED) |
+{ |
+ /* This part is from bfd_elf_generic_reloc. */ |
+ if (output_bfd != NULL |
+ && (symbol->flags & BSF_SECTION_SYM) == 0 |
+ && (!reloc_entry->howto->partial_inplace || reloc_entry->addend == 0)) |
+ { |
+ reloc_entry->address += input_section->output_offset; |
+ return bfd_reloc_ok; |
+ } |
+ |
+ if (output_bfd != NULL) |
+ /* FIXME: See bfd_perform_relocation. Is this right? */ |
+ return bfd_reloc_continue; |
+ |
+ return nios2_elf32_do_hiadj16_relocate (abfd, reloc_entry->howto, |
+ input_section, |
+ data, reloc_entry->address, |
+ (symbol->value |
+ + symbol->section->output_section->vma |
+ + symbol->section->output_offset), |
+ reloc_entry->addend); |
+} |
+ |
+static bfd_reloc_status_type |
+nios2_elf32_pcrel_lo16_relocate (bfd *abfd, arelent *reloc_entry, |
+ asymbol *symbol, void *data, |
+ asection *input_section, bfd *output_bfd, |
+ char **error_message ATTRIBUTE_UNUSED) |
+{ |
+ /* This part is from bfd_elf_generic_reloc. */ |
+ if (output_bfd != NULL |
+ && (symbol->flags & BSF_SECTION_SYM) == 0 |
+ && (!reloc_entry->howto->partial_inplace || reloc_entry->addend == 0)) |
+ { |
+ reloc_entry->address += input_section->output_offset; |
+ return bfd_reloc_ok; |
+ } |
+ |
+ if (output_bfd != NULL) |
+ /* FIXME: See bfd_perform_relocation. Is this right? */ |
+ return bfd_reloc_continue; |
+ |
+ return nios2_elf32_do_pcrel_lo16_relocate ( |
+ abfd, reloc_entry->howto, input_section, data, reloc_entry->address, |
+ (symbol->value + symbol->section->output_section->vma |
+ + symbol->section->output_offset), |
+ reloc_entry->addend); |
+} |
+ |
+static bfd_reloc_status_type |
+nios2_elf32_pcrel_hiadj16_relocate (bfd *abfd, arelent *reloc_entry, |
+ asymbol *symbol, void *data, |
+ asection *input_section, bfd *output_bfd, |
+ char **error_message ATTRIBUTE_UNUSED) |
+{ |
+ /* This part is from bfd_elf_generic_reloc. */ |
+ if (output_bfd != NULL |
+ && (symbol->flags & BSF_SECTION_SYM) == 0 |
+ && (!reloc_entry->howto->partial_inplace || reloc_entry->addend == 0)) |
+ { |
+ reloc_entry->address += input_section->output_offset; |
+ return bfd_reloc_ok; |
+ } |
+ |
+ if (output_bfd != NULL) |
+ /* FIXME: See bfd_perform_relocation. Is this right? */ |
+ return bfd_reloc_continue; |
+ |
+ return nios2_elf32_do_pcrel_hiadj16_relocate ( |
+ abfd, reloc_entry->howto, input_section, data, reloc_entry->address, |
+ (symbol->value + symbol->section->output_section->vma |
+ + symbol->section->output_offset), |
+ reloc_entry->addend); |
+} |
+ |
+static bfd_reloc_status_type |
+nios2_elf32_pcrel16_relocate (bfd *abfd, arelent *reloc_entry, asymbol *symbol, |
+ void *data, asection *input_section, |
+ bfd *output_bfd, |
+ char **error_message ATTRIBUTE_UNUSED) |
+{ |
+ /* This part is from bfd_elf_generic_reloc. */ |
+ if (output_bfd != NULL |
+ && (symbol->flags & BSF_SECTION_SYM) == 0 |
+ && (!reloc_entry->howto->partial_inplace || reloc_entry->addend == 0)) |
+ { |
+ reloc_entry->address += input_section->output_offset; |
+ return bfd_reloc_ok; |
+ } |
+ |
+ if (output_bfd != NULL) |
+ /* FIXME: See bfd_perform_relocation. Is this right? */ |
+ return bfd_reloc_continue; |
+ |
+ return nios2_elf32_do_pcrel16_relocate (abfd, reloc_entry->howto, |
+ input_section, |
+ data, reloc_entry->address, |
+ (symbol->value |
+ + symbol->section->output_section->vma |
+ + symbol->section->output_offset), |
+ reloc_entry->addend); |
+} |
+ |
+static bfd_reloc_status_type |
+nios2_elf32_call26_relocate (bfd *abfd, arelent *reloc_entry, asymbol *symbol, |
+ void *data, asection *input_section, |
+ bfd *output_bfd, |
+ char **error_message ATTRIBUTE_UNUSED) |
+{ |
+ /* This part is from bfd_elf_generic_reloc. */ |
+ if (output_bfd != NULL |
+ && (symbol->flags & BSF_SECTION_SYM) == 0 |
+ && (!reloc_entry->howto->partial_inplace || reloc_entry->addend == 0)) |
+ { |
+ reloc_entry->address += input_section->output_offset; |
+ return bfd_reloc_ok; |
+ } |
+ |
+ if (output_bfd != NULL) |
+ /* FIXME: See bfd_perform_relocation. Is this right? */ |
+ return bfd_reloc_continue; |
+ |
+ return nios2_elf32_do_call26_relocate (abfd, reloc_entry->howto, |
+ input_section, |
+ data, reloc_entry->address, |
+ (symbol->value |
+ + symbol->section->output_section->vma |
+ + symbol->section->output_offset), |
+ reloc_entry->addend); |
+} |
+ |
+static bfd_reloc_status_type |
+nios2_elf32_gprel_relocate (bfd *abfd, arelent *reloc_entry, asymbol *symbol, |
+ void *data, asection *input_section, |
+ bfd *output_bfd, char **msg) |
+{ |
+ bfd_vma relocation; |
+ bfd_vma gp; |
+ bfd_reloc_status_type r; |
+ |
+ |
+ /* This part is from bfd_elf_generic_reloc. */ |
+ if (output_bfd != NULL |
+ && (symbol->flags & BSF_SECTION_SYM) == 0 |
+ && (!reloc_entry->howto->partial_inplace || reloc_entry->addend == 0)) |
+ { |
+ reloc_entry->address += input_section->output_offset; |
+ return bfd_reloc_ok; |
+ } |
+ |
+ if (output_bfd != NULL) |
+ /* FIXME: See bfd_perform_relocation. Is this right? */ |
+ return bfd_reloc_continue; |
+ |
+ relocation = (symbol->value |
+ + symbol->section->output_section->vma |
+ + symbol->section->output_offset); |
+ |
+ /* This assumes we've already cached the _gp symbol. */ |
+ r = nios2_elf_final_gp (abfd, symbol, FALSE, msg, &gp); |
+ if (r == bfd_reloc_ok) |
+ { |
+ relocation = relocation + reloc_entry->addend - gp; |
+ reloc_entry->addend = 0; |
+ if ((signed) relocation < -32768 || (signed) relocation > 32767) |
+ { |
+ *msg = _("global pointer relative address out of range"); |
+ r = bfd_reloc_outofrange; |
+ } |
+ else |
+ r = nios2_elf32_do_gprel_relocate (abfd, reloc_entry->howto, |
+ input_section, |
+ data, reloc_entry->address, |
+ relocation, reloc_entry->addend); |
+ } |
+ |
+ return r; |
+} |
+ |
+static bfd_reloc_status_type |
+nios2_elf32_ujmp_relocate (bfd *abfd, arelent *reloc_entry, asymbol *symbol, |
+ void *data, asection *input_section, |
+ bfd *output_bfd, char **msg ATTRIBUTE_UNUSED) |
+{ |
+ /* This part is from bfd_elf_generic_reloc. */ |
+ if (output_bfd != NULL |
+ && (symbol->flags & BSF_SECTION_SYM) == 0 |
+ && (!reloc_entry->howto->partial_inplace || reloc_entry->addend == 0)) |
+ { |
+ reloc_entry->address += input_section->output_offset; |
+ return bfd_reloc_ok; |
+ } |
+ |
+ if (output_bfd != NULL) |
+ /* FIXME: See bfd_perform_relocation. Is this right? */ |
+ return bfd_reloc_continue; |
+ |
+ return nios2_elf32_do_ujmp_relocate (abfd, reloc_entry->howto, |
+ input_section, |
+ data, reloc_entry->address, |
+ (symbol->value |
+ + symbol->section->output_section->vma |
+ + symbol->section->output_offset), |
+ reloc_entry->addend); |
+} |
+ |
+static bfd_reloc_status_type |
+nios2_elf32_cjmp_relocate (bfd *abfd, arelent *reloc_entry, asymbol *symbol, |
+ void *data, asection *input_section, |
+ bfd *output_bfd, char **msg ATTRIBUTE_UNUSED) |
+{ |
+ /* This part is from bfd_elf_generic_reloc. */ |
+ if (output_bfd != NULL |
+ && (symbol->flags & BSF_SECTION_SYM) == 0 |
+ && (!reloc_entry->howto->partial_inplace || reloc_entry->addend == 0)) |
+ { |
+ reloc_entry->address += input_section->output_offset; |
+ return bfd_reloc_ok; |
+ } |
+ |
+ if (output_bfd != NULL) |
+ /* FIXME: See bfd_perform_relocation. Is this right? */ |
+ return bfd_reloc_continue; |
+ |
+ return nios2_elf32_do_cjmp_relocate (abfd, reloc_entry->howto, |
+ input_section, |
+ data, reloc_entry->address, |
+ (symbol->value |
+ + symbol->section->output_section->vma |
+ + symbol->section->output_offset), |
+ reloc_entry->addend); |
+} |
+ |
+static bfd_reloc_status_type |
+nios2_elf32_callr_relocate (bfd *abfd, arelent *reloc_entry, asymbol *symbol, |
+ void *data, asection *input_section, |
+ bfd *output_bfd, char **msg ATTRIBUTE_UNUSED) |
+{ |
+ /* This part is from bfd_elf_generic_reloc. */ |
+ if (output_bfd != NULL |
+ && (symbol->flags & BSF_SECTION_SYM) == 0 |
+ && (!reloc_entry->howto->partial_inplace || reloc_entry->addend == 0)) |
+ { |
+ reloc_entry->address += input_section->output_offset; |
+ return bfd_reloc_ok; |
+ } |
+ |
+ if (output_bfd != NULL) |
+ /* FIXME: See bfd_perform_relocation. Is this right? */ |
+ return bfd_reloc_continue; |
+ |
+ return nios2_elf32_do_callr_relocate (abfd, reloc_entry->howto, |
+ input_section, |
+ data, reloc_entry->address, |
+ (symbol->value |
+ + symbol->section->output_section->vma |
+ + symbol->section->output_offset), |
+ reloc_entry->addend); |
+} |
+ |
+ |
+/* Implement elf_backend_relocate_section. */ |
+static bfd_boolean |
+nios2_elf32_relocate_section (bfd *output_bfd, |
+ struct bfd_link_info *info, |
+ bfd *input_bfd, |
+ asection *input_section, |
+ bfd_byte *contents, |
+ Elf_Internal_Rela *relocs, |
+ Elf_Internal_Sym *local_syms, |
+ asection **local_sections) |
+{ |
+ Elf_Internal_Shdr *symtab_hdr; |
+ struct elf_link_hash_entry **sym_hashes; |
+ Elf_Internal_Rela *rel; |
+ Elf_Internal_Rela *relend; |
+ struct elf32_nios2_link_hash_table *htab; |
+ asection *sgot; |
+ asection *splt; |
+ asection *sreloc = NULL; |
+ bfd_vma *local_got_offsets; |
+ |
+ symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr; |
+ sym_hashes = elf_sym_hashes (input_bfd); |
+ relend = relocs + input_section->reloc_count; |
+ |
+ htab = elf32_nios2_hash_table (info); |
+ sgot = htab->root.sgot; |
+ splt = htab->root.splt; |
+ local_got_offsets = elf_local_got_offsets (input_bfd); |
+ |
+ for (rel = relocs; rel < relend; rel++) |
+ { |
+ reloc_howto_type *howto; |
+ unsigned long r_symndx; |
+ Elf_Internal_Sym *sym; |
+ asection *sec; |
+ struct elf_link_hash_entry *h; |
+ struct elf32_nios2_link_hash_entry *eh; |
+ bfd_vma relocation; |
+ bfd_vma gp; |
+ bfd_vma reloc_address; |
+ bfd_reloc_status_type r = bfd_reloc_ok; |
+ const char *name = NULL; |
+ int r_type; |
+ const char *format; |
+ char msgbuf[256]; |
+ const char* msg = (const char*) NULL; |
+ bfd_boolean unresolved_reloc; |
+ bfd_vma off; |
+ int use_plt; |
+ |
+ r_type = ELF32_R_TYPE (rel->r_info); |
+ r_symndx = ELF32_R_SYM (rel->r_info); |
+ |
+ howto = lookup_howto ((unsigned) ELF32_R_TYPE (rel->r_info)); |
+ h = NULL; |
+ sym = NULL; |
+ sec = NULL; |
+ |
+ if (r_symndx < symtab_hdr->sh_info) |
+ { |
+ sym = local_syms + r_symndx; |
+ sec = local_sections[r_symndx]; |
+ relocation = _bfd_elf_rela_local_sym (output_bfd, sym, &sec, rel); |
+ } |
+ else |
+ { |
+ bfd_boolean warned, ignored; |
+ |
+ RELOC_FOR_GLOBAL_SYMBOL (info, input_bfd, input_section, rel, |
+ r_symndx, symtab_hdr, sym_hashes, |
+ h, sec, relocation, |
+ unresolved_reloc, warned, ignored); |
+ } |
+ |
+ if (sec && discarded_section (sec)) |
+ RELOC_AGAINST_DISCARDED_SECTION (info, input_bfd, input_section, |
+ rel, 1, relend, howto, 0, contents); |
+ |
+ /* Nothing more to do unless this is a final link. */ |
+ if (info->relocatable) |
+ continue; |
+ |
+ if (sec && sec->output_section) |
+ reloc_address = (sec->output_section->vma + sec->output_offset |
+ + rel->r_offset); |
+ else |
+ reloc_address = 0; |
+ |
+ if (howto) |
+ { |
+ switch (howto->type) |
+ { |
+ case R_NIOS2_HI16: |
+ r = nios2_elf32_do_hi16_relocate (input_bfd, howto, |
+ input_section, |
+ contents, rel->r_offset, |
+ relocation, rel->r_addend); |
+ break; |
+ case R_NIOS2_LO16: |
+ r = nios2_elf32_do_lo16_relocate (input_bfd, howto, |
+ input_section, |
+ contents, rel->r_offset, |
+ relocation, rel->r_addend); |
+ break; |
+ case R_NIOS2_PCREL_LO: |
+ r = nios2_elf32_do_pcrel_lo16_relocate (input_bfd, howto, |
+ input_section, |
+ contents, |
+ rel->r_offset, |
+ relocation, |
+ rel->r_addend); |
+ break; |
+ case R_NIOS2_HIADJ16: |
+ r = nios2_elf32_do_hiadj16_relocate (input_bfd, howto, |
+ input_section, contents, |
+ rel->r_offset, relocation, |
+ rel->r_addend); |
+ break; |
+ case R_NIOS2_PCREL_HA: |
+ r = nios2_elf32_do_pcrel_hiadj16_relocate (input_bfd, howto, |
+ input_section, |
+ contents, |
+ rel->r_offset, |
+ relocation, |
+ rel->r_addend); |
+ break; |
+ case R_NIOS2_PCREL16: |
+ r = nios2_elf32_do_pcrel16_relocate (input_bfd, howto, |
+ input_section, contents, |
+ rel->r_offset, relocation, |
+ rel->r_addend); |
+ break; |
+ case R_NIOS2_GPREL: |
+ /* Turns an absolute address into a gp-relative address. */ |
+ if (!nios2_elf_assign_gp (output_bfd, &gp, info)) |
+ { |
+ format = _("global pointer relative relocation at address " |
+ "0x%08x when _gp not defined\n"); |
+ sprintf (msgbuf, format, reloc_address); |
+ msg = msgbuf; |
+ r = bfd_reloc_dangerous; |
+ } |
+ else |
+ { |
+ bfd_vma symbol_address = rel->r_addend + relocation; |
+ relocation = relocation + rel->r_addend - gp; |
+ rel->r_addend = 0; |
+ if (((signed) relocation < -32768 |
+ || (signed) relocation > 32767) |
+ && (!h |
+ || h->root.type == bfd_link_hash_defined |
+ || h->root.type == bfd_link_hash_defweak)) |
+ { |
+ format = _("Unable to reach %s (at 0x%08x) from the " |
+ "global pointer (at 0x%08x) because the " |
+ "offset (%d) is out of the allowed range, " |
+ "-32678 to 32767.\n" ); |
+ sprintf (msgbuf, format, name, symbol_address, gp, |
+ (signed)relocation); |
+ msg = msgbuf; |
+ r = bfd_reloc_outofrange; |
+ } |
+ else |
+ r = _bfd_final_link_relocate (howto, input_bfd, |
+ input_section, contents, |
+ rel->r_offset, relocation, |
+ rel->r_addend); |
+ } |
+ |
+ break; |
+ case R_NIOS2_UJMP: |
+ r = nios2_elf32_do_ujmp_relocate (input_bfd, howto, |
+ input_section, |
+ contents, rel->r_offset, |
+ relocation, rel->r_addend); |
+ break; |
+ case R_NIOS2_CJMP: |
+ r = nios2_elf32_do_cjmp_relocate (input_bfd, howto, |
+ input_section, |
+ contents, rel->r_offset, |
+ relocation, rel->r_addend); |
+ break; |
+ case R_NIOS2_CALLR: |
+ r = nios2_elf32_do_callr_relocate (input_bfd, howto, |
+ input_section, contents, |
+ rel->r_offset, relocation, |
+ rel->r_addend); |
+ break; |
+ case R_NIOS2_CALL26: |
+ /* If we have a call to an undefined weak symbol, we just want |
+ to stuff a zero in the bits of the call instruction and |
+ bypass the normal call26 relocation handling, because it'll |
+ diagnose an overflow error if address 0 isn't in the same |
+ 256MB segment as the call site. Presumably the call |
+ should be guarded by a null check anyway. */ |
+ if (h != NULL && h->root.type == bfd_link_hash_undefweak) |
+ { |
+ BFD_ASSERT (relocation == 0 && rel->r_addend == 0); |
+ r = _bfd_final_link_relocate (howto, input_bfd, |
+ input_section, contents, |
+ rel->r_offset, relocation, |
+ rel->r_addend); |
+ break; |
+ } |
+ /* Handle relocations which should use the PLT entry. |
+ NIOS2_BFD_RELOC_32 relocations will use the symbol's value, |
+ which may point to a PLT entry, but we don't need to handle |
+ that here. If we created a PLT entry, all branches in this |
+ object should go to it. */ |
+ if (h != NULL && splt != NULL && h->plt.offset != (bfd_vma) -1) |
+ { |
+ /* If we've created a .plt section, and assigned a PLT entry |
+ to this function, it should not be known to bind locally. |
+ If it were, we would have cleared the PLT entry. */ |
+ BFD_ASSERT (!SYMBOL_CALLS_LOCAL (info, h)); |
+ |
+ relocation = (splt->output_section->vma |
+ + splt->output_offset |
+ + h->plt.offset); |
+ |
+ unresolved_reloc = FALSE; |
+ } |
+ r = nios2_elf32_do_call26_relocate (input_bfd, howto, |
+ input_section, contents, |
+ rel->r_offset, relocation, |
+ rel->r_addend); |
+ break; |
+ case R_NIOS2_ALIGN: |
+ r = bfd_reloc_ok; |
+ /* For symmetry this would be |
+ r = nios2_elf32_do_ignore_reloc (input_bfd, howto, |
+ input_section, contents, |
+ rel->r_offset, relocation, |
+ rel->r_addend); |
+ but do_ignore_reloc would do no more than return |
+ bfd_reloc_ok. */ |
+ break; |
+ |
+ case R_NIOS2_GOT16: |
+ case R_NIOS2_CALL16: |
+ /* Relocation is to the entry for this symbol in the |
+ global offset table. */ |
+ if (sgot == NULL) |
+ { |
+ r = bfd_reloc_notsupported; |
+ break; |
+ } |
+ |
+ use_plt = 0; |
+ |
+ if (h != NULL) |
+ { |
+ bfd_boolean dyn; |
+ |
+ eh = (struct elf32_nios2_link_hash_entry *)h; |
+ use_plt = (eh->got_types_used == CALL16_USED |
+ && h->plt.offset != (bfd_vma) -1); |
+ |
+ off = h->got.offset; |
+ BFD_ASSERT (off != (bfd_vma) -1); |
+ dyn = elf_hash_table (info)->dynamic_sections_created; |
+ if (! WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info->shared, h) |
+ || (info->shared |
+ && SYMBOL_REFERENCES_LOCAL (info, h)) |
+ || (ELF_ST_VISIBILITY (h->other) |
+ && h->root.type == bfd_link_hash_undefweak)) |
+ { |
+ /* This is actually a static link, or it is a -Bsymbolic |
+ link and the symbol is defined locally. We must |
+ initialize this entry in the global offset table. |
+ Since the offset must always be a multiple of 4, we |
+ use the least significant bit to record whether we |
+ have initialized it already. |
+ |
+ When doing a dynamic link, we create a .rela.got |
+ relocation entry to initialize the value. This is |
+ done in the finish_dynamic_symbol routine. */ |
+ if ((off & 1) != 0) |
+ off &= ~1; |
+ else |
+ { |
+ bfd_put_32 (output_bfd, relocation, |
+ sgot->contents + off); |
+ h->got.offset |= 1; |
+ } |
+ } |
+ else |
+ unresolved_reloc = FALSE; |
+ } |
+ else |
+ { |
+ BFD_ASSERT (local_got_offsets != NULL |
+ && local_got_offsets[r_symndx] != (bfd_vma) -1); |
+ |
+ off = local_got_offsets[r_symndx]; |
+ |
+ /* The offset must always be a multiple of 4. We use the |
+ least significant bit to record whether we have already |
+ generated the necessary reloc. */ |
+ if ((off & 1) != 0) |
+ off &= ~1; |
+ else |
+ { |
+ bfd_put_32 (output_bfd, relocation, |
+ sgot->contents + off); |
+ |
+ if (info->shared) |
+ { |
+ asection *srelgot; |
+ Elf_Internal_Rela outrel; |
+ bfd_byte *loc; |
+ |
+ srelgot = htab->root.srelgot; |
+ BFD_ASSERT (srelgot != NULL); |
+ |
+ outrel.r_addend = relocation; |
+ outrel.r_offset = (sgot->output_section->vma |
+ + sgot->output_offset |
+ + off); |
+ outrel.r_info = ELF32_R_INFO (0, R_NIOS2_RELATIVE); |
+ loc = srelgot->contents; |
+ loc += (srelgot->reloc_count++ * |
+ sizeof (Elf32_External_Rela)); |
+ bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc); |
+ } |
+ |
+ local_got_offsets[r_symndx] |= 1; |
+ } |
+ } |
+ |
+ if (use_plt && info->shared) |
+ { |
+ off = ((h->plt.offset - 24) / 12 + 3) * 4; |
+ relocation = htab->root.sgotplt->output_offset + off; |
+ } |
+ else |
+ relocation = sgot->output_offset + off; |
+ |
+ /* This relocation does not use the addend. */ |
+ rel->r_addend = 0; |
+ |
+ r = _bfd_final_link_relocate (howto, input_bfd, input_section, |
+ contents, rel->r_offset, |
+ relocation, rel->r_addend); |
+ break; |
+ |
+ case R_NIOS2_GOTOFF_LO: |
+ case R_NIOS2_GOTOFF_HA: |
+ case R_NIOS2_GOTOFF: |
+ /* Relocation is relative to the start of the |
+ global offset table. */ |
+ |
+ BFD_ASSERT (sgot != NULL); |
+ if (sgot == NULL) |
+ { |
+ r = bfd_reloc_notsupported; |
+ break; |
+ } |
+ |
+ /* Note that sgot->output_offset is not involved in this |
+ calculation. We always want the start of .got. If we |
+ define _GLOBAL_OFFSET_TABLE in a different way, as is |
+ permitted by the ABI, we might have to change this |
+ calculation. */ |
+ relocation -= sgot->output_section->vma; |
+ switch (howto->type) |
+ { |
+ case R_NIOS2_GOTOFF_LO: |
+ r = nios2_elf32_do_lo16_relocate (input_bfd, howto, |
+ input_section, contents, |
+ rel->r_offset, relocation, |
+ rel->r_addend); |
+ break; |
+ case R_NIOS2_GOTOFF_HA: |
+ r = nios2_elf32_do_hiadj16_relocate (input_bfd, howto, |
+ input_section, contents, |
+ rel->r_offset, |
+ relocation, |
+ rel->r_addend); |
+ break; |
+ default: |
+ r = _bfd_final_link_relocate (howto, input_bfd, |
+ input_section, contents, |
+ rel->r_offset, relocation, |
+ rel->r_addend); |
+ break; |
+ } |
+ break; |
+ |
+ case R_NIOS2_TLS_LDO16: |
+ relocation -= dtpoff_base (info) + DTP_OFFSET; |
+ |
+ r = _bfd_final_link_relocate (howto, input_bfd, input_section, |
+ contents, rel->r_offset, |
+ relocation, rel->r_addend); |
+ break; |
+ case R_NIOS2_TLS_LDM16: |
+ if (htab->root.sgot == NULL) |
+ abort (); |
+ |
+ off = htab->tls_ldm_got.offset; |
+ |
+ if ((off & 1) != 0) |
+ off &= ~1; |
+ else |
+ { |
+ /* If we don't know the module number, create a relocation |
+ for it. */ |
+ if (info->shared) |
+ { |
+ Elf_Internal_Rela outrel; |
+ bfd_byte *loc; |
+ |
+ if (htab->root.srelgot == NULL) |
+ abort (); |
+ |
+ outrel.r_addend = 0; |
+ outrel.r_offset = (htab->root.sgot->output_section->vma |
+ + htab->root.sgot->output_offset |
+ + off); |
+ outrel.r_info = ELF32_R_INFO (0, R_NIOS2_TLS_DTPMOD); |
+ |
+ loc = htab->root.srelgot->contents; |
+ loc += (htab->root.srelgot->reloc_count++ |
+ * sizeof (Elf32_External_Rela)); |
+ bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc); |
+ } |
+ else |
+ bfd_put_32 (output_bfd, 1, |
+ htab->root.sgot->contents + off); |
+ |
+ htab->tls_ldm_got.offset |= 1; |
+ } |
+ |
+ relocation = (htab->root.sgot->output_offset + off); |
+ |
+ r = _bfd_final_link_relocate (howto, input_bfd, input_section, |
+ contents, rel->r_offset, |
+ relocation, rel->r_addend); |
+ |
+ break; |
+ case R_NIOS2_TLS_GD16: |
+ case R_NIOS2_TLS_IE16: |
+ { |
+ int indx; |
+ char tls_type; |
+ |
+ if (htab->root.sgot == NULL) |
+ abort (); |
+ |
+ indx = 0; |
+ if (h != NULL) |
+ { |
+ bfd_boolean dyn; |
+ dyn = htab->root.dynamic_sections_created; |
+ if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info->shared, h) |
+ && (!info->shared |
+ || !SYMBOL_REFERENCES_LOCAL (info, h))) |
+ { |
+ unresolved_reloc = FALSE; |
+ indx = h->dynindx; |
+ } |
+ off = h->got.offset; |
+ tls_type = (((struct elf32_nios2_link_hash_entry *) h) |
+ ->tls_type); |
+ } |
+ else |
+ { |
+ if (local_got_offsets == NULL) |
+ abort (); |
+ off = local_got_offsets[r_symndx]; |
+ tls_type = (elf32_nios2_local_got_tls_type (input_bfd) |
+ [r_symndx]); |
+ } |
+ |
+ if (tls_type == GOT_UNKNOWN) |
+ abort (); |
+ |
+ if ((off & 1) != 0) |
+ off &= ~1; |
+ else |
+ { |
+ bfd_boolean need_relocs = FALSE; |
+ Elf_Internal_Rela outrel; |
+ bfd_byte *loc = NULL; |
+ int cur_off = off; |
+ |
+ /* The GOT entries have not been initialized yet. Do it |
+ now, and emit any relocations. If both an IE GOT and a |
+ GD GOT are necessary, we emit the GD first. */ |
+ |
+ if ((info->shared || indx != 0) |
+ && (h == NULL |
+ || ELF_ST_VISIBILITY (h->other) == STV_DEFAULT |
+ || h->root.type != bfd_link_hash_undefweak)) |
+ { |
+ need_relocs = TRUE; |
+ if (htab->root.srelgot == NULL) |
+ abort (); |
+ loc = htab->root.srelgot->contents; |
+ loc += (htab->root.srelgot->reloc_count * |
+ sizeof (Elf32_External_Rela)); |
+ } |
+ |
+ if (tls_type & GOT_TLS_GD) |
+ { |
+ if (need_relocs) |
+ { |
+ outrel.r_addend = 0; |
+ outrel.r_offset = (htab->root.sgot->output_section->vma |
+ + htab->root.sgot->output_offset |
+ + cur_off); |
+ outrel.r_info = ELF32_R_INFO (indx, |
+ R_NIOS2_TLS_DTPMOD); |
+ |
+ bfd_elf32_swap_reloca_out (output_bfd, &outrel, |
+ loc); |
+ htab->root.srelgot->reloc_count++; |
+ loc += sizeof (Elf32_External_Rela); |
+ |
+ if (indx == 0) |
+ bfd_put_32 (output_bfd, |
+ (relocation - dtpoff_base (info) - |
+ DTP_OFFSET), |
+ htab->root.sgot->contents + cur_off + 4); |
+ else |
+ { |
+ outrel.r_addend = 0; |
+ outrel.r_info = ELF32_R_INFO (indx, |
+ R_NIOS2_TLS_DTPREL); |
+ outrel.r_offset += 4; |
+ |
+ bfd_elf32_swap_reloca_out (output_bfd, &outrel, |
+ loc); |
+ htab->root.srelgot->reloc_count++; |
+ loc += sizeof (Elf32_External_Rela); |
+ } |
+ } |
+ else |
+ { |
+ /* If we are not emitting relocations for a |
+ general dynamic reference, then we must be in a |
+ static link or an executable link with the |
+ symbol binding locally. Mark it as belonging |
+ to module 1, the executable. */ |
+ bfd_put_32 (output_bfd, 1, |
+ htab->root.sgot->contents + cur_off); |
+ bfd_put_32 (output_bfd, (relocation - |
+ dtpoff_base (info) - |
+ DTP_OFFSET), |
+ htab->root.sgot->contents + cur_off + 4); |
+ } |
+ |
+ cur_off += 8; |
+ } |
+ |
+ if (tls_type & GOT_TLS_IE) |
+ { |
+ if (need_relocs) |
+ { |
+ if (indx == 0) |
+ outrel.r_addend = (relocation - |
+ dtpoff_base (info)); |
+ else |
+ outrel.r_addend = 0; |
+ outrel.r_offset = (htab->root.sgot->output_section->vma |
+ + htab->root.sgot->output_offset |
+ + cur_off); |
+ outrel.r_info = ELF32_R_INFO (indx, |
+ R_NIOS2_TLS_TPREL); |
+ |
+ bfd_elf32_swap_reloca_out (output_bfd, &outrel, |
+ loc); |
+ htab->root.srelgot->reloc_count++; |
+ loc += sizeof (Elf32_External_Rela); |
+ } |
+ else |
+ bfd_put_32 (output_bfd, (tpoff (info, relocation) |
+ - TP_OFFSET), |
+ htab->root.sgot->contents + cur_off); |
+ cur_off += 4; |
+ } |
+ |
+ if (h != NULL) |
+ h->got.offset |= 1; |
+ else |
+ local_got_offsets[r_symndx] |= 1; |
+ } |
+ |
+ if ((tls_type & GOT_TLS_GD) && r_type != R_NIOS2_TLS_GD16) |
+ off += 8; |
+ relocation = (htab->root.sgot->output_offset + off); |
+ |
+ r = _bfd_final_link_relocate (howto, input_bfd, input_section, |
+ contents, rel->r_offset, |
+ relocation, rel->r_addend); |
+ } |
+ |
+ break; |
+ case R_NIOS2_TLS_LE16: |
+ if (info->shared && !info->pie) |
+ { |
+ (*_bfd_error_handler) |
+ (_("%B(%A+0x%lx): R_NIOS2_TLS_LE16 relocation not " |
+ "permitted in shared object"), |
+ input_bfd, input_section, |
+ (long) rel->r_offset, howto->name); |
+ return FALSE; |
+ } |
+ else |
+ relocation = tpoff (info, relocation) - TP_OFFSET; |
+ |
+ r = _bfd_final_link_relocate (howto, input_bfd, input_section, |
+ contents, rel->r_offset, |
+ relocation, rel->r_addend); |
+ break; |
+ |
+ case R_NIOS2_BFD_RELOC_32: |
+ if (info->shared |
+ && (input_section->flags & SEC_ALLOC) != 0 |
+ && (h == NULL |
+ || ELF_ST_VISIBILITY (h->other) == STV_DEFAULT |
+ || h->root.type != bfd_link_hash_undefweak)) |
+ { |
+ Elf_Internal_Rela outrel; |
+ bfd_byte *loc; |
+ bfd_boolean skip, relocate; |
+ |
+ /* When generating a shared object, these relocations |
+ are copied into the output file to be resolved at run |
+ time. */ |
+ |
+ skip = FALSE; |
+ relocate = FALSE; |
+ |
+ outrel.r_offset |
+ = _bfd_elf_section_offset (output_bfd, info, |
+ input_section, rel->r_offset); |
+ if (outrel.r_offset == (bfd_vma) -1) |
+ skip = TRUE; |
+ else if (outrel.r_offset == (bfd_vma) -2) |
+ skip = TRUE, relocate = TRUE; |
+ outrel.r_offset += (input_section->output_section->vma |
+ + input_section->output_offset); |
+ |
+ if (skip) |
+ memset (&outrel, 0, sizeof outrel); |
+ else if (h != NULL |
+ && h->dynindx != -1 |
+ && (!info->shared |
+ || !info->symbolic |
+ || !h->def_regular)) |
+ { |
+ outrel.r_info = ELF32_R_INFO (h->dynindx, r_type); |
+ outrel.r_addend = rel->r_addend; |
+ } |
+ else |
+ { |
+ /* This symbol is local, or marked to become local. */ |
+ outrel.r_addend = relocation + rel->r_addend; |
+ relocate = TRUE; |
+ outrel.r_info = ELF32_R_INFO (0, R_NIOS2_RELATIVE); |
+ } |
+ |
+ sreloc = elf_section_data (input_section)->sreloc; |
+ if (sreloc == NULL) |
+ abort (); |
+ |
+ loc = sreloc->contents; |
+ loc += sreloc->reloc_count++ * sizeof (Elf32_External_Rela); |
+ bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc); |
+ |
+ /* This reloc will be computed at runtime, so there's no |
+ need to do anything now, except for R_NIOS2_BFD_RELOC_32 |
+ relocations that have been turned into |
+ R_NIOS2_RELATIVE. */ |
+ if (!relocate) |
+ break; |
+ } |
+ |
+ r = _bfd_final_link_relocate (howto, input_bfd, |
+ input_section, contents, |
+ rel->r_offset, relocation, |
+ rel->r_addend); |
+ break; |
+ |
+ case R_NIOS2_TLS_DTPREL: |
+ relocation -= dtpoff_base (info); |
+ /* Fall through. */ |
+ |
+ default: |
+ r = _bfd_final_link_relocate (howto, input_bfd, |
+ input_section, contents, |
+ rel->r_offset, relocation, |
+ rel->r_addend); |
+ break; |
+ } |
+ } |
+ else |
+ r = bfd_reloc_notsupported; |
+ |
+ if (r != bfd_reloc_ok) |
+ { |
+ if (h != NULL) |
+ name = h->root.root.string; |
+ else |
+ { |
+ name = bfd_elf_string_from_elf_section (input_bfd, |
+ symtab_hdr->sh_link, |
+ sym->st_name); |
+ if (name == NULL || *name == '\0') |
+ name = bfd_section_name (input_bfd, sec); |
+ } |
+ |
+ switch (r) |
+ { |
+ case bfd_reloc_overflow: |
+ r = info->callbacks->reloc_overflow (info, NULL, name, |
+ howto->name, (bfd_vma) 0, |
+ input_bfd, input_section, |
+ rel->r_offset); |
+ break; |
+ |
+ case bfd_reloc_undefined: |
+ r = info->callbacks->undefined_symbol (info, name, input_bfd, |
+ input_section, |
+ rel->r_offset, TRUE); |
+ break; |
+ |
+ case bfd_reloc_outofrange: |
+ if (msg == NULL) |
+ msg = _("relocation out of range"); |
+ break; |
+ |
+ case bfd_reloc_notsupported: |
+ if (msg == NULL) |
+ msg = _("unsupported relocation"); |
+ break; |
+ |
+ case bfd_reloc_dangerous: |
+ if (msg == NULL) |
+ msg = _("dangerous relocation"); |
+ break; |
+ |
+ default: |
+ if (msg == NULL) |
+ msg = _("unknown error"); |
+ break; |
+ } |
+ |
+ if (msg) |
+ { |
+ r = info->callbacks->warning |
+ (info, msg, name, input_bfd, input_section, rel->r_offset); |
+ return FALSE; |
+ } |
+ } |
+ } |
+ return TRUE; |
+} |
+ |
+/* Implement elf-backend_section_flags: |
+ Convert NIOS2 specific section flags to bfd internal section flags. */ |
+static bfd_boolean |
+nios2_elf32_section_flags (flagword *flags, const Elf_Internal_Shdr *hdr) |
+{ |
+ if (hdr->sh_flags & SHF_NIOS2_GPREL) |
+ *flags |= SEC_SMALL_DATA; |
+ |
+ return TRUE; |
+} |
+ |
+/* Implement elf_backend_fake_sections: |
+ Set the correct type for an NIOS2 ELF section. We do this by the |
+ section name, which is a hack, but ought to work. */ |
+static bfd_boolean |
+nios2_elf32_fake_sections (bfd *abfd ATTRIBUTE_UNUSED, |
+ Elf_Internal_Shdr *hdr, asection *sec) |
+{ |
+ register const char *name = bfd_get_section_name (abfd, sec); |
+ |
+ if ((sec->flags & SEC_SMALL_DATA) |
+ || strcmp (name, ".sdata") == 0 |
+ || strcmp (name, ".sbss") == 0 |
+ || strcmp (name, ".lit4") == 0 || strcmp (name, ".lit8") == 0) |
+ hdr->sh_flags |= SHF_NIOS2_GPREL; |
+ |
+ return TRUE; |
+} |
+ |
+/* Create .got, .gotplt, and .rela.got sections in DYNOBJ, and set up |
+ shortcuts to them in our hash table. */ |
+static bfd_boolean |
+create_got_section (bfd *dynobj, struct bfd_link_info *info) |
+{ |
+ struct elf32_nios2_link_hash_table *htab; |
+ |
+ htab = elf32_nios2_hash_table (info); |
+ |
+ if (! _bfd_elf_create_got_section (dynobj, info)) |
+ return FALSE; |
+ |
+ /* In order for the two loads in .PLTresolve to share the same %hiadj, |
+ _GLOBAL_OFFSET_TABLE_ must be aligned to a 16-byte boundary. */ |
+ if (!bfd_set_section_alignment (dynobj, htab->root.sgotplt, 4)) |
+ return FALSE; |
+ |
+ return TRUE; |
+} |
+ |
+/* Implement elf_backend_create_dynamic_sections: |
+ Create .plt, .rela.plt, .got, .got.plt, .rela.got, .dynbss, and |
+ .rela.bss sections in DYNOBJ, and set up shortcuts to them in our |
+ hash table. */ |
+static bfd_boolean |
+nios2_elf32_create_dynamic_sections (bfd *dynobj, struct bfd_link_info *info) |
+{ |
+ struct elf32_nios2_link_hash_table *htab; |
+ |
+ htab = elf32_nios2_hash_table (info); |
+ if (!htab->root.sgot && !create_got_section (dynobj, info)) |
+ return FALSE; |
+ |
+ _bfd_elf_create_dynamic_sections (dynobj, info); |
+ |
+ /* In order for the two loads in a shared object .PLTresolve to share the |
+ same %hiadj, the start of the PLT (as well as the GOT) must be aligned |
+ to a 16-byte boundary. This is because the addresses for these loads |
+ include the -(.plt+4) PIC correction. */ |
+ if (!bfd_set_section_alignment (dynobj, htab->root.splt, 4)) |
+ return FALSE; |
+ |
+ htab->sdynbss = bfd_get_linker_section (dynobj, ".dynbss"); |
+ if (!htab->sdynbss) |
+ return FALSE; |
+ if (!info->shared) |
+ { |
+ htab->srelbss = bfd_get_linker_section (dynobj, ".rela.bss"); |
+ if (!htab->srelbss) |
+ return FALSE; |
+ } |
+ |
+ return TRUE; |
+} |
+ |
+/* Implement elf_backend_copy_indirect_symbol: |
+ Copy the extra info we tack onto an elf_link_hash_entry. */ |
+static void |
+nios2_elf32_copy_indirect_symbol (struct bfd_link_info *info, |
+ struct elf_link_hash_entry *dir, |
+ struct elf_link_hash_entry *ind) |
+{ |
+ struct elf32_nios2_link_hash_entry *edir, *eind; |
+ |
+ edir = (struct elf32_nios2_link_hash_entry *) dir; |
+ eind = (struct elf32_nios2_link_hash_entry *) ind; |
+ |
+ if (eind->dyn_relocs != NULL) |
+ { |
+ if (edir->dyn_relocs != NULL) |
+ { |
+ struct elf32_nios2_dyn_relocs **pp; |
+ struct elf32_nios2_dyn_relocs *p; |
+ |
+ /* Add reloc counts against the indirect sym to the direct sym |
+ list. Merge any entries against the same section. */ |
+ for (pp = &eind->dyn_relocs; (p = *pp) != NULL; ) |
+ { |
+ struct elf32_nios2_dyn_relocs *q; |
+ |
+ for (q = edir->dyn_relocs; q != NULL; q = q->next) |
+ if (q->sec == p->sec) |
+ { |
+ q->pc_count += p->pc_count; |
+ q->count += p->count; |
+ *pp = p->next; |
+ break; |
+ } |
+ if (q == NULL) |
+ pp = &p->next; |
+ } |
+ *pp = edir->dyn_relocs; |
+ } |
+ |
+ edir->dyn_relocs = eind->dyn_relocs; |
+ eind->dyn_relocs = NULL; |
+ } |
+ |
+ if (ind->root.type == bfd_link_hash_indirect |
+ && dir->got.refcount <= 0) |
+ { |
+ edir->tls_type = eind->tls_type; |
+ eind->tls_type = GOT_UNKNOWN; |
+ } |
+ |
+ edir->got_types_used |= eind->got_types_used; |
+ |
+ _bfd_elf_link_hash_copy_indirect (info, dir, ind); |
+} |
+ |
+/* Implement elf_backend_check_relocs: |
+ Look through the relocs for a section during the first phase. */ |
+static bfd_boolean |
+nios2_elf32_check_relocs (bfd *abfd, struct bfd_link_info *info, |
+ asection *sec, const Elf_Internal_Rela *relocs) |
+{ |
+ bfd *dynobj; |
+ Elf_Internal_Shdr *symtab_hdr; |
+ struct elf_link_hash_entry **sym_hashes, **sym_hashes_end; |
+ const Elf_Internal_Rela *rel; |
+ const Elf_Internal_Rela *rel_end; |
+ struct elf32_nios2_link_hash_table *htab; |
+ asection *sgot; |
+ asection *srelgot; |
+ asection *sreloc = NULL; |
+ bfd_signed_vma *local_got_refcounts; |
+ |
+ if (info->relocatable) |
+ return TRUE; |
+ |
+ dynobj = elf_hash_table (info)->dynobj; |
+ symtab_hdr = &elf_tdata (abfd)->symtab_hdr; |
+ sym_hashes = elf_sym_hashes (abfd); |
+ sym_hashes_end = (sym_hashes |
+ + symtab_hdr->sh_size / sizeof (Elf32_External_Sym)); |
+ if (!elf_bad_symtab (abfd)) |
+ sym_hashes_end -= symtab_hdr->sh_info; |
+ local_got_refcounts = elf_local_got_refcounts (abfd); |
+ |
+ htab = elf32_nios2_hash_table (info); |
+ sgot = htab->root.sgot; |
+ srelgot = htab->root.srelgot; |
+ |
+ rel_end = relocs + sec->reloc_count; |
+ for (rel = relocs; rel < rel_end; rel++) |
+ { |
+ unsigned int r_type; |
+ struct elf_link_hash_entry *h; |
+ unsigned long r_symndx; |
+ |
+ r_symndx = ELF32_R_SYM (rel->r_info); |
+ if (r_symndx < symtab_hdr->sh_info) |
+ h = NULL; |
+ else |
+ { |
+ h = sym_hashes[r_symndx - symtab_hdr->sh_info]; |
+ while (h->root.type == bfd_link_hash_indirect |
+ || h->root.type == bfd_link_hash_warning) |
+ h = (struct elf_link_hash_entry *) h->root.u.i.link; |
+ |
+ /* PR15323, ref flags aren't set for references in the same |
+ object. */ |
+ h->root.non_ir_ref = 1; |
+ } |
+ |
+ r_type = ELF32_R_TYPE (rel->r_info); |
+ |
+ switch (r_type) |
+ { |
+ case R_NIOS2_GOT16: |
+ case R_NIOS2_CALL16: |
+ case R_NIOS2_TLS_GD16: |
+ case R_NIOS2_TLS_IE16: |
+ /* This symbol requires a global offset table entry. */ |
+ { |
+ int tls_type, old_tls_type; |
+ |
+ switch (r_type) |
+ { |
+ default: |
+ case R_NIOS2_GOT16: |
+ case R_NIOS2_CALL16: |
+ tls_type = GOT_NORMAL; |
+ break; |
+ case R_NIOS2_TLS_GD16: |
+ tls_type = GOT_TLS_GD; |
+ break; |
+ case R_NIOS2_TLS_IE16: |
+ tls_type = GOT_TLS_IE; |
+ break; |
+ } |
+ |
+ if (dynobj == NULL) |
+ { |
+ /* Create the .got section. */ |
+ elf_hash_table (info)->dynobj = dynobj = abfd; |
+ nios2_elf32_create_dynamic_sections (dynobj, info); |
+ } |
+ |
+ if (sgot == NULL) |
+ { |
+ sgot = htab->root.sgot; |
+ BFD_ASSERT (sgot != NULL); |
+ } |
+ |
+ if (srelgot == NULL |
+ && (h != NULL || info->shared)) |
+ { |
+ srelgot = htab->root.srelgot; |
+ BFD_ASSERT (srelgot != NULL); |
+ } |
+ |
+ if (h != NULL) |
+ { |
+ struct elf32_nios2_link_hash_entry *eh |
+ = (struct elf32_nios2_link_hash_entry *)h; |
+ h->got.refcount++; |
+ old_tls_type = elf32_nios2_hash_entry(h)->tls_type; |
+ if (r_type == R_NIOS2_CALL16) |
+ { |
+ /* Make sure a plt entry is created for this symbol if |
+ it turns out to be a function defined by a dynamic |
+ object. */ |
+ h->plt.refcount++; |
+ h->needs_plt = 1; |
+ h->type = STT_FUNC; |
+ eh->got_types_used |= CALL16_USED; |
+ } |
+ else |
+ eh->got_types_used |= GOT16_USED; |
+ } |
+ else |
+ { |
+ /* This is a global offset table entry for a local symbol. */ |
+ if (local_got_refcounts == NULL) |
+ { |
+ bfd_size_type size; |
+ |
+ size = symtab_hdr->sh_info; |
+ size *= (sizeof (bfd_signed_vma) + sizeof (char)); |
+ local_got_refcounts |
+ = ((bfd_signed_vma *) bfd_zalloc (abfd, size)); |
+ if (local_got_refcounts == NULL) |
+ return FALSE; |
+ elf_local_got_refcounts (abfd) = local_got_refcounts; |
+ elf32_nios2_local_got_tls_type (abfd) |
+ = (char *) (local_got_refcounts + symtab_hdr->sh_info); |
+ } |
+ local_got_refcounts[r_symndx]++; |
+ old_tls_type = elf32_nios2_local_got_tls_type (abfd) [r_symndx]; |
+ } |
+ |
+ /* We will already have issued an error message if there is a |
+ TLS / non-TLS mismatch, based on the symbol type. We don't |
+ support any linker relaxations. So just combine any TLS |
+ types needed. */ |
+ if (old_tls_type != GOT_UNKNOWN && old_tls_type != GOT_NORMAL |
+ && tls_type != GOT_NORMAL) |
+ tls_type |= old_tls_type; |
+ |
+ if (old_tls_type != tls_type) |
+ { |
+ if (h != NULL) |
+ elf32_nios2_hash_entry (h)->tls_type = tls_type; |
+ else |
+ elf32_nios2_local_got_tls_type (abfd) [r_symndx] = tls_type; |
+ } |
+ } |
+ /* Fall through */ |
+ case R_NIOS2_TLS_LDM16: |
+ if (r_type == R_NIOS2_TLS_LDM16) |
+ htab->tls_ldm_got.refcount++; |
+ |
+ if (htab->root.sgot == NULL) |
+ { |
+ if (htab->root.dynobj == NULL) |
+ htab->root.dynobj = abfd; |
+ if (!create_got_section (htab->root.dynobj, info)) |
+ return FALSE; |
+ } |
+ break; |
+ |
+ /* This relocation describes the C++ object vtable hierarchy. |
+ Reconstruct it for later use during GC. */ |
+ case R_NIOS2_GNU_VTINHERIT: |
+ if (!bfd_elf_gc_record_vtinherit (abfd, sec, h, rel->r_offset)) |
+ return FALSE; |
+ break; |
+ |
+ /* This relocation describes which C++ vtable entries are actually |
+ used. Record for later use during GC. */ |
+ case R_NIOS2_GNU_VTENTRY: |
+ if (!bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_addend)) |
+ return FALSE; |
+ break; |
+ |
+ case R_NIOS2_BFD_RELOC_32: |
+ case R_NIOS2_CALL26: |
+ case R_NIOS2_HIADJ16: |
+ case R_NIOS2_LO16: |
+ |
+ if (h != NULL) |
+ { |
+ /* If this reloc is in a read-only section, we might |
+ need a copy reloc. We can't check reliably at this |
+ stage whether the section is read-only, as input |
+ sections have not yet been mapped to output sections. |
+ Tentatively set the flag for now, and correct in |
+ adjust_dynamic_symbol. */ |
+ if (!info->shared) |
+ h->non_got_ref = 1; |
+ |
+ /* Make sure a plt entry is created for this symbol if it |
+ turns out to be a function defined by a dynamic object. */ |
+ h->plt.refcount++; |
+ |
+ if (r_type == R_NIOS2_CALL26) |
+ h->needs_plt = 1; |
+ } |
+ |
+ /* If we are creating a shared library, we need to copy the |
+ reloc into the shared library. */ |
+ if (info->shared |
+ && (sec->flags & SEC_ALLOC) != 0 |
+ && (r_type == R_NIOS2_BFD_RELOC_32 |
+ || (h != NULL && ! h->needs_plt |
+ && (! info->symbolic || ! h->def_regular)))) |
+ { |
+ struct elf32_nios2_dyn_relocs *p; |
+ struct elf32_nios2_dyn_relocs **head; |
+ |
+ /* When creating a shared object, we must copy these |
+ reloc types into the output file. We create a reloc |
+ section in dynobj and make room for this reloc. */ |
+ if (sreloc == NULL) |
+ { |
+ sreloc = _bfd_elf_make_dynamic_reloc_section |
+ (sec, dynobj, 2, abfd, TRUE); |
+ if (sreloc == NULL) |
+ return FALSE; |
+ } |
+ |
+ /* If this is a global symbol, we count the number of |
+ relocations we need for this symbol. */ |
+ if (h != NULL) |
+ head = &((struct elf32_nios2_link_hash_entry *) h)->dyn_relocs; |
+ else |
+ { |
+ /* Track dynamic relocs needed for local syms too. |
+ We really need local syms available to do this |
+ easily. Oh well. */ |
+ |
+ asection *s; |
+ void *vpp; |
+ Elf_Internal_Sym *isym; |
+ |
+ isym = bfd_sym_from_r_symndx (&htab->sym_cache, |
+ abfd, r_symndx); |
+ if (isym == NULL) |
+ return FALSE; |
+ |
+ s = bfd_section_from_elf_index (abfd, isym->st_shndx); |
+ if (s == NULL) |
+ s = sec; |
+ |
+ vpp = &elf_section_data (s)->local_dynrel; |
+ head = (struct elf32_nios2_dyn_relocs **) vpp; |
+ } |
+ |
+ p = *head; |
+ if (p == NULL || p->sec != sec) |
+ { |
+ bfd_size_type amt = sizeof *p; |
+ p = ((struct elf32_nios2_dyn_relocs *) |
+ bfd_alloc (htab->root.dynobj, amt)); |
+ if (p == NULL) |
+ return FALSE; |
+ p->next = *head; |
+ *head = p; |
+ p->sec = sec; |
+ p->count = 0; |
+ p->pc_count = 0; |
+ } |
+ |
+ p->count += 1; |
+ |
+ } |
+ break; |
+ } |
+ } |
+ |
+ return TRUE; |
+} |
+ |
+ |
+/* Implement elf_backend_gc_mark_hook: |
+ Return the section that should be marked against GC for a given |
+ relocation. */ |
+static asection * |
+nios2_elf32_gc_mark_hook (asection *sec, |
+ struct bfd_link_info *info, |
+ Elf_Internal_Rela *rel, |
+ struct elf_link_hash_entry *h, |
+ Elf_Internal_Sym *sym) |
+{ |
+ if (h != NULL) |
+ switch (ELF32_R_TYPE (rel->r_info)) |
+ { |
+ case R_NIOS2_GNU_VTINHERIT: |
+ case R_NIOS2_GNU_VTENTRY: |
+ return NULL; |
+ } |
+ return _bfd_elf_gc_mark_hook (sec, info, rel, h, sym); |
+} |
+ |
+/* Implement elf_backend_gc_sweep_hook: |
+ Update the got entry reference counts for the section being removed. */ |
+static bfd_boolean |
+nios2_elf32_gc_sweep_hook (bfd *abfd, |
+ struct bfd_link_info *info, |
+ asection *sec, |
+ const Elf_Internal_Rela *relocs) |
+{ |
+ Elf_Internal_Shdr *symtab_hdr; |
+ struct elf_link_hash_entry **sym_hashes; |
+ bfd_signed_vma *local_got_refcounts; |
+ const Elf_Internal_Rela *rel, *relend; |
+ bfd *dynobj; |
+ |
+ if (info->relocatable) |
+ return TRUE; |
+ |
+ elf_section_data (sec)->local_dynrel = NULL; |
+ |
+ dynobj = elf_hash_table (info)->dynobj; |
+ if (dynobj == NULL) |
+ return TRUE; |
+ |
+ symtab_hdr = &elf_tdata (abfd)->symtab_hdr; |
+ sym_hashes = elf_sym_hashes (abfd); |
+ local_got_refcounts = elf_local_got_refcounts (abfd); |
+ |
+ relend = relocs + sec->reloc_count; |
+ for (rel = relocs; rel < relend; rel++) |
+ { |
+ unsigned long r_symndx; |
+ struct elf_link_hash_entry *h = NULL; |
+ int r_type; |
+ |
+ r_symndx = ELF32_R_SYM (rel->r_info); |
+ if (r_symndx >= symtab_hdr->sh_info) |
+ { |
+ h = sym_hashes[r_symndx - symtab_hdr->sh_info]; |
+ while (h->root.type == bfd_link_hash_indirect |
+ || h->root.type == bfd_link_hash_warning) |
+ h = (struct elf_link_hash_entry *) h->root.u.i.link; |
+ } |
+ |
+ r_type = ELF32_R_TYPE (rel->r_info); |
+ switch (r_type) |
+ { |
+ case R_NIOS2_GOT16: |
+ case R_NIOS2_CALL16: |
+ if (h != NULL) |
+ { |
+ if (h->got.refcount > 0) |
+ --h->got.refcount; |
+ } |
+ else if (local_got_refcounts != NULL) |
+ { |
+ if (local_got_refcounts[r_symndx] > 0) |
+ --local_got_refcounts[r_symndx]; |
+ } |
+ break; |
+ |
+ case R_NIOS2_PCREL_LO: |
+ case R_NIOS2_PCREL_HA: |
+ case R_NIOS2_BFD_RELOC_32: |
+ case R_NIOS2_CALL26: |
+ if (h != NULL) |
+ { |
+ struct elf32_nios2_link_hash_entry *eh; |
+ struct elf32_nios2_dyn_relocs **pp; |
+ struct elf32_nios2_dyn_relocs *p; |
+ |
+ eh = (struct elf32_nios2_link_hash_entry *) h; |
+ |
+ if (h->plt.refcount > 0) |
+ --h->plt.refcount; |
+ |
+ if (r_type == R_NIOS2_PCREL_LO || r_type == R_NIOS2_PCREL_HA |
+ || r_type == R_NIOS2_BFD_RELOC_32) |
+ { |
+ for (pp = &eh->dyn_relocs; (p = *pp) != NULL; |
+ pp = &p->next) |
+ if (p->sec == sec) |
+ { |
+ p->count -= 1; |
+ if (p->count == 0) |
+ *pp = p->next; |
+ break; |
+ } |
+ } |
+ } |
+ break; |
+ |
+ default: |
+ break; |
+ } |
+ } |
+ |
+ return TRUE; |
+} |
+ |
+/* Install 16-bit immediate value VALUE at offset OFFSET into section SEC. */ |
+static void |
+nios2_elf32_install_imm16 (asection *sec, bfd_vma offset, bfd_vma value) |
+{ |
+ bfd_vma word = bfd_get_32 (sec->owner, sec->contents + offset); |
+ |
+ BFD_ASSERT(value <= 0xffff); |
+ |
+ bfd_put_32 (sec->owner, word | ((value & 0xffff) << 6), |
+ sec->contents + offset); |
+} |
+ |
+/* Install COUNT 32-bit values DATA starting at offset OFFSET into |
+ section SEC. */ |
+static void |
+nios2_elf32_install_data (asection *sec, const bfd_vma *data, bfd_vma offset, |
+ int count) |
+{ |
+ while (count--) |
+ { |
+ bfd_put_32 (sec->owner, *data, sec->contents + offset); |
+ offset += 4; |
+ ++data; |
+ } |
+} |
+ |
+/* Implement elf_backend_finish_dynamic_symbols: |
+ Finish up dynamic symbol handling. We set the contents of various |
+ dynamic sections here. */ |
+static bfd_boolean |
+nios2_elf32_finish_dynamic_symbol (bfd *output_bfd, |
+ struct bfd_link_info *info, |
+ struct elf_link_hash_entry *h, |
+ Elf_Internal_Sym *sym) |
+{ |
+ struct elf32_nios2_link_hash_table *htab; |
+ struct elf32_nios2_link_hash_entry *eh |
+ = (struct elf32_nios2_link_hash_entry *)h; |
+ int use_plt; |
+ |
+ htab = elf32_nios2_hash_table (info); |
+ |
+ if (h->plt.offset != (bfd_vma) -1) |
+ { |
+ asection *splt; |
+ asection *sgotplt; |
+ asection *srela; |
+ bfd_vma plt_index; |
+ bfd_vma got_offset; |
+ Elf_Internal_Rela rela; |
+ bfd_byte *loc; |
+ bfd_vma got_address; |
+ |
+ /* This symbol has an entry in the procedure linkage table. Set |
+ it up. */ |
+ BFD_ASSERT (h->dynindx != -1); |
+ splt = htab->root.splt; |
+ sgotplt = htab->root.sgotplt; |
+ srela = htab->root.srelplt; |
+ BFD_ASSERT (splt != NULL && sgotplt != NULL && srela != NULL); |
+ |
+ /* Emit the PLT entry. */ |
+ if (info->shared) |
+ { |
+ nios2_elf32_install_data (splt, nios2_so_plt_entry, h->plt.offset, |
+ 3); |
+ plt_index = (h->plt.offset - 24) / 12; |
+ got_offset = (plt_index + 3) * 4; |
+ nios2_elf32_install_imm16 (splt, h->plt.offset, |
+ hiadj(plt_index * 4)); |
+ nios2_elf32_install_imm16 (splt, h->plt.offset + 4, |
+ (plt_index * 4) & 0xffff); |
+ nios2_elf32_install_imm16 (splt, h->plt.offset + 8, |
+ 0xfff4 - h->plt.offset); |
+ got_address = (sgotplt->output_section->vma + sgotplt->output_offset |
+ + got_offset); |
+ |
+ /* Fill in the entry in the global offset table. There are no |
+ res_n slots for a shared object PLT, instead the .got.plt entries |
+ point to the PLT entries. */ |
+ bfd_put_32 (output_bfd, |
+ splt->output_section->vma + splt->output_offset |
+ + h->plt.offset, sgotplt->contents + got_offset); |
+ } |
+ else |
+ { |
+ plt_index = (h->plt.offset - 28 - htab->res_n_size) / 12; |
+ got_offset = (plt_index + 3) * 4; |
+ |
+ nios2_elf32_install_data (splt, nios2_plt_entry, h->plt.offset, 3); |
+ got_address = (sgotplt->output_section->vma + sgotplt->output_offset |
+ + got_offset); |
+ nios2_elf32_install_imm16 (splt, h->plt.offset, hiadj(got_address)); |
+ nios2_elf32_install_imm16 (splt, h->plt.offset + 4, |
+ got_address & 0xffff); |
+ |
+ /* Fill in the entry in the global offset table. */ |
+ bfd_put_32 (output_bfd, |
+ splt->output_section->vma + splt->output_offset |
+ + plt_index * 4, sgotplt->contents + got_offset); |
+ } |
+ |
+ /* Fill in the entry in the .rela.plt section. */ |
+ rela.r_offset = got_address; |
+ rela.r_info = ELF32_R_INFO (h->dynindx, R_NIOS2_JUMP_SLOT); |
+ rela.r_addend = 0; |
+ loc = srela->contents + plt_index * sizeof (Elf32_External_Rela); |
+ bfd_elf32_swap_reloca_out (output_bfd, &rela, loc); |
+ |
+ if (!h->def_regular) |
+ { |
+ /* Mark the symbol as undefined, rather than as defined in |
+ the .plt section. Leave the value alone. */ |
+ sym->st_shndx = SHN_UNDEF; |
+ /* If the symbol is weak, we do need to clear the value. |
+ Otherwise, the PLT entry would provide a definition for |
+ the symbol even if the symbol wasn't defined anywhere, |
+ and so the symbol would never be NULL. */ |
+ if (!h->ref_regular_nonweak) |
+ sym->st_value = 0; |
+ } |
+ } |
+ |
+ use_plt = (eh->got_types_used == CALL16_USED |
+ && h->plt.offset != (bfd_vma) -1); |
+ |
+ if (!use_plt && h->got.offset != (bfd_vma) -1 |
+ && (elf32_nios2_hash_entry (h)->tls_type & GOT_TLS_GD) == 0 |
+ && (elf32_nios2_hash_entry (h)->tls_type & GOT_TLS_IE) == 0) |
+ { |
+ asection *sgot; |
+ asection *srela; |
+ Elf_Internal_Rela rela; |
+ bfd_byte *loc; |
+ bfd_vma offset; |
+ |
+ /* This symbol has an entry in the global offset table. Set it |
+ up. */ |
+ sgot = htab->root.sgot; |
+ srela = htab->root.srelgot; |
+ BFD_ASSERT (sgot != NULL && srela != NULL); |
+ |
+ offset = (h->got.offset & ~(bfd_vma) 1); |
+ rela.r_offset = (sgot->output_section->vma |
+ + sgot->output_offset + offset); |
+ |
+ /* If this is a -Bsymbolic link, and the symbol is defined |
+ locally, we just want to emit a RELATIVE reloc. Likewise if |
+ the symbol was forced to be local because of a version file. |
+ The entry in the global offset table will already have been |
+ initialized in the relocate_section function. */ |
+ |
+ if (info->shared && SYMBOL_REFERENCES_LOCAL (info, h)) |
+ { |
+ rela.r_info = ELF32_R_INFO (0, R_NIOS2_RELATIVE); |
+ rela.r_addend = bfd_get_signed_32 (output_bfd, |
+ (sgot->contents + offset)); |
+ bfd_put_32 (output_bfd, (bfd_vma) 0, sgot->contents + offset); |
+ } |
+ else |
+ { |
+ bfd_put_32 (output_bfd, (bfd_vma) 0, |
+ sgot->contents + offset); |
+ rela.r_info = ELF32_R_INFO (h->dynindx, R_NIOS2_GLOB_DAT); |
+ rela.r_addend = 0; |
+ } |
+ |
+ loc = srela->contents; |
+ loc += srela->reloc_count++ * sizeof (Elf32_External_Rela); |
+ bfd_elf32_swap_reloca_out (output_bfd, &rela, loc); |
+ } |
+ |
+ if (use_plt && h->got.offset != (bfd_vma) -1) |
+ { |
+ bfd_vma offset = (h->got.offset & ~(bfd_vma) 1); |
+ asection *sgot = htab->root.sgot; |
+ asection *splt = htab->root.splt; |
+ bfd_put_32 (output_bfd, (splt->output_section->vma + splt->output_offset |
+ + h->plt.offset), |
+ sgot->contents + offset); |
+ } |
+ |
+ if (h->needs_copy) |
+ { |
+ asection *s; |
+ Elf_Internal_Rela rela; |
+ bfd_byte *loc; |
+ |
+ /* This symbol needs a copy reloc. Set it up. */ |
+ BFD_ASSERT (h->dynindx != -1 |
+ && (h->root.type == bfd_link_hash_defined |
+ || h->root.type == bfd_link_hash_defweak)); |
+ |
+ s = htab->srelbss; |
+ BFD_ASSERT (s != NULL); |
+ |
+ rela.r_offset = (h->root.u.def.value |
+ + h->root.u.def.section->output_section->vma |
+ + h->root.u.def.section->output_offset); |
+ rela.r_info = ELF32_R_INFO (h->dynindx, R_NIOS2_COPY); |
+ rela.r_addend = 0; |
+ loc = s->contents + s->reloc_count++ * sizeof (Elf32_External_Rela); |
+ bfd_elf32_swap_reloca_out (output_bfd, &rela, loc); |
+ } |
+ |
+ /* Mark _DYNAMIC and _GLOBAL_OFFSET_TABLE_ as absolute. */ |
+ if (strcmp (h->root.root.string, "_DYNAMIC") == 0 |
+ || h == elf_hash_table (info)->hgot) |
+ sym->st_shndx = SHN_ABS; |
+ |
+ return TRUE; |
+} |
+ |
+/* Implement elf_backend_finish_dynamic_sections. */ |
+static bfd_boolean |
+nios2_elf32_finish_dynamic_sections (bfd *output_bfd, |
+ struct bfd_link_info *info) |
+{ |
+ bfd *dynobj; |
+ asection *sgotplt; |
+ asection *sdyn; |
+ struct elf32_nios2_link_hash_table *htab; |
+ |
+ htab = elf32_nios2_hash_table (info); |
+ dynobj = elf_hash_table (info)->dynobj; |
+ sgotplt = htab->root.sgotplt; |
+ BFD_ASSERT (sgotplt != NULL); |
+ sdyn = bfd_get_linker_section (dynobj, ".dynamic"); |
+ |
+ if (elf_hash_table (info)->dynamic_sections_created) |
+ { |
+ asection *splt; |
+ Elf32_External_Dyn *dyncon, *dynconend; |
+ |
+ splt = htab->root.splt; |
+ BFD_ASSERT (splt != NULL && sdyn != NULL); |
+ |
+ dyncon = (Elf32_External_Dyn *) sdyn->contents; |
+ dynconend = (Elf32_External_Dyn *) (sdyn->contents + sdyn->size); |
+ for (; dyncon < dynconend; dyncon++) |
+ { |
+ Elf_Internal_Dyn dyn; |
+ asection *s; |
+ |
+ bfd_elf32_swap_dyn_in (dynobj, dyncon, &dyn); |
+ |
+ switch (dyn.d_tag) |
+ { |
+ default: |
+ break; |
+ |
+ case DT_PLTGOT: |
+ s = htab->root.sgot; |
+ BFD_ASSERT (s != NULL); |
+ dyn.d_un.d_ptr = s->output_section->vma; |
+ bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon); |
+ break; |
+ |
+ case DT_JMPREL: |
+ s = htab->root.srelplt; |
+ BFD_ASSERT (s != NULL); |
+ dyn.d_un.d_ptr = s->output_section->vma; |
+ bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon); |
+ break; |
+ |
+ case DT_PLTRELSZ: |
+ s = htab->root.srelplt; |
+ BFD_ASSERT (s != NULL); |
+ dyn.d_un.d_val = s->size; |
+ bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon); |
+ break; |
+ |
+ case DT_RELASZ: |
+ /* The procedure linkage table relocs (DT_JMPREL) should |
+ not be included in the overall relocs (DT_RELA). |
+ Therefore, we override the DT_RELASZ entry here to |
+ make it not include the JMPREL relocs. Since the |
+ linker script arranges for .rela.plt to follow all |
+ other relocation sections, we don't have to worry |
+ about changing the DT_RELA entry. */ |
+ s = htab->root.srelplt; |
+ if (s != NULL) |
+ dyn.d_un.d_val -= s->size; |
+ bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon); |
+ break; |
+ |
+ case DT_NIOS2_GP: |
+ s = htab->root.sgot; |
+ BFD_ASSERT (s != NULL); |
+ dyn.d_un.d_ptr = s->output_section->vma + 0x7ff0; |
+ bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon); |
+ break; |
+ } |
+ } |
+ |
+ /* Fill in the first entry in the procedure linkage table. */ |
+ if (splt->size > 0) |
+ { |
+ bfd_vma got_address = (sgotplt->output_section->vma |
+ + sgotplt->output_offset); |
+ if (info->shared) |
+ { |
+ bfd_vma corrected = got_address - (splt->output_section->vma |
+ + splt->output_offset + 4); |
+ nios2_elf32_install_data (splt, nios2_so_plt0_entry, 0, 6); |
+ nios2_elf32_install_imm16 (splt, 4, hiadj (corrected)); |
+ nios2_elf32_install_imm16 (splt, 12, (corrected & 0xffff) + 4); |
+ nios2_elf32_install_imm16 (splt, 16, (corrected & 0xffff) + 8); |
+ } |
+ else |
+ { |
+ /* Divide by 4 here, not 3 because we already corrected for the |
+ res_N branches. */ |
+ bfd_vma res_size = (splt->size - 28) / 4; |
+ bfd_vma res_start = (splt->output_section->vma |
+ + splt->output_offset); |
+ bfd_vma res_offset; |
+ |
+ for (res_offset = 0; res_offset < res_size; res_offset += 4) |
+ bfd_put_32 (output_bfd, |
+ 6 | ((res_size - (res_offset + 4)) << 6), |
+ splt->contents + res_offset); |
+ |
+ nios2_elf32_install_data (splt, nios2_plt0_entry, res_size, 7); |
+ nios2_elf32_install_imm16 (splt, res_size, hiadj (res_start)); |
+ nios2_elf32_install_imm16 (splt, res_size + 4, |
+ res_start & 0xffff); |
+ nios2_elf32_install_imm16 (splt, res_size + 12, |
+ hiadj (got_address)); |
+ nios2_elf32_install_imm16 (splt, res_size + 16, |
+ (got_address & 0xffff) + 4); |
+ nios2_elf32_install_imm16 (splt, res_size + 20, |
+ (got_address & 0xffff) + 8); |
+ } |
+ } |
+ } |
+ /* Fill in the first three entries in the global offset table. */ |
+ if (sgotplt->size > 0) |
+ { |
+ if (sdyn == NULL) |
+ bfd_put_32 (output_bfd, (bfd_vma) 0, sgotplt->contents); |
+ else |
+ bfd_put_32 (output_bfd, |
+ sdyn->output_section->vma + sdyn->output_offset, |
+ sgotplt->contents); |
+ bfd_put_32 (output_bfd, (bfd_vma) 0, sgotplt->contents + 4); |
+ bfd_put_32 (output_bfd, (bfd_vma) 0, sgotplt->contents + 8); |
+ } |
+ |
+ elf_section_data (sgotplt->output_section)->this_hdr.sh_entsize = 4; |
+ |
+ return TRUE; |
+} |
+ |
+/* Implement elf_backend_adjust_dynamic_symbol: |
+ Adjust a symbol defined by a dynamic object and referenced by a |
+ regular object. The current definition is in some section of the |
+ dynamic object, but we're not including those sections. We have to |
+ change the definition to something the rest of the link can |
+ understand. */ |
+static bfd_boolean |
+nios2_elf32_adjust_dynamic_symbol (struct bfd_link_info *info, |
+ struct elf_link_hash_entry *h) |
+{ |
+ struct elf32_nios2_link_hash_table *htab; |
+ bfd *dynobj; |
+ asection *s; |
+ unsigned align2; |
+ |
+ htab = elf32_nios2_hash_table (info); |
+ dynobj = elf_hash_table (info)->dynobj; |
+ |
+ /* Make sure we know what is going on here. */ |
+ BFD_ASSERT (dynobj != NULL |
+ && (h->needs_plt |
+ || h->u.weakdef != NULL |
+ || (h->def_dynamic |
+ && h->ref_regular |
+ && !h->def_regular))); |
+ |
+ /* If this is a function, put it in the procedure linkage table. We |
+ will fill in the contents of the procedure linkage table later, |
+ when we know the address of the .got section. */ |
+ if (h->type == STT_FUNC || h->needs_plt) |
+ { |
+ if (h->plt.refcount <= 0 |
+ || SYMBOL_CALLS_LOCAL (info, h) |
+ || (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT |
+ && h->root.type == bfd_link_hash_undefweak)) |
+ { |
+ /* This case can occur if we saw a PLT reloc in an input |
+ file, but the symbol was never referred to by a dynamic |
+ object, or if all references were garbage collected. In |
+ such a case, we don't actually need to build a procedure |
+ linkage table, and we can just do a PCREL reloc instead. */ |
+ h->plt.offset = (bfd_vma) -1; |
+ h->needs_plt = 0; |
+ } |
+ |
+ return TRUE; |
+ } |
+ |
+ /* Reinitialize the plt offset now that it is not used as a reference |
+ count any more. */ |
+ h->plt.offset = (bfd_vma) -1; |
+ |
+ /* If this is a weak symbol, and there is a real definition, the |
+ processor independent code will have arranged for us to see the |
+ real definition first, and we can just use the same value. */ |
+ if (h->u.weakdef != NULL) |
+ { |
+ BFD_ASSERT (h->u.weakdef->root.type == bfd_link_hash_defined |
+ || h->u.weakdef->root.type == bfd_link_hash_defweak); |
+ h->root.u.def.section = h->u.weakdef->root.u.def.section; |
+ h->root.u.def.value = h->u.weakdef->root.u.def.value; |
+ return TRUE; |
+ } |
+ |
+ /* If there are no non-GOT references, we do not need a copy |
+ relocation. */ |
+ if (!h->non_got_ref) |
+ return TRUE; |
+ |
+ /* This is a reference to a symbol defined by a dynamic object which |
+ is not a function. |
+ If we are creating a shared library, we must presume that the |
+ only references to the symbol are via the global offset table. |
+ For such cases we need not do anything here; the relocations will |
+ be handled correctly by relocate_section. */ |
+ if (info->shared) |
+ return TRUE; |
+ |
+ if (h->size == 0) |
+ { |
+ (*_bfd_error_handler) (_("dynamic variable `%s' is zero size"), |
+ h->root.root.string); |
+ return TRUE; |
+ } |
+ |
+ /* We must allocate the symbol in our .dynbss section, which will |
+ become part of the .bss section of the executable. There will be |
+ an entry for this symbol in the .dynsym section. The dynamic |
+ object will contain position independent code, so all references |
+ from the dynamic object to this symbol will go through the global |
+ offset table. The dynamic linker will use the .dynsym entry to |
+ determine the address it must put in the global offset table, so |
+ both the dynamic object and the regular object will refer to the |
+ same memory location for the variable. */ |
+ s = htab->sdynbss; |
+ BFD_ASSERT (s != NULL); |
+ |
+ /* We must generate a R_NIOS2_COPY reloc to tell the dynamic linker to |
+ copy the initial value out of the dynamic object and into the |
+ runtime process image. We need to remember the offset into the |
+ .rela.bss section we are going to use. */ |
+ if ((h->root.u.def.section->flags & SEC_ALLOC) != 0) |
+ { |
+ asection *srel; |
+ |
+ srel = htab->srelbss; |
+ BFD_ASSERT (srel != NULL); |
+ srel->size += sizeof (Elf32_External_Rela); |
+ h->needs_copy = 1; |
+ } |
+ |
+ align2 = bfd_log2 (h->size); |
+ if (align2 > h->root.u.def.section->alignment_power) |
+ align2 = h->root.u.def.section->alignment_power; |
+ |
+ /* Align dynbss. */ |
+ s->size = BFD_ALIGN (s->size, (bfd_size_type)1 << align2); |
+ if (align2 > bfd_get_section_alignment (dynobj, s) |
+ && !bfd_set_section_alignment (dynobj, s, align2)) |
+ return FALSE; |
+ |
+ /* Define the symbol as being at this point in the section. */ |
+ h->root.u.def.section = s; |
+ h->root.u.def.value = s->size; |
+ |
+ /* Increment the section size to make room for the symbol. */ |
+ s->size += h->size; |
+ |
+ return TRUE; |
+} |
+ |
+/* Worker function for nios2_elf32_size_dynamic_sections. */ |
+static bfd_boolean |
+adjust_dynrelocs (struct elf_link_hash_entry *h, PTR inf) |
+{ |
+ struct bfd_link_info *info; |
+ struct elf32_nios2_link_hash_table *htab; |
+ |
+ if (h->root.type == bfd_link_hash_indirect) |
+ return TRUE; |
+ |
+ if (h->root.type == bfd_link_hash_warning) |
+ /* When warning symbols are created, they **replace** the "real" |
+ entry in the hash table, thus we never get to see the real |
+ symbol in a hash traversal. So look at it now. */ |
+ h = (struct elf_link_hash_entry *) h->root.u.i.link; |
+ |
+ info = (struct bfd_link_info *) inf; |
+ htab = elf32_nios2_hash_table (info); |
+ |
+ if (h->plt.offset != (bfd_vma)-1) |
+ h->plt.offset += htab->res_n_size; |
+ if (htab->root.splt == h->root.u.def.section) |
+ h->root.u.def.value += htab->res_n_size; |
+ |
+ return TRUE; |
+} |
+ |
+/* Another worker function for nios2_elf32_size_dynamic_sections. |
+ Allocate space in .plt, .got and associated reloc sections for |
+ dynamic relocs. */ |
+static bfd_boolean |
+allocate_dynrelocs (struct elf_link_hash_entry *h, PTR inf) |
+{ |
+ struct bfd_link_info *info; |
+ struct elf32_nios2_link_hash_table *htab; |
+ struct elf32_nios2_link_hash_entry *eh; |
+ struct elf32_nios2_dyn_relocs *p; |
+ int use_plt; |
+ |
+ if (h->root.type == bfd_link_hash_indirect) |
+ return TRUE; |
+ |
+ if (h->root.type == bfd_link_hash_warning) |
+ /* When warning symbols are created, they **replace** the "real" |
+ entry in the hash table, thus we never get to see the real |
+ symbol in a hash traversal. So look at it now. */ |
+ h = (struct elf_link_hash_entry *) h->root.u.i.link; |
+ |
+ info = (struct bfd_link_info *) inf; |
+ htab = elf32_nios2_hash_table (info); |
+ |
+ if (htab->root.dynamic_sections_created |
+ && h->plt.refcount > 0) |
+ { |
+ /* Make sure this symbol is output as a dynamic symbol. |
+ Undefined weak syms won't yet be marked as dynamic. */ |
+ if (h->dynindx == -1 |
+ && !h->forced_local |
+ && !bfd_elf_link_record_dynamic_symbol (info, h)) |
+ return FALSE; |
+ |
+ if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (1, info->shared, h)) |
+ { |
+ asection *s = htab->root.splt; |
+ |
+ /* Allocate room for the header. */ |
+ if (s->size == 0) |
+ { |
+ if (info->shared) |
+ s->size = 24; |
+ else |
+ s->size = 28; |
+ } |
+ |
+ h->plt.offset = s->size; |
+ |
+ /* If this symbol is not defined in a regular file, and we are |
+ not generating a shared library, then set the symbol to this |
+ location in the .plt. This is required to make function |
+ pointers compare as equal between the normal executable and |
+ the shared library. */ |
+ if (! info->shared |
+ && !h->def_regular) |
+ { |
+ h->root.u.def.section = s; |
+ h->root.u.def.value = h->plt.offset; |
+ } |
+ |
+ /* Make room for this entry. */ |
+ s->size += 12; |
+ |
+ /* We also need to make an entry in the .rela.plt section. */ |
+ htab->root.srelplt->size += sizeof (Elf32_External_Rela); |
+ |
+ /* And the .got.plt section. */ |
+ htab->root.sgotplt->size += 4; |
+ } |
+ else |
+ { |
+ h->plt.offset = (bfd_vma) -1; |
+ h->needs_plt = 0; |
+ } |
+ } |
+ else |
+ { |
+ h->plt.offset = (bfd_vma) -1; |
+ h->needs_plt = 0; |
+ } |
+ |
+ eh = (struct elf32_nios2_link_hash_entry *) h; |
+ use_plt = (eh->got_types_used == CALL16_USED |
+ && h->plt.offset != (bfd_vma) -1); |
+ |
+ if (h->got.refcount > 0) |
+ { |
+ asection *s; |
+ bfd_boolean dyn; |
+ int tls_type = eh->tls_type; |
+ int indx; |
+ |
+ /* Make sure this symbol is output as a dynamic symbol. |
+ Undefined weak syms won't yet be marked as dynamic. */ |
+ if (h->dynindx == -1 |
+ && !h->forced_local |
+ && !bfd_elf_link_record_dynamic_symbol (info, h)) |
+ return FALSE; |
+ |
+ s = htab->root.sgot; |
+ h->got.offset = s->size; |
+ |
+ if (tls_type == GOT_UNKNOWN) |
+ abort (); |
+ |
+ if (tls_type == GOT_NORMAL) |
+ /* Non-TLS symbols need one GOT slot. */ |
+ s->size += 4; |
+ else |
+ { |
+ if (tls_type & GOT_TLS_GD) |
+ /* R_NIOS2_TLS_GD16 needs 2 consecutive GOT slots. */ |
+ s->size += 8; |
+ if (tls_type & GOT_TLS_IE) |
+ /* R_NIOS2_TLS_IE16 needs one GOT slot. */ |
+ s->size += 4; |
+ } |
+ |
+ dyn = htab->root.dynamic_sections_created; |
+ |
+ indx = 0; |
+ if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info->shared, h) |
+ && (!info->shared |
+ || !SYMBOL_REFERENCES_LOCAL (info, h))) |
+ indx = h->dynindx; |
+ |
+ if (tls_type != GOT_NORMAL |
+ && (info->shared || indx != 0) |
+ && (ELF_ST_VISIBILITY (h->other) == STV_DEFAULT |
+ || h->root.type != bfd_link_hash_undefweak)) |
+ { |
+ if (tls_type & GOT_TLS_IE) |
+ htab->root.srelgot->size += sizeof (Elf32_External_Rela); |
+ |
+ if (tls_type & GOT_TLS_GD) |
+ htab->root.srelgot->size += sizeof (Elf32_External_Rela); |
+ |
+ if ((tls_type & GOT_TLS_GD) && indx != 0) |
+ htab->root.srelgot->size += sizeof (Elf32_External_Rela); |
+ } |
+ else if ((ELF_ST_VISIBILITY (h->other) == STV_DEFAULT |
+ || h->root.type != bfd_link_hash_undefweak) |
+ && !use_plt |
+ && (info->shared |
+ || WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, 0, h))) |
+ htab->root.srelgot->size += sizeof (Elf32_External_Rela); |
+ } |
+ else |
+ h->got.offset = (bfd_vma) -1; |
+ |
+ if (eh->dyn_relocs == NULL) |
+ return TRUE; |
+ |
+ /* In the shared -Bsymbolic case, discard space allocated for |
+ dynamic pc-relative relocs against symbols which turn out to be |
+ defined in regular objects. For the normal shared case, discard |
+ space for pc-relative relocs that have become local due to symbol |
+ visibility changes. */ |
+ |
+ if (info->shared) |
+ { |
+ if (h->def_regular |
+ && (h->forced_local || info->symbolic)) |
+ { |
+ struct elf32_nios2_dyn_relocs **pp; |
+ |
+ for (pp = &eh->dyn_relocs; (p = *pp) != NULL; ) |
+ { |
+ p->count -= p->pc_count; |
+ p->pc_count = 0; |
+ if (p->count == 0) |
+ *pp = p->next; |
+ else |
+ pp = &p->next; |
+ } |
+ } |
+ |
+ /* Also discard relocs on undefined weak syms with non-default |
+ visibility. */ |
+ if (eh->dyn_relocs != NULL |
+ && h->root.type == bfd_link_hash_undefweak) |
+ { |
+ if (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT) |
+ eh->dyn_relocs = NULL; |
+ |
+ /* Make sure undefined weak symbols are output as a dynamic |
+ symbol in PIEs. */ |
+ else if (h->dynindx == -1 |
+ && !h->forced_local |
+ && !bfd_elf_link_record_dynamic_symbol (info, h)) |
+ return FALSE; |
+ } |
+ } |
+ else |
+ { |
+ /* For the non-shared case, discard space for relocs against |
+ symbols which turn out to need copy relocs or are not |
+ dynamic. */ |
+ |
+ if (!h->non_got_ref |
+ && ((h->def_dynamic && !h->def_regular) |
+ || (htab->root.dynamic_sections_created |
+ && (h->root.type == bfd_link_hash_undefweak |
+ || h->root.type == bfd_link_hash_undefined)))) |
+ { |
+ /* Make sure this symbol is output as a dynamic symbol. |
+ Undefined weak syms won't yet be marked as dynamic. */ |
+ if (h->dynindx == -1 |
+ && !h->forced_local |
+ && !bfd_elf_link_record_dynamic_symbol (info, h)) |
+ return FALSE; |
+ |
+ /* If that succeeded, we know we'll be keeping all the |
+ relocs. */ |
+ if (h->dynindx != -1) |
+ goto keep; |
+ } |
+ |
+ eh->dyn_relocs = NULL; |
+ |
+ keep: ; |
+ } |
+ |
+ /* Finally, allocate space. */ |
+ for (p = eh->dyn_relocs; p != NULL; p = p->next) |
+ { |
+ asection *sreloc = elf_section_data (p->sec)->sreloc; |
+ sreloc->size += p->count * sizeof (Elf32_External_Rela); |
+ } |
+ |
+ return TRUE; |
+} |
+ |
+/* Implement elf_backend_size_dynamic_sections: |
+ Set the sizes of the dynamic sections. */ |
+static bfd_boolean |
+nios2_elf32_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, |
+ struct bfd_link_info *info) |
+{ |
+ bfd *dynobj; |
+ asection *s; |
+ bfd_boolean plt; |
+ bfd_boolean got; |
+ bfd_boolean relocs; |
+ bfd *ibfd; |
+ struct elf32_nios2_link_hash_table *htab; |
+ |
+ htab = elf32_nios2_hash_table (info); |
+ dynobj = elf_hash_table (info)->dynobj; |
+ BFD_ASSERT (dynobj != NULL); |
+ |
+ htab->res_n_size = 0; |
+ if (elf_hash_table (info)->dynamic_sections_created) |
+ { |
+ /* Set the contents of the .interp section to the interpreter. */ |
+ if (info->executable) |
+ { |
+ s = bfd_get_linker_section (dynobj, ".interp"); |
+ BFD_ASSERT (s != NULL); |
+ s->size = sizeof ELF_DYNAMIC_INTERPRETER; |
+ s->contents = (unsigned char *) ELF_DYNAMIC_INTERPRETER; |
+ } |
+ } |
+ else |
+ { |
+ /* We may have created entries in the .rela.got section. |
+ However, if we are not creating the dynamic sections, we will |
+ not actually use these entries. Reset the size of .rela.got, |
+ which will cause it to get stripped from the output file |
+ below. */ |
+ s = htab->root.srelgot; |
+ if (s != NULL) |
+ s->size = 0; |
+ } |
+ |
+ /* Set up .got offsets for local syms, and space for local dynamic |
+ relocs. */ |
+ for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next) |
+ { |
+ bfd_signed_vma *local_got; |
+ bfd_signed_vma *end_local_got; |
+ char *local_tls_type; |
+ bfd_size_type locsymcount; |
+ Elf_Internal_Shdr *symtab_hdr; |
+ asection *srel; |
+ |
+ if (bfd_get_flavour (ibfd) != bfd_target_elf_flavour) |
+ continue; |
+ |
+ for (s = ibfd->sections; s != NULL; s = s->next) |
+ { |
+ struct elf32_nios2_dyn_relocs *p; |
+ |
+ for (p = elf_section_data (s)->local_dynrel; p != NULL; p = p->next) |
+ { |
+ if (!bfd_is_abs_section (p->sec) |
+ && bfd_is_abs_section (p->sec->output_section)) |
+ { |
+ /* Input section has been discarded, either because |
+ it is a copy of a linkonce section or due to |
+ linker script /DISCARD/, so we'll be discarding |
+ the relocs too. */ |
+ } |
+ else if (p->count != 0) |
+ { |
+ srel = elf_section_data (p->sec)->sreloc; |
+ srel->size += p->count * sizeof (Elf32_External_Rela); |
+ if ((p->sec->output_section->flags & SEC_READONLY) != 0) |
+ info->flags |= DF_TEXTREL; |
+ } |
+ } |
+ } |
+ |
+ local_got = elf_local_got_refcounts (ibfd); |
+ if (!local_got) |
+ continue; |
+ |
+ symtab_hdr = &elf_tdata (ibfd)->symtab_hdr; |
+ locsymcount = symtab_hdr->sh_info; |
+ end_local_got = local_got + locsymcount; |
+ local_tls_type = elf32_nios2_local_got_tls_type (ibfd); |
+ s = htab->root.sgot; |
+ srel = htab->root.srelgot; |
+ for (; local_got < end_local_got; ++local_got, ++local_tls_type) |
+ { |
+ if (*local_got > 0) |
+ { |
+ *local_got = s->size; |
+ if (*local_tls_type & GOT_TLS_GD) |
+ /* TLS_GD relocs need an 8-byte structure in the GOT. */ |
+ s->size += 8; |
+ if (*local_tls_type & GOT_TLS_IE) |
+ s->size += 4; |
+ if (*local_tls_type == GOT_NORMAL) |
+ s->size += 4; |
+ |
+ if (info->shared || *local_tls_type == GOT_TLS_GD) |
+ srel->size += sizeof (Elf32_External_Rela); |
+ } |
+ else |
+ *local_got = (bfd_vma) -1; |
+ } |
+ } |
+ |
+ if (htab->tls_ldm_got.refcount > 0) |
+ { |
+ /* Allocate two GOT entries and one dynamic relocation (if necessary) |
+ for R_NIOS2_TLS_LDM16 relocations. */ |
+ htab->tls_ldm_got.offset = htab->root.sgot->size; |
+ htab->root.sgot->size += 8; |
+ if (info->shared) |
+ htab->root.srelgot->size += sizeof (Elf32_External_Rela); |
+ } |
+ else |
+ htab->tls_ldm_got.offset = -1; |
+ |
+ /* Allocate global sym .plt and .got entries, and space for global |
+ sym dynamic relocs. */ |
+ elf_link_hash_traverse (& htab->root, allocate_dynrelocs, info); |
+ |
+ /* The check_relocs and adjust_dynamic_symbol entry points have |
+ determined the sizes of the various dynamic sections. Allocate |
+ memory for them. */ |
+ plt = FALSE; |
+ got = FALSE; |
+ relocs = FALSE; |
+ for (s = dynobj->sections; s != NULL; s = s->next) |
+ { |
+ const char *name; |
+ |
+ if ((s->flags & SEC_LINKER_CREATED) == 0) |
+ continue; |
+ |
+ /* It's OK to base decisions on the section name, because none |
+ of the dynobj section names depend upon the input files. */ |
+ name = bfd_get_section_name (dynobj, s); |
+ |
+ if (strcmp (name, ".plt") == 0) |
+ { |
+ /* Remember whether there is a PLT. */ |
+ plt = s->size != 0; |
+ |
+ /* Correct for the number of res_N branches. */ |
+ if (plt && !info->shared) |
+ { |
+ htab->res_n_size = (s->size-28) / 3; |
+ s->size += htab->res_n_size; |
+ } |
+ } |
+ else if (CONST_STRNEQ (name, ".rela")) |
+ { |
+ if (s->size != 0) |
+ { |
+ relocs = TRUE; |
+ |
+ /* We use the reloc_count field as a counter if we need |
+ to copy relocs into the output file. */ |
+ s->reloc_count = 0; |
+ } |
+ } |
+ else if (CONST_STRNEQ (name, ".got")) |
+ got = s->size != 0; |
+ else if (strcmp (name, ".dynbss") != 0) |
+ /* It's not one of our sections, so don't allocate space. */ |
+ continue; |
+ |
+ if (s->size == 0) |
+ { |
+ /* If we don't need this section, strip it from the |
+ output file. This is mostly to handle .rela.bss and |
+ .rela.plt. We must create both sections in |
+ create_dynamic_sections, because they must be created |
+ before the linker maps input sections to output |
+ sections. The linker does that before |
+ adjust_dynamic_symbol is called, and it is that |
+ function which decides whether anything needs to go |
+ into these sections. */ |
+ s->flags |= SEC_EXCLUDE; |
+ continue; |
+ } |
+ |
+ if ((s->flags & SEC_HAS_CONTENTS) == 0) |
+ continue; |
+ |
+ /* Allocate memory for the section contents. */ |
+ /* FIXME: This should be a call to bfd_alloc not bfd_zalloc. |
+ Unused entries should be reclaimed before the section's contents |
+ are written out, but at the moment this does not happen. Thus in |
+ order to prevent writing out garbage, we initialize the section's |
+ contents to zero. */ |
+ s->contents = (bfd_byte *) bfd_zalloc (dynobj, s->size); |
+ if (s->contents == NULL) |
+ return FALSE; |
+ } |
+ |
+ /* Adjust dynamic symbols that point to the plt to account for the |
+ now-known number of resN slots. */ |
+ if (htab->res_n_size) |
+ elf_link_hash_traverse (& htab->root, adjust_dynrelocs, info); |
+ |
+ if (elf_hash_table (info)->dynamic_sections_created) |
+ { |
+ /* Add some entries to the .dynamic section. We fill in the |
+ values later, in elf_nios2_finish_dynamic_sections, but we |
+ must add the entries now so that we get the correct size for |
+ the .dynamic section. The DT_DEBUG entry is filled in by the |
+ dynamic linker and used by the debugger. */ |
+#define add_dynamic_entry(TAG, VAL) \ |
+ _bfd_elf_add_dynamic_entry (info, TAG, VAL) |
+ |
+ if (!info->shared && !add_dynamic_entry (DT_DEBUG, 0)) |
+ return FALSE; |
+ |
+ if (got && !add_dynamic_entry (DT_PLTGOT, 0)) |
+ return FALSE; |
+ |
+ if (plt |
+ && (!add_dynamic_entry (DT_PLTRELSZ, 0) |
+ || !add_dynamic_entry (DT_PLTREL, DT_RELA) |
+ || !add_dynamic_entry (DT_JMPREL, 0))) |
+ return FALSE; |
+ |
+ if (relocs |
+ && (!add_dynamic_entry (DT_RELA, 0) |
+ || !add_dynamic_entry (DT_RELASZ, 0) |
+ || !add_dynamic_entry (DT_RELAENT, sizeof (Elf32_External_Rela)))) |
+ return FALSE; |
+ |
+ if (!info->shared && !add_dynamic_entry (DT_NIOS2_GP, 0)) |
+ return FALSE; |
+ |
+ if ((info->flags & DF_TEXTREL) != 0 |
+ && !add_dynamic_entry (DT_TEXTREL, 0)) |
+ return FALSE; |
+ } |
+#undef add_dynamic_entry |
+ |
+ return TRUE; |
+} |
+ |
+/* Implement bfd_elf32_bfd_link_hash_table_create. */ |
+static struct bfd_link_hash_table * |
+nios2_elf32_link_hash_table_create (bfd *abfd) |
+{ |
+ struct elf32_nios2_link_hash_table *ret; |
+ bfd_size_type amt = sizeof (struct elf32_nios2_link_hash_table); |
+ |
+ ret = bfd_zmalloc (amt); |
+ if (ret == NULL) |
+ return NULL; |
+ |
+ if (!_bfd_elf_link_hash_table_init (&ret->root, abfd, |
+ link_hash_newfunc, |
+ sizeof (struct |
+ elf32_nios2_link_hash_entry), |
+ NIOS2_ELF_DATA)) |
+ { |
+ free (ret); |
+ return NULL; |
+ } |
+ |
+ return &ret->root.root; |
+} |
+ |
+/* Implement elf_backend_reloc_type_class. */ |
+static enum elf_reloc_type_class |
+nios2_elf32_reloc_type_class (const struct bfd_link_info *info ATTRIBUTE_UNUSED, |
+ const asection *rel_sec ATTRIBUTE_UNUSED, |
+ const Elf_Internal_Rela *rela) |
+{ |
+ switch ((int) ELF32_R_TYPE (rela->r_info)) |
+ { |
+ case R_NIOS2_RELATIVE: |
+ return reloc_class_relative; |
+ case R_NIOS2_JUMP_SLOT: |
+ return reloc_class_plt; |
+ case R_NIOS2_COPY: |
+ return reloc_class_copy; |
+ default: |
+ return reloc_class_normal; |
+ } |
+} |
+ |
+/* Return 1 if target is one of ours. */ |
+static bfd_boolean |
+is_nios2_elf_target (const struct bfd_target *targ) |
+{ |
+ return (targ == &bfd_elf32_littlenios2_vec |
+ || targ == &bfd_elf32_bignios2_vec); |
+} |
+ |
+/* Implement elf_backend_add_symbol_hook. |
+ This hook is called by the linker when adding symbols from an object |
+ file. We use it to put .comm items in .sbss, and not .bss. */ |
+static bfd_boolean |
+nios2_elf_add_symbol_hook (bfd *abfd, |
+ struct bfd_link_info *info, |
+ Elf_Internal_Sym *sym, |
+ const char **namep ATTRIBUTE_UNUSED, |
+ flagword *flagsp ATTRIBUTE_UNUSED, |
+ asection **secp, |
+ bfd_vma *valp) |
+{ |
+ bfd *dynobj; |
+ |
+ if (sym->st_shndx == SHN_COMMON |
+ && !info->relocatable |
+ && sym->st_size <= elf_gp_size (abfd) |
+ && is_nios2_elf_target (info->output_bfd->xvec)) |
+ { |
+ /* Common symbols less than or equal to -G nn bytes are automatically |
+ put into .sbss. */ |
+ struct elf32_nios2_link_hash_table *htab; |
+ |
+ htab = elf32_nios2_hash_table (info); |
+ if (htab->sbss == NULL) |
+ { |
+ flagword flags = SEC_IS_COMMON | SEC_LINKER_CREATED; |
+ |
+ dynobj = elf_hash_table (info)->dynobj; |
+ if (!dynobj) |
+ dynobj = abfd; |
+ |
+ htab->sbss = bfd_make_section_anyway_with_flags (dynobj, ".sbss", |
+ flags); |
+ if (htab->sbss == NULL) |
+ return FALSE; |
+ } |
+ |
+ *secp = htab->sbss; |
+ *valp = sym->st_size; |
+ } |
+ |
+ return TRUE; |
+} |
+ |
+/* Implement elf_backend_can_make_relative_eh_frame: |
+ Decide whether to attempt to turn absptr or lsda encodings in |
+ shared libraries into pcrel within the given input section. */ |
+static bfd_boolean |
+nios2_elf32_can_make_relative_eh_frame (bfd *input_bfd ATTRIBUTE_UNUSED, |
+ struct bfd_link_info *info |
+ ATTRIBUTE_UNUSED, |
+ asection *eh_frame_section |
+ ATTRIBUTE_UNUSED) |
+{ |
+ /* We can't use PC-relative encodings in the .eh_frame section. */ |
+ return FALSE; |
+} |
+ |
+/* Implement elf_backend_special_sections. */ |
+const struct bfd_elf_special_section elf32_nios2_special_sections[] = |
+{ |
+ { STRING_COMMA_LEN (".sbss"), -2, SHT_NOBITS, |
+ SHF_ALLOC + SHF_WRITE + SHF_NIOS2_GPREL }, |
+ { STRING_COMMA_LEN (".sdata"), -2, SHT_PROGBITS, |
+ SHF_ALLOC + SHF_WRITE + SHF_NIOS2_GPREL }, |
+ { NULL, 0, 0, 0, 0 } |
+}; |
+ |
+#define ELF_ARCH bfd_arch_nios2 |
+#define ELF_TARGET_ID NIOS2_ELF_DATA |
+#define ELF_MACHINE_CODE EM_ALTERA_NIOS2 |
+ |
+/* The Nios II MMU uses a 4K page size. */ |
+ |
+#define ELF_MAXPAGESIZE 0x1000 |
+ |
+#define bfd_elf32_bfd_link_hash_table_create \ |
+ nios2_elf32_link_hash_table_create |
+ |
+/* Relocation table lookup macros. */ |
+ |
+#define bfd_elf32_bfd_reloc_type_lookup nios2_elf32_bfd_reloc_type_lookup |
+#define bfd_elf32_bfd_reloc_name_lookup nios2_elf32_bfd_reloc_name_lookup |
+ |
+/* JUMP_TABLE_LINK macros. */ |
+ |
+/* elf_info_to_howto (using RELA relocations). */ |
+ |
+#define elf_info_to_howto nios2_elf32_info_to_howto |
+ |
+/* elf backend functions. */ |
+ |
+#define elf_backend_can_gc_sections 1 |
+#define elf_backend_can_refcount 1 |
+#define elf_backend_plt_readonly 1 |
+#define elf_backend_want_got_plt 1 |
+#define elf_backend_rela_normal 1 |
+ |
+#define elf_backend_relocate_section nios2_elf32_relocate_section |
+#define elf_backend_section_flags nios2_elf32_section_flags |
+#define elf_backend_fake_sections nios2_elf32_fake_sections |
+#define elf_backend_check_relocs nios2_elf32_check_relocs |
+ |
+#define elf_backend_gc_mark_hook nios2_elf32_gc_mark_hook |
+#define elf_backend_gc_sweep_hook nios2_elf32_gc_sweep_hook |
+#define elf_backend_create_dynamic_sections \ |
+ nios2_elf32_create_dynamic_sections |
+#define elf_backend_finish_dynamic_symbol nios2_elf32_finish_dynamic_symbol |
+#define elf_backend_finish_dynamic_sections \ |
+ nios2_elf32_finish_dynamic_sections |
+#define elf_backend_adjust_dynamic_symbol nios2_elf32_adjust_dynamic_symbol |
+#define elf_backend_reloc_type_class nios2_elf32_reloc_type_class |
+#define elf_backend_size_dynamic_sections nios2_elf32_size_dynamic_sections |
+#define elf_backend_add_symbol_hook nios2_elf_add_symbol_hook |
+#define elf_backend_copy_indirect_symbol nios2_elf32_copy_indirect_symbol |
+ |
+#define elf_backend_grok_prstatus nios2_grok_prstatus |
+#define elf_backend_grok_psinfo nios2_grok_psinfo |
+ |
+#undef elf_backend_can_make_relative_eh_frame |
+#define elf_backend_can_make_relative_eh_frame \ |
+ nios2_elf32_can_make_relative_eh_frame |
+ |
+#define elf_backend_special_sections elf32_nios2_special_sections |
+ |
+#define TARGET_LITTLE_SYM bfd_elf32_littlenios2_vec |
+#define TARGET_LITTLE_NAME "elf32-littlenios2" |
+#define TARGET_BIG_SYM bfd_elf32_bignios2_vec |
+#define TARGET_BIG_NAME "elf32-bignios2" |
+ |
+#define elf_backend_got_header_size 12 |
+ |
+#include "elf32-target.h" |