diff options
Diffstat (limited to 'contrib/binutils/bfd/elf64-alpha.c')
-rw-r--r-- | contrib/binutils/bfd/elf64-alpha.c | 2383 |
1 files changed, 1765 insertions, 618 deletions
diff --git a/contrib/binutils/bfd/elf64-alpha.c b/contrib/binutils/bfd/elf64-alpha.c index a24a70c..e1d095d 100644 --- a/contrib/binutils/bfd/elf64-alpha.c +++ b/contrib/binutils/bfd/elf64-alpha.c @@ -73,7 +73,7 @@ static boolean elf64_alpha_mkobject static boolean elf64_alpha_object_p PARAMS((bfd *)); static boolean elf64_alpha_section_from_shdr - PARAMS((bfd *, Elf64_Internal_Shdr *, char *)); + PARAMS((bfd *, Elf64_Internal_Shdr *, const char *)); static boolean elf64_alpha_section_flags PARAMS((flagword *, Elf64_Internal_Shdr *)); static boolean elf64_alpha_fake_sections @@ -106,14 +106,27 @@ static boolean elf64_alpha_calc_got_offsets_for_symbol PARAMS ((struct alpha_elf_link_hash_entry *, PTR)); static void elf64_alpha_calc_got_offsets PARAMS ((struct bfd_link_info *)); static boolean elf64_alpha_size_got_sections - PARAMS ((bfd *, struct bfd_link_info *)); + PARAMS ((struct bfd_link_info *)); +static boolean elf64_alpha_size_plt_section + PARAMS ((struct bfd_link_info *)); +static boolean elf64_alpha_size_plt_section_1 + PARAMS ((struct alpha_elf_link_hash_entry *, PTR)); static boolean elf64_alpha_always_size_sections PARAMS ((bfd *, struct bfd_link_info *)); +static int alpha_dynamic_entries_for_reloc + PARAMS ((int, int, int)); static boolean elf64_alpha_calc_dynrel_sizes PARAMS ((struct alpha_elf_link_hash_entry *, struct bfd_link_info *)); +static boolean elf64_alpha_size_rela_got_section + PARAMS ((struct bfd_link_info *)); +static boolean elf64_alpha_size_rela_got_1 + PARAMS ((struct alpha_elf_link_hash_entry *, struct bfd_link_info *)); static boolean elf64_alpha_add_symbol_hook PARAMS ((bfd *, struct bfd_link_info *, const Elf_Internal_Sym *, const char **, flagword *, asection **, bfd_vma *)); +static struct alpha_elf_got_entry *get_got_entry + PARAMS ((bfd *, struct alpha_elf_link_hash_entry *, unsigned long, + unsigned long, bfd_vma)); static boolean elf64_alpha_check_relocs PARAMS((bfd *, struct bfd_link_info *, asection *sec, const Elf_Internal_Rela *)); @@ -121,6 +134,9 @@ static boolean elf64_alpha_adjust_dynamic_symbol PARAMS((struct bfd_link_info *, struct elf_link_hash_entry *)); static boolean elf64_alpha_size_dynamic_sections PARAMS((bfd *, struct bfd_link_info *)); +static boolean elf64_alpha_relocate_section_r + PARAMS((bfd *, struct bfd_link_info *, bfd *, asection *, bfd_byte *, + Elf_Internal_Rela *, Elf_Internal_Sym *, asection **)); static boolean elf64_alpha_relocate_section PARAMS((bfd *, struct bfd_link_info *, bfd *, asection *, bfd_byte *, Elf_Internal_Rela *, Elf_Internal_Sym *, asection **)); @@ -148,11 +164,15 @@ struct alpha_elf_link_hash_entry /* Cumulative flags for all the .got entries. */ int flags; - /* Contexts (LITUSE) in which a literal was referenced. */ -#define ALPHA_ELF_LINK_HASH_LU_ADDR 0x01 -#define ALPHA_ELF_LINK_HASH_LU_MEM 0x02 -#define ALPHA_ELF_LINK_HASH_LU_BYTE 0x04 -#define ALPHA_ELF_LINK_HASH_LU_FUNC 0x08 + /* Contexts in which a literal was referenced. */ +#define ALPHA_ELF_LINK_HASH_LU_ADDR 0x01 +#define ALPHA_ELF_LINK_HASH_LU_MEM 0x02 +#define ALPHA_ELF_LINK_HASH_LU_BYTE 0x04 +#define ALPHA_ELF_LINK_HASH_LU_JSR 0x08 +#define ALPHA_ELF_LINK_HASH_LU_TLSGD 0x10 +#define ALPHA_ELF_LINK_HASH_LU_TLSLDM 0x20 +#define ALPHA_ELF_LINK_HASH_LU_FUNC 0x38 +#define ALPHA_ELF_LINK_HASH_TLS_IE 0x40 /* Used to implement multiple .got subsections. */ struct alpha_elf_got_entry @@ -168,13 +188,20 @@ struct alpha_elf_link_hash_entry /* the .got offset for this entry. */ int got_offset; - int flags; + /* How many references to this entry? */ + int use_count; - /* Additional flags. */ -#define ALPHA_ELF_GOT_ENTRY_RELOCS_DONE 0x10 -#define ALPHA_ELF_GOT_ENTRY_RELOCS_XLATED 0x20 + /* The relocation type of this entry. */ + unsigned char reloc_type; - int use_count; + /* How a LITERAL is used. */ + unsigned char flags; + + /* Have we initialized the dynamic relocation for this entry? */ + unsigned char reloc_done; + + /* Have we adjusted this entry for SEC_MERGE? */ + unsigned char reloc_xlated; } *got_entries; /* used to count non-got, non-plt relocations for delayed sizing @@ -324,14 +351,14 @@ elf64_alpha_bfd_link_hash_table_create (abfd) struct alpha_elf_link_hash_table *ret; bfd_size_type amt = sizeof (struct alpha_elf_link_hash_table); - ret = (struct alpha_elf_link_hash_table *) bfd_zalloc (abfd, amt); + ret = (struct alpha_elf_link_hash_table *) bfd_zmalloc (amt); if (ret == (struct alpha_elf_link_hash_table *) NULL) return NULL; if (! _bfd_elf_link_hash_table_init (&ret->root, abfd, elf64_alpha_link_hash_newfunc)) { - bfd_release (abfd, ret); + free (ret); return NULL; } @@ -361,12 +388,12 @@ struct alpha_elf_obj_tdata /* For every got, this is the section. */ asection *got; - /* For every got, this is it's total number of *entries*. */ - int total_got_entries; + /* For every got, this is it's total number of words. */ + int total_got_size; - /* For every got, this is the sum of the number of *entries* required + /* For every got, this is the sum of the number of words required to hold all of the member object's local got. */ - int n_local_got_entries; + int local_got_size; }; #define alpha_elf_tdata(abfd) \ @@ -748,6 +775,203 @@ static reloc_howto_type elf64_alpha_howto_table[] = 0x1fffff, /* src_mask */ 0x1fffff, /* dst_mask */ true), /* pcrel_offset */ + + /* Creates a tls_index for the symbol in the got. */ + HOWTO (R_ALPHA_TLSGD, /* type */ + 0, /* rightshift */ + 1, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + false, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_signed, /* complain_on_overflow */ + 0, /* special_function */ + "TLSGD", /* name */ + false, /* partial_inplace */ + 0xffff, /* src_mask */ + 0xffff, /* dst_mask */ + false), /* pcrel_offset */ + + /* Creates a tls_index for the (current) module in the got. */ + HOWTO (R_ALPHA_TLSLDM, /* type */ + 0, /* rightshift */ + 1, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + false, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_signed, /* complain_on_overflow */ + 0, /* special_function */ + "TLSLDM", /* name */ + false, /* partial_inplace */ + 0xffff, /* src_mask */ + 0xffff, /* dst_mask */ + false), /* pcrel_offset */ + + /* A dynamic relocation for a DTP module entry. */ + HOWTO (R_ALPHA_DTPMOD64, /* type */ + 0, /* rightshift */ + 4, /* size (0 = byte, 1 = short, 2 = long) */ + 64, /* bitsize */ + false, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_bitfield, /* complain_on_overflow */ + 0, /* special_function */ + "DTPMOD64", /* name */ + false, /* partial_inplace */ + MINUS_ONE, /* src_mask */ + MINUS_ONE, /* dst_mask */ + false), /* pcrel_offset */ + + /* Creates a 64-bit offset in the got for the displacement + from DTP to the target. */ + HOWTO (R_ALPHA_GOTDTPREL, /* type */ + 0, /* rightshift */ + 1, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + false, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_signed, /* complain_on_overflow */ + 0, /* special_function */ + "GOTDTPREL", /* name */ + false, /* partial_inplace */ + 0xffff, /* src_mask */ + 0xffff, /* dst_mask */ + false), /* pcrel_offset */ + + /* A dynamic relocation for a displacement from DTP to the target. */ + HOWTO (R_ALPHA_DTPREL64, /* type */ + 0, /* rightshift */ + 4, /* size (0 = byte, 1 = short, 2 = long) */ + 64, /* bitsize */ + false, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_bitfield, /* complain_on_overflow */ + 0, /* special_function */ + "DTPREL64", /* name */ + false, /* partial_inplace */ + MINUS_ONE, /* src_mask */ + MINUS_ONE, /* dst_mask */ + false), /* pcrel_offset */ + + /* The high 16 bits of the displacement from DTP to the target. */ + HOWTO (R_ALPHA_DTPRELHI, /* type */ + 0, /* rightshift */ + 1, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + false, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_signed, /* complain_on_overflow */ + 0, /* special_function */ + "DTPRELHI", /* name */ + false, /* partial_inplace */ + 0xffff, /* src_mask */ + 0xffff, /* dst_mask */ + false), /* pcrel_offset */ + + /* The low 16 bits of the displacement from DTP to the target. */ + HOWTO (R_ALPHA_DTPRELLO, /* type */ + 0, /* rightshift */ + 1, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + false, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont, /* complain_on_overflow */ + 0, /* special_function */ + "DTPRELLO", /* name */ + false, /* partial_inplace */ + 0xffff, /* src_mask */ + 0xffff, /* dst_mask */ + false), /* pcrel_offset */ + + /* A 16-bit displacement from DTP to the target. */ + HOWTO (R_ALPHA_DTPREL16, /* type */ + 0, /* rightshift */ + 1, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + false, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_signed, /* complain_on_overflow */ + 0, /* special_function */ + "DTPREL16", /* name */ + false, /* partial_inplace */ + 0xffff, /* src_mask */ + 0xffff, /* dst_mask */ + false), /* pcrel_offset */ + + /* Creates a 64-bit offset in the got for the displacement + from TP to the target. */ + HOWTO (R_ALPHA_GOTTPREL, /* type */ + 0, /* rightshift */ + 1, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + false, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_signed, /* complain_on_overflow */ + 0, /* special_function */ + "GOTTPREL", /* name */ + false, /* partial_inplace */ + 0xffff, /* src_mask */ + 0xffff, /* dst_mask */ + false), /* pcrel_offset */ + + /* A dynamic relocation for a displacement from TP to the target. */ + HOWTO (R_ALPHA_TPREL64, /* type */ + 0, /* rightshift */ + 4, /* size (0 = byte, 1 = short, 2 = long) */ + 64, /* bitsize */ + false, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_bitfield, /* complain_on_overflow */ + 0, /* special_function */ + "TPREL64", /* name */ + false, /* partial_inplace */ + MINUS_ONE, /* src_mask */ + MINUS_ONE, /* dst_mask */ + false), /* pcrel_offset */ + + /* The high 16 bits of the displacement from TP to the target. */ + HOWTO (R_ALPHA_TPRELHI, /* type */ + 0, /* rightshift */ + 1, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + false, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_signed, /* complain_on_overflow */ + 0, /* special_function */ + "TPRELHI", /* name */ + false, /* partial_inplace */ + 0xffff, /* src_mask */ + 0xffff, /* dst_mask */ + false), /* pcrel_offset */ + + /* The low 16 bits of the displacement from TP to the target. */ + HOWTO (R_ALPHA_TPRELLO, /* type */ + 0, /* rightshift */ + 1, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + false, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont, /* complain_on_overflow */ + 0, /* special_function */ + "TPRELLO", /* name */ + false, /* partial_inplace */ + 0xffff, /* src_mask */ + 0xffff, /* dst_mask */ + false), /* pcrel_offset */ + + /* A 16-bit displacement from TP to the target. */ + HOWTO (R_ALPHA_TPREL16, /* type */ + 0, /* rightshift */ + 1, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + false, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_signed, /* complain_on_overflow */ + 0, /* special_function */ + "TPREL16", /* name */ + false, /* partial_inplace */ + 0xffff, /* src_mask */ + 0xffff, /* dst_mask */ + false), /* pcrel_offset */ }; /* A relocation function which doesn't do anything. */ @@ -902,6 +1126,19 @@ static const struct elf_reloc_map elf64_alpha_reloc_map[] = {BFD_RELOC_ALPHA_GPREL_LO16, R_ALPHA_GPRELLOW}, {BFD_RELOC_GPREL16, R_ALPHA_GPREL16}, {BFD_RELOC_ALPHA_BRSGP, R_ALPHA_BRSGP}, + {BFD_RELOC_ALPHA_TLSGD, R_ALPHA_TLSGD}, + {BFD_RELOC_ALPHA_TLSLDM, R_ALPHA_TLSLDM}, + {BFD_RELOC_ALPHA_DTPMOD64, R_ALPHA_DTPMOD64}, + {BFD_RELOC_ALPHA_GOTDTPREL16, R_ALPHA_GOTDTPREL}, + {BFD_RELOC_ALPHA_DTPREL64, R_ALPHA_DTPREL64}, + {BFD_RELOC_ALPHA_DTPREL_HI16, R_ALPHA_DTPRELHI}, + {BFD_RELOC_ALPHA_DTPREL_LO16, R_ALPHA_DTPRELLO}, + {BFD_RELOC_ALPHA_DTPREL16, R_ALPHA_DTPREL16}, + {BFD_RELOC_ALPHA_GOTTPREL16, R_ALPHA_GOTTPREL}, + {BFD_RELOC_ALPHA_TPREL64, R_ALPHA_TPREL64}, + {BFD_RELOC_ALPHA_TPREL_HI16, R_ALPHA_TPRELHI}, + {BFD_RELOC_ALPHA_TPREL_LO16, R_ALPHA_TPRELLO}, + {BFD_RELOC_ALPHA_TPREL16, R_ALPHA_TPREL16}, }; /* Given a BFD reloc type, return a HOWTO structure. */ @@ -936,6 +1173,19 @@ elf64_alpha_info_to_howto (abfd, cache_ptr, dst) BFD_ASSERT (r_type < (unsigned int) R_ALPHA_max); cache_ptr->howto = &elf64_alpha_howto_table[r_type]; } + +/* These two relocations create a two-word entry in the got. */ +#define alpha_got_entry_size(r_type) \ + (r_type == R_ALPHA_TLSGD || r_type == R_ALPHA_TLSLDM ? 16 : 8) + +/* This is PT_TLS segment p_vaddr. */ +#define alpha_get_dtprel_base(tlss) \ + ((tlss)->start) + +/* Main program TLS (whose template starts at PT_TLS p_vaddr) + is assigned offset round(16, PT_TLS p_align). */ +#define alpha_get_tprel_base(tlss) \ + ((tlss)->start - align_power ((bfd_vma) 16, (tlss)->align)) /* These functions do relaxation for Alpha ELF. @@ -958,35 +1208,45 @@ elf64_alpha_info_to_howto (abfd, cache_ptr, dst) #define OP_BR 0x30 #define OP_BSR 0x34 #define INSN_UNOP 0x2ffe0000 +#define INSN_ADDQ 0x40000400 +#define INSN_RDUNIQ 0x0000009e struct alpha_relax_info { bfd *abfd; asection *sec; bfd_byte *contents; + Elf_Internal_Shdr *symtab_hdr; Elf_Internal_Rela *relocs, *relend; struct bfd_link_info *link_info; - boolean changed_contents; - boolean changed_relocs; + struct elf_link_tls_segment *tls_segment; bfd_vma gp; bfd *gotobj; asection *tsec; struct alpha_elf_link_hash_entry *h; + struct alpha_elf_got_entry **first_gotent; struct alpha_elf_got_entry *gotent; + boolean changed_contents; + boolean changed_relocs; unsigned char other; }; -static Elf_Internal_Rela * elf64_alpha_relax_with_lituse - PARAMS((struct alpha_relax_info *info, bfd_vma symval, - Elf_Internal_Rela *irel, Elf_Internal_Rela *irelend)); - -static boolean elf64_alpha_relax_without_lituse +static boolean elf64_alpha_relax_with_lituse PARAMS((struct alpha_relax_info *info, bfd_vma symval, Elf_Internal_Rela *irel)); - static bfd_vma elf64_alpha_relax_opt_call PARAMS((struct alpha_relax_info *info, bfd_vma symval)); - +static boolean elf64_alpha_relax_got_load + PARAMS((struct alpha_relax_info *info, bfd_vma symval, + Elf_Internal_Rela *irel, unsigned long)); +static boolean elf64_alpha_relax_gprelhilo + PARAMS((struct alpha_relax_info *info, bfd_vma symval, + Elf_Internal_Rela *irel, boolean)); +static boolean elf64_alpha_relax_tls_get_addr + PARAMS((struct alpha_relax_info *info, bfd_vma symval, + Elf_Internal_Rela *irel, boolean)); +static struct elf_link_tls_segment *elf64_alpha_relax_find_tls_segment + PARAMS((struct alpha_relax_info *, struct elf_link_tls_segment *)); static boolean elf64_alpha_relax_section PARAMS((bfd *abfd, asection *sec, struct bfd_link_info *link_info, boolean *again)); @@ -1007,13 +1267,13 @@ elf64_alpha_find_reloc_at_ofs (rel, relend, offset, type) return NULL; } -static Elf_Internal_Rela * -elf64_alpha_relax_with_lituse (info, symval, irel, irelend) +static boolean +elf64_alpha_relax_with_lituse (info, symval, irel) struct alpha_relax_info *info; bfd_vma symval; - Elf_Internal_Rela *irel, *irelend; + Elf_Internal_Rela *irel; { - Elf_Internal_Rela *urel; + Elf_Internal_Rela *urel, *irelend = info->relend; int flags, count, i; bfd_signed_vma disp; boolean fits16; @@ -1029,9 +1289,13 @@ elf64_alpha_relax_with_lituse (info, symval, irel, irelend) ("%s: %s+0x%lx: warning: LITERAL relocation against unexpected insn", bfd_archive_filename (info->abfd), info->sec->name, (unsigned long) irel->r_offset)); - return irel; + return true; } + /* Can't relax dynamic symbols. */ + if (alpha_elf_dynamic_symbol_p (&info->h->root, info->link_info)) + return true; + /* Summarize how this particular LITERAL is used. */ for (urel = irel+1, flags = count = 0; urel < irelend; ++urel, ++count) { @@ -1054,25 +1318,27 @@ elf64_alpha_relax_with_lituse (info, symval, irel, irelend) switch (urel->r_addend) { - default: /* 0 = ADDRESS FORMAT */ + case LITUSE_ALPHA_ADDR: + default: /* This type is really just a placeholder to note that all uses cannot be optimized, but to still allow some. */ all_optimized = false; break; - case 1: /* MEM FORMAT */ + case LITUSE_ALPHA_BASE: /* We can always optimize 16-bit displacements. */ /* Extract the displacement from the instruction, sign-extending it if necessary, then test whether it is within 16 or 32 bits displacement from GP. */ insn_disp = insn & 0x0000ffff; - if (insn_disp & 0x00008000) - insn_disp |= 0xffff0000; /* Negative: sign-extend. */ + if (insn_disp & 0x8000) + insn_disp |= ~0xffff; /* Negative: sign-extend. */ xdisp = disp + insn_disp; - fits16 = (xdisp >= - (bfd_signed_vma) 0x00008000 && xdisp < 0x00008000); - fits32 = (xdisp >= - (bfd_signed_vma) 0x80000000 && xdisp < 0x7fff8000); + fits16 = (xdisp >= - (bfd_signed_vma) 0x8000 && xdisp < 0x8000); + fits32 = (xdisp >= - (bfd_signed_vma) 0x80000000 + && xdisp < 0x7fff8000); if (fits16) { @@ -1111,7 +1377,7 @@ elf64_alpha_relax_with_lituse (info, symval, irel, irelend) all_optimized = false; break; - case 2: /* BYTE OFFSET FORMAT */ + case LITUSE_ALPHA_BYTOFF: /* We can always optimize byte instructions. */ /* FIXME: sanity check the insn for byte op. Check that the @@ -1129,16 +1395,20 @@ elf64_alpha_relax_with_lituse (info, symval, irel, irelend) info->changed_contents = true; break; - case 3: /* CALL FORMAT */ + case LITUSE_ALPHA_JSR: + case LITUSE_ALPHA_TLSGD: + case LITUSE_ALPHA_TLSLDM: { - /* If not zero, place to jump without needing pv. */ - bfd_vma optdest = elf64_alpha_relax_opt_call (info, symval); - bfd_vma org = (info->sec->output_section->vma - + info->sec->output_offset - + urel->r_offset + 4); + bfd_vma optdest, org; bfd_signed_vma odisp; + /* If not zero, place to jump without needing pv. */ + optdest = elf64_alpha_relax_opt_call (info, symval); + org = (info->sec->output_section->vma + + info->sec->output_offset + + urel->r_offset + 4); odisp = (optdest ? optdest : symval) - org; + if (odisp >= -0x400000 && odisp < 0x400000) { Elf_Internal_Rela *xrel; @@ -1180,10 +1450,11 @@ elf64_alpha_relax_with_lituse (info, symval, irel, irelend) { Elf_Internal_Rela *gpdisp = (elf64_alpha_find_reloc_at_ofs - (irel, irelend, urel->r_offset + 4, R_ALPHA_GPDISP)); + (info->relocs, irelend, urel->r_offset + 4, + R_ALPHA_GPDISP)); if (gpdisp) { - bfd_byte *p_ldah = info->contents + gpdisp->r_offset; + bfd_byte *p_ldah = info->contents + gpdisp->r_offset; bfd_byte *p_lda = p_ldah + gpdisp->r_addend; unsigned int ldah = bfd_get_32 (info->abfd, p_ldah); unsigned int lda = bfd_get_32 (info->abfd, p_lda); @@ -1213,14 +1484,17 @@ elf64_alpha_relax_with_lituse (info, symval, irel, irelend) got entry by one, possibly eliminating it. */ if (all_optimized) { - info->gotent->use_count -= 1; - alpha_elf_tdata (info->gotent->gotobj)->total_got_entries -= 1; - if (!info->h) - alpha_elf_tdata (info->gotent->gotobj)->n_local_got_entries -= 1; + if (--info->gotent->use_count == 0) + { + int sz = alpha_got_entry_size (R_ALPHA_LITERAL); + alpha_elf_tdata (info->gotobj)->total_got_size -= sz; + if (!info->h) + alpha_elf_tdata (info->gotobj)->local_got_size -= sz; + } /* If the literal instruction is no longer needed (it may have been - reused. We can eliminate it. - ??? For now, I don't want to deal with compacting the section, + reused. We can eliminate it. */ + /* ??? For now, I don't want to deal with compacting the section, so just nop it out. */ if (!lit_reused) { @@ -1233,7 +1507,7 @@ elf64_alpha_relax_with_lituse (info, symval, irel, irelend) } } - return irel + count; + return true; } static bfd_vma @@ -1309,10 +1583,11 @@ elf64_alpha_relax_opt_call (info, symval) } static boolean -elf64_alpha_relax_without_lituse (info, symval, irel) +elf64_alpha_relax_got_load (info, symval, irel, r_type) struct alpha_relax_info *info; bfd_vma symval; Elf_Internal_Rela *irel; + unsigned long r_type; { unsigned int insn; bfd_signed_vma disp; @@ -1322,37 +1597,74 @@ elf64_alpha_relax_without_lituse (info, symval, irel) if (insn >> 26 != OP_LDQ) { + reloc_howto_type *howto = elf64_alpha_howto_table + r_type; ((*_bfd_error_handler) - ("%s: %s+0x%lx: warning: LITERAL relocation against unexpected insn", + ("%s: %s+0x%lx: warning: %s relocation against unexpected insn", bfd_archive_filename (info->abfd), info->sec->name, - (unsigned long) irel->r_offset)); + (unsigned long) irel->r_offset, howto->name)); return true; } - /* So we aren't told much. Do what we can with the address load and - fake the rest. All of the optimizations here require that the - offset from the GP fit in 16 bits. */ + /* Can't relax dynamic symbols. */ + if (alpha_elf_dynamic_symbol_p (&info->h->root, info->link_info)) + return true; - disp = symval - info->gp; - if (disp < -0x8000 || disp >= 0x8000) + /* Can't use local-exec relocations in shared libraries. */ + if (r_type == R_ALPHA_GOTTPREL && info->link_info->shared) return true; - /* On the LITERAL instruction itself, consider exchanging - `ldq R,X(gp)' for `lda R,Y(gp)'. */ + if (r_type == R_ALPHA_LITERAL) + disp = symval - info->gp; + else + { + bfd_vma dtp_base, tp_base; + + BFD_ASSERT (info->tls_segment != NULL); + dtp_base = alpha_get_dtprel_base (info->tls_segment); + tp_base = alpha_get_tprel_base (info->tls_segment); + disp = symval - (r_type == R_ALPHA_GOTDTPREL ? dtp_base : tp_base); + } - insn = (OP_LDA << 26) | (insn & 0x03ff0000); + if (disp < -0x8000 || disp >= 0x8000) + return true; + + /* Exchange LDQ for LDA. In the case of the TLS relocs, we're loading + a constant, so force the base register to be $31. */ + if (r_type == R_ALPHA_LITERAL) + insn = (OP_LDA << 26) | (insn & 0x03ff0000); + else + insn = (OP_LDA << 26) | (insn & (31 << 21)) | (31 << 16); bfd_put_32 (info->abfd, (bfd_vma) insn, info->contents + irel->r_offset); info->changed_contents = true; - irel->r_info = ELF64_R_INFO (ELF64_R_SYM (irel->r_info), R_ALPHA_GPREL16); + switch (r_type) + { + case R_ALPHA_LITERAL: + r_type = R_ALPHA_GPREL16; + break; + case R_ALPHA_GOTDTPREL: + r_type = R_ALPHA_DTPREL16; + break; + case R_ALPHA_GOTTPREL: + r_type = R_ALPHA_TPREL16; + break; + default: + BFD_ASSERT (0); + return false; + } + + irel->r_info = ELF64_R_INFO (ELF64_R_SYM (irel->r_info), r_type); info->changed_relocs = true; /* Reduce the use count on this got entry by one, possibly eliminating it. */ - info->gotent->use_count -= 1; - alpha_elf_tdata (info->gotent->gotobj)->total_got_entries -= 1; - if (!info->h) - alpha_elf_tdata (info->gotent->gotobj)->n_local_got_entries -= 1; + if (--info->gotent->use_count == 0) + { + int sz = alpha_got_entry_size (r_type); + alpha_elf_tdata (info->gotobj)->total_got_size -= sz; + if (!info->h) + alpha_elf_tdata (info->gotobj)->local_got_size -= sz; + } /* ??? Search forward through this basic block looking for insns that use the target register. Stop after an insn modifying the @@ -1371,6 +1683,353 @@ elf64_alpha_relax_without_lituse (info, symval, irel) } static boolean +elf64_alpha_relax_gprelhilo (info, symval, irel, hi) + struct alpha_relax_info *info; + bfd_vma symval; + Elf_Internal_Rela *irel; + boolean hi; +{ + unsigned int insn; + bfd_signed_vma disp; + bfd_byte *pos = info->contents + irel->r_offset; + + /* ??? This assumes that the compiler doesn't render + + array[i] + as + ldah t, array(gp) !gprelhigh + s8addl i, t, t + ldq r, array(t) !gprellow + + which would indeed be the most efficient way to implement this. */ + + return true; + + disp = symval - info->gp; + if (disp < -0x8000 || disp >= 0x8000) + return true; + + if (hi) + { + /* Nop out the high instruction. */ + + bfd_put_32 (info->abfd, (bfd_vma) INSN_UNOP, pos); + info->changed_contents = true; + + irel->r_info = ELF64_R_INFO (0, R_ALPHA_NONE); + irel->r_addend = 0; + info->changed_relocs = true; + } + else + { + /* Adjust the low instruction to reference GP directly. */ + + insn = bfd_get_32 (info->abfd, pos); + insn = (insn & 0xffe00000) | (29 << 16); + bfd_put_32 (info->abfd, (bfd_vma) insn, pos); + info->changed_contents = true; + + irel->r_info = ELF64_R_INFO (ELF64_R_SYM (irel->r_info), + R_ALPHA_GPREL16); + info->changed_relocs = true; + } + + return true; +} + +static boolean +elf64_alpha_relax_tls_get_addr (info, symval, irel, is_gd) + struct alpha_relax_info *info; + bfd_vma symval; + Elf_Internal_Rela *irel; + boolean is_gd; +{ + bfd_byte *pos[5]; + unsigned int insn; + Elf_Internal_Rela *gpdisp, *hint; + boolean dynamic, use_gottprel; + + dynamic = alpha_elf_dynamic_symbol_p (&info->h->root, info->link_info); + + /* ??? For LD relaxation, we need a symbol referencing the beginning + of the TLS segment. */ + if (!is_gd) + return true; + + /* If a TLS symbol is accessed using IE at least once, there is no point + to use dynamic model for it. */ + if (is_gd && info->h && (info->h->flags & ALPHA_ELF_LINK_HASH_TLS_IE)) + ; + + /* If the symbol is local, and we've already committed to DF_STATIC_TLS, + then we might as well relax to IE. */ + else if (info->link_info->shared && !dynamic + && (info->link_info->flags & DF_STATIC_TLS)) + ; + + /* Otherwise we must be building an executable to do anything. */ + else if (info->link_info->shared) + return true; + + /* The TLSGD/TLSLDM relocation must be followed by a LITERAL and + the matching LITUSE_TLS relocations. */ + if (irel + 2 >= info->relend) + return true; + if (ELF64_R_TYPE (irel[1].r_info) != R_ALPHA_LITERAL + || ELF64_R_TYPE (irel[2].r_info) != R_ALPHA_LITUSE + || irel[2].r_addend != (is_gd ? LITUSE_ALPHA_TLSGD : LITUSE_ALPHA_TLSLDM)) + return true; + + /* There must be a GPDISP relocation positioned immediately after the + LITUSE relocation. */ + gpdisp = elf64_alpha_find_reloc_at_ofs (info->relocs, info->relend, + irel[2].r_offset + 4, R_ALPHA_GPDISP); + if (!gpdisp) + return true; + + pos[0] = info->contents + irel[0].r_offset; + pos[1] = info->contents + irel[1].r_offset; + pos[2] = info->contents + irel[2].r_offset; + pos[3] = info->contents + gpdisp->r_offset; + pos[4] = pos[3] + gpdisp->r_addend; + + /* Only positions 0 and 1 are allowed to be out of order. */ + if (pos[1] < pos[0]) + { + bfd_byte *tmp = pos[0]; + pos[0] = pos[1]; + pos[1] = tmp; + } + if (pos[1] >= pos[2] || pos[2] >= pos[3] || pos[3] >= pos[4]) + return true; + + /* Reduce the use count on the LITERAL relocation. Do this before we + smash the symndx when we adjust the relocations below. */ + { + struct alpha_elf_got_entry *lit_gotent; + struct alpha_elf_link_hash_entry *lit_h; + unsigned long indx; + + BFD_ASSERT (ELF64_R_SYM (irel[1].r_info) >= info->symtab_hdr->sh_info); + indx = ELF64_R_SYM (irel[1].r_info) - info->symtab_hdr->sh_info; + lit_h = alpha_elf_sym_hashes (info->abfd)[indx]; + + while (lit_h->root.root.type == bfd_link_hash_indirect + || lit_h->root.root.type == bfd_link_hash_warning) + lit_h = (struct alpha_elf_link_hash_entry *) lit_h->root.root.u.i.link; + + for (lit_gotent = lit_h->got_entries; lit_gotent ; + lit_gotent = lit_gotent->next) + if (lit_gotent->gotobj == info->gotobj + && lit_gotent->reloc_type == R_ALPHA_LITERAL + && lit_gotent->addend == irel[1].r_addend) + break; + BFD_ASSERT (lit_gotent); + + if (--lit_gotent->use_count == 0) + { + int sz = alpha_got_entry_size (R_ALPHA_LITERAL); + alpha_elf_tdata (info->gotobj)->total_got_size -= sz; + } + } + + /* Change + + lda $16,x($gp) !tlsgd!1 + ldq $27,__tls_get_addr($gp) !literal!1 + jsr $26,($27)__tls_get_addr !lituse_tlsgd!1 + ldah $29,0($26) !gpdisp!2 + lda $29,0($29) !gpdisp!2 + to + ldq $16,x($gp) !gottprel + unop + call_pal rduniq + addq $16,$0,$0 + unop + or the first pair to + lda $16,x($gp) !tprel + unop + or + ldah $16,x($gp) !tprelhi + lda $16,x($16) !tprello + + as appropriate. */ + + use_gottprel = false; + switch (!dynamic && !info->link_info->shared) + { + case 1: + { + bfd_vma tp_base; + bfd_signed_vma disp; + + BFD_ASSERT (info->tls_segment != NULL); + tp_base = alpha_get_tprel_base (info->tls_segment); + disp = symval - tp_base; + + if (disp >= -0x8000 && disp < 0x8000) + { + insn = (OP_LDA << 26) | (16 << 21) | (31 << 16); + bfd_put_32 (info->abfd, (bfd_vma) insn, pos[0]); + bfd_put_32 (info->abfd, (bfd_vma) INSN_UNOP, pos[1]); + + irel[0].r_offset = pos[0] - info->contents; + irel[0].r_info = ELF64_R_INFO (ELF64_R_SYM (irel->r_info), + R_ALPHA_TPREL16); + irel[1].r_info = ELF64_R_INFO (0, R_ALPHA_NONE); + break; + } + else if (disp >= -(bfd_signed_vma) 0x80000000 + && disp < (bfd_signed_vma) 0x7fff8000) + { + insn = (OP_LDAH << 26) | (16 << 21) | (31 << 16); + bfd_put_32 (info->abfd, (bfd_vma) insn, pos[0]); + insn = (OP_LDA << 26) | (16 << 21) | (16 << 16); + bfd_put_32 (info->abfd, (bfd_vma) insn, pos[1]); + + irel[0].r_offset = pos[0] - info->contents; + irel[0].r_info = ELF64_R_INFO (ELF64_R_SYM (irel->r_info), + R_ALPHA_TPRELHI); + irel[1].r_offset = pos[1] - info->contents; + irel[1].r_info = ELF64_R_INFO (ELF64_R_SYM (irel->r_info), + R_ALPHA_TPRELLO); + break; + } + } + /* FALLTHRU */ + + default: + use_gottprel = true; + + insn = (OP_LDQ << 26) | (16 << 21) | (29 << 16); + bfd_put_32 (info->abfd, (bfd_vma) insn, pos[0]); + bfd_put_32 (info->abfd, (bfd_vma) INSN_UNOP, pos[1]); + + irel[0].r_offset = pos[0] - info->contents; + irel[0].r_info = ELF64_R_INFO (ELF64_R_SYM (irel->r_info), + R_ALPHA_GOTTPREL); + irel[1].r_info = ELF64_R_INFO (0, R_ALPHA_NONE); + break; + } + + bfd_put_32 (info->abfd, (bfd_vma) INSN_RDUNIQ, pos[2]); + + insn = INSN_ADDQ | (16 << 21) | (0 << 16) | (0 << 0); + bfd_put_32 (info->abfd, (bfd_vma) insn, pos[3]); + + bfd_put_32 (info->abfd, (bfd_vma) INSN_UNOP, pos[4]); + + irel[2].r_info = ELF64_R_INFO (0, R_ALPHA_NONE); + gpdisp->r_info = ELF64_R_INFO (0, R_ALPHA_NONE); + + hint = elf64_alpha_find_reloc_at_ofs (info->relocs, info->relend, + irel[2].r_offset, R_ALPHA_HINT); + if (hint) + hint->r_info = ELF64_R_INFO (0, R_ALPHA_NONE); + + info->changed_contents = true; + info->changed_relocs = true; + + /* Reduce the use count on the TLSGD/TLSLDM relocation. */ + if (--info->gotent->use_count == 0) + { + int sz = alpha_got_entry_size (info->gotent->reloc_type); + alpha_elf_tdata (info->gotobj)->total_got_size -= sz; + if (!info->h) + alpha_elf_tdata (info->gotobj)->local_got_size -= sz; + } + + /* If we've switched to a GOTTPREL relocation, increment the reference + count on that got entry. */ + if (use_gottprel) + { + struct alpha_elf_got_entry *tprel_gotent; + + for (tprel_gotent = *info->first_gotent; tprel_gotent ; + tprel_gotent = tprel_gotent->next) + if (tprel_gotent->gotobj == info->gotobj + && tprel_gotent->reloc_type == R_ALPHA_GOTTPREL + && tprel_gotent->addend == irel->r_addend) + break; + if (tprel_gotent) + tprel_gotent->use_count++; + else + { + if (info->gotent->use_count == 0) + tprel_gotent = info->gotent; + else + { + tprel_gotent = (struct alpha_elf_got_entry *) + bfd_alloc (info->abfd, sizeof (struct alpha_elf_got_entry)); + if (!tprel_gotent) + return false; + + tprel_gotent->next = *info->first_gotent; + *info->first_gotent = tprel_gotent; + + tprel_gotent->gotobj = info->gotobj; + tprel_gotent->addend = irel->r_addend; + tprel_gotent->got_offset = -1; + tprel_gotent->reloc_done = 0; + tprel_gotent->reloc_xlated = 0; + } + + tprel_gotent->use_count = 1; + tprel_gotent->reloc_type = R_ALPHA_GOTTPREL; + } + } + + return true; +} + +static struct elf_link_tls_segment * +elf64_alpha_relax_find_tls_segment (info, seg) + struct alpha_relax_info *info; + struct elf_link_tls_segment *seg; +{ + bfd *output_bfd = info->sec->output_section->owner; + asection *o; + unsigned int align; + bfd_vma base, end; + + for (o = output_bfd->sections; o ; o = o->next) + if ((o->flags & SEC_THREAD_LOCAL) != 0 + && (o->flags & SEC_LOAD) != 0) + break; + if (!o) + return NULL; + + base = o->vma; + align = 0; + + do + { + bfd_vma size; + + if (bfd_get_section_alignment (output_bfd, o) > align) + align = bfd_get_section_alignment (output_bfd, o); + + size = o->_raw_size; + if (size == 0 && (o->flags & SEC_HAS_CONTENTS) == 0) + { + struct bfd_link_order *lo; + for (lo = o->link_order_head; lo ; lo = lo->next) + if (size < lo->offset + lo->size) + size = lo->offset + lo->size; + } + end = o->vma + size; + o = o->next; + } + while (o && (o->flags & SEC_THREAD_LOCAL)); + + seg->start = base; + seg->size = end - base; + seg->align = align; + + return seg; +} + +static boolean elf64_alpha_relax_section (abfd, sec, link_info, again) bfd *abfd; asection *sec; @@ -1378,16 +2037,12 @@ elf64_alpha_relax_section (abfd, sec, link_info, again) boolean *again; { Elf_Internal_Shdr *symtab_hdr; - Elf_Internal_Shdr *shndx_hdr; Elf_Internal_Rela *internal_relocs; - Elf_Internal_Rela *free_relocs = NULL; Elf_Internal_Rela *irel, *irelend; - bfd_byte *free_contents = NULL; - Elf64_External_Sym *extsyms = NULL; - Elf64_External_Sym *free_extsyms = NULL; - Elf_External_Sym_Shndx *shndx_buf = NULL; + Elf_Internal_Sym *isymbuf = NULL; struct alpha_elf_got_entry **local_got_entries; struct alpha_relax_info info; + struct elf_link_tls_segment tls_segment; /* We are not currently changing any sizes, so only one pass. */ *again = false; @@ -1410,116 +2065,99 @@ elf64_alpha_relax_section (abfd, sec, link_info, again) (abfd, sec, (PTR) NULL, (Elf_Internal_Rela *) NULL, link_info->keep_memory)); if (internal_relocs == NULL) - goto error_return; - if (! link_info->keep_memory) - free_relocs = internal_relocs; + return false; memset(&info, 0, sizeof (info)); info.abfd = abfd; info.sec = sec; info.link_info = link_info; + info.symtab_hdr = symtab_hdr; info.relocs = internal_relocs; info.relend = irelend = internal_relocs + sec->reloc_count; - /* Find the GP for this object. */ + /* Find the GP for this object. Do not store the result back via + _bfd_set_gp_value, since this could change again before final. */ info.gotobj = alpha_elf_tdata (abfd)->gotobj; if (info.gotobj) { asection *sgot = alpha_elf_tdata (info.gotobj)->got; - info.gp = _bfd_get_gp_value (info.gotobj); - if (info.gp == 0) - { - info.gp = (sgot->output_section->vma - + sgot->output_offset - + 0x8000); - _bfd_set_gp_value (info.gotobj, info.gp); - } + info.gp = (sgot->output_section->vma + + sgot->output_offset + + 0x8000); + } + + /* Get the section contents. */ + if (elf_section_data (sec)->this_hdr.contents != NULL) + info.contents = elf_section_data (sec)->this_hdr.contents; + else + { + info.contents = (bfd_byte *) bfd_malloc (sec->_raw_size); + if (info.contents == NULL) + goto error_return; + + if (! bfd_get_section_contents (abfd, sec, info.contents, + (file_ptr) 0, sec->_raw_size)) + goto error_return; } + /* Compute the TLS segment information. The version normally found in + elf_hash_table (link_info)->tls_segment isn't built until final_link. + ??? Probably should look into extracting this into a common function. */ + info.tls_segment = elf64_alpha_relax_find_tls_segment (&info, &tls_segment); + for (irel = internal_relocs; irel < irelend; irel++) { bfd_vma symval; - Elf_Internal_Sym isym; struct alpha_elf_got_entry *gotent; + unsigned long r_type = ELF64_R_TYPE (irel->r_info); - if (ELF64_R_TYPE (irel->r_info) != (int) R_ALPHA_LITERAL) - continue; - - /* Get the section contents. */ - if (info.contents == NULL) + /* Early exit for unhandled or unrelaxable relocations. */ + switch (r_type) { - if (elf_section_data (sec)->this_hdr.contents != NULL) - info.contents = elf_section_data (sec)->this_hdr.contents; - else - { - info.contents = (bfd_byte *) bfd_malloc (sec->_raw_size); - if (info.contents == NULL) - goto error_return; - free_contents = info.contents; - - if (! bfd_get_section_contents (abfd, sec, info.contents, - (file_ptr) 0, sec->_raw_size)) - goto error_return; - } + case R_ALPHA_LITERAL: + case R_ALPHA_GPRELHIGH: + case R_ALPHA_GPRELLOW: + case R_ALPHA_GOTDTPREL: + case R_ALPHA_GOTTPREL: + case R_ALPHA_TLSGD: + case R_ALPHA_TLSLDM: + break; + default: + continue; } - /* Read this BFD's symbols if we haven't done so already. */ - if (extsyms == NULL) + /* Get the value of the symbol referred to by the reloc. */ + if (ELF64_R_SYM (irel->r_info) < symtab_hdr->sh_info) { - bfd_size_type amt; - - if (symtab_hdr->contents != NULL) - extsyms = (Elf64_External_Sym *) symtab_hdr->contents; - else - { - amt = symtab_hdr->sh_info; - amt *= sizeof (Elf64_External_Sym); - extsyms = (Elf64_External_Sym *) bfd_malloc (amt); - if (extsyms == NULL) - goto error_return; - free_extsyms = extsyms; - if (bfd_seek (abfd, symtab_hdr->sh_offset, SEEK_SET) != 0 - || bfd_bread ((PTR) extsyms, amt, abfd) != amt) - goto error_return; - } + /* A local symbol. */ + Elf_Internal_Sym *isym; - shndx_hdr = &elf_tdata (abfd)->symtab_shndx_hdr; - if (shndx_hdr->sh_size != 0) + /* Read this BFD's local symbols. */ + if (isymbuf == NULL) { - amt = symtab_hdr->sh_info; - amt *= sizeof (Elf_External_Sym_Shndx); - shndx_buf = (Elf_External_Sym_Shndx *) bfd_malloc (amt); - if (shndx_buf == NULL) - goto error_return; - if (bfd_seek (abfd, shndx_hdr->sh_offset, SEEK_SET) != 0 - || bfd_bread ((PTR) shndx_buf, amt, abfd) != amt) + isymbuf = (Elf_Internal_Sym *) symtab_hdr->contents; + if (isymbuf == NULL) + isymbuf = bfd_elf_get_elf_syms (abfd, symtab_hdr, + symtab_hdr->sh_info, 0, + NULL, NULL, NULL); + if (isymbuf == NULL) goto error_return; } - } - /* Get the value of the symbol referred to by the reloc. */ - if (ELF64_R_SYM (irel->r_info) < symtab_hdr->sh_info) - { - /* A local symbol. */ - Elf64_External_Sym *esym; - Elf_External_Sym_Shndx *shndx; - - esym = extsyms + ELF64_R_SYM (irel->r_info); - shndx = shndx_buf + (shndx_buf ? ELF64_R_SYM (irel->r_info) : 0); - bfd_elf64_swap_symbol_in (abfd, esym, shndx, &isym); - if (isym.st_shndx == SHN_UNDEF) - info.tsec = bfd_und_section_ptr; - else if (isym.st_shndx == SHN_ABS) + isym = isymbuf + ELF64_R_SYM (irel->r_info); + if (isym->st_shndx == SHN_UNDEF) + continue; + else if (isym->st_shndx == SHN_ABS) info.tsec = bfd_abs_section_ptr; - else if (isym.st_shndx == SHN_COMMON) + else if (isym->st_shndx == SHN_COMMON) info.tsec = bfd_com_section_ptr; else - info.tsec = bfd_section_from_elf_index (abfd, isym.st_shndx); + info.tsec = bfd_section_from_elf_index (abfd, isym->st_shndx); info.h = NULL; - info.other = isym.st_other; - gotent = local_got_entries[ELF64_R_SYM(irel->r_info)]; - symval = isym.st_value; + info.other = isym->st_other; + info.first_gotent = &local_got_entries[ELF64_R_SYM(irel->r_info)]; + symval = isym->st_value; } else { @@ -1534,66 +2172,103 @@ elf64_alpha_relax_section (abfd, sec, link_info, again) || h->root.root.type == bfd_link_hash_warning) h = (struct alpha_elf_link_hash_entry *)h->root.root.u.i.link; - /* We can't do anthing with undefined or dynamic symbols. */ - if (h->root.root.type == bfd_link_hash_undefined - || h->root.root.type == bfd_link_hash_undefweak - || alpha_elf_dynamic_symbol_p (&h->root, link_info)) + /* If the symbol is undefined, we can't do anything with it. */ + if (h->root.root.type == bfd_link_hash_undefweak + || h->root.root.type == bfd_link_hash_undefined) + continue; + + /* If the symbol isn't defined in the current module, again + we can't do anything. */ + if (!(h->root.elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR)) continue; info.h = h; info.tsec = h->root.root.u.def.section; info.other = h->root.other; - gotent = h->got_entries; + info.first_gotent = &h->got_entries; symval = h->root.root.u.def.value; } /* Search for the got entry to be used by this relocation. */ - while (gotent->gotobj != info.gotobj || gotent->addend != irel->r_addend) - gotent = gotent->next; + for (gotent = *info.first_gotent; gotent ; gotent = gotent->next) + if (gotent->gotobj == info.gotobj + && gotent->reloc_type == r_type + && gotent->addend == irel->r_addend) + break; info.gotent = gotent; symval += info.tsec->output_section->vma + info.tsec->output_offset; symval += irel->r_addend; - BFD_ASSERT(info.gotent != NULL); + switch (r_type) + { + case R_ALPHA_LITERAL: + BFD_ASSERT(info.gotent != NULL); - /* If there exist LITUSE relocations immediately following, this - opens up all sorts of interesting optimizations, because we - now know every location that this address load is used. */ + /* If there exist LITUSE relocations immediately following, this + opens up all sorts of interesting optimizations, because we + now know every location that this address load is used. */ + if (irel+1 < irelend + && ELF64_R_TYPE (irel[1].r_info) == R_ALPHA_LITUSE) + { + if (!elf64_alpha_relax_with_lituse (&info, symval, irel)) + goto error_return; + } + else + { + if (!elf64_alpha_relax_got_load (&info, symval, irel, r_type)) + goto error_return; + } + break; - if (irel+1 < irelend && ELF64_R_TYPE (irel[1].r_info) == R_ALPHA_LITUSE) - { - irel = elf64_alpha_relax_with_lituse (&info, symval, irel, irelend); - if (irel == NULL) + case R_ALPHA_GPRELHIGH: + case R_ALPHA_GPRELLOW: + if (!elf64_alpha_relax_gprelhilo (&info, symval, irel, + r_type == R_ALPHA_GPRELHIGH)) goto error_return; - } - else - { - if (!elf64_alpha_relax_without_lituse (&info, symval, irel)) + break; + + case R_ALPHA_GOTDTPREL: + case R_ALPHA_GOTTPREL: + BFD_ASSERT(info.gotent != NULL); + if (!elf64_alpha_relax_got_load (&info, symval, irel, r_type)) goto error_return; + break; + + case R_ALPHA_TLSGD: + case R_ALPHA_TLSLDM: + BFD_ASSERT(info.gotent != NULL); + if (!elf64_alpha_relax_tls_get_addr (&info, symval, irel, + r_type == R_ALPHA_TLSGD)) + goto error_return; + break; } } - if (!elf64_alpha_size_got_sections (abfd, link_info)) + if (!elf64_alpha_size_plt_section (link_info)) + return false; + if (!elf64_alpha_size_got_sections (link_info)) + return false; + if (!elf64_alpha_size_rela_got_section (link_info)) return false; - if (info.changed_relocs) - { - elf_section_data (sec)->relocs = internal_relocs; - } - else if (free_relocs != NULL) + if (isymbuf != NULL + && symtab_hdr->contents != (unsigned char *) isymbuf) { - free (free_relocs); + if (!link_info->keep_memory) + free (isymbuf); + else + { + /* Cache the symbols for elf_link_input_bfd. */ + symtab_hdr->contents = (unsigned char *) isymbuf; + } } - if (info.changed_contents) + if (info.contents != NULL + && elf_section_data (sec)->this_hdr.contents != info.contents) { - elf_section_data (sec)->this_hdr.contents = info.contents; - } - else if (free_contents != NULL) - { - if (! link_info->keep_memory) - free (free_contents); + if (!info.changed_contents && !link_info->keep_memory) + free (info.contents); else { /* Cache the section contents for elf_link_input_bfd. */ @@ -1601,18 +2276,12 @@ elf64_alpha_relax_section (abfd, sec, link_info, again) } } - if (shndx_buf != NULL) - free (shndx_buf); - - if (free_extsyms != NULL) + if (elf_section_data (sec)->relocs != internal_relocs) { - if (! link_info->keep_memory) - free (free_extsyms); + if (!info.changed_relocs) + free (internal_relocs); else - { - /* Cache the symbols for elf_link_input_bfd. */ - symtab_hdr->contents = (unsigned char *) extsyms; - } + elf_section_data (sec)->relocs = internal_relocs; } *again = info.changed_contents || info.changed_relocs; @@ -1620,14 +2289,15 @@ elf64_alpha_relax_section (abfd, sec, link_info, again) return true; error_return: - if (free_relocs != NULL) - free (free_relocs); - if (free_contents != NULL) - free (free_contents); - if (shndx_buf != NULL) - free (shndx_buf); - if (free_extsyms != NULL) - free (free_extsyms); + if (isymbuf != NULL + && symtab_hdr->contents != (unsigned char *) isymbuf) + free (isymbuf); + if (info.contents != NULL + && elf_section_data (sec)->this_hdr.contents != info.contents) + free (info.contents); + if (internal_relocs != NULL + && elf_section_data (sec)->relocs != internal_relocs) + free (internal_relocs); return false; } @@ -1643,7 +2313,7 @@ elf64_alpha_relax_section (abfd, sec, link_info, again) #define PLT_ENTRY_WORD2 0 #define PLT_ENTRY_WORD3 0 -#define MAX_GOT_ENTRIES (64*1024 / 8) +#define MAX_GOT_SIZE (64*1024) #define ELF_DYNAMIC_INTERPRETER "/usr/lib/ld.so" @@ -1656,7 +2326,7 @@ static boolean elf64_alpha_section_from_shdr (abfd, hdr, name) bfd *abfd; Elf64_Internal_Shdr *hdr; - char *name; + const char *name; { asection *newsect; @@ -1908,9 +2578,8 @@ elf64_alpha_read_ecoff_info (abfd, section, debug) if (ext_hdr == NULL && swap->external_hdr_size != 0) goto error_return; - if (bfd_get_section_contents (abfd, section, ext_hdr, (file_ptr) 0, - swap->external_hdr_size) - == false) + if (! bfd_get_section_contents (abfd, section, ext_hdr, (file_ptr) 0, + swap->external_hdr_size)) goto error_return; symhdr = &debug->symbolic_header; @@ -2244,18 +2913,86 @@ elf64_alpha_output_extsym (h, data) return true; } + +/* Search for and possibly create a got entry. */ -/* FIXME: Create a runtime procedure table from the .mdebug section. - -static boolean -mips_elf_create_procedure_table (handle, abfd, info, s, debug) - PTR handle; +static struct alpha_elf_got_entry * +get_got_entry (abfd, h, r_type, r_symndx, r_addend) bfd *abfd; - struct bfd_link_info *info; - asection *s; - struct ecoff_debug_info *debug; -*/ - + struct alpha_elf_link_hash_entry *h; + unsigned long r_type, r_symndx; + bfd_vma r_addend; +{ + struct alpha_elf_got_entry *gotent; + struct alpha_elf_got_entry **slot; + + if (h) + slot = &h->got_entries; + else + { + /* This is a local .got entry -- record for merge. */ + + struct alpha_elf_got_entry **local_got_entries; + + local_got_entries = alpha_elf_tdata(abfd)->local_got_entries; + if (!local_got_entries) + { + bfd_size_type size; + Elf_Internal_Shdr *symtab_hdr; + + symtab_hdr = &elf_tdata(abfd)->symtab_hdr; + size = symtab_hdr->sh_info; + size *= sizeof (struct alpha_elf_got_entry *); + + local_got_entries + = (struct alpha_elf_got_entry **) bfd_zalloc (abfd, size); + if (!local_got_entries) + return NULL; + + alpha_elf_tdata (abfd)->local_got_entries = local_got_entries; + } + + slot = &local_got_entries[r_symndx]; + } + + for (gotent = *slot; gotent ; gotent = gotent->next) + if (gotent->gotobj == abfd + && gotent->reloc_type == r_type + && gotent->addend == r_addend) + break; + + if (!gotent) + { + int entry_size; + bfd_size_type amt; + + amt = sizeof (struct alpha_elf_got_entry); + gotent = (struct alpha_elf_got_entry *) bfd_alloc (abfd, amt); + if (!gotent) + return NULL; + + gotent->gotobj = abfd; + gotent->addend = r_addend; + gotent->got_offset = -1; + gotent->use_count = 1; + gotent->reloc_type = r_type; + gotent->reloc_done = 0; + gotent->reloc_xlated = 0; + + gotent->next = *slot; + *slot = gotent; + + entry_size = alpha_got_entry_size (r_type); + alpha_elf_tdata (abfd)->total_got_size += entry_size; + if (!h) + alpha_elf_tdata(abfd)->local_got_size += entry_size; + } + else + gotent->use_count += 1; + + return gotent; +} + /* Handle dynamic relocations when doing an Alpha ELF link. */ static boolean @@ -2270,9 +3007,8 @@ elf64_alpha_check_relocs (abfd, info, sec, relocs) const char *rel_sec_name; Elf_Internal_Shdr *symtab_hdr; struct alpha_elf_link_hash_entry **sym_hashes; - struct alpha_elf_got_entry **local_got_entries; const Elf_Internal_Rela *rel, *relend; - int got_created; + boolean got_created; bfd_size_type amt; if (info->relocateable) @@ -2286,14 +3022,23 @@ elf64_alpha_check_relocs (abfd, info, sec, relocs) rel_sec_name = NULL; symtab_hdr = &elf_tdata(abfd)->symtab_hdr; sym_hashes = alpha_elf_sym_hashes(abfd); - local_got_entries = alpha_elf_tdata(abfd)->local_got_entries; - got_created = 0; + got_created = false; relend = relocs + sec->reloc_count; for (rel = relocs; rel < relend; ++rel) { + enum { + NEED_GOT = 1, + NEED_GOT_ENTRY = 2, + NEED_DYNREL = 4 + }; + unsigned long r_symndx, r_type; struct alpha_elf_link_hash_entry *h; + unsigned int gotent_flags; + boolean maybe_dynamic; + unsigned int need; + bfd_vma addend; r_symndx = ELF64_R_SYM (rel->r_info); if (r_symndx < symtab_hdr->sh_info) @@ -2308,125 +3053,40 @@ elf64_alpha_check_relocs (abfd, info, sec, relocs) h->root.elf_link_hash_flags |= ELF_LINK_HASH_REF_REGULAR; } + + /* We can only get preliminary data on whether a symbol is + locally or externally defined, as not all of the input files + have yet been processed. Do something with what we know, as + this may help reduce memory usage and processing time later. */ + maybe_dynamic = false; + if (h && ((info->shared + && (!info->symbolic || info->allow_shlib_undefined)) + || ! (h->root.elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) + || h->root.root.type == bfd_link_hash_defweak)) + maybe_dynamic = true; + + need = 0; + gotent_flags = 0; r_type = ELF64_R_TYPE (rel->r_info); + addend = rel->r_addend; switch (r_type) { case R_ALPHA_LITERAL: - { - struct alpha_elf_got_entry *gotent; - int flags = 0; - - if (h) - { - /* Search for and possibly create a got entry. */ - for (gotent = h->got_entries; gotent ; gotent = gotent->next) - if (gotent->gotobj == abfd && - gotent->addend == rel->r_addend) - break; - - if (!gotent) - { - amt = sizeof (struct alpha_elf_got_entry); - gotent = ((struct alpha_elf_got_entry *) - bfd_alloc (abfd, amt)); - if (!gotent) - return false; - - gotent->gotobj = abfd; - gotent->addend = rel->r_addend; - gotent->got_offset = -1; - gotent->flags = 0; - gotent->use_count = 1; - - gotent->next = h->got_entries; - h->got_entries = gotent; - - alpha_elf_tdata (abfd)->total_got_entries++; - } - else - gotent->use_count += 1; - } - else - { - /* This is a local .got entry -- record for merge. */ - if (!local_got_entries) - { - bfd_size_type size; - size = symtab_hdr->sh_info; - size *= sizeof (struct alpha_elf_got_entry *); - - local_got_entries = ((struct alpha_elf_got_entry **) - bfd_alloc (abfd, size)); - if (!local_got_entries) - return false; - - memset (local_got_entries, 0, (size_t) size); - alpha_elf_tdata (abfd)->local_got_entries = - local_got_entries; - } - - for (gotent = local_got_entries[ELF64_R_SYM(rel->r_info)]; - gotent != NULL && gotent->addend != rel->r_addend; - gotent = gotent->next) - continue; - if (!gotent) - { - amt = sizeof (struct alpha_elf_got_entry); - gotent = ((struct alpha_elf_got_entry *) - bfd_alloc (abfd, amt)); - if (!gotent) - return false; - - gotent->gotobj = abfd; - gotent->addend = rel->r_addend; - gotent->got_offset = -1; - gotent->flags = 0; - gotent->use_count = 1; - - gotent->next = local_got_entries[ELF64_R_SYM(rel->r_info)]; - local_got_entries[ELF64_R_SYM(rel->r_info)] = gotent; - - alpha_elf_tdata(abfd)->total_got_entries++; - alpha_elf_tdata(abfd)->n_local_got_entries++; - } - else - gotent->use_count += 1; - } - - /* Remember how this literal is used from its LITUSEs. - This will be important when it comes to decide if we can - create a .plt entry for a function symbol. */ - if (rel+1 < relend - && ELF64_R_TYPE (rel[1].r_info) == R_ALPHA_LITUSE) - { - do - { - ++rel; - if (rel->r_addend >= 1 && rel->r_addend <= 3) - flags |= 1 << rel->r_addend; - } - while (rel+1 < relend && - ELF64_R_TYPE (rel[1].r_info) == R_ALPHA_LITUSE); - } - else - { - /* No LITUSEs -- presumably the address is not being - loaded for nothing. */ - flags = ALPHA_ELF_LINK_HASH_LU_ADDR; - } - - gotent->flags |= flags; - if (h) - { - /* Make a guess as to whether a .plt entry will be needed. */ - if ((h->flags |= flags) == ALPHA_ELF_LINK_HASH_LU_FUNC) - h->root.elf_link_hash_flags |= ELF_LINK_HASH_NEEDS_PLT; - else - h->root.elf_link_hash_flags &= ~ELF_LINK_HASH_NEEDS_PLT; - } - } - /* FALLTHRU */ + need = NEED_GOT | NEED_GOT_ENTRY; + + /* Remember how this literal is used from its LITUSEs. + This will be important when it comes to decide if we can + create a .plt entry for a function symbol. */ + while (++rel < relend && ELF64_R_TYPE (rel->r_info) == R_ALPHA_LITUSE) + if (rel->r_addend >= 1 && rel->r_addend <= 5) + gotent_flags |= 1 << rel->r_addend; + --rel; + + /* No LITUSEs -- presumably the address is used somehow. */ + if (gotent_flags == 0) + gotent_flags = ALPHA_ELF_LINK_HASH_LU_ADDR; + break; case R_ALPHA_GPDISP: case R_ALPHA_GPREL16: @@ -2434,9 +3094,38 @@ elf64_alpha_check_relocs (abfd, info, sec, relocs) case R_ALPHA_GPRELHIGH: case R_ALPHA_GPRELLOW: case R_ALPHA_BRSGP: - /* We don't actually use the .got here, but the sections must - be created before the linker maps input sections to output - sections. */ + need = NEED_GOT; + break; + + case R_ALPHA_REFLONG: + case R_ALPHA_REFQUAD: + if (info->shared || maybe_dynamic) + need = NEED_DYNREL; + break; + + case R_ALPHA_TLSGD: + case R_ALPHA_TLSLDM: + case R_ALPHA_GOTDTPREL: + need = NEED_GOT | NEED_GOT_ENTRY; + break; + + case R_ALPHA_GOTTPREL: + need = NEED_GOT | NEED_GOT_ENTRY; + gotent_flags = ALPHA_ELF_LINK_HASH_TLS_IE; + if (info->shared) + info->flags |= DF_STATIC_TLS; + break; + + case R_ALPHA_TPREL64: + if (info->shared || maybe_dynamic) + need = NEED_DYNREL; + if (info->shared) + info->flags |= DF_STATIC_TLS; + break; + } + + if (need & NEED_GOT) + { if (!got_created) { if (!elf64_alpha_create_got_section (abfd, info)) @@ -2450,17 +3139,36 @@ elf64_alpha_check_relocs (abfd, info, sec, relocs) got_created = 1; } - break; + } - case R_ALPHA_SREL16: - case R_ALPHA_SREL32: - case R_ALPHA_SREL64: - if (h == NULL) - break; - /* FALLTHRU */ + if (need & NEED_GOT_ENTRY) + { + struct alpha_elf_got_entry *gotent; - case R_ALPHA_REFLONG: - case R_ALPHA_REFQUAD: + gotent = get_got_entry (abfd, h, r_type, r_symndx, addend); + if (!gotent) + return false; + + if (gotent_flags) + { + gotent->flags |= gotent_flags; + if (h) + { + gotent_flags |= h->flags; + h->flags = gotent_flags; + + /* Make a guess as to whether a .plt entry is needed. */ + if ((gotent_flags & ALPHA_ELF_LINK_HASH_LU_FUNC) + && !(gotent_flags & ~ALPHA_ELF_LINK_HASH_LU_FUNC)) + h->root.elf_link_hash_flags |= ELF_LINK_HASH_NEEDS_PLT; + else + h->root.elf_link_hash_flags &= ~ELF_LINK_HASH_NEEDS_PLT; + } + } + } + + if (need & NEED_DYNREL) + { if (rel_sec_name == NULL) { rel_sec_name = (bfd_elf_string_from_elf_section @@ -2530,15 +3238,15 @@ elf64_alpha_check_relocs (abfd, info, sec, relocs) else rent->count++; } - else if (info->shared && (sec->flags & SEC_ALLOC)) + else if (info->shared) { /* If this is a shared library, and the section is to be loaded into memory, we need a RELATIVE reloc. */ sreloc->_raw_size += sizeof (Elf64_External_Rela); - if (sec->flags & SEC_READONLY) + if ((sec->flags & (SEC_READONLY | SEC_ALLOC)) + == (SEC_READONLY | SEC_ALLOC)) info->flags |= DF_TEXTREL; } - break; } } @@ -2570,7 +3278,8 @@ elf64_alpha_adjust_dynamic_symbol (info, h) && ((h->type == STT_FUNC && !(ah->flags & ALPHA_ELF_LINK_HASH_LU_ADDR)) || (h->type == STT_NOTYPE - && ah->flags == ALPHA_ELF_LINK_HASH_LU_FUNC)) + && (ah->flags & ALPHA_ELF_LINK_HASH_LU_FUNC) + && !(ah->flags & ~ALPHA_ELF_LINK_HASH_LU_FUNC))) /* Don't prevent otherwise valid programs from linking by attempting to create a new .got entry somewhere. A Correct Solution would be to add a new .got section to a new object file and let it be merged @@ -2667,8 +3376,13 @@ elf64_alpha_merge_ind_symbols (hi, dummy) { gin = gi->next; for (gs = gsh; gs ; gs = gs->next) - if (gi->gotobj == gs->gotobj && gi->addend == gs->addend) - goto got_found; + if (gi->gotobj == gs->gotobj + && gi->reloc_type == gs->reloc_type + && gi->addend == gs->addend) + { + gi->use_count += gs->use_count; + goto got_found; + } gi->next = hs->got_entries; hs->got_entries = gi; got_found:; @@ -2689,7 +3403,7 @@ elf64_alpha_merge_ind_symbols (hi, dummy) { rin = ri->next; for (rs = rsh; rs ; rs = rs->next) - if (ri->rtype == rs->rtype) + if (ri->rtype == rs->rtype && ri->srel == rs->srel) { rs->count += ri->count; goto found_reloc; @@ -2710,15 +3424,15 @@ static boolean elf64_alpha_can_merge_gots (a, b) bfd *a, *b; { - int total = alpha_elf_tdata (a)->total_got_entries; + int total = alpha_elf_tdata (a)->total_got_size; bfd *bsub; /* Trivial quick fallout test. */ - if (total + alpha_elf_tdata (b)->total_got_entries <= MAX_GOT_ENTRIES) + if (total + alpha_elf_tdata (b)->total_got_size <= MAX_GOT_SIZE) return true; /* By their nature, local .got entries cannot be merged. */ - if ((total += alpha_elf_tdata (b)->n_local_got_entries) > MAX_GOT_ENTRIES) + if ((total += alpha_elf_tdata (b)->local_got_size) > MAX_GOT_SIZE) return false; /* Failing the common trivial comparison, we must effectively @@ -2749,10 +3463,13 @@ elf64_alpha_can_merge_gots (a, b) continue; for (ae = h->got_entries; ae ; ae = ae->next) - if (ae->gotobj == a && ae->addend == be->addend) + if (ae->gotobj == a + && ae->reloc_type == be->reloc_type + && ae->addend == be->addend) goto global_found; - if (++total > MAX_GOT_ENTRIES) + total += alpha_got_entry_size (be->reloc_type); + if (total > MAX_GOT_SIZE) return false; global_found:; } @@ -2768,14 +3485,14 @@ static void elf64_alpha_merge_gots (a, b) bfd *a, *b; { - int total = alpha_elf_tdata (a)->total_got_entries; + int total = alpha_elf_tdata (a)->total_got_size; bfd *bsub; /* Remember local expansion. */ { - int e = alpha_elf_tdata (b)->n_local_got_entries; + int e = alpha_elf_tdata (b)->local_got_size; total += e; - alpha_elf_tdata (a)->n_local_got_entries += e; + alpha_elf_tdata (a)->local_got_size += e; } for (bsub = b; bsub ; bsub = alpha_elf_tdata (bsub)->in_got_link_next) @@ -2825,7 +3542,9 @@ elf64_alpha_merge_gots (a, b) continue; for (ae = *start; ae ; ae = ae->next) - if (ae->gotobj == a && ae->addend == be->addend) + if (ae->gotobj == a + && ae->reloc_type == be->reloc_type + && ae->addend == be->addend) { ae->flags |= be->flags; ae->use_count += be->use_count; @@ -2833,7 +3552,7 @@ elf64_alpha_merge_gots (a, b) goto global_found; } be->gotobj = a; - total += 1; + total += alpha_got_entry_size (be->reloc_type); global_found:; } @@ -2841,7 +3560,7 @@ elf64_alpha_merge_gots (a, b) alpha_elf_tdata (bsub)->gotobj = a; } - alpha_elf_tdata (a)->total_got_entries = total; + alpha_elf_tdata (a)->total_got_size = total; /* Merge the two in_got chains. */ { @@ -2874,7 +3593,7 @@ elf64_alpha_calc_got_offsets_for_symbol (h, arg) = &alpha_elf_tdata (gotent->gotobj)->got->_raw_size; gotent->got_offset = *plge; - *plge += 8; + *plge += alpha_got_entry_size (gotent->reloc_type); } return true; @@ -2916,7 +3635,7 @@ elf64_alpha_calc_got_offsets (info) if (gotent->use_count > 0) { gotent->got_offset = got_offset; - got_offset += 8; + got_offset += alpha_got_entry_size (gotent->reloc_type); } } @@ -2928,8 +3647,7 @@ elf64_alpha_calc_got_offsets (info) /* Constructs the gots. */ static boolean -elf64_alpha_size_got_sections (output_bfd, info) - bfd *output_bfd ATTRIBUTE_UNUSED; +elf64_alpha_size_got_sections (info) struct bfd_link_info *info; { bfd *i, *got_list, *cur_got_obj = NULL; @@ -2950,13 +3668,13 @@ elf64_alpha_size_got_sections (output_bfd, info) /* We are assuming no merging has yet ocurred. */ BFD_ASSERT (this_got == i); - if (alpha_elf_tdata (this_got)->total_got_entries > MAX_GOT_ENTRIES) + if (alpha_elf_tdata (this_got)->total_got_size > MAX_GOT_SIZE) { /* Yikes! A single object file has too many entries. */ (*_bfd_error_handler) (_("%s: .got subsegment exceeds 64K (size %d)"), bfd_archive_filename (i), - alpha_elf_tdata (this_got)->total_got_entries * 8); + alpha_elf_tdata (this_got)->total_got_size); return false; } @@ -3003,9 +3721,80 @@ elf64_alpha_size_got_sections (output_bfd, info) return true; } +/* Called from relax_section to rebuild the PLT in light of + potential changes in the function's status. */ + +static boolean +elf64_alpha_size_plt_section (info) + struct bfd_link_info *info; +{ + asection *splt, *spltrel; + unsigned long entries; + bfd *dynobj; + + dynobj = elf_hash_table(info)->dynobj; + splt = bfd_get_section_by_name(dynobj, ".plt"); + if (splt == NULL) + return true; + + splt->_raw_size = 0; + + alpha_elf_link_hash_traverse (alpha_elf_hash_table (info), + elf64_alpha_size_plt_section_1, splt); + + splt->_cooked_size = splt->_raw_size; + + /* Every plt entry requires a JMP_SLOT relocation. */ + spltrel = bfd_get_section_by_name (dynobj, ".rela.plt"); + if (splt->_raw_size) + entries = (splt->_raw_size - PLT_HEADER_SIZE) / PLT_ENTRY_SIZE; + else + entries = 0; + spltrel->_raw_size = entries * sizeof (Elf64_External_Rela); + spltrel->_cooked_size = spltrel->_raw_size; + + return true; +} + +static boolean +elf64_alpha_size_plt_section_1 (h, data) + struct alpha_elf_link_hash_entry *h; + PTR data; +{ + asection *splt = (asection *) data; + struct alpha_elf_got_entry *gotent; + + /* If we didn't need an entry before, we still don't. */ + if (!(h->root.elf_link_hash_flags & ELF_LINK_HASH_NEEDS_PLT)) + return true; + + /* There must still be a LITERAL got entry for the function. */ + for (gotent = h->got_entries; gotent ; gotent = gotent->next) + if (gotent->reloc_type == R_ALPHA_LITERAL + && gotent->use_count > 0) + break; + + /* If there is, reset the PLT offset. If not, there's no longer + a need for the PLT entry. */ + if (gotent) + { + if (splt->_raw_size == 0) + splt->_raw_size = PLT_HEADER_SIZE; + h->root.plt.offset = splt->_raw_size; + splt->_raw_size += PLT_ENTRY_SIZE; + } + else + { + h->root.elf_link_hash_flags &= ~ELF_LINK_HASH_NEEDS_PLT; + h->root.plt.offset = -1; + } + + return true; +} + static boolean elf64_alpha_always_size_sections (output_bfd, info) - bfd *output_bfd; + bfd *output_bfd ATTRIBUTE_UNUSED; struct bfd_link_info *info; { bfd *i; @@ -3018,7 +3807,7 @@ elf64_alpha_always_size_sections (output_bfd, info) elf64_alpha_merge_ind_symbols, NULL); - if (!elf64_alpha_size_got_sections (output_bfd, info)) + if (!elf64_alpha_size_got_sections (info)) return false; /* Allocate space for all of the .got subsections. */ @@ -3037,6 +3826,40 @@ elf64_alpha_always_size_sections (output_bfd, info) return true; } +/* The number of dynamic relocations required by a static relocation. */ + +static int +alpha_dynamic_entries_for_reloc (r_type, dynamic, shared) + int r_type, dynamic, shared; +{ + switch (r_type) + { + /* May appear in GOT entries. */ + case R_ALPHA_TLSGD: + return (dynamic ? 2 : shared ? 1 : 0); + case R_ALPHA_TLSLDM: + return shared; + case R_ALPHA_LITERAL: + return dynamic || shared; + case R_ALPHA_GOTDTPREL: + case R_ALPHA_GOTTPREL: + return dynamic; + + /* May appear in data sections. */ + case R_ALPHA_REFLONG: + case R_ALPHA_REFQUAD: + return dynamic || shared; + case R_ALPHA_SREL64: + case R_ALPHA_TPREL64: + return dynamic; + + /* Everything else is illegal. We'll issue an error during + relocate_section. */ + default: + return 0; + } +} + /* Work out the sizes of the dynamic relocation entries. */ static boolean @@ -3044,6 +3867,10 @@ elf64_alpha_calc_dynrel_sizes (h, info) struct alpha_elf_link_hash_entry *h; struct bfd_link_info *info; { + boolean dynamic; + struct alpha_elf_reloc_entry *relent; + unsigned long entries; + if (h->root.root.type == bfd_link_hash_warning) h = (struct alpha_elf_link_hash_entry *) h->root.root.u.i.link; @@ -3062,51 +3889,125 @@ elf64_alpha_calc_dynrel_sizes (h, info) && (h->root.root.type == bfd_link_hash_defined || h->root.root.type == bfd_link_hash_defweak) && !(h->root.root.u.def.section->owner->flags & DYNAMIC)) - { - h->root.elf_link_hash_flags |= ELF_LINK_HASH_DEF_REGULAR; - } + h->root.elf_link_hash_flags |= ELF_LINK_HASH_DEF_REGULAR; /* If the symbol is dynamic, we'll need all the relocations in their natural form. If this is a shared object, and it has been forced local, we'll need the same number of RELATIVE relocations. */ - if (alpha_elf_dynamic_symbol_p (&h->root, info) || info->shared) + dynamic = alpha_elf_dynamic_symbol_p (&h->root, info); + + for (relent = h->reloc_entries; relent; relent = relent->next) { - struct alpha_elf_reloc_entry *relent; - bfd *dynobj; - struct alpha_elf_got_entry *gotent; - bfd_size_type count; - asection *srel; + entries = alpha_dynamic_entries_for_reloc (relent->rtype, dynamic, + info->shared); + if (entries) + { + relent->srel->_raw_size += + entries * sizeof (Elf64_External_Rela) * relent->count; + if (relent->reltext) + info->flags |= DT_TEXTREL; + } + } - for (relent = h->reloc_entries; relent; relent = relent->next) - if (relent->rtype == R_ALPHA_REFLONG - || relent->rtype == R_ALPHA_REFQUAD) - { - relent->srel->_raw_size += - sizeof (Elf64_External_Rela) * relent->count; - if (relent->reltext) - info->flags |= DT_TEXTREL; - } + return true; +} + +/* Set the sizes of the dynamic relocation sections. */ - dynobj = elf_hash_table(info)->dynobj; - count = 0; +static boolean +elf64_alpha_size_rela_got_section (info) + struct bfd_link_info *info; +{ + unsigned long entries; + bfd *i, *dynobj; + asection *srel; - for (gotent = h->got_entries; gotent ; gotent = gotent->next) - count++; + /* Shared libraries often require RELATIVE relocs, and some relocs + require attention for the main application as well. */ - /* If we are using a .plt entry, subtract one, as the first - reference uses a .rela.plt entry instead. */ - if (h->root.plt.offset != MINUS_ONE) - count--; + entries = 0; + for (i = alpha_elf_hash_table(info)->got_list; + i ; i = alpha_elf_tdata(i)->got_link_next) + { + bfd *j; - if (count > 0) + for (j = i; j ; j = alpha_elf_tdata(j)->in_got_link_next) { - srel = bfd_get_section_by_name (dynobj, ".rela.got"); - BFD_ASSERT (srel != NULL); - srel->_raw_size += sizeof (Elf64_External_Rela) * count; + struct alpha_elf_got_entry **local_got_entries, *gotent; + int k, n; + + local_got_entries = alpha_elf_tdata(j)->local_got_entries; + if (!local_got_entries) + continue; + + for (k = 0, n = elf_tdata(j)->symtab_hdr.sh_info; k < n; ++k) + for (gotent = local_got_entries[k]; + gotent ; gotent = gotent->next) + if (gotent->use_count > 0) + entries += (alpha_dynamic_entries_for_reloc + (gotent->reloc_type, 0, info->shared)); } } + dynobj = elf_hash_table(info)->dynobj; + srel = bfd_get_section_by_name (dynobj, ".rela.got"); + if (!srel) + { + BFD_ASSERT (entries == 0); + return true; + } + srel->_raw_size = sizeof (Elf64_External_Rela) * entries; + + /* Now do the non-local symbols. */ + alpha_elf_link_hash_traverse (alpha_elf_hash_table (info), + elf64_alpha_size_rela_got_1, info); + + srel->_cooked_size = srel->_raw_size; + + return true; +} + +/* Subroutine of elf64_alpha_size_rela_got_section for doing the + global symbols. */ + +static boolean +elf64_alpha_size_rela_got_1 (h, info) + struct alpha_elf_link_hash_entry *h; + struct bfd_link_info *info; +{ + boolean dynamic; + struct alpha_elf_got_entry *gotent; + unsigned long entries; + + if (h->root.root.type == bfd_link_hash_warning) + h = (struct alpha_elf_link_hash_entry *) h->root.root.u.i.link; + + /* If the symbol is dynamic, we'll need all the relocations in their + natural form. If this is a shared object, and it has been forced + local, we'll need the same number of RELATIVE relocations. */ + + dynamic = alpha_elf_dynamic_symbol_p (&h->root, info); + + entries = 0; + for (gotent = h->got_entries; gotent ; gotent = gotent->next) + if (gotent->use_count > 0) + entries += alpha_dynamic_entries_for_reloc (gotent->reloc_type, + dynamic, info->shared); + + /* If we are using a .plt entry, subtract one, as the first + reference uses a .rela.plt entry instead. */ + if (h->root.plt.offset != MINUS_ONE) + entries--; + + if (entries > 0) + { + bfd *dynobj = elf_hash_table(info)->dynobj; + asection *srel = bfd_get_section_by_name (dynobj, ".rela.got"); + BFD_ASSERT (srel != NULL); + srel->_raw_size += sizeof (Elf64_External_Rela) * entries; + } + return true; } @@ -3140,27 +4041,9 @@ elf64_alpha_size_dynamic_sections (output_bfd, info) collected information in check_relocs that we can now apply to size the dynamic relocation sections. */ alpha_elf_link_hash_traverse (alpha_elf_hash_table (info), - elf64_alpha_calc_dynrel_sizes, - info); - - /* When building shared libraries, each local .got entry needs a - RELATIVE reloc. */ - if (info->shared) - { - bfd *i; - asection *srel; - bfd_size_type count; - - srel = bfd_get_section_by_name (dynobj, ".rela.got"); - BFD_ASSERT (srel != NULL); + elf64_alpha_calc_dynrel_sizes, info); - for (i = alpha_elf_hash_table(info)->got_list, count = 0; - i != NULL; - i = alpha_elf_tdata(i)->got_link_next) - count += alpha_elf_tdata(i)->n_local_got_entries; - - srel->_raw_size += count * sizeof (Elf64_External_Rela); - } + elf64_alpha_size_rela_got_section (info); } /* else we're not dynamic and by definition we don't need such things. */ @@ -3262,6 +4145,71 @@ elf64_alpha_size_dynamic_sections (output_bfd, info) return true; } +/* Relocate an Alpha ELF section for a relocatable link. + + We don't have to change anything unless the reloc is against a section + symbol, in which case we have to adjust according to where the section + symbol winds up in the output section. */ + +static boolean +elf64_alpha_relocate_section_r (output_bfd, info, input_bfd, input_section, + contents, relocs, local_syms, local_sections) + bfd *output_bfd ATTRIBUTE_UNUSED; + struct bfd_link_info *info ATTRIBUTE_UNUSED; + bfd *input_bfd; + asection *input_section; + bfd_byte *contents ATTRIBUTE_UNUSED; + Elf_Internal_Rela *relocs; + Elf_Internal_Sym *local_syms; + asection **local_sections; +{ + unsigned long symtab_hdr_sh_info; + Elf_Internal_Rela *rel; + Elf_Internal_Rela *relend; + boolean ret_val = true; + + symtab_hdr_sh_info = elf_tdata (input_bfd)->symtab_hdr.sh_info; + + relend = relocs + input_section->reloc_count; + for (rel = relocs; rel < relend; rel++) + { + unsigned long r_symndx; + Elf_Internal_Sym *sym; + asection *sec; + unsigned long r_type; + + r_type = ELF64_R_TYPE(rel->r_info); + if (r_type >= R_ALPHA_max) + { + (*_bfd_error_handler) + (_("%s: unknown relocation type %d"), + bfd_archive_filename (input_bfd), (int)r_type); + bfd_set_error (bfd_error_bad_value); + ret_val = false; + continue; + } + + r_symndx = ELF64_R_SYM(rel->r_info); + + /* The symbol associated with GPDISP and LITUSE is + immaterial. Only the addend is significant. */ + if (r_type == R_ALPHA_GPDISP || r_type == R_ALPHA_LITUSE) + continue; + + if (r_symndx < symtab_hdr_sh_info) + { + sym = local_syms + r_symndx; + if (ELF_ST_TYPE(sym->st_info) == STT_SECTION) + { + sec = local_sections[r_symndx]; + rel->r_addend += sec->output_offset + sym->st_value; + } + } + } + + return ret_val; +} + /* Relocate an Alpha ELF section. */ static boolean @@ -3279,22 +4227,39 @@ elf64_alpha_relocate_section (output_bfd, info, input_bfd, input_section, Elf_Internal_Shdr *symtab_hdr; Elf_Internal_Rela *rel; Elf_Internal_Rela *relend; - asection *sec, *sgot, *srel, *srelgot; + struct elf_link_tls_segment *tls_segment; + asection *sgot, *srel, *srelgot; bfd *dynobj, *gotobj; - bfd_vma gp; - boolean ret_val = true; + bfd_vma gp, tp_base, dtp_base; + struct alpha_elf_got_entry **local_got_entries; + boolean ret_val; + const char *section_name; + + /* Handle relocatable links with a smaller loop. */ + if (info->relocateable) + return elf64_alpha_relocate_section_r (output_bfd, info, input_bfd, + input_section, contents, relocs, + local_syms, local_sections); + + /* This is a final link. */ + + ret_val = true; - srelgot = srel = NULL; symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr; + dynobj = elf_hash_table (info)->dynobj; if (dynobj) - { - srelgot = bfd_get_section_by_name (dynobj, ".rela.got"); - } + srelgot = bfd_get_section_by_name (dynobj, ".rela.got"); + else + srelgot = NULL; + + section_name = (bfd_elf_string_from_elf_section + (input_bfd, elf_elfheader(input_bfd)->e_shstrndx, + elf_section_data(input_section)->rel_hdr.sh_name)); + BFD_ASSERT(section_name != NULL); + srel = bfd_get_section_by_name (dynobj, section_name); /* Find the gp value for this input bfd. */ - sgot = NULL; - gp = 0; gotobj = alpha_elf_tdata (input_bfd)->gotobj; if (gotobj) { @@ -3308,66 +4273,97 @@ elf64_alpha_relocate_section (output_bfd, info, input_bfd, input_section, _bfd_set_gp_value (gotobj, gp); } } + else + { + sgot = NULL; + gp = 0; + } + + local_got_entries = alpha_elf_tdata(input_bfd)->local_got_entries; + + tls_segment = elf_hash_table (info)->tls_segment; + if (tls_segment) + { + dtp_base = alpha_get_dtprel_base (tls_segment); + tp_base = alpha_get_tprel_base (tls_segment); + } + else + dtp_base = tp_base = 0; - rel = relocs; relend = relocs + input_section->reloc_count; - for (; rel < relend; rel++) + for (rel = relocs; rel < relend; rel++) { - int r_type; + struct alpha_elf_link_hash_entry *h = NULL; + struct alpha_elf_got_entry *gotent; + bfd_reloc_status_type r; reloc_howto_type *howto; unsigned long r_symndx; - struct alpha_elf_link_hash_entry *h; - Elf_Internal_Sym *sym; - bfd_vma relocation; + Elf_Internal_Sym *sym = NULL; + asection *sec = NULL; + bfd_vma value; bfd_vma addend; - bfd_reloc_status_type r; + boolean dynamic_symbol_p; + boolean undef_weak_ref = false; + unsigned long r_type; r_type = ELF64_R_TYPE(rel->r_info); - if (r_type < 0 || r_type >= (int) R_ALPHA_max) + if (r_type >= R_ALPHA_max) { + (*_bfd_error_handler) + (_("%s: unknown relocation type %d"), + bfd_archive_filename (input_bfd), (int)r_type); bfd_set_error (bfd_error_bad_value); - return false; + ret_val = false; + continue; } - howto = elf64_alpha_howto_table + r_type; + howto = elf64_alpha_howto_table + r_type; r_symndx = ELF64_R_SYM(rel->r_info); - if (info->relocateable) + if (r_symndx < symtab_hdr->sh_info) { - /* This is a relocateable link. We don't have to change - anything, unless the reloc is against a section symbol, - in which case we have to adjust according to where the - section symbol winds up in the output section. */ - - /* The symbol associated with GPDISP and LITUSE is - immaterial. Only the addend is significant. */ - if (r_type == R_ALPHA_GPDISP || r_type == R_ALPHA_LITUSE) - continue; + sym = local_syms + r_symndx; + sec = local_sections[r_symndx]; + value = _bfd_elf_rela_local_sym (output_bfd, sym, sec, rel); - if (r_symndx < symtab_hdr->sh_info) + if (local_got_entries) + gotent = local_got_entries[r_symndx]; + else + gotent = NULL; + + /* Need to adjust local GOT entries' addends for SEC_MERGE + unless it has been done already. */ + if ((sec->flags & SEC_MERGE) + && ELF_ST_TYPE (sym->st_info) == STT_SECTION + && (elf_section_data (sec)->sec_info_type + == ELF_INFO_TYPE_MERGE) + && gotent + && !gotent->reloc_xlated) { - sym = local_syms + r_symndx; - if (ELF_ST_TYPE(sym->st_info) == STT_SECTION) + struct alpha_elf_got_entry *ent; + asection *msec; + + for (ent = gotent; ent; ent = ent->next) { - sec = local_sections[r_symndx]; - rel->r_addend += sec->output_offset + sym->st_value; + ent->reloc_xlated = 1; + if (ent->use_count == 0) + continue; + msec = sec; + ent->addend = + _bfd_merged_section_offset (output_bfd, &msec, + elf_section_data (sec)-> + sec_info, + sym->st_value + ent->addend, + (bfd_vma) 0); + ent->addend -= sym->st_value; + ent->addend += msec->output_section->vma + + msec->output_offset + - sec->output_section->vma + - sec->output_offset; } } - continue; - } - - /* This is a final link. */ - - 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); + dynamic_symbol_p = false; } else { @@ -3377,27 +4373,32 @@ elf64_alpha_relocate_section (output_bfd, info, input_bfd, input_section, || h->root.root.type == bfd_link_hash_warning) h = (struct alpha_elf_link_hash_entry *)h->root.root.u.i.link; + value = 0; if (h->root.root.type == bfd_link_hash_defined || h->root.root.type == bfd_link_hash_defweak) { sec = h->root.root.u.def.section; - if (sec->output_section == NULL) - relocation = 0; - else - { - relocation = (h->root.root.u.def.value - + sec->output_section->vma - + sec->output_offset); - } + /* Detect the cases that sym_sec->output_section is + expected to be NULL -- all cases in which the symbol + is defined in another shared module. This includes + PLT relocs for which we've created a PLT entry and + other relocs for which we're prepared to create + dynamic relocations. */ + /* ??? Just accept it NULL and continue. */ + + if (sec->output_section != NULL) + value = (h->root.root.u.def.value + + sec->output_section->vma + + sec->output_offset); } else if (h->root.root.type == bfd_link_hash_undefweak) - relocation = 0; + undef_weak_ref = true; else if (info->shared && (!info->symbolic || info->allow_shlib_undefined) && !info->no_undefined && ELF_ST_VISIBILITY (h->root.other) == STV_DEFAULT) - relocation = 0; + ; else { if (!((*info->callbacks->undefined_symbol) @@ -3405,11 +4406,24 @@ elf64_alpha_relocate_section (output_bfd, info, input_bfd, input_section, input_section, rel->r_offset, (!info->shared || info->no_undefined || ELF_ST_VISIBILITY (h->root.other))))) - ret_val = false; - relocation = 0; + return false; + ret_val = false; + continue; } + + dynamic_symbol_p = alpha_elf_dynamic_symbol_p (&h->root, info); + gotent = h->got_entries; } + addend = rel->r_addend; + value += addend; + + /* Search for the proper got entry. */ + for (; gotent ; gotent = gotent->next) + if (gotent->gotobj == gotobj + && gotent->reloc_type == r_type + && gotent->addend == addend) + break; switch (r_type) { @@ -3419,124 +4433,66 @@ elf64_alpha_relocate_section (output_bfd, info, input_bfd, input_section, BFD_ASSERT(gp != 0); - relocation = (input_section->output_section->vma - + input_section->output_offset - + rel->r_offset); + value = (input_section->output_section->vma + + input_section->output_offset + + rel->r_offset); - p_ldah = contents + rel->r_offset - input_section->vma; + p_ldah = contents + rel->r_offset; p_lda = p_ldah + rel->r_addend; - r = elf64_alpha_do_reloc_gpdisp (input_bfd, gp - relocation, + r = elf64_alpha_do_reloc_gpdisp (input_bfd, gp - value, p_ldah, p_lda); } break; case R_ALPHA_LITERAL: - { - struct alpha_elf_got_entry *gotent; - boolean dynamic_symbol; - - BFD_ASSERT(sgot != NULL); - BFD_ASSERT(gp != 0); - - if (h != NULL) - { - gotent = h->got_entries; - dynamic_symbol = alpha_elf_dynamic_symbol_p (&h->root, info); - } - else - { - gotent = (alpha_elf_tdata(input_bfd)-> - local_got_entries[r_symndx]); - dynamic_symbol = false; - - /* Need to adjust local GOT entries' addends for SEC_MERGE - unless it has been done already. */ - if ((sec->flags & SEC_MERGE) - && ELF_ST_TYPE (sym->st_info) == STT_SECTION - && (elf_section_data (sec)->sec_info_type - == ELF_INFO_TYPE_MERGE) - && (gotent->flags & ALPHA_ELF_GOT_ENTRY_RELOCS_XLATED) == 0) - { - struct alpha_elf_got_entry *ent; - asection *msec; - - for (ent = gotent; ent; ent = ent->next) - { - ent->flags |= ALPHA_ELF_GOT_ENTRY_RELOCS_XLATED; - if (ent->use_count == 0) - continue; - msec = sec; - ent->addend = - _bfd_merged_section_offset (output_bfd, &msec, - elf_section_data (sec)-> - sec_info, - sym->st_value - + ent->addend, - (bfd_vma) 0); - ent->addend -= sym->st_value; - ent->addend += msec->output_section->vma - + msec->output_offset - - sec->output_section->vma - - sec->output_offset; - } - } - } + BFD_ASSERT(sgot != NULL); + BFD_ASSERT(gp != 0); + BFD_ASSERT(gotent != NULL); + BFD_ASSERT(gotent->use_count >= 1); - BFD_ASSERT(gotent != NULL); + if (!gotent->reloc_done) + { + gotent->reloc_done = 1; - while (gotent->gotobj != gotobj || gotent->addend != addend) - gotent = gotent->next; + bfd_put_64 (output_bfd, value, + sgot->contents + gotent->got_offset); - BFD_ASSERT(gotent->use_count >= 1); + /* If the symbol has been forced local, output a + RELATIVE reloc, otherwise it will be handled in + finish_dynamic_symbol. */ + if (info->shared && !dynamic_symbol_p) + { + Elf_Internal_Rela outrel; - /* Initialize the .got entry's value. */ - if (!(gotent->flags & ALPHA_ELF_GOT_ENTRY_RELOCS_DONE)) - { - bfd_put_64 (output_bfd, relocation + addend, - sgot->contents + gotent->got_offset); + BFD_ASSERT(srelgot != NULL); - /* If the symbol has been forced local, output a - RELATIVE reloc, otherwise it will be handled in - finish_dynamic_symbol. */ - if (info->shared && !dynamic_symbol) - { - Elf_Internal_Rela outrel; - - BFD_ASSERT(srelgot != NULL); - - outrel.r_offset = (sgot->output_section->vma - + sgot->output_offset - + gotent->got_offset); - outrel.r_info = ELF64_R_INFO(0, R_ALPHA_RELATIVE); - outrel.r_addend = relocation + addend; - - bfd_elf64_swap_reloca_out (output_bfd, &outrel, - ((Elf64_External_Rela *) - srelgot->contents) - + srelgot->reloc_count++); - BFD_ASSERT (sizeof (Elf64_External_Rela) - * srelgot->reloc_count - <= srelgot->_cooked_size); - } + outrel.r_offset = (sgot->output_section->vma + + sgot->output_offset + + gotent->got_offset); + outrel.r_info = ELF64_R_INFO (0, R_ALPHA_RELATIVE); + outrel.r_addend = value; - gotent->flags |= ALPHA_ELF_GOT_ENTRY_RELOCS_DONE; - } + bfd_elf64_swap_reloca_out (output_bfd, &outrel, + ((Elf64_External_Rela *) + srelgot->contents) + + srelgot->reloc_count++); + BFD_ASSERT (sizeof (Elf64_External_Rela) + * srelgot->reloc_count + <= srelgot->_cooked_size); + } + } - /* Figure the gprel relocation. */ - addend = 0; - relocation = (sgot->output_section->vma - + sgot->output_offset - + gotent->got_offset); - relocation -= gp; - } - /* overflow handled by _bfd_final_link_relocate */ + value = (sgot->output_section->vma + + sgot->output_offset + + gotent->got_offset); + value -= gp; goto default_reloc; case R_ALPHA_GPREL16: case R_ALPHA_GPREL32: case R_ALPHA_GPRELLOW: - if (h && alpha_elf_dynamic_symbol_p (&h->root, info)) + if (dynamic_symbol_p) { (*_bfd_error_handler) (_("%s: gp-relative relocation against dynamic symbol %s"), @@ -3544,11 +4500,11 @@ elf64_alpha_relocate_section (output_bfd, info, input_bfd, input_section, ret_val = false; } BFD_ASSERT(gp != 0); - relocation -= gp; + value -= gp; goto default_reloc; case R_ALPHA_GPRELHIGH: - if (h && alpha_elf_dynamic_symbol_p (&h->root, info)) + if (dynamic_symbol_p) { (*_bfd_error_handler) (_("%s: gp-relative relocation against dynamic symbol %s"), @@ -3556,27 +4512,34 @@ elf64_alpha_relocate_section (output_bfd, info, input_bfd, input_section, ret_val = false; } BFD_ASSERT(gp != 0); - relocation -= gp; - relocation += addend; - addend = 0; - relocation = (((bfd_signed_vma) relocation >> 16) - + ((relocation >> 15) & 1)); + value -= gp; + value = ((bfd_signed_vma) value >> 16) + ((value >> 15) & 1); goto default_reloc; case R_ALPHA_HINT: /* A call to a dynamic symbol is definitely out of range of the 16-bit displacement. Don't bother writing anything. */ - if (h && alpha_elf_dynamic_symbol_p (&h->root, info)) + if (dynamic_symbol_p) { r = bfd_reloc_ok; break; } - /* FALLTHRU */ + /* The regular PC-relative stuff measures from the start of + the instruction rather than the end. */ + value -= 4; + goto default_reloc; case R_ALPHA_BRADDR: + if (dynamic_symbol_p) + { + (*_bfd_error_handler) + (_("%s: pc-relative relocation against dynamic symbol %s"), + bfd_archive_filename (input_bfd), h->root.root.root.string); + ret_val = false; + } /* The regular PC-relative stuff measures from the start of the instruction rather than the end. */ - addend -= 4; + value -= 4; goto default_reloc; case R_ALPHA_BRSGP: @@ -3586,12 +4549,12 @@ elf64_alpha_relocate_section (output_bfd, info, input_bfd, input_section, /* The regular PC-relative stuff measures from the start of the instruction rather than the end. */ - addend -= 4; + value -= 4; /* The source and destination gp must be the same. Note that the source will always have an assigned gp, since we forced one in check_relocs, but that the destination may not, as - it might not have had any relocations at all. Also take + it might not have had any relocations at all. Also take care not to crash if H is an undefined symbol. */ if (h != NULL && sec != NULL && alpha_elf_tdata (sec->owner)->gotobj @@ -3639,41 +4602,52 @@ elf64_alpha_relocate_section (output_bfd, info, input_bfd, input_section, case R_ALPHA_REFLONG: case R_ALPHA_REFQUAD: + case R_ALPHA_DTPREL64: + case R_ALPHA_TPREL64: { Elf_Internal_Rela outrel; /* Careful here to remember RELATIVE relocations for global variables for symbolic shared objects. */ - if (h && alpha_elf_dynamic_symbol_p (&h->root, info)) + if (dynamic_symbol_p) { BFD_ASSERT(h->root.dynindx != -1); - outrel.r_info = ELF64_R_INFO(h->root.dynindx, r_type); + outrel.r_info = ELF64_R_INFO (h->root.dynindx, r_type); outrel.r_addend = addend; - addend = 0, relocation = 0; + addend = 0, value = 0; + } + else if (r_type == R_ALPHA_DTPREL64) + { + BFD_ASSERT(tls_segment != NULL); + value -= dtp_base; + goto default_reloc; + } + else if (r_type == R_ALPHA_TPREL64) + { + BFD_ASSERT(tls_segment != NULL); + value -= dtp_base; + goto default_reloc; } else if (info->shared && r_symndx != 0 && (input_section->flags & SEC_ALLOC)) { - outrel.r_info = ELF64_R_INFO(0, R_ALPHA_RELATIVE); - outrel.r_addend = relocation + addend; + if (r_type == R_ALPHA_REFLONG) + { + (*_bfd_error_handler) + (_("%s: unhandled dynamic relocation against %s"), + bfd_archive_filename (input_bfd), + h->root.root.root.string); + ret_val = false; + } + outrel.r_info = ELF64_R_INFO (0, R_ALPHA_RELATIVE); + outrel.r_addend = value; } else goto default_reloc; - if (!srel) - { - const char *name; - - name = (bfd_elf_string_from_elf_section - (input_bfd, elf_elfheader(input_bfd)->e_shstrndx, - elf_section_data(input_section)->rel_hdr.sh_name)); - BFD_ASSERT(name != NULL); - - srel = bfd_get_section_by_name (dynobj, name); - BFD_ASSERT(srel != NULL); - } + BFD_ASSERT(srel != NULL); outrel.r_offset = _bfd_elf_section_offset (output_bfd, info, input_section, @@ -3693,8 +4667,17 @@ elf64_alpha_relocate_section (output_bfd, info, input_bfd, input_section, } goto default_reloc; + case R_ALPHA_SREL16: case R_ALPHA_SREL32: case R_ALPHA_SREL64: + if (dynamic_symbol_p) + { + (*_bfd_error_handler) + (_("%s: pc-relative relocation against dynamic symbol %s"), + bfd_archive_filename (input_bfd), h->root.root.root.string); + ret_val = false; + } + /* ??? .eh_frame references to discarded sections will be smashed to relocations against SHN_UNDEF. The .eh_frame format allows NULL to be encoded as 0 in any format, so this works here. */ @@ -3703,11 +4686,134 @@ elf64_alpha_relocate_section (output_bfd, info, input_bfd, input_section, + (r_type - R_ALPHA_SREL32 + R_ALPHA_REFLONG)); goto default_reloc; + case R_ALPHA_TLSLDM: + /* Ignore the symbol for the relocation. The result is always + the current module. */ + dynamic_symbol_p = 0; + /* FALLTHRU */ + + case R_ALPHA_TLSGD: + if (!gotent->reloc_done) + { + gotent->reloc_done = 1; + + /* Note that the module index for the main program is 1. */ + bfd_put_64 (output_bfd, !info->shared && !dynamic_symbol_p, + sgot->contents + gotent->got_offset); + + /* If the symbol has been forced local, output a + DTPMOD64 reloc, otherwise it will be handled in + finish_dynamic_symbol. */ + if (info->shared && !dynamic_symbol_p) + { + Elf_Internal_Rela outrel; + + BFD_ASSERT(srelgot != NULL); + + outrel.r_offset = (sgot->output_section->vma + + sgot->output_offset + + gotent->got_offset); + /* ??? Proper dynindx here. */ + outrel.r_info = ELF64_R_INFO (0, R_ALPHA_DTPMOD64); + outrel.r_addend = 0; + + bfd_elf64_swap_reloca_out (output_bfd, &outrel, + ((Elf64_External_Rela *) + srelgot->contents) + + srelgot->reloc_count++); + BFD_ASSERT (sizeof (Elf64_External_Rela) + * srelgot->reloc_count + <= srelgot->_cooked_size); + } + + if (dynamic_symbol_p || r_type == R_ALPHA_TLSLDM) + value = 0; + else + { + BFD_ASSERT(tls_segment != NULL); + value -= dtp_base; + } + bfd_put_64 (output_bfd, value, + sgot->contents + gotent->got_offset + 8); + } + + value = (sgot->output_section->vma + + sgot->output_offset + + gotent->got_offset); + value -= gp; + goto default_reloc; + + case R_ALPHA_DTPRELHI: + case R_ALPHA_DTPRELLO: + case R_ALPHA_DTPREL16: + if (dynamic_symbol_p) + { + (*_bfd_error_handler) + (_("%s: dtp-relative relocation against dynamic symbol %s"), + bfd_archive_filename (input_bfd), h->root.root.root.string); + ret_val = false; + } + BFD_ASSERT(tls_segment != NULL); + value -= dtp_base; + if (r_type == R_ALPHA_DTPRELHI) + value = ((bfd_signed_vma) value >> 16) + ((value >> 15) & 1); + goto default_reloc; + + case R_ALPHA_TPRELHI: + case R_ALPHA_TPRELLO: + case R_ALPHA_TPREL16: + if (info->shared) + { + (*_bfd_error_handler) + (_("%s: TLS local exec code cannot be linked into shared objects"), + bfd_archive_filename (input_bfd)); + ret_val = false; + } + else if (dynamic_symbol_p) + { + (*_bfd_error_handler) + (_("%s: tp-relative relocation against dynamic symbol %s"), + bfd_archive_filename (input_bfd), h->root.root.root.string); + ret_val = false; + } + BFD_ASSERT(tls_segment != NULL); + value -= tp_base; + if (r_type == R_ALPHA_TPRELHI) + value = ((bfd_signed_vma) value >> 16) + ((value >> 15) & 1); + goto default_reloc; + + case R_ALPHA_GOTDTPREL: + case R_ALPHA_GOTTPREL: + BFD_ASSERT(sgot != NULL); + BFD_ASSERT(gp != 0); + BFD_ASSERT(gotent != NULL); + BFD_ASSERT(gotent->use_count >= 1); + + if (!gotent->reloc_done) + { + gotent->reloc_done = 1; + + if (dynamic_symbol_p) + value = 0; + else + { + BFD_ASSERT(tls_segment != NULL); + value -= (r_type == R_ALPHA_GOTDTPREL ? dtp_base : tp_base); + } + bfd_put_64 (output_bfd, value, + sgot->contents + gotent->got_offset); + } + + value = (sgot->output_section->vma + + sgot->output_offset + + gotent->got_offset); + value -= gp; + goto default_reloc; + default: default_reloc: r = _bfd_final_link_relocate (howto, input_bfd, input_section, - contents, rel->r_offset, relocation, - addend); + contents, rel->r_offset, value, 0); break; } @@ -3879,20 +4985,58 @@ elf64_alpha_finish_dynamic_symbol (output_bfd, info, h, sym) srel = bfd_get_section_by_name (dynobj, ".rela.got"); BFD_ASSERT (srel != NULL); - outrel.r_info = ELF64_R_INFO (h->dynindx, R_ALPHA_GLOB_DAT); for (gotent = ((struct alpha_elf_link_hash_entry *) h)->got_entries; gotent != NULL; gotent = gotent->next) { - asection *sgot = alpha_elf_tdata (gotent->gotobj)->got; + asection *sgot; + int r_type; + + if (gotent->use_count == 0) + continue; + + sgot = alpha_elf_tdata (gotent->gotobj)->got; outrel.r_offset = (sgot->output_section->vma + sgot->output_offset + gotent->got_offset); + + r_type = gotent->reloc_type; + switch (r_type) + { + case R_ALPHA_LITERAL: + r_type = R_ALPHA_GLOB_DAT; + break; + case R_ALPHA_TLSGD: + r_type = R_ALPHA_DTPMOD64; + break; + case R_ALPHA_GOTDTPREL: + r_type = R_ALPHA_DTPREL64; + break; + case R_ALPHA_GOTTPREL: + r_type = R_ALPHA_TPREL64; + break; + case R_ALPHA_TLSLDM: + default: + abort (); + } + + outrel.r_info = ELF64_R_INFO (h->dynindx, r_type); outrel.r_addend = gotent->addend; bfd_elf64_swap_reloca_out (output_bfd, &outrel, ((Elf64_External_Rela *)srel->contents + srel->reloc_count++)); + + if (gotent->reloc_type == R_ALPHA_TLSGD) + { + outrel.r_offset += 8; + outrel.r_info = ELF64_R_INFO (h->dynindx, R_ALPHA_DTPREL64); + + bfd_elf64_swap_reloca_out (output_bfd, &outrel, + ((Elf64_External_Rela *)srel->contents + + srel->reloc_count++)); + } + BFD_ASSERT (sizeof (Elf64_External_Rela) * srel->reloc_count <= srel->_cooked_size); } @@ -4116,7 +5260,7 @@ elf64_alpha_final_link (abfd, info) if (p->type != bfd_indirect_link_order) { - if (p->type == bfd_fill_link_order) + if (p->type == bfd_data_link_order) continue; abort (); } @@ -4336,7 +5480,7 @@ elf64_alpha_ecoff_debug_swap = /* Use a non-standard hash bucket size of 8. */ -const struct elf_size_info alpha_elf_size_info = +static const struct elf_size_info alpha_elf_size_info = { sizeof (Elf64_External_Ehdr), sizeof (Elf64_External_Phdr), @@ -4353,6 +5497,7 @@ const struct elf_size_info alpha_elf_size_info = bfd_elf64_write_out_phdrs, bfd_elf64_write_shdrs_and_ehdr, bfd_elf64_write_relocs, + bfd_elf64_swap_symbol_in, bfd_elf64_swap_symbol_out, bfd_elf64_slurp_reloc_table, bfd_elf64_slurp_symbol_table, @@ -4364,11 +5509,13 @@ const struct elf_size_info alpha_elf_size_info = NULL }; +#ifndef ELF_ARCH #define TARGET_LITTLE_SYM bfd_elf64_alpha_vec #define TARGET_LITTLE_NAME "elf64-alpha" #define ELF_ARCH bfd_arch_alpha #define ELF_MACHINE_CODE EM_ALPHA #define ELF_MAXPAGESIZE 0x10000 +#endif /* ELF_ARCH */ #define bfd_elf64_bfd_link_hash_table_create \ elf64_alpha_bfd_link_hash_table_create |