From bdbbe876990c3414aaaba7e80413f2ac13cffb12 Mon Sep 17 00:00:00 2001 From: Indu Bhagat Date: Thu, 6 Feb 2025 13:38:04 -0800 Subject: [PATCH] ld: bfd: sframe: fix incorrect offset in RELA entries PR/32666 Incorrect .rela.sframe when using ld -r Input SFrame sections are merged using _bfd_elf_merge_section_sframe (), which clubs all SFrame FDEs together in one blob and all SFrame FREs in another. This, of course, means the offset of an FDE in the output section cannot be simply derived from the output_offset of the sections. Fix this by providing _bfd_elf_sframe_section_offset () which returns the new offset of the SFrame FDE in the merged SFrame section. TBD: - Q: The _bfd_elf_sframe_section_offset () function does assume that _bfd_elf_merge_section_sframe () has not been called on the associated .sframe section yet. Is this an OK assumption ? bfd/ * elf-bfd.h: New declaration. * elf-sframe.c (_bfd_elf_sframe_section_offset): New definition. * elf.c (_bfd_elf_section_offset): Adjust offset if SFrame section. * elflink.c (elf_link_input_bfd): RELA offset adjust be done conditionally. ld/testsuite/ * ld-x86-64/x86-64.exp: New test. * ld-x86-64/sframe-reloc-1.d: New test. --- bfd/elf-bfd.h | 2 + bfd/elf-sframe.c | 50 +++++++++++++++++++++++++ bfd/elf.c | 3 ++ bfd/elflink.c | 3 +- ld/testsuite/ld-x86-64/sframe-reloc-1.d | 34 +++++++++++++++++ ld/testsuite/ld-x86-64/x86-64.exp | 1 + 6 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 ld/testsuite/ld-x86-64/sframe-reloc-1.d diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h index d2bf8e5cbae..f5b253a0d42 100644 --- a/bfd/elf-bfd.h +++ b/bfd/elf-bfd.h @@ -2535,6 +2535,8 @@ extern bool _bfd_elf_discard_section_sframe (asection *, bool (*) (bfd_vma, void *), struct elf_reloc_cookie *); extern bool _bfd_elf_merge_section_sframe (bfd *, struct bfd_link_info *, asection *, bfd_byte *); +extern bfd_vma _bfd_elf_sframe_section_offset + (bfd *, struct bfd_link_info *, asection *, bfd_vma); extern bool _bfd_elf_write_section_sframe (bfd *, struct bfd_link_info *); extern bool _bfd_elf_set_section_sframe (bfd *, struct bfd_link_info *); diff --git a/bfd/elf-sframe.c b/bfd/elf-sframe.c index 67ed874e025..92245792fe8 100644 --- a/bfd/elf-sframe.c +++ b/bfd/elf-sframe.c @@ -515,6 +515,56 @@ _bfd_elf_merge_section_sframe (bfd *abfd, return true; } +/* Adjust an address in the .sframe section. Given OFFSET within + SEC, this returns the new offset in the merged .sframe section, + or -1 if the address refers to an FDE which has been removed. + + PS: This function assumes that _bfd_elf_merge_section_sframe has not been + called on the input section SEC yet. */ + +bfd_vma +_bfd_elf_sframe_section_offset (bfd *output_bfd ATTRIBUTE_UNUSED, + struct bfd_link_info *info, + asection *sec, + bfd_vma offset) +{ + struct sframe_dec_info *sfd_info; + struct sframe_enc_info *sfe_info; + sframe_decoder_ctx *sfd_ctx; + sframe_encoder_ctx *sfe_ctx; + struct elf_link_hash_table *htab; + + unsigned int sec_fde_idx, out_fde_idx = 0; + unsigned int i, sfe_num_fdes; + + if (sec->sec_info_type != SEC_INFO_TYPE_SFRAME) + return offset; + + sfd_info = (struct sframe_dec_info *) elf_section_data (sec)->sec_info; + sfd_ctx = sfd_info->sfd_ctx; + + htab = elf_hash_table (info); + sfe_info = &(htab->sfe_info); + sfe_ctx = sfe_info->sfe_ctx; + + sfe_num_fdes = sframe_encoder_get_num_fidx (sfe_ctx); + /* Identify the index of the FDE (at OFFSET) in the input section. */ + sec_fde_idx = ((offset - sframe_decoder_get_hdr_size (sfd_ctx)) + / sizeof (sframe_func_desc_entry)); + /* The index of this FDE in the output section depends on number of deleted + functions, if any. */ + for (i = 0; i < sec_fde_idx; i++) + { + if (!sframe_decoder_func_deleted_p (sfd_info, i)) + out_fde_idx++; + } + /* The actual index of the FDE in the output SFrame section. */ + out_fde_idx += sfe_num_fdes; + + return (sframe_decoder_get_hdr_size (sfd_ctx) + + out_fde_idx * sizeof (sframe_func_desc_entry)); +} + /* Write out the .sframe section. This must be called after _bfd_elf_merge_section_sframe has been called on all input .sframe sections. */ diff --git a/bfd/elf.c b/bfd/elf.c index 3f8bc838bfb..3d65486ad5b 100644 --- a/bfd/elf.c +++ b/bfd/elf.c @@ -13491,6 +13491,9 @@ _bfd_elf_section_offset (bfd *abfd, case SEC_INFO_TYPE_EH_FRAME: return _bfd_elf_eh_frame_section_offset (abfd, info, sec, offset); + case SEC_INFO_TYPE_SFRAME: + return _bfd_elf_sframe_section_offset (abfd, info, sec, offset); + default: if ((sec->flags & SEC_ELF_REVERSE_COPY) != 0) { diff --git a/bfd/elflink.c b/bfd/elflink.c index 0df19769483..8e6711f6ae1 100644 --- a/bfd/elflink.c +++ b/bfd/elflink.c @@ -11974,7 +11974,8 @@ elf_link_input_bfd (struct elf_final_link_info *flinfo, bfd *input_bfd) continue; } - irela->r_offset += o->output_offset; + if (o->sec_info_type != SEC_INFO_TYPE_SFRAME) + irela->r_offset += o->output_offset; /* Relocs in an executable have to be virtual addresses. */ if (!bfd_link_relocatable (flinfo->info)) diff --git a/ld/testsuite/ld-x86-64/sframe-reloc-1.d b/ld/testsuite/ld-x86-64/sframe-reloc-1.d new file mode 100644 index 00000000000..308f94bd76c --- /dev/null +++ b/ld/testsuite/ld-x86-64/sframe-reloc-1.d @@ -0,0 +1,34 @@ +#as: --gsframe +#source: sframe-foo.s +#source: sframe-bar.s +#objdump: --sframe=.sframe +#ld: -r --no-rosegment +#name: SFrame simple link - relocatable + +.*: +file format .* + +Contents of the SFrame section .sframe: + Header : + + Version: SFRAME_VERSION_2 + Flags: SFRAME_F_FDE_SORTED + CFA fixed RA offset: \-8 + Num FDEs: 2 + Num FREs: 8 + + Function Index : + + + func idx \[0\]: pc = 0x0, size = 53 bytes + STARTPC +CFA +FP +RA + + 0+0000 +sp\+8 +u +f + + 0+0001 +sp\+16 +c-16 +f + + 0+0004 +fp\+16 +c-16 +f + + 0+0034 +sp\+8 +c-16 +f + + + func idx \[1\]: pc = 0x35, size = 37 bytes + STARTPC +CFA +FP +RA + + 0+0035 +sp\+8 +u +f + + 0+0036 +sp\+16 +c-16 +f + + 0+0039 +fp\+16 +c-16 +f + + 0+0059 +sp\+8 +c-16 +f + diff --git a/ld/testsuite/ld-x86-64/x86-64.exp b/ld/testsuite/ld-x86-64/x86-64.exp index 01d6459b5d7..eed06af80df 100644 --- a/ld/testsuite/ld-x86-64/x86-64.exp +++ b/ld/testsuite/ld-x86-64/x86-64.exp @@ -566,6 +566,7 @@ run_dump_test "pr32591-4-x32" if { ![skip_sframe_tests] } { run_dump_test "sframe-simple-1" + run_dump_test "sframe-reloc-1" run_dump_test "sframe-plt-1" run_dump_test "sframe-ibt-plt-1" run_dump_test "sframe-pltgot-1" -- 2.47.2