-*- text -*-
+* SFrame stack trace format now represents an undefined return address as
+ an SFrame FRE without any offsets. libsframe provides a new API to test
+ for RA undefined, which is used when dumping SFrame information (e.g. using
+ objdump and readelf) to show such FREs as "RA undefined".
+
* Add --got-contents option to readelf to display the contents of
Global Offset Table (GOT) sections.
-*- text -*-
+* Emit an SFrame FRE with zero offsets to convey an undefined return address
+ in the SFrame stack trace format.
+
* ELF targets can now have section entity size specified for arbitrary
sections, using the new attribute letter 'E'.
{
fre->ra_loc = SFRAME_FRE_ELEM_LOC_STACK;
fre->ra_offset = ra_offset;
+ fre->ra_undefined_p = false;
fre->merge_candidate = false;
}
initialize it in sframe_row_entry_initialize () with the sticky
bit if set. */
fre->mangled_ra_p = false;
+ /* Reset the RA undefined status by to zero by default. */
+ fre->ra_undefined_p = false;
return fre;
}
unsigned int fre_num_offsets;
unsigned int fre_offset_size;
unsigned int fre_base_reg;
+ bool fre_mangled_ra_p;
expressionS exp;
unsigned int fre_addr_size;
}
/* Create the fre_info using the CFA base register, number of offsets and max
- size of offset in this frame row entry. */
- fre_base_reg = get_fre_base_reg_id (sframe_fre);
- fre_num_offsets = get_fre_num_offsets (sframe_fre);
- fre_offset_size = sframe_get_fre_offset_size (sframe_fre);
+ size of offset in this frame row entry. Represent RA undefined as FRE
+ without any offsets and all FRE info word fields zeroed. */
+ if (sframe_fre->ra_undefined_p)
+ {
+ fre_base_reg = 0;
+ fre_num_offsets = 0;
+ fre_offset_size = 0;
+ fre_mangled_ra_p = 0;
+ }
+ else
+ {
+ fre_base_reg = get_fre_base_reg_id (sframe_fre);
+ fre_num_offsets = get_fre_num_offsets (sframe_fre);
+ fre_offset_size = sframe_get_fre_offset_size (sframe_fre);
+ fre_mangled_ra_p = sframe_fre->mangled_ra_p;
+ }
fre_info = sframe_set_fre_info (fre_base_reg, fre_num_offsets,
- fre_offset_size, sframe_fre->mangled_ra_p);
+ fre_offset_size, fre_mangled_ra_p);
out_one (fre_info);
+ /* Represent RA undefined as FRE without any offsets. */
+ if (sframe_fre->ra_undefined_p)
+ return;
+
idx = sframe_fre_offset_func_map_index (fre_offset_size);
gas_assert (idx < SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_MAX);
/* Treat RA mangling as a sticky bit. It retains its value until another
.cfi_negate_ra_state is seen. */
cur_fre->mangled_ra_p = prev_fre->mangled_ra_p;
+ /* Treat RA undefined as a sticky bit. It retains its value until a
+ .cfi_offset RA, .cfi_register RA, .cfi_restore RA, or .cfi_same_value RA
+ is seen. */
+ cur_fre->ra_undefined_p = prev_fre->ra_undefined_p;
}
/* Return SFrame register name for SP, FP, and RA, or NULL if other. */
gas_assert (cur_fre);
cur_fre->ra_loc = cie_fre->ra_loc;
cur_fre->ra_offset = cie_fre->ra_offset;
+ cur_fre->ra_undefined_p = cie_fre->ra_undefined_p;
cur_fre->merge_candidate = false;
}
return SFRAME_XLATE_OK;
/* Translate DW_CFA_undefined into SFrame context.
DW_CFA_undefined op indicates that from now on, the previous value of
- register can’t be restored anymore. In SFrame stack trace, we cannot
- represent such a semantic. So, we skip generating an SFrame FDE for this,
- when a register of interest is used with DW_CFA_undefined.
+ register can’t be restored anymore. In DWARF, for the return address (RA)
+ register, this indicates to an unwinder that there is no return address
+ and the unwind is complete.
+
+ In SFrame, represent the use of the RA register with DW_CFA_undefined as
+ SFrame FRE without any offsets. Stack tracers can use this as indication
+ that an outermost frame has been reached and the stack trace is complete.
+ The use of other registers of interest with DW_CFA_undefined cannot be
+ represented in SFrame. Therefore skip generating an SFrame FDE.
Return SFRAME_XLATE_OK if success. */
const struct cfi_insn_data *cfi_insn)
{
if (cfi_insn->u.r == SFRAME_CFA_FP_REG
- || cfi_insn->u.r == SFRAME_CFA_RA_REG
|| cfi_insn->u.r == SFRAME_CFA_SP_REG)
{
as_warn (_("no SFrame FDE emitted; %s reg %u in .cfi_undefined"),
sframe_register_name (cfi_insn->u.r), cfi_insn->u.r);
return SFRAME_XLATE_ERR_NOTREPRESENTED; /* Not represented. */
}
+ else if (cfi_insn->u.r == SFRAME_CFA_RA_REG)
+ {
+ /* Represent RA undefined (i.e. outermost frame) as FRE without any
+ offsets. */
+ struct sframe_row_entry *cur_fre = xlate_ctx->cur_fre;
+
+ gas_assert (cur_fre);
+ /* Set RA undefined status bit. */
+ cur_fre->ra_undefined_p = true;
+ cur_fre->merge_candidate = false;
+ }
- /* Safe to skip. */
return SFRAME_XLATE_OK;
}
{
cur_fre->ra_loc = SFRAME_FRE_ELEM_LOC_REG;
cur_fre->ra_offset = 0;
+ cur_fre->ra_undefined_p = false;
cur_fre->merge_candidate = false;
}
else if (cfi_insn->u.r == SFRAME_CFA_FP_REG)
/* Whether the return address is mangled with pauth code. */
bool mangled_ra_p;
+ /* Whether RA is undefined. */
+ bool ra_undefined_p;
+
/* Track CFA base (architectural) register ID. */
unsigned int cfa_base_reg;
/* Offset from the CFA base register for recovering CFA. */
--- /dev/null
+#as: --gsframe
+#objdump: --sframe=.sframe
+#name: SFrame generation on aarch64 - .cfi_undefined RA
+#...
+Contents of the SFrame section .sframe:
+ Header :
+
+ Version: SFRAME_VERSION_2
+ Flags: SFRAME_F_FDE_FUNC_START_PCREL
+ Num FDEs: 1
+ Num FREs: 4
+
+ Function Index :
+
+ func idx \[0\]: pc = 0x0, size = 16 bytes
+ STARTPC +CFA +FP +RA +
+ 0+0000 +sp\+0 +u +u +
+ 0+0004 +sp\+16 +c\-16 +c\-8 +
+ 0+0008 +RA undefined
+ 0+000c +RA undefined
--- /dev/null
+ .cfi_startproc
+ stp fp, lr, [sp, #-16]!
+ .cfi_def_cfa_offset 16
+ .cfi_offset 29, -16
+ .cfi_offset 30, -8
+ nop
+ .cfi_undefined 30
+ ldp fp, lr, [sp], #16
+ .cfi_restore 20
+ .cfi_restore 19
+ .cfi_def_cfa_offset 0
+ ret lr
+ .cfi_endproc
--- /dev/null
+#name: SFrame generation on s390x - .cfi_undefined RA
+#as: --gsframe
+#objdump: --sframe=.sframe
+#...
+Contents of the SFrame section .sframe:
+
+ Header :
+
+ Version: SFRAME_VERSION_2
+ Flags: SFRAME_F_FDE_FUNC_START_PCREL
+ Num FDEs: 1
+ Num FREs: 4
+
+ Function Index :
+
+ func idx \[0\]: pc = 0x0, size = 18 bytes
+ STARTPC +CFA +FP +RA +
+ 0+0000 +sp\+160 +u +u +
+ 0+0006 +sp\+160 +u +c\-48 +
+ 0+000a +RA undefined
+ 0+0010 +sp\+160 +u +u +
--- /dev/null
+ .cfi_startproc
+ stmg %r14,%r15,112(%r15)
+ .cfi_offset 14, -48
+ .cfi_offset 15, -40
+ nop
+ .cfi_undefined 14
+ lmg %r14,%r15,112(%r15)
+ .cfi_restore 15
+ .cfi_restore 14
+ br %r14
+ .cfi_endproc
--- /dev/null
+#as: --gsframe -O0
+#objdump: --sframe=.sframe
+#name: SFrame generation on x86_64 - .cfi_undefined RA
+#...
+Contents of the SFrame section .sframe:
+
+ Header :
+
+ Version: SFRAME_VERSION_2
+ Flags: SFRAME_F_FDE_FUNC_START_PCREL
+ CFA fixed RA offset: \-8
+ Num FDEs: 1
+ Num FREs: 4
+
+ Function Index :
+
+ func idx \[0\]: pc = 0x0, size = 6 bytes
+ STARTPC +CFA +FP +RA +
+ 0+0000 +sp\+8 +u +f +
+ 0+0001 +sp\+16 +c\-16 +f +
+ 0+0004 +fp\+16 +c\-16 +f +
+ 0+0005 +RA undefined
--- /dev/null
+ .cfi_startproc
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset 6, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register 6
+ nop
+ .cfi_undefined 16
+ .cfi_def_cfa 7, 8
+ ret
+ .cfi_endproc
run_dump_test "cfi-sframe-x86_64-empty-2"
run_dump_test "cfi-sframe-x86_64-empty-3"
run_dump_test "cfi-sframe-x86_64-empty-4"
+ run_dump_test "cfi-sframe-x86_64-ra-undefined-1"
set ASFLAGS "$old_ASFLAGS"
}
}
run_dump_test "cfi-sframe-aarch64-3"
run_dump_test "cfi-sframe-aarch64-4"
run_dump_test "cfi-sframe-aarch64-pac-ab-key-1"
+ run_dump_test "cfi-sframe-aarch64-ra-undefined-1"
}
# s390x specific tests
run_dump_test "cfi-sframe-s390x-fpra-offset-2"
run_dump_test "cfi-sframe-s390x-fpra-register-1"
run_dump_test "cfi-sframe-s390x-fpra-register-2"
+ run_dump_test "cfi-sframe-s390x-ra-undefined-1"
}
#define SFRAME_V1_FRE_OFFSET_COUNT(data) (((data) >> 1) & 0xf)
#define SFRAME_V1_FRE_OFFSET_SIZE(data) (((data) >> 5) & 0x3)
#define SFRAME_V1_FRE_MANGLED_RA_P(data) (((data) >> 7) & 0x1)
+#define SFRAME_V2_FRE_RA_UNDEFINED_P(data) (SFRAME_V1_FRE_OFFSET_COUNT (data) == 0)
/* SFrame Frame Row Entry definitions.
@end itemize
@item
[Errata 1] An ELF SFrame section has the type SHT_GNU_SFRAME.
+@item
+[Errata 2] An SFrame FRE info word offset count of zero indicates that the
+return address (RA) is undefined for the range of PCs covered by the SFrame FRE.
+A stack tracer may use this as indication that an outermost frame has been
+reached and the stack trace is complete.
@end itemize
SFrame version 1 is now obsolete and should not be used.
@item 1-4
@tab @code{fre_offset_count}
@tab A max value of 15 is allowed. Typically, a value of upto 3 is sufficient
-for most ABIs to track all three of CFA, FP and RA.
+for most ABIs to track all three of CFA, FP and RA. A value of zero indicates
+that the return address (RA) is undefined. A stack tracer may use this as
+indication that an outermost frame has been reached and the stack trace is
+complete.
@item 0
@tab @code{fre_cfa_base_reg_id}
uint64_t func_start_pc_vma = 0;
uint64_t fre_start_pc_vma = 0;
const char *base_reg_str[] = {"fp", "sp"};
+ bool ra_undefined_p = false;
int32_t cfa_offset = 0;
int32_t fp_offset = 0;
int32_t ra_offset = 0;
: func_start_pc_vma + fre.fre_start_addr);
/* FIXME - fixup the err caching in array.
- assert no error for base reg id. */
+ assert no error for base reg id and RA undefined. */
base_reg_id = sframe_fre_get_base_reg_id (&fre, &err[0]);
+ ra_undefined_p = sframe_fre_get_ra_undefined_p (sfd_ctx, &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. */
+ /* Dump VMA. */
printf ("\n");
printf (" %016"PRIx64, fre_start_pc_vma);
+
+ /* Dump RA undefined (FRE without any offsets). */
+ if (ra_undefined_p)
+ {
+ printf (" RA undefined");
+ continue;
+ }
+
+ /* Dump CFA info. */
sprintf (temp, "%s+%d", base_reg_str[base_reg_id], cfa_offset);
printf (" %-10s", temp);
static bool
sframe_get_fre_ra_undefined_p (uint8_t fre_info)
{
- return SFRAME_V1_FRE_OFFSET_COUNT (fre_info) == 0;
+ return SFRAME_V2_FRE_RA_UNDEFINED_P (fre_info);
}
/* Access functions for info from function descriptor entry. */
int8_t fp_offset = sframe_decoder_get_fixed_fp_offset (dctx);
/* If the FP offset is not being tracked, return the fixed FP offset
from the SFrame header. */
- if (fp_offset != SFRAME_CFA_FIXED_FP_INVALID)
+ if (fp_offset != SFRAME_CFA_FIXED_FP_INVALID
+ && !sframe_get_fre_ra_undefined_p (fre->fre_info))
{
if (errp)
*errp = 0;
int8_t ra_offset = sframe_decoder_get_fixed_ra_offset (dctx);
/* If the RA offset was not being tracked, return the fixed RA offset
from the SFrame header. */
- if (ra_offset != SFRAME_CFA_FIXED_RA_INVALID)
+ if (ra_offset != SFRAME_CFA_FIXED_RA_INVALID
+ && !sframe_get_fre_ra_undefined_p (fre->fre_info))
{
if (errp)
*errp = 0;