diff options
-rw-r--r-- | bfd/ChangeLog | 14 | ||||
-rw-r--r-- | bfd/elf64-alpha.c | 121 |
2 files changed, 104 insertions, 31 deletions
diff --git a/bfd/ChangeLog b/bfd/ChangeLog index 6e1a3a1..f292935 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,3 +1,17 @@ +2003-01-21 Richard Henderson <rth@redhat.com> + + * elf64-alpha.c (ALPHA_ELF_LINK_HASH_PLT_LOC): New. + (struct alpha_elf_link_hash_entry): Add plt_old_section, plt_old_value. + (elf64_alpha_adjust_dynamic_symbol): Set them. + (elf64_alpha_size_plt_section_1): Reset them when plt entry removed. + (elf64_alpha_relax_tls_get_addr): Handle LDM relocs. Frob the + symbol index when relaxing LDM to TPREL. + (elf64_alpha_relax_section): Likewise. Allow relaxation of GD + relocs, even if the target isn't locally defined. + (elf64_alpha_check_relocs): Frob LDM reloc symndx to zero. + (elf64_alpha_relocate_section): Likewise. Force TP-relative + relocs vs symndx 0 to the tp base. + 2003-01-21 Fabio Alemagna <falemagn@aros.org> * config.bfd: Handle i[3456]86-*-aros*. diff --git a/bfd/elf64-alpha.c b/bfd/elf64-alpha.c index 029c1d4..9569b38 100644 --- a/bfd/elf64-alpha.c +++ b/bfd/elf64-alpha.c @@ -177,6 +177,11 @@ struct alpha_elf_link_hash_entry #define ALPHA_ELF_LINK_HASH_LU_TLSLDM 0x20 #define ALPHA_ELF_LINK_HASH_LU_FUNC 0x38 #define ALPHA_ELF_LINK_HASH_TLS_IE 0x40 +#define ALPHA_ELF_LINK_HASH_PLT_LOC 0x80 + + /* Used to undo the localization of a plt symbol. */ + asection *plt_old_section; + bfd_vma plt_old_value; /* Used to implement multiple .got subsections. */ struct alpha_elf_got_entry @@ -1753,16 +1758,10 @@ elf64_alpha_relax_tls_get_addr (info, symval, irel, is_gd) 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); - /* ??? For LD relaxation, we need a symbol referencing the beginning - of the TLS segment. */ - /* ??? The STN_UNDEF symbol (dynindex 0) works fine for this. Adjust - the code below to expect that. */ - 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)) @@ -1868,6 +1867,7 @@ elf64_alpha_relax_tls_get_addr (info, symval, irel, is_gd) as appropriate. */ use_gottprel = FALSE; + new_symndx = is_gd ? ELF64_R_SYM (irel->r_info) : 0; switch (!dynamic && !info->link_info->shared) { case 1: @@ -1886,8 +1886,7 @@ elf64_alpha_relax_tls_get_addr (info, symval, irel, is_gd) 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[0].r_info = ELF64_R_INFO (new_symndx, R_ALPHA_TPREL16); irel[1].r_info = ELF64_R_INFO (0, R_ALPHA_NONE); break; } @@ -1901,11 +1900,9 @@ elf64_alpha_relax_tls_get_addr (info, symval, irel, is_gd) 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[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 (ELF64_R_SYM (irel->r_info), - R_ALPHA_TPRELLO); + irel[1].r_info = ELF64_R_INFO (new_symndx, R_ALPHA_TPRELLO); break; } } @@ -1919,8 +1916,7 @@ elf64_alpha_relax_tls_get_addr (info, symval, irel, is_gd) 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[0].r_info = ELF64_R_INFO (new_symndx, R_ALPHA_GOTTPREL); irel[1].r_info = ELF64_R_INFO (0, R_ALPHA_NONE); break; } @@ -2123,6 +2119,7 @@ elf64_alpha_relax_section (abfd, sec, link_info, again) 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) @@ -2133,14 +2130,20 @@ elf64_alpha_relax_section (abfd, sec, link_info, again) 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 (ELF64_R_SYM (irel->r_info) < symtab_hdr->sh_info) + if (r_symndx < symtab_hdr->sh_info) { /* A local symbol. */ Elf_Internal_Sym *isym; @@ -2157,27 +2160,38 @@ elf64_alpha_relax_section (abfd, sec, link_info, again) goto error_return; } - 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) - info.tsec = bfd_com_section_ptr; + 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.tls_segment); + } else - info.tsec = bfd_section_from_elf_index (abfd, isym->st_shndx); + { + 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); + } info.h = NULL; info.other = isym->st_other; - info.first_gotent = &local_got_entries[ELF64_R_SYM(irel->r_info)]; - symval = isym->st_value; + info.first_gotent = &local_got_entries[r_symndx]; } else { unsigned long indx; struct alpha_elf_link_hash_entry *h; - indx = ELF64_R_SYM (irel->r_info) - symtab_hdr->sh_info; + indx = r_symndx - symtab_hdr->sh_info; h = alpha_elf_sym_hashes (abfd)[indx]; BFD_ASSERT (h != NULL); @@ -2193,13 +2207,23 @@ elf64_alpha_relax_section (abfd, sec, link_info, again) /* 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; + { + /* Except for TLSGD relocs, which can sometimes be + relaxed to GOTTPREL relocs. */ + if (r_type != R_ALPHA_TLSGD) + continue; + info.tsec = bfd_abs_section_ptr; + symval = 0; + } + else + { + info.tsec = h->root.root.u.def.section; + symval = h->root.root.u.def.value; + } info.h = h; - info.tsec = h->root.root.u.def.section; info.other = h->root.other; info.first_gotent = &h->got_entries; - symval = h->root.root.u.def.value; } /* Search for the got entry to be used by this relocation. */ @@ -3117,8 +3141,15 @@ elf64_alpha_check_relocs (abfd, info, sec, relocs) need = NEED_DYNREL; break; - case R_ALPHA_TLSGD: 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; + h = 0; + maybe_dynamic = FALSE; + /* FALLTHRU */ + + case R_ALPHA_TLSGD: case R_ALPHA_GOTDTPREL: need = NEED_GOT | NEED_GOT_ENTRY; break; @@ -3320,6 +3351,9 @@ elf64_alpha_adjust_dynamic_symbol (info, h) if (! info->shared && h->root.type != bfd_link_hash_defweak) { + ah->plt_old_section = h->root.u.def.section; + ah->plt_old_value = h->root.u.def.value; + ah->flags |= ALPHA_ELF_LINK_HASH_PLT_LOC; h->root.u.def.section = s; h->root.u.def.value = h->plt.offset; } @@ -3801,6 +3835,14 @@ elf64_alpha_size_plt_section_1 (h, data) { h->root.elf_link_hash_flags &= ~ELF_LINK_HASH_NEEDS_PLT; h->root.plt.offset = -1; + + /* Undo the definition frobbing begun in adjust_dynamic_symbol. */ + if (h->flags & ALPHA_ELF_LINK_HASH_PLT_LOC) + { + h->root.root.u.def.section = h->plt_old_section; + h->root.root.u.def.value = h->plt_old_value; + h->flags &= ~ALPHA_ELF_LINK_HASH_PLT_LOC; + } } return TRUE; @@ -4364,12 +4406,29 @@ elf64_alpha_relocate_section (output_bfd, info, input_bfd, input_section, howto = elf64_alpha_howto_table + r_type; r_symndx = ELF64_R_SYM(rel->r_info); + /* The symbol for a TLSLDM reloc is ignored. Collapse the + reloc to the 0 symbol so that they all match. */ + if (r_type == R_ALPHA_TLSLDM) + r_symndx = 0; + if (r_symndx < symtab_hdr->sh_info) { sym = local_syms + r_symndx; sec = local_sections[r_symndx]; value = _bfd_elf_rela_local_sym (output_bfd, sym, sec, rel); + /* If this is a tp-relative relocation against sym 0, + this is hackery from relax_section. Force the value to + be the tls base. */ + if (r_symndx == 0 + && (r_type == R_ALPHA_TLSLDM + || r_type == R_ALPHA_GOTTPREL + || r_type == R_ALPHA_TPREL64 + || r_type == R_ALPHA_TPRELHI + || r_type == R_ALPHA_TPRELLO + || r_type == R_ALPHA_TPREL16)) + value = tp_base; + if (local_got_entries) gotent = local_got_entries[r_symndx]; else |