- BFD_ASSERT (elf_hash_table (info->link_info)->tls_sec != NULL);
- dtp_base = alpha_get_dtprel_base (info->link_info);
- tp_base = alpha_get_tprel_base (info->link_info);
- 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;
-
- /* 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;
- }
-
- /* Smash the existing GOT relocation for its 16-bit immediate pair. */
- 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;
-
- /* ??? 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 bfd_boolean
-elf64_alpha_relax_gprelhilo (info, symval, irel, hi)
- struct alpha_relax_info *info;
- bfd_vma symval;
- Elf_Internal_Rela *irel;
- bfd_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 bfd_boolean
-elf64_alpha_relax_tls_get_addr (info, symval, irel, is_gd)
- struct alpha_relax_info *info;
- bfd_vma symval;
- Elf_Internal_Rela *irel;
- bfd_boolean is_gd;
-{
- bfd_byte *pos[5];
- unsigned int insn;
- Elf_Internal_Rela *gpdisp, *hint;
- bfd_boolean dynamic, use_gottprel, pos1_unusable;
- unsigned long new_symndx;
-
- dynamic = alpha_elf_dynamic_symbol_p (&info->h->root, info->link_info);
-
- /* 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;
- pos1_unusable = FALSE;
-
- /* Generally, the positions are not allowed to be out of order, lest the
- modified insn sequence have different register lifetimes. We can make
- an exception when pos 1 is adjacent to pos 0. */
- if (pos[1] + 4 == pos[0])
- {
- bfd_byte *tmp = pos[0];
- pos[0] = pos[1];
- pos[1] = tmp;
- }
- else if (pos[1] < pos[0])
- pos1_unusable = TRUE;
- if (pos[1] >= pos[2] || pos[2] >= pos[3])
- 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;
- new_symndx = is_gd ? ELF64_R_SYM (irel->r_info) : 0;
- switch (!dynamic && !info->link_info->shared)
- {
- case 1:
- {
- bfd_vma tp_base;
- bfd_signed_vma disp;
-
- BFD_ASSERT (elf_hash_table (info->link_info)->tls_sec != NULL);
- tp_base = alpha_get_tprel_base (info->link_info);
- 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 (new_symndx, 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
- && !pos1_unusable)
- {
- 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 (new_symndx, R_ALPHA_TPRELHI);
- irel[1].r_offset = pos[1] - info->contents;
- irel[1].r_info = ELF64_R_INFO (new_symndx, 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 (new_symndx, 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 bfd_boolean
-elf64_alpha_relax_section (abfd, sec, link_info, again)
- bfd *abfd;
- asection *sec;
- struct bfd_link_info *link_info;
- bfd_boolean *again;
-{
- Elf_Internal_Shdr *symtab_hdr;
- Elf_Internal_Rela *internal_relocs;
- Elf_Internal_Rela *irel, *irelend;
- Elf_Internal_Sym *isymbuf = NULL;
- struct alpha_elf_got_entry **local_got_entries;
- struct alpha_relax_info info;
-
- /* We are not currently changing any sizes, so only one pass. */
- *again = FALSE;
-
- if (link_info->relocatable
- || (sec->flags & SEC_RELOC) == 0
- || sec->reloc_count == 0)
- return TRUE;
-
- /* If this is the first time we have been called for this section,
- initialize the cooked size. */
- if (sec->_cooked_size == 0)
- sec->_cooked_size = sec->_raw_size;
-
- symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
- local_got_entries = alpha_elf_tdata(abfd)->local_got_entries;
-
- /* Load the relocations for this section. */
- internal_relocs = (_bfd_elf_link_read_relocs
- (abfd, sec, (PTR) NULL, (Elf_Internal_Rela *) NULL,
- link_info->keep_memory));
- if (internal_relocs == NULL)
- 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. 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 = (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;
- }
-
- for (irel = internal_relocs; irel < irelend; irel++)
- {
- bfd_vma symval;
- struct alpha_elf_got_entry *gotent;
- unsigned long r_type = ELF64_R_TYPE (irel->r_info);
- unsigned long r_symndx = ELF64_R_SYM (irel->r_info);
-
- /* Early exit for unhandled or unrelaxable relocations. */
- switch (r_type)
- {
- case R_ALPHA_LITERAL:
- case R_ALPHA_GPRELHIGH:
- case R_ALPHA_GPRELLOW:
- case R_ALPHA_GOTDTPREL:
- case R_ALPHA_GOTTPREL:
- case R_ALPHA_TLSGD:
- break;
-
- case R_ALPHA_TLSLDM:
- /* The symbol for a TLSLDM reloc is ignored. Collapse the
- reloc to the 0 symbol so that they all match. */
- r_symndx = 0;
- break;
-
- default:
- continue;
- }
-
- /* Get the value of the symbol referred to by the reloc. */
- if (r_symndx < symtab_hdr->sh_info)
- {
- /* A local symbol. */
- Elf_Internal_Sym *isym;
-
- /* Read this BFD's local symbols. */
- if (isymbuf == NULL)
- {
- 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;
- }
-
- isym = isymbuf + r_symndx;
-
- /* Given the symbol for a TLSLDM reloc is ignored, this also
- means forcing the symbol value to the tp base. */
- if (r_type == R_ALPHA_TLSLDM)
- {
- info.tsec = bfd_abs_section_ptr;
- symval = alpha_get_tprel_base (info.link_info);
- }
- else
- {
- symval = isym->st_value;
- 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)
- info.tsec = bfd_com_section_ptr;
- else
- info.tsec = bfd_section_from_elf_index (abfd, isym->st_shndx);
- }