mirror of
https://review.haiku-os.org/buildtools
synced 2024-11-23 07:18:49 +01:00
563 lines
17 KiB
C
563 lines
17 KiB
C
/* .sframe section processing.
|
|
Copyright (C) 2022-2023 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_malloc_and_get_section (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:
|
|
free (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;
|
|
}
|