+ /* Can't relax dynamic symbols. */
+ if (alpha_elf_dynamic_symbol_p (&info->h->root, info->link_info))
+ return true;
+
+ /* Can't use local-exec relocations in shared libraries. */
+ if (r_type == R_ALPHA_GOTTPREL && info->link_info->shared)
+ return true;
+
+ 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);
+ }
+
+ 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;
+
+ 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. */
+ 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
+ register is seen, or after a branch or call.
+
+ Any such memory load insn may be substituted by a load directly
+ off the GP. This allows the memory load insn to be issued before
+ the calculated GP register would otherwise be ready.
+
+ Any such jsr insn can be replaced by a bsr if it is in range.
+
+ This would mean that we'd have to _add_ relocations, the pain of
+ which gives one pause. */
+
+ return true;
+}
+
+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;