|
|
|
|
/* sframe-dump.c - Textual dump of .sframe.
|
|
|
|
|
|
|
|
|
|
Copyright (C) 2022-2024 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"
|
|
|
|
|
|
|
|
|
|
#define SFRAME_HEADER_FLAGS_STR_MAX_LEN 50
|
|
|
|
|
|
|
|
|
|
/* 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 (sframe_decoder_ctx *sfd_ctx)
|
|
|
|
|
{
|
|
|
|
|
uint8_t ver;
|
|
|
|
|
uint8_t flags;
|
|
|
|
|
char *flags_str;
|
|
|
|
|
const char *ver_str = NULL;
|
|
|
|
|
const sframe_header *header = &(sfd_ctx->sfd_header);
|
|
|
|
|
|
|
|
|
|
/* Prepare SFrame section version string. */
|
|
|
|
|
const char *version_names[]
|
|
|
|
|
= { "NULL",
|
|
|
|
|
"SFRAME_VERSION_1",
|
|
|
|
|
"SFRAME_VERSION_2" };
|
|
|
|
|
|
|
|
|
|
/* PS: Keep SFRAME_HEADER_FLAGS_STR_MAX_LEN in sync if adding more members to
|
|
|
|
|
this array. */
|
|
|
|
|
const char *flag_names[]
|
|
|
|
|
= { "SFRAME_F_FDE_SORTED",
|
|
|
|
|
"SFRAME_F_FRAME_POINTER" };
|
|
|
|
|
|
|
|
|
|
ver = sframe_decoder_get_version (sfd_ctx);
|
|
|
|
|
if (ver <= SFRAME_VERSION)
|
|
|
|
|
ver_str = version_names[ver];
|
|
|
|
|
|
|
|
|
|
/* Prepare SFrame section flags string. */
|
|
|
|
|
flags = header->sfh_preamble.sfp_flags;
|
|
|
|
|
flags_str = (char*) calloc (sizeof (char), SFRAME_HEADER_FLAGS_STR_MAX_LEN);
|
|
|
|
|
if (flags)
|
|
|
|
|
{
|
|
|
|
|
if (flags & SFRAME_F_FDE_SORTED)
|
|
|
|
|
strcpy (flags_str, flag_names[0]);
|
|
|
|
|
if (flags & SFRAME_F_FRAME_POINTER)
|
|
|
|
|
{
|
|
|
|
|
if (strlen (flags_str) > 0)
|
|
|
|
|
strcpy (flags_str, ",");
|
|
|
|
|
strcpy (flags_str, flag_names[1]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
strcpy (flags_str, "NONE");
|
|
|
|
|
|
|
|
|
|
const char* subsec_name = "Header";
|
|
|
|
|
printf ("\n");
|
|
|
|
|
printf (" %s :\n", subsec_name);
|
|
|
|
|
printf ("\n");
|
|
|
|
|
printf (" Version: %s\n", ver_str);
|
|
|
|
|
printf (" Flags: %s\n", flags_str);
|
|
|
|
|
printf (" Num FDEs: %d\n", sframe_decoder_get_num_fidx (sfd_ctx));
|
|
|
|
|
printf (" Num FREs: %d\n", header->sfh_num_fres);
|
|
|
|
|
|
|
|
|
|
free (flags_str);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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. */
|
|
|
|
|
func_start_pc_vma = func_start_address + sec_addr;
|
|
|
|
|
|
|
|
|
|
/* 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);
|
|
|
|
|
|
libsframe: update the semantics of sframe_fre_get_ra_offset
Until now, sframe_fre_get_ra_offset () would return
SFRAME_ERR_FREOFFSET_NOPRESENT if the ABI uses fixed RA offset (e.g.,
AMD64). A stack tracer, then, will call an explicit
sframe_decoder_get_fixed_ra_offset () to get the RA offset.
On second look, it appears to make sense to hide these details of
whether the RA offset is fixed or not from the consumer. Now, with the
changed semantics, the call to sframe_fre_get_ra_offset () will fetch
the fixed RA offset if applicable, or get the RA offset from FRE when
there is no fixed RA offset.
Adjustments need to be made to ensure the textual dump remains the same
as preivous. Currently, e.g., if RA is not being tracked per FRE,
following is seen with objdump --sframe:
STARTPC CFA FP RA
000000000000NNNN sp+X u u
This patch changes the behavior of sframe_fre_get_ra_offset: it turns an
error into non-error. This change will be included with the next
release of libsframe, where all exposed symbols will be versioned for
the first time.
libsframe/
* sframe.c (sframe_fre_get_ra_offset): Return the fixed offset,
if applicable. Else return the RA offset from the FRE.
* sframe-dump.c (dump_sframe_func_with_fres): Make adjustments
to keep the textual dump same as previous.
3 years ago
|
|
|
/* Dump RA info.
|
|
|
|
|
If an ABI does not track RA offset, e.g., AMD64, display a 'u',
|
|
|
|
|
else display the offset d as 'c+-d'. */
|
|
|
|
|
if (sframe_decoder_get_fixed_ra_offset(sfd_ctx)
|
|
|
|
|
!= SFRAME_CFA_FIXED_RA_INVALID)
|
|
|
|
|
strcpy (temp, "u");
|
libsframe: update the semantics of sframe_fre_get_ra_offset
Until now, sframe_fre_get_ra_offset () would return
SFRAME_ERR_FREOFFSET_NOPRESENT if the ABI uses fixed RA offset (e.g.,
AMD64). A stack tracer, then, will call an explicit
sframe_decoder_get_fixed_ra_offset () to get the RA offset.
On second look, it appears to make sense to hide these details of
whether the RA offset is fixed or not from the consumer. Now, with the
changed semantics, the call to sframe_fre_get_ra_offset () will fetch
the fixed RA offset if applicable, or get the RA offset from FRE when
there is no fixed RA offset.
Adjustments need to be made to ensure the textual dump remains the same
as preivous. Currently, e.g., if RA is not being tracked per FRE,
following is seen with objdump --sframe:
STARTPC CFA FP RA
000000000000NNNN sp+X u u
This patch changes the behavior of sframe_fre_get_ra_offset: it turns an
error into non-error. This change will be included with the next
release of libsframe, where all exposed symbols will be versioned for
the first time.
libsframe/
* sframe.c (sframe_fre_get_ra_offset): Return the fixed offset,
if applicable. Else return the RA offset from the FRE.
* sframe-dump.c (dump_sframe_func_with_fres): Make adjustments
to keep the textual dump same as previous.
3 years ago
|
|
|
else if (err[2] == 0)
|
|
|
|
|
sprintf (temp, "c%+d", ra_offset);
|
|
|
|
|
|
|
|
|
|
/* 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");
|
|
|
|
|
}
|