diff options
Diffstat (limited to 'bfd/elfxx-ia64.c')
-rw-r--r-- | bfd/elfxx-ia64.c | 274 |
1 files changed, 258 insertions, 16 deletions
diff --git a/bfd/elfxx-ia64.c b/bfd/elfxx-ia64.c index f92ab6a..943159b 100644 --- a/bfd/elfxx-ia64.c +++ b/bfd/elfxx-ia64.c @@ -79,6 +79,9 @@ struct elfNN_ia64_dyn_sym_info bfd_vma pltoff_offset; bfd_vma plt_offset; bfd_vma plt2_offset; + bfd_vma tprel_offset; + bfd_vma dtpmod_offset; + bfd_vma dtprel_offset; /* The symbol table entry, if any, that this was derrived from. */ struct elf_link_hash_entry *h; @@ -97,6 +100,9 @@ struct elfNN_ia64_dyn_sym_info unsigned got_done : 1; unsigned fptr_done : 1; unsigned pltoff_done : 1; + unsigned tprel_done : 1; + unsigned dtpmod_done : 1; + unsigned dtprel_done : 1; /* True for the different kinds of linker data we want created. */ unsigned want_got : 1; @@ -105,6 +111,9 @@ struct elfNN_ia64_dyn_sym_info unsigned want_plt : 1; unsigned want_plt2 : 1; unsigned want_pltoff : 1; + unsigned want_tprel : 1; + unsigned want_dtpmod : 1; + unsigned want_dtprel : 1; }; struct elfNN_ia64_local_hash_entry @@ -285,6 +294,10 @@ static bfd_vma set_pltoff_entry PARAMS ((bfd *abfd, struct bfd_link_info *info, struct elfNN_ia64_dyn_sym_info *dyn_i, bfd_vma value, boolean)); +static bfd_vma elfNN_ia64_tprel_base + PARAMS ((struct bfd_link_info *info)); +static bfd_vma elfNN_ia64_dtprel_base + PARAMS ((struct bfd_link_info *info)); static int elfNN_ia64_unwind_entry_compare PARAMS ((const PTR, const PTR)); static boolean elfNN_ia64_final_link @@ -423,10 +436,25 @@ static reloc_howto_type ia64_howto_table[] = IA64_HOWTO (R_IA64_LTOFF22X, "LTOFF22X", 0, false, true), IA64_HOWTO (R_IA64_LDXMOV, "LDXMOV", 0, false, true), + IA64_HOWTO (R_IA64_TPREL14, "TPREL14", 0, false, false), IA64_HOWTO (R_IA64_TPREL22, "TPREL22", 0, false, false), + IA64_HOWTO (R_IA64_TPREL64I, "TPREL64I", 0, false, false), IA64_HOWTO (R_IA64_TPREL64MSB, "TPREL64MSB", 8, false, false), IA64_HOWTO (R_IA64_TPREL64LSB, "TPREL64LSB", 8, false, false), - IA64_HOWTO (R_IA64_LTOFF_TP22, "LTOFF_TP22", 0, false, false), + IA64_HOWTO (R_IA64_LTOFF_TPREL22, "LTOFF_TPREL22", 0, false, false), + + IA64_HOWTO (R_IA64_DTPMOD64MSB, "TPREL64MSB", 8, false, false), + IA64_HOWTO (R_IA64_DTPMOD64LSB, "TPREL64LSB", 8, false, false), + IA64_HOWTO (R_IA64_LTOFF_DTPMOD22, "LTOFF_DTPMOD22", 0, false, false), + + IA64_HOWTO (R_IA64_DTPREL14, "DTPREL14", 0, false, false), + IA64_HOWTO (R_IA64_DTPREL22, "DTPREL22", 0, false, false), + IA64_HOWTO (R_IA64_DTPREL64I, "DTPREL64I", 0, false, false), + IA64_HOWTO (R_IA64_DTPREL32MSB, "DTPREL32MSB", 4, false, false), + IA64_HOWTO (R_IA64_DTPREL32LSB, "DTPREL32LSB", 4, false, false), + IA64_HOWTO (R_IA64_DTPREL64MSB, "DTPREL64MSB", 8, false, false), + IA64_HOWTO (R_IA64_DTPREL64LSB, "DTPREL64LSB", 8, false, false), + IA64_HOWTO (R_IA64_LTOFF_DTPREL22, "LTOFF_DTPREL22", 0, false, false), }; static unsigned char elf_code_to_howto_index[R_IA64_MAX_RELOC_CODE + 1]; @@ -541,10 +569,25 @@ elfNN_ia64_reloc_type_lookup (abfd, bfd_code) case BFD_RELOC_IA64_LTOFF22X: rtype = R_IA64_LTOFF22X; break; case BFD_RELOC_IA64_LDXMOV: rtype = R_IA64_LDXMOV; break; + case BFD_RELOC_IA64_TPREL14: rtype = R_IA64_TPREL14; break; case BFD_RELOC_IA64_TPREL22: rtype = R_IA64_TPREL22; break; + case BFD_RELOC_IA64_TPREL64I: rtype = R_IA64_TPREL64I; break; case BFD_RELOC_IA64_TPREL64MSB: rtype = R_IA64_TPREL64MSB; break; case BFD_RELOC_IA64_TPREL64LSB: rtype = R_IA64_TPREL64LSB; break; - case BFD_RELOC_IA64_LTOFF_TP22: rtype = R_IA64_LTOFF_TP22; break; + case BFD_RELOC_IA64_LTOFF_TPREL22: rtype = R_IA64_LTOFF_TPREL22; break; + + case BFD_RELOC_IA64_DTPMOD64MSB: rtype = R_IA64_DTPMOD64MSB; break; + case BFD_RELOC_IA64_DTPMOD64LSB: rtype = R_IA64_DTPMOD64LSB; break; + case BFD_RELOC_IA64_LTOFF_DTPMOD22: rtype = R_IA64_LTOFF_DTPMOD22; break; + + case BFD_RELOC_IA64_DTPREL14: rtype = R_IA64_DTPREL14; break; + case BFD_RELOC_IA64_DTPREL22: rtype = R_IA64_DTPREL22; break; + case BFD_RELOC_IA64_DTPREL64I: rtype = R_IA64_DTPREL64I; break; + case BFD_RELOC_IA64_DTPREL32MSB: rtype = R_IA64_DTPREL32MSB; break; + case BFD_RELOC_IA64_DTPREL32LSB: rtype = R_IA64_DTPREL32LSB; break; + case BFD_RELOC_IA64_DTPREL64MSB: rtype = R_IA64_DTPREL64MSB; break; + case BFD_RELOC_IA64_DTPREL64LSB: rtype = R_IA64_DTPREL64LSB; break; + case BFD_RELOC_IA64_LTOFF_DTPREL22: rtype = R_IA64_LTOFF_DTPREL22; break; default: return 0; } @@ -2097,6 +2140,9 @@ elfNN_ia64_check_relocs (abfd, info, sec, relocs) NEED_FULL_PLT = 16, NEED_DYNREL = 32, NEED_LTOFF_FPTR = 64, + NEED_TPREL = 128, + NEED_DTPMOD = 256, + NEED_DTPREL = 512 }; struct elf_link_hash_entry *h = NULL; @@ -2134,11 +2180,42 @@ elfNN_ia64_check_relocs (abfd, info, sec, relocs) need_entry = 0; switch (ELFNN_R_TYPE (rel->r_info)) { - case R_IA64_TPREL22: case R_IA64_TPREL64MSB: case R_IA64_TPREL64LSB: - case R_IA64_LTOFF_TP22: - return false; + if (info->shared || maybe_dynamic) + need_entry = NEED_DYNREL; + dynrel_type = R_IA64_TPREL64LSB; + if (info->shared) + info->flags |= DF_STATIC_TLS; + break; + + case R_IA64_LTOFF_TPREL22: + need_entry = NEED_TPREL; + if (info->shared) + info->flags |= DF_STATIC_TLS; + break; + + case R_IA64_DTPREL64MSB: + case R_IA64_DTPREL64LSB: + if (info->shared || maybe_dynamic) + need_entry = NEED_DYNREL; + dynrel_type = R_IA64_DTPREL64LSB; + break; + + case R_IA64_LTOFF_DTPREL22: + need_entry = NEED_DTPREL; + break; + + case R_IA64_DTPMOD64MSB: + case R_IA64_DTPMOD64LSB: + if (info->shared || maybe_dynamic) + need_entry = NEED_DYNREL; + dynrel_type = R_IA64_DTPMOD64LSB; + break; + + case R_IA64_LTOFF_DTPMOD22: + need_entry = NEED_DTPMOD; + break; case R_IA64_LTOFF_FPTR22: case R_IA64_LTOFF_FPTR64I: @@ -2248,7 +2325,7 @@ elfNN_ia64_check_relocs (abfd, info, sec, relocs) dyn_i->h = h; /* Create what's needed. */ - if (need_entry & NEED_GOT) + if (need_entry & (NEED_GOT | NEED_TPREL | NEED_DTPMOD | NEED_DTPREL)) { if (!got) { @@ -2256,7 +2333,14 @@ elfNN_ia64_check_relocs (abfd, info, sec, relocs) if (!got) return false; } - dyn_i->want_got = 1; + if (need_entry & NEED_GOT) + dyn_i->want_got = 1; + if (need_entry & NEED_TPREL) + dyn_i->want_tprel = 1; + if (need_entry & NEED_DTPMOD) + dyn_i->want_dtpmod = 1; + if (need_entry & NEED_DTPREL) + dyn_i->want_dtprel = 1; } if (need_entry & NEED_FPTR) { @@ -2336,6 +2420,21 @@ allocate_global_data_got (dyn_i, data) dyn_i->got_offset = x->ofs; x->ofs += 8; } + if (dyn_i->want_tprel) + { + dyn_i->tprel_offset = x->ofs; + x->ofs += 8; + } + if (dyn_i->want_dtpmod) + { + dyn_i->dtpmod_offset = x->ofs; + x->ofs += 8; + } + if (dyn_i->want_dtprel) + { + dyn_i->dtprel_offset = x->ofs; + x->ofs += 8; + } return true; } @@ -2582,6 +2681,10 @@ allocate_dynrel_entries (dyn_i, data) if (!dynamic_symbol) count *= 2; break; + case R_IA64_TPREL64LSB: + case R_IA64_DTPREL64LSB: + case R_IA64_DTPMOD64LSB: + break; default: abort (); } @@ -2593,6 +2696,12 @@ allocate_dynrel_entries (dyn_i, data) if (((dynamic_symbol || shared) && dyn_i->want_got) || (dyn_i->want_ltoff_fptr && dyn_i->h && dyn_i->h->dynindx != -1)) ia64_info->rel_got_sec->_raw_size += sizeof (ElfNN_External_Rela); + if ((dynamic_symbol || shared) && dyn_i->want_tprel) + ia64_info->rel_got_sec->_raw_size += sizeof (ElfNN_External_Rela); + if ((dynamic_symbol || shared) && dyn_i->want_dtpmod) + ia64_info->rel_got_sec->_raw_size += sizeof (ElfNN_External_Rela); + if (dynamic_symbol && dyn_i->want_dtprel) + ia64_info->rel_got_sec->_raw_size += sizeof (ElfNN_External_Rela); if (dyn_i->want_pltoff) { @@ -2903,7 +3012,11 @@ elfNN_ia64_install_value (abfd, hit_addr, v, r_type) /* Instruction relocations. */ - case R_IA64_IMM14: opnd = IA64_OPND_IMM14; break; + case R_IA64_IMM14: + case R_IA64_TPREL14: + case R_IA64_DTPREL14: + opnd = IA64_OPND_IMM14; + break; case R_IA64_PCREL21F: opnd = IA64_OPND_TGT25; break; case R_IA64_PCREL21M: opnd = IA64_OPND_TGT25b; break; @@ -2920,6 +3033,11 @@ elfNN_ia64_install_value (abfd, hit_addr, v, r_type) case R_IA64_PLTOFF22: case R_IA64_PCREL22: case R_IA64_LTOFF_FPTR22: + case R_IA64_TPREL22: + case R_IA64_DTPREL22: + case R_IA64_LTOFF_TPREL22: + case R_IA64_LTOFF_DTPMOD22: + case R_IA64_LTOFF_DTPREL22: opnd = IA64_OPND_IMM22; break; @@ -2930,6 +3048,8 @@ elfNN_ia64_install_value (abfd, hit_addr, v, r_type) case R_IA64_PCREL64I: case R_IA64_FPTR64I: case R_IA64_LTOFF_FPTR64I: + case R_IA64_TPREL64I: + case R_IA64_DTPREL64I: opnd = IA64_OPND_IMMU64; break; @@ -2943,6 +3063,7 @@ elfNN_ia64_install_value (abfd, hit_addr, v, r_type) case R_IA64_SEGREL32MSB: case R_IA64_SECREL32MSB: case R_IA64_LTV32MSB: + case R_IA64_DTPREL32MSB: size = 4; bigendian = 1; break; @@ -2954,6 +3075,7 @@ elfNN_ia64_install_value (abfd, hit_addr, v, r_type) case R_IA64_SEGREL32LSB: case R_IA64_SECREL32LSB: case R_IA64_LTV32LSB: + case R_IA64_DTPREL32LSB: size = 4; bigendian = 0; break; @@ -2966,6 +3088,9 @@ elfNN_ia64_install_value (abfd, hit_addr, v, r_type) case R_IA64_SEGREL64MSB: case R_IA64_SECREL64MSB: case R_IA64_LTV64MSB: + case R_IA64_TPREL64MSB: + case R_IA64_DTPMOD64MSB: + case R_IA64_DTPREL64MSB: size = 8; bigendian = 1; break; @@ -2978,6 +3103,9 @@ elfNN_ia64_install_value (abfd, hit_addr, v, r_type) case R_IA64_SEGREL64LSB: case R_IA64_SECREL64LSB: case R_IA64_LTV64LSB: + case R_IA64_TPREL64LSB: + case R_IA64_DTPMOD64LSB: + case R_IA64_DTPREL64LSB: size = 8; bigendian = 0; break; @@ -3132,26 +3260,53 @@ set_got_entry (abfd, info, dyn_i, dynindx, addend, value, dyn_r_type) { struct elfNN_ia64_link_hash_table *ia64_info; asection *got_sec; + boolean done; + bfd_vma got_offset; ia64_info = elfNN_ia64_hash_table (info); got_sec = ia64_info->got_sec; - BFD_ASSERT ((dyn_i->got_offset & 7) == 0); - - if (! dyn_i->got_done) + switch (dyn_r_type) { + case R_IA64_TPREL64LSB: + done = dyn_i->tprel_done; + dyn_i->tprel_done = true; + got_offset = dyn_i->tprel_offset; + break; + case R_IA64_DTPMOD64LSB: + done = dyn_i->dtpmod_done; + dyn_i->dtpmod_done = true; + got_offset = dyn_i->dtpmod_offset; + break; + case R_IA64_DTPREL64LSB: + done = dyn_i->dtprel_done; + dyn_i->dtprel_done = true; + got_offset = dyn_i->dtprel_offset; + break; + default: + done = dyn_i->got_done; dyn_i->got_done = true; + got_offset = dyn_i->got_offset; + break; + } + BFD_ASSERT ((got_offset & 7) == 0); + + if (! done) + { /* Store the target address in the linkage table entry. */ - bfd_put_64 (abfd, value, got_sec->contents + dyn_i->got_offset); + bfd_put_64 (abfd, value, got_sec->contents + got_offset); /* Install a dynamic relocation if needed. */ - if (info->shared + if ((info->shared && dyn_r_type != R_IA64_DTPREL64LSB) || elfNN_ia64_dynamic_symbol_p (dyn_i->h, info) || elfNN_ia64_aix_vec (abfd->xvec) || (dynindx != -1 && dyn_r_type == R_IA64_FPTR64LSB)) { - if (dynindx == -1) + if (dynindx == -1 + && dyn_r_type != R_IA64_TPREL64LSB + && dyn_r_type != R_IA64_DTPMOD64LSB + && dyn_r_type != R_IA64_DTPREL64LSB) { dyn_r_type = R_IA64_REL64LSB; dynindx = 0; @@ -3171,6 +3326,15 @@ set_got_entry (abfd, info, dyn_i, dynindx, addend, value, dyn_r_type) case R_IA64_FPTR64LSB: dyn_r_type = R_IA64_FPTR64MSB; break; + case R_IA64_TPREL64LSB: + dyn_r_type = R_IA64_TPREL64MSB; + break; + case R_IA64_DTPMOD64LSB: + dyn_r_type = R_IA64_DTPMOD64MSB; + break; + case R_IA64_DTPREL64LSB: + dyn_r_type = R_IA64_DTPREL64MSB; + break; default: BFD_ASSERT (false); break; @@ -3179,7 +3343,7 @@ set_got_entry (abfd, info, dyn_i, dynindx, addend, value, dyn_r_type) elfNN_ia64_install_dyn_reloc (abfd, NULL, got_sec, ia64_info->rel_got_sec, - dyn_i->got_offset, dyn_r_type, + got_offset, dyn_r_type, dynindx, addend); } } @@ -3187,7 +3351,7 @@ set_got_entry (abfd, info, dyn_i, dynindx, addend, value, dyn_r_type) /* Return the address of the linkage table entry. */ value = (got_sec->output_section->vma + got_sec->output_offset - + dyn_i->got_offset); + + got_offset); return value; } @@ -3285,6 +3449,35 @@ set_pltoff_entry (abfd, info, dyn_i, value, is_plt) return value; } +/* Return the base VMA address which should be subtracted from real addresses + when resolving @tprel() relocation. + Main program TLS (whose template starts at PT_TLS p_vaddr) + is assigned offset round(16, PT_TLS p_align). */ + +static bfd_vma +elfNN_ia64_tprel_base (info) + struct bfd_link_info *info; +{ + struct elf_link_tls_segment *tls_segment + = elf_hash_table (info)->tls_segment; + + BFD_ASSERT (tls_segment != NULL); + return (tls_segment->start + - align_power ((bfd_vma) 16, tls_segment->align)); +} + +/* Return the base VMA address which should be subtracted from real addresses + when resolving @dtprel() relocation. + This is PT_TLS segment p_vaddr. */ + +static bfd_vma +elfNN_ia64_dtprel_base (info) + struct bfd_link_info *info; +{ + BFD_ASSERT (elf_hash_table (info)->tls_segment != NULL); + return elf_hash_table (info)->tls_segment->start; +} + /* Called through qsort to sort the .IA_64.unwind section during a non-relocatable link. Set elfNN_ia64_unwind_entry_compare_bfd to the output bfd so we can do proper endianness frobbing. */ @@ -4030,6 +4223,55 @@ elfNN_ia64_relocate_section (output_bfd, info, input_bfd, input_section, r_type); break; + case R_IA64_TPREL14: + case R_IA64_TPREL22: + case R_IA64_TPREL64I: + value -= elfNN_ia64_tprel_base (info); + r = elfNN_ia64_install_value (output_bfd, hit_addr, value, r_type); + break; + + case R_IA64_DTPREL14: + case R_IA64_DTPREL22: + case R_IA64_DTPREL64I: + value -= elfNN_ia64_dtprel_base (info); + r = elfNN_ia64_install_value (output_bfd, hit_addr, value, r_type); + break; + + case R_IA64_LTOFF_TPREL22: + case R_IA64_LTOFF_DTPMOD22: + case R_IA64_LTOFF_DTPREL22: + { + int got_r_type; + + switch (r_type) + { + default: + case R_IA64_LTOFF_TPREL22: + if (!dynamic_symbol_p && !info->shared) + value -= elfNN_ia64_tprel_base (info); + got_r_type = R_IA64_TPREL64LSB; + break; + case R_IA64_LTOFF_DTPMOD22: + if (!dynamic_symbol_p && !info->shared) + value = 1; + got_r_type = R_IA64_DTPMOD64LSB; + break; + case R_IA64_LTOFF_DTPREL22: + if (!dynamic_symbol_p) + value -= elfNN_ia64_dtprel_base (info); + got_r_type = R_IA64_DTPREL64LSB; + break; + } + dyn_i = get_dyn_sym_info (ia64_info, h, input_bfd, rel, false); + value = set_got_entry (input_bfd, info, dyn_i, + (h ? h->dynindx : -1), rel->r_addend, + value, got_r_type); + value -= gp_val; + r = elfNN_ia64_install_value (output_bfd, hit_addr, value, + r_type); + } + break; + default: r = bfd_reloc_notsupported; break; |