aboutsummaryrefslogtreecommitdiff
path: root/bfd/elf-sframe.c
diff options
context:
space:
mode:
Diffstat (limited to 'bfd/elf-sframe.c')
-rw-r--r--bfd/elf-sframe.c130
1 files changed, 125 insertions, 5 deletions
diff --git a/bfd/elf-sframe.c b/bfd/elf-sframe.c
index 97e0073..24a3d35 100644
--- a/bfd/elf-sframe.c
+++ b/bfd/elf-sframe.c
@@ -212,10 +212,11 @@ _bfd_elf_parse_sframe (bfd *abfd,
/* Decode the buffer and keep decoded contents for later use.
Relocations are performed later, but are such that the section's
size is unaffected. */
- sfd_info = bfd_alloc (abfd, sizeof (*sfd_info));
+ sfd_info = bfd_zalloc (abfd, sizeof (*sfd_info));
sf_size = sec->size;
sfd_info->sfd_ctx = sframe_decode ((const char*)sfbuf, sf_size, &decerr);
+ sfd_info->sfd_state = SFRAME_SEC_DECODED;
sfd_ctx = sfd_info->sfd_ctx;
if (!sfd_ctx)
/* Free'ing up any memory held by decoder context is done by
@@ -328,6 +329,8 @@ _bfd_elf_merge_section_sframe (bfd *abfd,
int8_t sfd_ctx_fixed_ra_offset;
uint8_t dctx_version;
uint8_t ectx_version;
+ uint8_t dctx_flags;
+ uint8_t ectx_flags;
int encerr = 0;
struct elf_link_hash_table *htab;
@@ -351,6 +354,8 @@ _bfd_elf_merge_section_sframe (bfd *abfd,
if (sfd_ctx == NULL || sfe_info == NULL)
return false;
+ dctx_flags = sframe_decoder_get_flags (sfd_ctx);
+
if (htab->sfe_info.sfe_ctx == NULL)
{
sfd_ctx_abi_arch = sframe_decoder_get_abi_arch (sfd_ctx);
@@ -361,8 +366,18 @@ _bfd_elf_merge_section_sframe (bfd *abfd,
if (!sfd_ctx_abi_arch)
return false;
+ /* In-memory FDEs in the encoder object are unsorted during linking and
+ will be sorted before emission. Reset SFRAME_F_FDE_SORTED to aptly
+ reflect that (doing so has no other functional value at this time
+ though). */
+ uint8_t tflags = dctx_flags & ~SFRAME_F_FDE_SORTED;
+ /* ld always generates an output section with
+ SFRAME_F_FDE_FUNC_START_PCREL flag set. Later using
+ SFRAME_F_LD_MUSTHAVE_FLAGS, it is enforced that the provided input
+ sections also have this flag set. */
+ tflags |= SFRAME_F_FDE_FUNC_START_PCREL;
htab->sfe_info.sfe_ctx = sframe_encode (SFRAME_VERSION_2,
- 0, /* SFrame flags. */
+ tflags, /* SFrame flags. */
sfd_ctx_abi_arch,
sfd_ctx_fixed_fp_offset,
sfd_ctx_fixed_ra_offset,
@@ -411,6 +426,18 @@ _bfd_elf_merge_section_sframe (bfd *abfd,
return false;
}
+ /* Check that all SFrame sections being linked have the 'data encoding'
+ related flags set. The implementation does not support updating these
+ data encodings on the fly; confirm by checking the ectx_flags. */
+ ectx_flags = sframe_encoder_get_flags (sfe_ctx);
+ if ((dctx_flags & ectx_flags & SFRAME_F_LD_MUSTHAVE_FLAGS)
+ != SFRAME_F_LD_MUSTHAVE_FLAGS)
+ {
+ _bfd_error_handler
+ (_("SFrame sections with unexpected data encoding prevent"
+ " .sframe generation"));
+ return false;
+ }
/* Iterate over the function descriptor entries and the FREs of the
function from the decoder context. Add each of them to the encoder
@@ -481,6 +508,15 @@ _bfd_elf_merge_section_sframe (bfd *abfd,
address += sframe_read_value (abfd, contents,
pltn_r_offset, 4);
address += (sec->output_offset + r_offset);
+ /* SFrame FDE function start address is an offset from the
+ sfde_func_start_address field to the start PC. The
+ calculation below is the distance of sfde_func_start_address
+ field from the start of the output SFrame section. */
+ uint32_t offsetof_fde_in_sec
+ = sframe_encoder_get_offsetof_fde_start_addr (sfe_ctx,
+ cur_fidx + num_enc_fidx,
+ NULL);
+ address -= offsetof_fde_in_sec;
/* FIXME For testing only. Cleanup later. */
// address += (sec->output_section->vma);
@@ -508,12 +544,98 @@ _bfd_elf_merge_section_sframe (bfd *abfd,
}
}
}
+ sfd_info->sfd_state = SFRAME_SEC_MERGED;
/* Free the SFrame decoder context. */
sframe_decoder_free (&sfd_ctx);
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. Note how it uses
+ sframe_encoder_get_num_fidx () to figure out the offset of FDE
+ in the output section. */
+
+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_num_fdes;
+ unsigned int sfd_num_fdes, sfe_num_fdes;
+ uint32_t sfd_fde_offset;
+ bfd_vma new_offset;
+
+ if (sec->sec_info_type != SEC_INFO_TYPE_SFRAME)
+ return offset;
+
+ sfd_info = elf_section_data (sec)->sec_info;
+ sfd_ctx = sfd_info->sfd_ctx;
+ sfd_num_fdes = sframe_decoder_get_num_fidx (sfd_ctx);
+
+ BFD_ASSERT (sfd_info->sfd_state == SFRAME_SEC_DECODED);
+
+ 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);
+
+ /* The index of this FDE in the output section depends on number of deleted
+ functions (between index 0 and sec_fde_idx), if any. */
+ out_num_fdes = 0;
+ sec_fde_idx = 0;
+ for (unsigned int i = 0; i < sfd_num_fdes; i++)
+ {
+ sfd_fde_offset = sframe_decoder_get_offsetof_fde_start_addr (sfd_ctx,
+ i, NULL);
+ if (!sframe_decoder_func_deleted_p (sfd_info, i))
+ out_num_fdes++;
+
+ if (sfd_fde_offset == offset)
+ {
+ /* Found the index of the FDE (at OFFSET) in the input section. */
+ sec_fde_idx = i;
+ break;
+ }
+ }
+
+ if (sframe_decoder_func_deleted_p (sfd_info, sec_fde_idx))
+ return (bfd_vma) -1;
+
+ /* The number of FDEs in the output SFrame section. Note that the output
+ index of the FDE of interest will be (out_num_fdes - 1). */
+ out_num_fdes += sfe_num_fdes;
+
+ new_offset = sframe_decoder_get_offsetof_fde_start_addr (sfd_ctx,
+ out_num_fdes - 1,
+ NULL);
+ /* Recall that SFrame section merging has distinct requirements: All SFrame
+ FDEs from input sections are clubbed together in the beginning of the
+ output section. So, at this point in the current function, the new_offset
+ is the correct offset in the merged output SFrame section. Note, however,
+ that the default mechanism in the _elf_link_input_bfd will do the
+ following adjustment:
+ irela->r_offset += o->output_offset;
+ for all section types. However, such an adjustment in the RELA offset is
+ _not_ needed for SFrame sections. Perform the reverse adjustment here so
+ that the default mechanism does not need additional SFrame specific
+ checks. */
+ new_offset -= sec->output_offset;
+
+ return new_offset;
+}
+
/* Write out the .sframe section. This must be called after
_bfd_elf_merge_section_sframe has been called on all input
.sframe sections. */
@@ -546,13 +668,11 @@ _bfd_elf_write_section_sframe (bfd *abfd, struct bfd_link_info *info)
(file_ptr) sec->output_offset,
sec->size))
retval = false;
- else if (!bfd_link_relocatable (info))
+ else
{
Elf_Internal_Shdr *hdr = &elf_section_data (sec)->this_hdr;
hdr->sh_size = sec->size;
}
- /* For relocatable links, do not update the section size as the section
- contents have not been relocated. */
sframe_encoder_free (&sfe_ctx);