From 9e866f3c26ec60bec31e65de334c1c3fec9868af Mon Sep 17 00:00:00 2001 From: John David Anglin Date: Mon, 24 Nov 2025 18:10:23 -0500 Subject: [PATCH] hppa64: Fix R_PARISC_LTOFF_FPTR64 and R_PARISC_LTOFF_TP64 relocation support The elf_hppa_final_link_relocate function didn't handle R_PARISC_LTOFF_FPTR64 and R_PARISC_LTOFF_TP64 relocations for local symbols. DLT and OPD output relocation support for local symbols is consolidated in elf_hppa_dlt_dynrel_reloc() and elf_hppa_opd_dynrel_relocs(). 2025-11-24 John David Anglin bfd/ChangeLog: * elf64-hppa.c (elf_hppa_dlt_dynrel_reloc): New. (elf_hppa_opd_dynrel_relocs): New. (elf_hppa_final_link_relocate): Use elf_hppa_dlt_dynrel_reloc() and elf_hppa_opd_dynrel_relocs(). Fix relocation support for R_PARISC_LTOFF_FPTR64 and R_PARISC_LTOFF_TP64. --- bfd/elf64-hppa.c | 396 +++++++++++++++++++++++------------------------ 1 file changed, 193 insertions(+), 203 deletions(-) diff --git a/bfd/elf64-hppa.c b/bfd/elf64-hppa.c index 87044fff807..8d3a8878aa0 100644 --- a/bfd/elf64-hppa.c +++ b/bfd/elf64-hppa.c @@ -3119,6 +3119,132 @@ elf_hppa_relocate_insn (int insn, int sym_value, unsigned int r_type) } } +/* Output DLT relocation for a local symbol. */ + +static void +elf_hppa_dlt_dynrel_reloc (Elf_Internal_Rela *rel, + bfd_vma relocation, + struct bfd_link_info *info, + asection *sym_sec, + bfd_vma dlt_offset) +{ + Elf_Internal_Rela rela; + bfd_byte *loc; + int dynindx; + asection *sec; + asection *sdlt, *sdltrel; + int dynrel_type; + bfd_vma value; + bfd_signed_vma addend = rel->r_addend; + struct elf64_hppa_link_hash_table *hppa_info = hppa_link_hash_table (info); + + sdlt = hppa_info->dlt_sec; + sdltrel = hppa_info->dlt_rel_sec; + + /* The offset of this relocation is the absolute address + of the .dlt entry. */ + rela.r_offset = dlt_offset + sdlt->output_offset + sdlt->output_section->vma; + + if (sym_sec->flags & SEC_READONLY) + { + sec = hppa_info->text_hash_entry->root.u.def.section; + dynindx = hppa_info->text_hash_entry->dynindx; + dynrel_type = R_PARISC_FPTR64; + } + else + { + sec = hppa_info->data_hash_entry->root.u.def.section; + dynindx = hppa_info->data_hash_entry->dynindx; + dynrel_type = R_PARISC_DIR64; + } + + /* Adjust addend using the difference of the symbol's + location and the section symbol's address. */ + value = relocation + addend - sec->output_offset - sec->output_section->vma; + + /* The result becomes the addend of the relocation. */ + rela.r_addend = value; + + BFD_ASSERT (dynindx != -1); + rela.r_info = ELF64_R_INFO (dynindx, dynrel_type); + + loc = sdltrel->contents; + loc += sdltrel->reloc_count++ * sizeof (Elf64_External_Rela); + bfd_elf64_swap_reloca_out (info->output_bfd, &rela, loc); +} + +/* Output OPD EPLT and FPTR64 relocations for a local symbol. */ + +static void +elf_hppa_opd_dynrel_relocs (Elf_Internal_Rela *rel, + asection *input_section, + bfd_vma relocation, + struct bfd_link_info *info, + bfd_vma opd_offset) +{ + Elf_Internal_Rela rela; + bfd_byte *loc; + int dynindx; + asection *sopd, *sopdrel; + asection *sec, *s; + bfd_vma value; + bfd_vma offset = rel->r_offset; + bfd_signed_vma addend = rel->r_addend; + struct elf64_hppa_link_hash_table *hppa_info = hppa_link_hash_table (info); + + sopd = hppa_info->opd_sec; + sopdrel = hppa_info->opd_rel_sec; + + /* The offset of the EPLT relocation is the absolute address + of the .opd entry for this symbol. */ + rela.r_offset = opd_offset + sopd->output_offset + sopd->output_section->vma; + + /* Select text base segment and its dynindx. */ + sec = hppa_info->text_hash_entry->root.u.def.section; + dynindx = hppa_info->text_hash_entry->dynindx; + + /* Adjust the addend with the difference between the + symbol's address and the base segment's address. */ + value = relocation + addend - sec->output_offset - sec->output_section->vma; + + /* The result becomes the addend of the relocation. */ + rela.r_addend = value; + + BFD_ASSERT (dynindx != -1); + rela.r_info = ELF64_R_INFO (dynindx, R_PARISC_EPLT); + + loc = sopdrel->contents; + loc += sopdrel->reloc_count++ * sizeof (Elf64_External_Rela); + bfd_elf64_swap_reloca_out (info->output_bfd, &rela, loc); + + /* The offset of the FPTR relocation is the absolute address + of this symbol. */ + rela.r_offset = (offset + input_section->output_offset + + input_section->output_section->vma); + + /* OPD's address. */ + value = opd_offset + sopd->output_offset + sopd->output_section->vma; + + /* Select data base segment and its dynindx. */ + sec = hppa_info->data_hash_entry->root.u.def.section; + dynindx = hppa_info->data_hash_entry->dynindx; + + /* Compute the difference between the opd's address + and the base segment's address. */ + value -= sec->output_offset + sec->output_section->vma; + + /* The result becomes the addend of the relocation. */ + rela.r_addend = value; + + BFD_ASSERT (dynindx != -1); + rela.r_info = ELF64_R_INFO (dynindx, R_PARISC_FPTR64); + + s = hppa_info->other_rel_sec; + loc = s->contents; + loc += s->reloc_count++ * sizeof (Elf64_External_Rela); + bfd_elf64_swap_reloca_out (info->output_bfd, &rela, loc); +} + /* Compute the value for a relocation (REL) during a final link stage, then insert the value into the proper location in CONTENTS. @@ -3299,12 +3425,9 @@ elf_hppa_final_link_relocate (Elf_Internal_Rela *rel, { bfd_vma off; bfd_vma relocation = value; - asection *sopd, *sopdrel; - bool need_dlt_reloc = false; - bool need_dynrel_relocs = false; + asection *sopd; sopd = hppa_info->opd_sec; - sopdrel = hppa_info->opd_rel_sec; /* If this relocation was against a local symbol, then we still have not set up the DLT entry (it's not convenient to do so @@ -3347,9 +3470,10 @@ elf_hppa_final_link_relocate (Elf_Internal_Rela *rel, local_opd_offsets[r_symndx] |= 1; /* If we are building a shared library, we need a EPLT - and FPTY64 relocation for each OPD entry. */ + and FPTR64 relocation for each OPD entry. */ if (bfd_link_pic (info)) - need_dynrel_relocs = true; + elf_hppa_opd_dynrel_relocs (rel, input_section, + relocation, info, off); /* The third word is the address of the function. */ bfd_put_64 (sopd->owner, value + addend, @@ -3362,77 +3486,6 @@ elf_hppa_final_link_relocate (Elf_Internal_Rela *rel, } } - if (need_dynrel_relocs) - { - Elf_Internal_Rela rela; - bfd_byte *loc; - int dynindx; - asection *sec, *s; - - /* The offset of the EPLT relocation is the absolute address - of the .opd entry for this symbol. */ - rela.r_offset = (off - + sopd->output_offset - + sopd->output_section->vma); - - /* Select base segment. */ - if (sym_sec->flags & SEC_READONLY) - { - sec = hppa_info->text_hash_entry->root.u.def.section; - dynindx = hppa_info->text_hash_entry->dynindx; - } - else - { - sec = hppa_info->data_hash_entry->root.u.def.section; - dynindx = hppa_info->data_hash_entry->dynindx; - } - - /* Adjust the addend with the difference between the - symbol's address and the base segment's address. */ - value = (relocation + addend - - sec->output_offset - - sec->output_section->vma); - - /* The result becomes the addend of the relocation. */ - rela.r_addend = value; - - dynindx = hppa_info->text_hash_entry->dynindx; - BFD_ASSERT (dynindx != -1); - rela.r_info = ELF64_R_INFO (dynindx, R_PARISC_EPLT); - - loc = sopdrel->contents; - loc += sopdrel->reloc_count++ * sizeof (Elf64_External_Rela); - bfd_elf64_swap_reloca_out (info->output_bfd, &rela, loc); - - /* The offset of the FPTR relocation is the absolute address - of this symbol. */ - rela.r_offset = (offset + input_section->output_offset - + input_section->output_section->vma); - - /* OPD's address. */ - value = (off + sopd->output_offset - + sopd->output_section->vma); - - /* Base segment and its dynindx. */ - sec = hppa_info->data_hash_entry->root.u.def.section; - dynindx = hppa_info->data_hash_entry->dynindx; - - /* Compute the difference between the opd's address - and the base segment's address. */ - value -= sec->output_offset + sec->output_section->vma; - - /* The result becomes the addend of the relocation. */ - rela.r_addend = value; - - BFD_ASSERT (dynindx != -1); - rela.r_info = ELF64_R_INFO (dynindx, R_PARISC_FPTR64); - - s = hppa_info->other_rel_sec; - loc = s->contents; - loc += s->reloc_count++ * sizeof (Elf64_External_Rela); - bfd_elf64_swap_reloca_out (info->output_bfd, &rela, loc); - } - local_dlt_offsets = local_offsets; off = local_dlt_offsets[r_symndx]; @@ -3447,7 +3500,8 @@ elf_hppa_final_link_relocate (Elf_Internal_Rela *rel, if (bfd_link_pic (info) || _bfd_elf_link_lookup_local_dynindx (info, input_bfd, r_symndx) != -1) - need_dlt_reloc = true; + elf_hppa_dlt_dynrel_reloc (rel, relocation, info, + sym_sec, off); bfd_put_64 (hppa_info->dlt_sec->owner, value + addend, @@ -3488,53 +3542,6 @@ elf_hppa_final_link_relocate (Elf_Internal_Rela *rel, value = hppa_field_adjust (value, 0, e_rsel); insn = elf_hppa_relocate_insn (insn, (int) value, r_type); - - if (need_dlt_reloc) - { - Elf_Internal_Rela rela; - bfd_byte *loc; - int dynindx; - asection *sec; - asection *sdlt, *sdltrel; - int dynrel_type; - - sdlt = hppa_info->dlt_sec; - sdltrel = hppa_info->dlt_rel_sec; - - /* The offset of this relocation is the absolute address - of the .dlt entry. */ - rela.r_offset = (off + sdlt->output_offset - + sdlt->output_section->vma); - - if (sym_sec->flags & SEC_READONLY) - { - sec = hppa_info->text_hash_entry->root.u.def.section; - dynindx = hppa_info->text_hash_entry->dynindx; - dynrel_type = R_PARISC_FPTR64; - } - else - { - sec = hppa_info->data_hash_entry->root.u.def.section; - dynindx = hppa_info->data_hash_entry->dynindx; - dynrel_type = R_PARISC_DIR64; - } - - /* Adjust addend using the difference of the symbol's - location and the section symbol's address. */ - value = (relocation + addend - - sec->output_offset - - sec->output_section->vma); - - /* The result becomes the addend of the relocation. */ - rela.r_addend = value; - - BFD_ASSERT (dynindx != -1); - rela.r_info = ELF64_R_INFO (dynindx, dynrel_type); - - loc = sdltrel->contents; - loc += sdltrel->reloc_count++ * sizeof (Elf64_External_Rela); - bfd_elf64_swap_reloca_out (output_bfd, &rela, loc); - } break; } @@ -3669,34 +3676,88 @@ elf_hppa_final_link_relocate (Elf_Internal_Rela *rel, case R_PARISC_LTOFF_FPTR64: case R_PARISC_LTOFF_TP64: { + bfd_vma relocation = value; + bfd_vma *local_opd_offsets, *local_dlt_offsets; + bfd_vma dlt_off, opd_off; + + if (eh == NULL) + { + if (r_type == R_PARISC_LTOFF_FPTR64) + { + local_opd_offsets = local_offsets + 2 * symtab_hdr->sh_info; + opd_off = local_opd_offsets[r_symndx]; + + /* The last bit records whether we've already initialised + this local .opd entry. */ + if ((opd_off & 1) != 0) + { + BFD_ASSERT (opd_off != (bfd_vma) -1); + opd_off &= ~1; + } + else + { + local_opd_offsets[r_symndx] |= 1; + + /* If we are building a shared library, we need a EPLT + and FPTR64 relocation for each OPD entry. */ + if (bfd_link_pic (info)) + elf_hppa_opd_dynrel_relocs (rel, input_section, + relocation, info, opd_off); + } + } + + local_dlt_offsets = local_offsets; + dlt_off = local_dlt_offsets[r_symndx]; + + if ((dlt_off & 1) != 0) + { + BFD_ASSERT (dlt_off != (bfd_vma) -1); + dlt_off &= ~1; + } + else + { + local_dlt_offsets[r_symndx] |= 1; + if (bfd_link_pic (info) + || _bfd_elf_link_lookup_local_dynindx (info, input_bfd, + r_symndx) != -1) + elf_hppa_dlt_dynrel_reloc (rel, relocation, info, + sym_sec, dlt_off); + } + } + else + { + opd_off = hh->opd_offset; + dlt_off = hh->dlt_offset; + } + /* We may still need to create the FPTR itself if it was for a local symbol. */ if (eh == NULL && r_type == R_PARISC_LTOFF_FPTR64) { /* The first word is the address of the function. */ bfd_put_64 (hppa_info->opd_sec->owner, value + addend, - (hppa_info->opd_sec->contents + hh->opd_offset)); + (hppa_info->opd_sec->contents + opd_off + 16)); /* The last word is our local __gp value. */ value = _bfd_get_gp_value (info->output_bfd); bfd_put_64 (hppa_info->opd_sec->owner, value, - hppa_info->opd_sec->contents + hh->opd_offset + 8); + hppa_info->opd_sec->contents + opd_off + 24); /* The DLT value is the address of the .opd entry. */ - value = (hh->opd_offset + value = (opd_off + hppa_info->opd_sec->output_offset + hppa_info->opd_sec->output_section->vma); bfd_put_64 (hppa_info->dlt_sec->owner, value, - hppa_info->dlt_sec->contents + hh->dlt_offset); + hppa_info->dlt_sec->contents + dlt_off); } /* We want the value of the DLT offset for this symbol, not the symbol's actual address. Note that __gp may not point to the start of the DLT, so we have to compute the absolute address, then subtract out the value of __gp. */ - value = (hh->dlt_offset + value = (dlt_off + hppa_info->dlt_sec->output_offset + hppa_info->dlt_sec->output_section->vma); value -= _bfd_get_gp_value (output_bfd); @@ -3776,11 +3837,8 @@ elf_hppa_final_link_relocate (Elf_Internal_Rela *rel, bfd_vma off; bfd_vma relocation = value; asection *sopd; - asection *sopdrel; - bool need_dynrel_relocs = false; sopd = hppa_info->opd_sec; - sopdrel = hppa_info->opd_rel_sec; /* We may still need to create the FPTR itself if it was for a local symbol. */ @@ -3808,7 +3866,8 @@ elf_hppa_final_link_relocate (Elf_Internal_Rela *rel, /* If we are building a shared library, we need a EPLT and a FPTR64 relocation for each OPD entry. */ if (bfd_link_pic (info)) - need_dynrel_relocs = true; + elf_hppa_opd_dynrel_relocs (rel, input_section, + relocation, info, off); /* The third word is the address of the function. */ bfd_put_64 (sopd->owner, value + addend, @@ -3830,75 +3889,6 @@ elf_hppa_final_link_relocate (Elf_Internal_Rela *rel, value += addend; bfd_put_64 (output_bfd, value, hit_data); - - if (need_dynrel_relocs) - { - Elf_Internal_Rela rela; - bfd_byte *loc; - int dynindx; - asection *sec, *s; - - /* The offset of the EPLT relocation is the absolute address - of the .opd entry for this symbol. */ - rela.r_offset = (off + sopd->output_offset - + sopd->output_section->vma); - - /* Select base segment. */ - if (sym_sec->flags & SEC_READONLY) - { - sec = hppa_info->text_hash_entry->root.u.def.section; - dynindx = hppa_info->text_hash_entry->dynindx; - } - else - { - sec = hppa_info->data_hash_entry->root.u.def.section; - dynindx = hppa_info->data_hash_entry->dynindx; - } - - /* Compute the difference between the symbol's address - and the base segment's address. */ - value = (relocation + addend - - sec->output_offset - - sec->output_section->vma); - - /* The result becomes the addend of the relocation. */ - rela.r_addend = value; - - BFD_ASSERT (dynindx != -1); - rela.r_info = ELF64_R_INFO (dynindx, R_PARISC_EPLT); - - loc = sopdrel->contents; - loc += sopdrel->reloc_count++ * sizeof (Elf64_External_Rela); - bfd_elf64_swap_reloca_out (info->output_bfd, &rela, loc); - - /* The offset of the FPTR64 relocation is the absolute address - of this symbol. */ - rela.r_offset = (offset + input_section->output_offset - + input_section->output_section->vma); - - /* OPD's address. */ - value = (off + sopd->output_offset + sopd->output_section->vma); - - /* Base segment and its dynindx. */ - sec = hppa_info->data_hash_entry->root.u.def.section; - dynindx = hppa_info->data_hash_entry->dynindx; - - /* Compute the difference between the opd's address - and the base segment's address. */ - value -= sec->output_offset + sec->output_section->vma; - - /* The result becomes the addend of the relocation. */ - rela.r_addend = value; - - BFD_ASSERT (dynindx != -1); - rela.r_info = ELF64_R_INFO (dynindx, R_PARISC_FPTR64); - - s = hppa_info->other_rel_sec; - loc = s->contents; - loc += s->reloc_count++ * sizeof (Elf64_External_Rela); - bfd_elf64_swap_reloca_out (info->output_bfd, &rela, loc); - } - return bfd_reloc_ok; } -- 2.47.3