Files
binutils-gdb/libsframe/sframe-dump.c
Indu Bhagat 0b1bf2fc98 bfd: gas: ld: libsframe: adopt new encoding for FDE func start addr field
This patch convenes a set of changes in bfd, gas, ld, libsframe towards
moving to the new encoding for the 'sfde_func_start_address' field in
SFrame FDE.

First, gas must now mark all SFrame sections with the new flag
SFRAME_F_FDE_FUNC_START_PCREL.  gas was already emitting the field
in the said encoding.

	* gas/gen-sframe.c (output_sframe_internal): Emit the flag
	SFRAME_F_FDE_FUNC_START_PCREL.

Similarly for ld, adopt the new semantics of sfde_func_start_address
consistently.  This means:
  - When merging SFrame sections, check that all input SFrame sections
    have the SFRAME_F_FDE_FUNC_START_PCREL flag set.  If the check
    fails, ld errors out.
  - When merging SFrame sections, keep even the in-memory contents of
    the FDE function start address (buffer passed to libsframe
    sframe_encoder_write () for writing out) encoded in the new
    semantics.  While it is, in theory, possible that instead of doing this
    change here, we adjust the value of sfde_func_start_address at the final
    write (sframe_encoder_write) time.  But latter is not favorable for
    maintenanance and may be generally confusing for developers.
  - When creating SFrame for PLT entries, emit flag
    SFRAME_F_FDE_FUNC_START_PCREL.

include/
        * sframe-api.h (SFRAME_F_LD_MUSTHAVE_FLAGS): New definition.
bfd/
	* elf-sframe.c (_bfd_elf_merge_section_sframe): Check for flag
	combinatation SFRAME_F_LD_MUSTHAVE_FLAGS set for all input and
	output SFrame sections.  If not, error out.  Also, adopt the new
        semantics of function start address encoding.
	* bfd/elfxx-x86.c (_bfd_x86_elf_create_sframe_plt): Emit flag
	SFRAME_F_FDE_FUNC_START_PCREL.

Next, for dumping SFrame sections, now that we are emitting the same
encoding in GAS, non-relocatable and relocatable SFrame links, it is the
time to set relocate to TRUE in debug_displays[].

binutils/
	* dwarf.c (struct dwarf_section_display): Allow sframe sections
	  to now be relocated.
gas/testsuite/
	* gas/cfi-sframe/cfi-sframe-aarch64-pac-ab-key-1.d: Update the
	test.  Relocatable SFrame sections now display non-zero value
	(appropriate function start address).

Now, as the SFrame sections on-disk and in-memory use the new semantics of
sfde_func_start_address encoding (i.e., function start address is the
offset from the sfde_func_start_address field to the start PC), the
calculation to make it human readable (i.e., relatable to the addresses
in .text sections) needs adjustment.

libsframe/
	* sframe-dump.c (dump_sframe_func_with_fres): Adjust the
	function start address for dumping.

Now that both the emission of the new encoding, and the relocation of
sections before dumping them is in place, it is time to adjust the
testcases.

gas/testsuite/
	* gas/cfi-sframe/cfi-sframe-aarch64-1.d: Update expected output
	to include SFRAME_F_FDE_FUNC_START_PCREL instead of NONE.
	* gas/cfi-sframe/cfi-sframe-aarch64-2.d: Likewise.
	* gas/cfi-sframe/cfi-sframe-aarch64-3.d: Likewise.
	* gas/cfi-sframe/cfi-sframe-aarch64-4.d: Likewise.
	* gas/cfi-sframe/cfi-sframe-common-1.d: Likewise.
	* gas/cfi-sframe/cfi-sframe-common-10.d: Likewise.
	* gas/cfi-sframe/cfi-sframe-common-11.d: Likewise.
	* gas/cfi-sframe/cfi-sframe-common-2.d: Likewise.
	* gas/cfi-sframe/cfi-sframe-common-3.d: Likewise.
	* gas/cfi-sframe/cfi-sframe-common-4.d: Likewise.
	* gas/cfi-sframe/cfi-sframe-common-5.d: Likewise.
	* gas/cfi-sframe/cfi-sframe-common-6.d: Likewise.
	* gas/cfi-sframe/cfi-sframe-common-7.d: Likewise.
	* gas/cfi-sframe/cfi-sframe-common-8.d: Likewise.
	* gas/cfi-sframe/cfi-sframe-common-9.d: Likewise.
	* gas/cfi-sframe/cfi-sframe-x86_64-1.d: Likewise.
	* gas/cfi-sframe/cfi-sframe-x86_64-2.d: Likewise.
	* gas/cfi-sframe/cfi-sframe-x86_64-empty-1.d: Likewise.
	* gas/cfi-sframe/cfi-sframe-x86_64-empty-2.d: Likewise.
	* gas/cfi-sframe/cfi-sframe-x86_64-empty-3.d: Likewise.
	* gas/cfi-sframe/cfi-sframe-x86_64-empty-4.d: Likewise.
	* gas/cfi-sframe/common-empty-1.d: Likewise.
	* gas/cfi-sframe/common-empty-2.d: Likewise.
	* gas/cfi-sframe/common-empty-3.d: Likewise.
	* gas/scfi/x86_64/scfi-cfi-sections-1.d: Likewise.
	* gas/scfi/x86_64/scfi-dyn-stack-1.d: Likewise.
ld/testsuite/
	* ld-aarch64/sframe-simple-1.d: Update expected output to
	include SFRAME_F_FDE_FUNC_START_PCREL.
	* ld-x86-64/sframe-ibt-plt-1.d: Likewise.
	* ld-x86-64/sframe-plt-1.d: Likewise.
	* ld-x86-64/sframe-pltgot-1.d: Likewise.
	* ld-x86-64/sframe-pltgot-2.d: Likewise.
	* ld-x86-64/sframe-simple-1.d: Likewise.

Naturally, the change of semantics for 'SFrame FDE function start address'
has consequences on the implementation in libsframe.  As per the new
semantics:
  - Function start address in the SFrame FDE (sfde_func_start_address)
    is an offset from the FDE function start address field to the start
    PC of the associated function.

Note that, the libsframe library brings the SFrame section contents into
its own memory to create a sframe_decoder_ctx object via sframe_decode
().  Many internal and user-interfacing APIs then may use
sframe_decoder_ctx object to interact and fulfill the work.

In context of changing semantics for sfde_func_start_address, following
relevant examples may help understand the impact:
  - sframe_find_fre () finds a the SFrame stack trace data (SFrame FRE)
    given a lookup offset (offset of lookup_pc from the start of SFrame
    section).  Now that the sfde_func_start_address includes the
    distance from the sfde_func_start_address field to the start of
    SFrame section itself, the comparison checks of
    sfde_func_start_address with the incoming lookup offset need
    adjustment.
  - Some internal functions (sframe_get_funcdesc_with_addr_internal ()
    finds SFrame FDE by using binary seach comparing
    sfde_func_start_address fields, etc.) need adjustments.
  - sframe_encoder_write () sorts the SFrame FDEs before writing out
    the SFrame data.  Sorting of SFrame FDE via the internal function
    sframe_sort_funcdesc() needs adjustments: the new encoding of
    sfde_func_start_address means the distances are not from the same
    anchor, so cannot be sorted directly.

This patch takes the approach of adding a new internal function:
  - sframe_decoder_get_secrel_func_start_addr (): This function returns
    the offset of the start PC of the function from the start of SFrame
    section, i.e., it gives a section-relative offset.

As the sframe_decoder_get_secrel_func_start_addr () API needs the value
of the function index in the FDE list, another internal API needs
sframe_fre_check_range_p () adjustments too.

Sorting the FDEs (via sframe_sort_funcdesc ()) is done by first bringing
all offsets in sfde_func_start_address relative to start of SFrame
section, followed by sorting, and then readjusting the offsets accroding
to the new position in the FDE list.

libsframe/
	* sframe.c (sframe_decoder_get_secrel_func_start_addr): New
	static function.
        (sframe_fre_check_range_p): Adjust the interface a bit.
	(sframe_get_funcdesc_with_addr_internal): Use
	sframe_decoder_get_secrel_func_start_addr () when comparing
	sfde_func_start_address with user input offset.
        (sframe_find_fre): Adopt the new semantics.
        (sframe_sort_funcdesc): Likewise.

For the libsframe testsuite, use the new encoding for FDE func start
addr: distance between the FDE sfde_func_start_address field and the
start PC of the function itself.

Use SFRAME_F_FDE_FUNC_START_PCREL flag, though the sframe_encode ()
interface in libsframe applies no sanity checks for the encoding itself.

libsframe/testsuite/
	* libsframe.find/findfre-1.c: Adjust to use the new
	SFRAME_F_FDE_FUNC_START_PCREL specific encoding.
	* libsframe.find/findfunc-1.c: Likewise.
	* libsframe.find/plt-findfre-1.c: Likewise.
	* libsframe/testsuite/libsframe.decode/DATA2: Update data file
	due to usage of new SFRAME_F_FDE_FUNC_START_PCREL flag.
	* libsframe/testsuite/libsframe.encode/encode-1.c: Use flag
	SFRAME_F_FDE_FUNC_START_PCREL.
2025-07-06 12:53:03 -07:00

246 lines
7.2 KiB
C

/* sframe-dump.c - Textual dump of .sframe.
Copyright (C) 2022-2025 Free Software Foundation, Inc.
This file is part of libsframe.
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, see <http://www.gnu.org/licenses/>. */
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include "sframe-impl.h"
/* Return TRUE if the SFrame section is associated with the aarch64 ABIs. */
static bool
is_sframe_abi_arch_aarch64 (sframe_decoder_ctx *sfd_ctx)
{
bool aarch64_p = false;
uint8_t abi_arch = sframe_decoder_get_abi_arch (sfd_ctx);
if (abi_arch == SFRAME_ABI_AARCH64_ENDIAN_BIG
|| abi_arch == SFRAME_ABI_AARCH64_ENDIAN_LITTLE)
aarch64_p = true;
return aarch64_p;
}
static void
dump_sframe_header_flags (sframe_decoder_ctx *sfd_ctx)
{
uint8_t flags;
const char *prefix = "Flags: ";
flags = sframe_decoder_get_flags (sfd_ctx);
if (!flags)
{
printf ("%11sNONE\n", prefix);
return;
}
#define PRINT_FLAG(x) \
if (flags & (x)) \
{ flags = (flags & ~(x)); \
printf ("%11s%s%s\n", prefix, #x, flags ? "," : ""); \
prefix = " "; \
}
PRINT_FLAG (SFRAME_F_FDE_SORTED);
PRINT_FLAG (SFRAME_F_FRAME_POINTER);
PRINT_FLAG (SFRAME_F_FDE_FUNC_START_PCREL);
#undef PRINT_FLAG
/* Print any residual flags, should this implementation be out of sync when
new flags are added. */
if (flags)
printf ("%11s%d\n", prefix, flags);
}
static void
dump_sframe_header (sframe_decoder_ctx *sfd_ctx)
{
uint8_t ver;
const char *ver_str = NULL;
int8_t cfa_fixed_fp_offset;
int8_t cfa_fixed_ra_offset;
const sframe_header *header = &(sfd_ctx->sfd_header);
/* Prepare SFrame section version string. */
const char *version_names[]
= { "NULL",
"SFRAME_VERSION_1",
"SFRAME_VERSION_2" };
ver = sframe_decoder_get_version (sfd_ctx);
if (ver <= SFRAME_VERSION)
ver_str = version_names[ver];
/* CFA fixed FP and RA offsets. */
cfa_fixed_fp_offset = header->sfh_cfa_fixed_fp_offset;
cfa_fixed_ra_offset = header->sfh_cfa_fixed_ra_offset;
const char* subsec_name = "Header";
printf ("\n");
printf (" %s :\n", subsec_name);
printf ("\n");
printf (" Version: %s\n", ver_str);
dump_sframe_header_flags (sfd_ctx);
if (cfa_fixed_fp_offset != SFRAME_CFA_FIXED_FP_INVALID)
printf (" CFA fixed FP offset: %d\n", cfa_fixed_fp_offset);
if (cfa_fixed_ra_offset != SFRAME_CFA_FIXED_RA_INVALID)
printf (" CFA fixed RA offset: %d\n", cfa_fixed_ra_offset);
printf (" Num FDEs: %d\n", sframe_decoder_get_num_fidx (sfd_ctx));
printf (" Num FREs: %d\n", header->sfh_num_fres);
}
static void
dump_sframe_func_with_fres (sframe_decoder_ctx *sfd_ctx,
unsigned int funcidx,
uint64_t sec_addr)
{
uint32_t j = 0;
uint32_t num_fres = 0;
uint32_t func_size = 0;
int32_t func_start_address = 0;
unsigned char func_info = 0;
uint64_t func_start_pc_vma = 0;
uint64_t fre_start_pc_vma = 0;
const char *base_reg_str[] = {"fp", "sp"};
int32_t cfa_offset = 0;
int32_t fp_offset = 0;
int32_t ra_offset = 0;
uint8_t base_reg_id = 0;
int err[3] = {0, 0, 0};
sframe_frame_row_entry fre;
/* Get the SFrame function descriptor. */
sframe_decoder_get_funcdesc (sfd_ctx, funcidx, &num_fres,
&func_size, &func_start_address, &func_info);
/* Calculate the virtual memory address for function start pc. Some older
SFrame V2 sections in ET_DYN or ET_EXEC may still have the
SFRAME_F_FDE_FUNC_START_PCREL flag unset, and hence may be using the
old encoding. Continue to support dumping the sections at least. */
func_start_pc_vma = func_start_address + sec_addr;
if (sframe_decoder_get_flags (sfd_ctx) & SFRAME_F_FDE_FUNC_START_PCREL)
func_start_pc_vma += sframe_decoder_get_offsetof_fde_start_addr (sfd_ctx,
funcidx,
NULL);
/* Mark FDEs with [m] where the FRE start address is interpreted as a
mask. */
int fde_type_addrmask_p = (SFRAME_V1_FUNC_FDE_TYPE (func_info)
== SFRAME_FDE_TYPE_PCMASK);
const char *fde_type_marker
= (fde_type_addrmask_p ? "[m]" : " ");
printf ("\n func idx [%d]: pc = 0x%"PRIx64 ", size = %d bytes",
funcidx,
func_start_pc_vma,
func_size);
if (is_sframe_abi_arch_aarch64 (sfd_ctx)
&& (SFRAME_V1_FUNC_PAUTH_KEY (func_info) == SFRAME_AARCH64_PAUTH_KEY_B))
printf (", pauth = B key");
char temp[100];
printf ("\n %-7s%-8s %-10s%-10s%-13s",
"STARTPC", fde_type_marker, "CFA", "FP", "RA");
for (j = 0; j < num_fres; j++)
{
sframe_decoder_get_fre (sfd_ctx, funcidx, j, &fre);
fre_start_pc_vma = (fde_type_addrmask_p
? fre.fre_start_addr
: func_start_pc_vma + fre.fre_start_addr);
/* FIXME - fixup the err caching in array.
assert no error for base reg id. */
base_reg_id = sframe_fre_get_base_reg_id (&fre, &err[0]);
cfa_offset = sframe_fre_get_cfa_offset (sfd_ctx, &fre, &err[0]);
fp_offset = sframe_fre_get_fp_offset (sfd_ctx, &fre, &err[1]);
ra_offset = sframe_fre_get_ra_offset (sfd_ctx, &fre, &err[2]);
/* Dump CFA info. */
printf ("\n");
printf (" %016"PRIx64, fre_start_pc_vma);
sprintf (temp, "%s+%d", base_reg_str[base_reg_id], cfa_offset);
printf (" %-10s", temp);
/* Dump SP/FP info. */
if (err[1] == 0)
sprintf (temp, "c%+d", fp_offset);
else
strcpy (temp, "u");
printf ("%-10s", temp);
/* Dump RA info.
If an ABI does not track RA offset, e.g., AMD64, display 'f',
else display the offset d as 'c+-d'. */
if (sframe_decoder_get_fixed_ra_offset (sfd_ctx)
!= SFRAME_CFA_FIXED_RA_INVALID)
strcpy (temp, "f");
else if (err[2] == 0)
sprintf (temp, "c%+d", ra_offset);
else
strcpy (temp, "u");
/* Mark SFrame FRE's RA information with "[s]" if the RA is mangled
with signature bits. */
const char *ra_mangled_p_str
= ((sframe_fre_get_ra_mangled_p (sfd_ctx, &fre, &err[2]))
? "[s]" : " ");
strcat (temp, ra_mangled_p_str);
printf ("%-13s", temp);
}
}
static void
dump_sframe_functions (sframe_decoder_ctx *sfd_ctx, uint64_t sec_addr)
{
uint32_t i;
uint32_t num_fdes;
const char* subsec_name = "Function Index";
printf ("\n %s :\n", subsec_name);
num_fdes = sframe_decoder_get_num_fidx (sfd_ctx);
for (i = 0; i < num_fdes; i++)
{
dump_sframe_func_with_fres (sfd_ctx, i, sec_addr);
printf ("\n");
}
}
void
dump_sframe (sframe_decoder_ctx *sfd_ctx, uint64_t sec_addr)
{
uint8_t ver;
dump_sframe_header (sfd_ctx);
ver = sframe_decoder_get_version (sfd_ctx);
if (ver == SFRAME_VERSION)
dump_sframe_functions (sfd_ctx, sec_addr);
else
printf ("\n No further information can be displayed. %s",
"SFrame version not supported\n");
}