| /* .sframe section processing. | |
| Copyright (C) 2022-2024 Free Software Foundation, Inc. | |
| This file is part of BFD, the Binary File Descriptor library. | |
| This program is free software; you can redistribute it and/or modify | |
| it under the terms of the GNU General Public License as published by | |
| the Free Software Foundation; either version 3 of the License, or | |
| (at your option) any later version. | |
| This program is distributed in the hope that it will be useful, | |
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| GNU General Public License for more details. | |
| You should have received a copy of the GNU General Public License | |
| along with this program; if not, write to the Free Software | |
| Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, | |
| MA 02110-1301, USA. */ | |
| #include "sysdep.h" | |
| #include "bfd.h" | |
| #include "libbfd.h" | |
| #include "elf-bfd.h" | |
| #include "sframe-api.h" | |
| /* Return TRUE if the function has been marked for deletion during the linking | |
| process. */ | |
| static bool | |
| sframe_decoder_func_deleted_p (struct sframe_dec_info *sfd_info, | |
| unsigned int func_idx) | |
| { | |
| if (func_idx < sfd_info->sfd_fde_count) | |
| return sfd_info->sfd_func_bfdinfo[func_idx].func_deleted_p; | |
| return false; | |
| } | |
| /* Mark the function in the decoder info for deletion. */ | |
| static void | |
| sframe_decoder_mark_func_deleted (struct sframe_dec_info *sfd_info, | |
| unsigned int func_idx) | |
| { | |
| if (func_idx < sfd_info->sfd_fde_count) | |
| sfd_info->sfd_func_bfdinfo[func_idx].func_deleted_p = true; | |
| } | |
| /* Get the relocation offset from the decoder info for the given function. */ | |
| static unsigned int | |
| sframe_decoder_get_func_r_offset (struct sframe_dec_info *sfd_info, | |
| unsigned int func_idx) | |
| { | |
| BFD_ASSERT (func_idx < sfd_info->sfd_fde_count); | |
| unsigned int func_r_offset | |
| = sfd_info->sfd_func_bfdinfo[func_idx].func_r_offset; | |
| /* There must have been a reloc. */ | |
| BFD_ASSERT (func_r_offset); | |
| return func_r_offset; | |
| } | |
| /* Bookkeep the function relocation offset in the decoder info. */ | |
| static void | |
| sframe_decoder_set_func_r_offset (struct sframe_dec_info *sfd_info, | |
| unsigned int func_idx, | |
| unsigned int r_offset) | |
| { | |
| if (func_idx < sfd_info->sfd_fde_count) | |
| sfd_info->sfd_func_bfdinfo[func_idx].func_r_offset = r_offset; | |
| } | |
| /* Get the relocation index in the elf_reloc_cookie for the function. */ | |
| static unsigned int | |
| sframe_decoder_get_func_reloc_index (struct sframe_dec_info *sfd_info, | |
| unsigned int func_idx) | |
| { | |
| BFD_ASSERT (func_idx < sfd_info->sfd_fde_count); | |
| return sfd_info->sfd_func_bfdinfo[func_idx].func_reloc_index; | |
| } | |
| /* Bookkeep the relocation index in the elf_reloc_cookie for the function. */ | |
| static void | |
| sframe_decoder_set_func_reloc_index (struct sframe_dec_info *sfd_info, | |
| unsigned int func_idx, | |
| unsigned int reloc_index) | |
| { | |
| if (func_idx < sfd_info->sfd_fde_count) | |
| sfd_info->sfd_func_bfdinfo[func_idx].func_reloc_index = reloc_index; | |
| } | |
| /* Initialize the set of additional information in CFD_INFO, | |
| needed for linking SEC. Returns TRUE if setup is done successfully. */ | |
| static bool | |
| sframe_decoder_init_func_bfdinfo (asection *sec, | |
| struct sframe_dec_info *sfd_info, | |
| struct elf_reloc_cookie *cookie) | |
| { | |
| unsigned int fde_count; | |
| unsigned int func_bfdinfo_size, i; | |
| fde_count = sframe_decoder_get_num_fidx (sfd_info->sfd_ctx); | |
| sfd_info->sfd_fde_count = fde_count; | |
| /* Allocate and clear the memory. */ | |
| func_bfdinfo_size = (sizeof (struct sframe_func_bfdinfo)) * fde_count; | |
| sfd_info->sfd_func_bfdinfo | |
| = (struct sframe_func_bfdinfo*) bfd_malloc (func_bfdinfo_size); | |
| if (sfd_info->sfd_func_bfdinfo == NULL) | |
| return false; | |
| memset (sfd_info->sfd_func_bfdinfo, 0, func_bfdinfo_size); | |
| /* For linker generated .sframe sections, we have no relocs. Skip. */ | |
| if ((sec->flags & SEC_LINKER_CREATED) && cookie->rels == NULL) | |
| return true; | |
| for (i = 0; i < fde_count; i++) | |
| { | |
| cookie->rel = cookie->rels + i; | |
| BFD_ASSERT (cookie->rel < cookie->relend); | |
| /* Bookkeep the relocation offset and relocation index of each function | |
| for later use. */ | |
| sframe_decoder_set_func_r_offset (sfd_info, i, cookie->rel->r_offset); | |
| sframe_decoder_set_func_reloc_index (sfd_info, i, | |
| (cookie->rel - cookie->rels)); | |
| cookie->rel++; | |
| } | |
| BFD_ASSERT (cookie->rel == cookie->relend); | |
| return true; | |
| } | |
| /* Read the value from CONTENTS at the specified OFFSET for the given ABFD. */ | |
| static bfd_vma | |
| sframe_read_value (bfd *abfd, bfd_byte *contents, unsigned int offset, | |
| unsigned int width) | |
| { | |
| BFD_ASSERT (contents && offset); | |
| /* Supporting the usecase of reading only the 4-byte relocated | |
| value (signed offset for func start addr) for now. */ | |
| BFD_ASSERT (width == 4); | |
| /* FIXME endianness ?? */ | |
| unsigned char *buf = contents + offset; | |
| bfd_vma value = bfd_get_signed_32 (abfd, buf); | |
| return value; | |
| } | |
| /* Return true if there is at least one non-empty .sframe section in | |
| input files. Can only be called after ld has mapped input to | |
| output sections, and before sections are stripped. */ | |
| bool | |
| _bfd_elf_sframe_present (struct bfd_link_info *info) | |
| { | |
| asection *sframe = bfd_get_section_by_name (info->output_bfd, ".sframe"); | |
| if (sframe == NULL) | |
| return false; | |
| /* Count only sections which have at least a single FDE. */ | |
| for (sframe = sframe->map_head.s; sframe != NULL; sframe = sframe->map_head.s) | |
| /* Note that this may become an approximate check in the future when | |
| some ABI/arch begin to use the sfh_auxhdr_len. When sfh_auxhdr_len has | |
| non-zero value, it will need to be accounted for in the calculation of | |
| the SFrame header size. */ | |
| if (sframe->size > sizeof (sframe_header)) | |
| return true; | |
| return false; | |
| } | |
| /* Try to parse .sframe section SEC, which belongs to ABFD. Store the | |
| information in the section's sec_info field on success. COOKIE | |
| describes the relocations in SEC. | |
| Returns TRUE if success, FALSE if any error or failure. */ | |
| bool | |
| _bfd_elf_parse_sframe (bfd *abfd, | |
| struct bfd_link_info *info ATTRIBUTE_UNUSED, | |
| asection *sec, struct elf_reloc_cookie *cookie) | |
| { | |
| bfd_byte *sfbuf = NULL; | |
| struct sframe_dec_info *sfd_info; | |
| sframe_decoder_ctx *sfd_ctx; | |
| bfd_size_type sf_size; | |
| int decerr = 0; | |
| if (sec->size == 0 | |
| || (sec->flags & SEC_HAS_CONTENTS) == 0 | |
| || sec->sec_info_type != SEC_INFO_TYPE_NONE) | |
| { | |
| /* This file does not contain .sframe information. */ | |
| return false; | |
| } | |
| if (bfd_is_abs_section (sec->output_section)) | |
| { | |
| /* At least one of the sections is being discarded from the | |
| link, so we should just ignore them. */ | |
| return false; | |
| } | |
| /* Read the SFrame stack trace information from abfd. */ | |
| if (!_bfd_elf_mmap_section_contents (abfd, sec, &sfbuf)) | |
| goto fail_no_free; | |
| /* 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_malloc (sizeof (struct sframe_dec_info)); | |
| sf_size = sec->size; | |
| sfd_info->sfd_ctx = sframe_decode ((const char*)sfbuf, sf_size, &decerr); | |
| sfd_ctx = sfd_info->sfd_ctx; | |
| if (!sfd_ctx) | |
| /* Free'ing up any memory held by decoder context is done by | |
| sframe_decode in case of error. */ | |
| goto fail_no_free; | |
| if (!sframe_decoder_init_func_bfdinfo (sec, sfd_info, cookie)) | |
| { | |
| sframe_decoder_free (&sfd_ctx); | |
| goto fail_no_free; | |
| } | |
| elf_section_data (sec)->sec_info = sfd_info; | |
| sec->sec_info_type = SEC_INFO_TYPE_SFRAME; | |
| goto success; | |
| fail_no_free: | |
| _bfd_error_handler | |
| (_("error in %pB(%pA); no .sframe will be created"), | |
| abfd, sec); | |
| return false; | |
| success: | |
| _bfd_elf_munmap_section_contents (sec, sfbuf); | |
| return true; | |
| } | |
| /* This function is called for each input file before the .sframe section | |
| is relocated. It marks the SFrame FDE for the discarded functions for | |
| deletion. | |
| The function returns TRUE iff any entries have been deleted. */ | |
| bool | |
| _bfd_elf_discard_section_sframe | |
| (asection *sec, | |
| bool (*reloc_symbol_deleted_p) (bfd_vma, void *), | |
| struct elf_reloc_cookie *cookie) | |
| { | |
| bool changed; | |
| bool keep; | |
| unsigned int i; | |
| unsigned int func_desc_offset; | |
| unsigned int num_fidx; | |
| struct sframe_dec_info *sfd_info; | |
| changed = false; | |
| /* FIXME - if relocatable link and changed = true, how does the final | |
| .rela.sframe get updated ?. */ | |
| keep = false; | |
| sfd_info = (struct sframe_dec_info *) elf_section_data (sec)->sec_info; | |
| /* Skip checking for the linker created .sframe sections | |
| (for PLT sections). */ | |
| if ((sec->flags & SEC_LINKER_CREATED) == 0 || cookie->rels != NULL) | |
| { | |
| num_fidx = sframe_decoder_get_num_fidx (sfd_info->sfd_ctx); | |
| for (i = 0; i < num_fidx; i++) | |
| { | |
| func_desc_offset = sframe_decoder_get_func_r_offset (sfd_info, i); | |
| cookie->rel = cookie->rels | |
| + sframe_decoder_get_func_reloc_index (sfd_info, i); | |
| keep = !(*reloc_symbol_deleted_p) (func_desc_offset, cookie); | |
| if (!keep) | |
| { | |
| sframe_decoder_mark_func_deleted (sfd_info, i); | |
| changed = true; | |
| } | |
| } | |
| } | |
| return changed; | |
| } | |
| /* Update the reference to the output .sframe section in the output ELF | |
| BFD ABFD. Returns true if no error. */ | |
| bool | |
| _bfd_elf_set_section_sframe (bfd *abfd, | |
| struct bfd_link_info *info) | |
| { | |
| asection *cfsec; | |
| cfsec = bfd_get_section_by_name (info->output_bfd, ".sframe"); | |
| if (!cfsec) | |
| return false; | |
| elf_sframe (abfd) = cfsec; | |
| return true; | |
| } | |
| /* Merge .sframe section SEC. This is called with the relocated | |
| CONTENTS. */ | |
| bool | |
| _bfd_elf_merge_section_sframe (bfd *abfd, | |
| struct bfd_link_info *info, | |
| asection *sec, | |
| bfd_byte *contents) | |
| { | |
| struct sframe_dec_info *sfd_info; | |
| struct sframe_enc_info *sfe_info; | |
| sframe_decoder_ctx *sfd_ctx; | |
| sframe_encoder_ctx *sfe_ctx; | |
| uint8_t sfd_ctx_abi_arch; | |
| int8_t sfd_ctx_fixed_fp_offset; | |
| int8_t sfd_ctx_fixed_ra_offset; | |
| uint8_t dctx_version; | |
| uint8_t ectx_version; | |
| int encerr = 0; | |
| struct elf_link_hash_table *htab; | |
| asection *cfsec; | |
| /* Sanity check - handle SFrame sections only. */ | |
| if (sec->sec_info_type != SEC_INFO_TYPE_SFRAME) | |
| return false; | |
| 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; | |
| /* All input bfds are expected to have a valid SFrame section. Even if | |
| the SFrame section is empty with only a header, there must be a valid | |
| SFrame decoder context by now. The SFrame encoder context, however, | |
| will get set later here, if this is the first call to the function. */ | |
| if (sfd_ctx == NULL || sfe_info == NULL) | |
| return false; | |
| if (htab->sfe_info.sfe_ctx == NULL) | |
| { | |
| sfd_ctx_abi_arch = sframe_decoder_get_abi_arch (sfd_ctx); | |
| sfd_ctx_fixed_fp_offset = sframe_decoder_get_fixed_fp_offset (sfd_ctx); | |
| sfd_ctx_fixed_ra_offset = sframe_decoder_get_fixed_ra_offset (sfd_ctx); | |
| /* Valid values are non-zero. */ | |
| if (!sfd_ctx_abi_arch) | |
| return false; | |
| htab->sfe_info.sfe_ctx = sframe_encode (SFRAME_VERSION_2, | |
| 0, /* SFrame flags. */ | |
| sfd_ctx_abi_arch, | |
| sfd_ctx_fixed_fp_offset, | |
| sfd_ctx_fixed_ra_offset, | |
| &encerr); | |
| /* Handle errors from sframe_encode. */ | |
| if (htab->sfe_info.sfe_ctx == NULL) | |
| return false; | |
| } | |
| sfe_ctx = sfe_info->sfe_ctx; | |
| if (sfe_info->sframe_section == NULL) | |
| { | |
| /* Make sure things are set for an eventual write. | |
| Size of the output section is not known until | |
| _bfd_elf_write_section_sframe is ready with the buffer | |
| to write out. */ | |
| cfsec = bfd_get_section_by_name (info->output_bfd, ".sframe"); | |
| if (cfsec) | |
| { | |
| sfe_info->sframe_section = cfsec; | |
| // elf_sframe (abfd) = cfsec; | |
| } | |
| else | |
| return false; | |
| } | |
| /* Check that all .sframe sections being linked have the same | |
| ABI/arch. */ | |
| if (sframe_decoder_get_abi_arch (sfd_ctx) | |
| != sframe_encoder_get_abi_arch (sfe_ctx)) | |
| { | |
| _bfd_error_handler | |
| (_("input SFrame sections with different abi prevent .sframe" | |
| " generation")); | |
| return false; | |
| } | |
| /* Check that all .sframe sections being linked have the same version. */ | |
| dctx_version = sframe_decoder_get_version (sfd_ctx); | |
| ectx_version = sframe_encoder_get_version (sfe_ctx); | |
| if (dctx_version != SFRAME_VERSION_2 || dctx_version != ectx_version) | |
| { | |
| _bfd_error_handler | |
| (_("input SFrame sections with different format versions 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 | |
| context, if suitable. */ | |
| uint32_t i = 0, j = 0, cur_fidx = 0; | |
| uint32_t num_fidx = sframe_decoder_get_num_fidx (sfd_ctx); | |
| uint32_t num_enc_fidx = sframe_encoder_get_num_fidx (sfe_ctx); | |
| for (i = 0; i < num_fidx; i++) | |
| { | |
| unsigned int num_fres = 0; | |
| int32_t func_start_addr; | |
| bfd_vma address; | |
| uint32_t func_size = 0; | |
| unsigned char func_info = 0; | |
| unsigned int r_offset = 0; | |
| bool pltn_reloc_by_hand = false; | |
| unsigned int pltn_r_offset = 0; | |
| uint8_t rep_block_size = 0; | |
| if (!sframe_decoder_get_funcdesc_v2 (sfd_ctx, i, &num_fres, &func_size, | |
| &func_start_addr, &func_info, | |
| &rep_block_size)) | |
| { | |
| /* If function belongs to a deleted section, skip editing the | |
| function descriptor entry. */ | |
| if (sframe_decoder_func_deleted_p(sfd_info, i)) | |
| continue; | |
| /* Don't edit function descriptor entries for relocatable link. */ | |
| if (!bfd_link_relocatable (info)) | |
| { | |
| if (!(sec->flags & SEC_LINKER_CREATED)) | |
| { | |
| /* Get relocated contents by reading the value of the | |
| relocated function start address at the beginning of the | |
| function descriptor entry. */ | |
| r_offset = sframe_decoder_get_func_r_offset (sfd_info, i); | |
| } | |
| else | |
| { | |
| /* Expected to land here when SFrame stack trace info is | |
| created dynamically for the .plt* sections. These | |
| sections are expected to have upto two SFrame FDE entries. | |
| Although the code should work for > 2, leaving this | |
| assert here for safety. */ | |
| BFD_ASSERT (num_fidx <= 2); | |
| /* For the first entry, we know the offset of the SFrame FDE's | |
| sfde_func_start_address. Side note: see how the value | |
| of PLT_SFRAME_FDE_START_OFFSET is also set to the | |
| same. */ | |
| r_offset = sframe_decoder_get_hdr_size (sfd_ctx); | |
| /* For any further SFrame FDEs, the generator has already put | |
| in an offset in place of sfde_func_start_address of the | |
| corresponding FDE. We will use it by hand to relocate. */ | |
| if (i > 0) | |
| { | |
| pltn_r_offset | |
| = r_offset + (i * sizeof (sframe_func_desc_entry)); | |
| pltn_reloc_by_hand = true; | |
| } | |
| } | |
| /* Get the SFrame FDE function start address after relocation. */ | |
| address = sframe_read_value (abfd, contents, r_offset, 4); | |
| if (pltn_reloc_by_hand) | |
| address += sframe_read_value (abfd, contents, | |
| pltn_r_offset, 4); | |
| address += (sec->output_offset + r_offset); | |
| /* FIXME For testing only. Cleanup later. */ | |
| // address += (sec->output_section->vma); | |
| func_start_addr = address; | |
| } | |
| /* Update the encoder context with updated content. */ | |
| int err = sframe_encoder_add_funcdesc_v2 (sfe_ctx, func_start_addr, | |
| func_size, func_info, | |
| rep_block_size, num_fres); | |
| cur_fidx++; | |
| BFD_ASSERT (!err); | |
| } | |
| for (j = 0; j < num_fres; j++) | |
| { | |
| sframe_frame_row_entry fre; | |
| if (!sframe_decoder_get_fre (sfd_ctx, i, j, &fre)) | |
| { | |
| int err = sframe_encoder_add_fre (sfe_ctx, | |
| cur_fidx-1+num_enc_fidx, | |
| &fre); | |
| BFD_ASSERT (!err); | |
| } | |
| } | |
| } | |
| /* Free the SFrame decoder context. */ | |
| sframe_decoder_free (&sfd_ctx); | |
| return true; | |
| } | |
| /* Write out the .sframe section. This must be called after | |
| _bfd_elf_merge_section_sframe has been called on all input | |
| .sframe sections. */ | |
| bool | |
| _bfd_elf_write_section_sframe (bfd *abfd, struct bfd_link_info *info) | |
| { | |
| bool retval = true; | |
| struct elf_link_hash_table *htab; | |
| struct sframe_enc_info *sfe_info; | |
| sframe_encoder_ctx *sfe_ctx; | |
| asection *sec; | |
| void *contents; | |
| size_t sec_size; | |
| int err = 0; | |
| htab = elf_hash_table (info); | |
| sfe_info = &htab->sfe_info; | |
| sec = sfe_info->sframe_section; | |
| sfe_ctx = sfe_info->sfe_ctx; | |
| if (sec == NULL) | |
| return true; | |
| contents = sframe_encoder_write (sfe_ctx, &sec_size, &err); | |
| sec->size = (bfd_size_type) sec_size; | |
| if (!bfd_set_section_contents (abfd, sec->output_section, contents, | |
| (file_ptr) sec->output_offset, | |
| sec->size)) | |
| retval = false; | |
| else if (!bfd_link_relocatable (info)) | |
| { | |
| 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); | |
| return retval; | |
| } |