#include "sframe-internal.h"
#include "gen-sframe.h"
#include "dw2gencfi.h"
+#include "leb128.h"
#ifdef support_sframe_p
sframe_fre_set_begin_addr (cur_fre,
get_dw_fde_start_addrS (xlate_ctx->dw_fde));
}
- /* Define the current CFA rule to use the provided register and
- offset. However, if the register is not FP/SP, skip creating
- SFrame stack trace info for the function. */
- if (cfi_insn->u.ri.reg != SFRAME_CFA_SP_REG
- && cfi_insn->u.ri.reg != SFRAME_CFA_FP_REG)
- {
- as_warn (_("no SFrame FDE emitted; "
- "non-SP/FP register %u in .cfi_def_cfa"),
- cfi_insn->u.ri.reg);
- return SFRAME_XLATE_ERR_NOTREPRESENTED; /* Not represented. */
- }
- else if (sframe_fre_stack_offset_bound_p (cfi_insn->u.ri.offset, true))
- {
- sframe_fre_set_cfa_base_reg (cur_fre, cfi_insn->u.ri.reg);
- sframe_fre_set_cfa_offset (cur_fre, cfi_insn->u.ri.offset);
- cur_fre->merge_candidate = false;
- }
- else
+
+ offsetT offset = cfi_insn->u.ri.offset;
+ bool bound_p = sframe_fre_stack_offset_bound_p (offset, true);
+ if (!bound_p)
{
as_warn (_("no SFrame FDE emitted; "
".cfi_def_cfa with unsupported offset value"));
return SFRAME_XLATE_ERR_NOTREPRESENTED;
}
+ /* Define the current CFA rule to use the provided register and
+ offset. Typically, the CFA rule uses SP/FP based CFA. However, with
+ SFrame V3 specification, if the CFA register is not FP/SP, SFrame FDE type
+ SFRAME_FDE_TYPE_FLEX type may be used.
+
+ GAS uses the hook sframe_support_flex_fde_p () to determine if SFrame FDE
+ of type SFRAME_FDE_TYPE_FLEX can be emitted for the specific target.
+ Non-SP/FP based CFA may be seen for:
+ - AMD64 (e.g., DRAP, stack alignment), or
+ - s390x, where this may be seen for (GCC) generated code for static stack
+ clash protection. */
+ if (cfi_insn->u.ri.reg != SFRAME_CFA_SP_REG
+ && cfi_insn->u.ri.reg != SFRAME_CFA_FP_REG)
+ {
+ if (!sframe_support_flex_fde_p ())
+ {
+ as_warn (_("no SFrame FDE emitted; "
+ "non-SP/FP register %u in .cfi_def_cfa"),
+ cfi_insn->u.ri.reg);
+ return SFRAME_XLATE_ERR_NOTREPRESENTED;
+ }
+ else
+ xlate_ctx->flex_p = true;
+ }
+
+ sframe_fre_set_cfa_base_reg (cur_fre, cfi_insn->u.ri.reg);
+ sframe_fre_set_cfa_offset (cur_fre, cfi_insn->u.ri.offset);
+ cur_fre->merge_candidate = false;
+ cur_fre->cfa_deref_p = false;
+
return SFRAME_XLATE_OK;
}
if (cfi_insn->u.r != SFRAME_CFA_SP_REG
&& cfi_insn->u.r != SFRAME_CFA_FP_REG)
{
- as_warn (_("no SFrame FDE emitted; "
- "non-SP/FP register %u in .cfi_def_cfa_register"),
- cfi_insn->u.r);
- return SFRAME_XLATE_ERR_NOTREPRESENTED; /* Not represented. */
+ if (!sframe_support_flex_fde_p ())
+ {
+ as_warn (_("no SFrame FDE emitted; "
+ "non-SP/FP register %u in .cfi_def_cfa_register"),
+ cfi_insn->u.ri.reg);
+ return SFRAME_XLATE_ERR_NOTREPRESENTED;
+ }
+ else
+ /* Currently, SFRAME_FDE_TYPE_FLEX is generated for AMD64 only. */
+ xlate_ctx->flex_p = true;
}
+
sframe_fre_set_cfa_base_reg (cur_fre, cfi_insn->u.r);
if (last_fre)
sframe_fre_set_cfa_offset (cur_fre, sframe_fre_get_cfa_offset (last_fre));
+ cur_fre->cfa_deref_p = false;
cur_fre->merge_candidate = false;
return SFRAME_XLATE_ERR_NOTREPRESENTED; /* Not represented. */
}
+/* Translate a DWARF sleb128 offset in the CFI escape data E to an offsetT. */
+
+static offsetT
+sframe_xlate_escape_sleb128_to_offsetT (const struct cfi_escape_data *e)
+{
+ offsetT offset;
+
+ gas_assert (e->type == CFI_ESC_byte || e->type == CFI_ESC_sleb128);
+ /* Read the offset. */
+ if (e->type == CFI_ESC_byte)
+ {
+ /* The user/compiler may provide an sleb128 encoded data of a single byte
+ length (DWARF offset of DW_OP_bregN is sleb128). On a big-endian
+ host, the endianness of data itself needs to be accommodated then. To
+ keep it simple, gather the LSB, and translate it to int64. */
+ unsigned char sleb_data = e->exp.X_add_number & 0xff;
+ const unsigned char *buf_start = (const unsigned char *)&sleb_data;
+ const unsigned char *buf_end = buf_start + 1;
+ int64_t value = 0;
+ size_t read = read_sleb128_to_int64 (buf_start, buf_end, &value);
+ gas_assert (read);
+ offset = (offsetT) value;
+ }
+ else
+ /* offset must be CFI_ESC_sleb128. */
+ offset = e->exp.X_add_number;
+
+ return offset;
+}
+
+/* Handle DW_CFA_def_cfa_expression in .cfi_escape.
+
+ As with sframe_xlate_do_cfi_escape, the intent of this function is to detect
+ only the simple-to-process but common cases. All other CFA escape
+ expressions continue to be inadmissible (no SFrame FDE emitted).
+
+ Sets CALLER_WARN_P for skipped cases (and returns SFRAME_XLATE_OK) where the
+ caller must warn. The caller then must also set
+ SFRAME_XLATE_ERR_NOTREPRESENTED for their callers. */
+
+static int
+sframe_xlate_do_escape_cfa_expr (struct sframe_xlate_ctx *xlate_ctx,
+ const struct cfi_insn_data *cfi_insn,
+ bool *caller_warn_p)
+{
+ const struct cfi_escape_data *e = cfi_insn->u.esc;
+ const struct cfi_escape_data *e_offset = NULL;
+ int err = SFRAME_XLATE_OK;
+ unsigned int opcode1, opcode2;
+ offsetT offset;
+ unsigned int reg = SFRAME_FRE_REG_INVALID;
+ unsigned int i = 0;
+ bool x86_cfa_deref_p = false;
+
+ /* Check roughly for an expression like so:
+ DW_CFA_def_cfa_expression (DW_OP_breg6 (rbp): -8; DW_OP_deref). */
+#define CFI_ESC_NUM_EXP 4
+ offsetT items[CFI_ESC_NUM_EXP] = {0};
+ while (e->next)
+ {
+ e = e->next;
+ /* Bounds check, must be constant, no relocs. */
+ if (i >= CFI_ESC_NUM_EXP
+ || e->exp.X_op != O_constant
+ || e->reloc != TC_PARSE_CONS_RETURN_NONE)
+ goto warn_and_exit;
+ /* Other checks based on index i.
+ - For item[2], allow byte OR sleb128.
+ - items at index 0, 1, and 3: Must be byte. */
+ if (i == 2 && (e->type != CFI_ESC_byte && e->type != CFI_ESC_sleb128))
+ goto warn_and_exit;
+ else if (i != 2 && e->type != CFI_ESC_byte)
+ goto warn_and_exit;
+ /* Block length (items[0]) of 3 in DWARF expr. */
+ if (i == 1 && items[0] != 3)
+ goto warn_and_exit;
+
+ if (i == 2)
+ e_offset = e;
+
+ items[i] = e->exp.X_add_number;
+ i++;
+ }
+
+ if (i != CFI_ESC_NUM_EXP)
+ goto warn_and_exit;
+#undef CFI_ESC_NUM_EXP
+
+ opcode1 = items[1];
+ opcode2 = items[3];
+ /* DW_OP_breg6 is rbp. FIXME - this stub can be enhanced to handle more
+ regs. */
+ if (sframe_get_abi_arch () == SFRAME_ABI_AMD64_ENDIAN_LITTLE
+ && sframe_support_flex_fde_p ()
+ && opcode1 == DW_OP_breg6 && opcode2 == DW_OP_deref)
+ {
+ x86_cfa_deref_p = true;
+ reg = SFRAME_CFA_FP_REG;
+ }
+
+ offset = sframe_xlate_escape_sleb128_to_offsetT (e_offset);
+
+ struct sframe_row_entry *cur_fre = xlate_ctx->cur_fre;
+ gas_assert (cur_fre);
+
+ /* Handle the specific CFA expression mentioned above. */
+ if (x86_cfa_deref_p
+ && sframe_fre_stack_offset_bound_p (offset, false)
+ && reg != SFRAME_FRE_REG_INVALID)
+ {
+ xlate_ctx->flex_p = true;
+ sframe_fre_set_cfa_base_reg (cur_fre, reg);
+ sframe_fre_set_cfa_offset (cur_fre, offset);
+ cur_fre->cfa_deref_p = true;
+ cur_fre->merge_candidate = false;
+ /* Done handling here. */
+ caller_warn_p = false;
+
+ return err;
+ }
+ /* Any other CFA expression may not be safe to skip. Fall through to
+ warn_and_exit. */
+
+warn_and_exit:
+ *caller_warn_p = true;
+ return err;
+}
+
/* Handle DW_CFA_expression in .cfi_escape.
As with sframe_xlate_do_cfi_escape, the intent of this function is to detect
SFRAME_XLATE_ERR_NOTREPRESENTED for their callers. */
static int
-sframe_xlate_do_escape_expr (const struct sframe_xlate_ctx *xlate_ctx,
+sframe_xlate_do_escape_expr (struct sframe_xlate_ctx *xlate_ctx,
const struct cfi_insn_data *cfi_insn,
bool *caller_warn_p)
{
const struct cfi_escape_data *e = cfi_insn->u.esc;
+ const struct cfi_escape_data *e_offset = NULL;
int err = SFRAME_XLATE_OK;
- unsigned int reg = 0;
+ offsetT offset;
unsigned int i = 0;
/* Check roughly for an expression
while (e->next)
{
e = e->next;
- if ((i == 2 && (items[1] != 2)) /* Expected len of 2 in DWARF expr. */
- /* We do not care for the exact values of items[2] and items[3],
- so an explicit check for O_constant isnt necessary either. */
- || i >= CFI_ESC_NUM_EXP
- || (i < 2
- && (e->exp.X_op != O_constant
- || e->type != CFI_ESC_byte
- || e->reloc != TC_PARSE_CONS_RETURN_NONE)))
+ /* Bounds check, must be constant, no relocs. */
+ if (i >= CFI_ESC_NUM_EXP
+ || e->exp.X_op != O_constant
+ || e->reloc != TC_PARSE_CONS_RETURN_NONE)
+ goto warn_and_exit;
+ /* Other checks based on index i.
+ - For item[3], allow byte OR sleb128.
+ - items at index 0, 1, and 2: Must be byte. */
+ if (i == 3 && (e->type != CFI_ESC_byte && e->type != CFI_ESC_sleb128))
goto warn_and_exit;
+ else if (i != 3 && e->type != CFI_ESC_byte)
+ goto warn_and_exit;
+ /* Block length (items[1]) of 2 in DWARF expr. */
+ if (i == 2 && items[1] != 2)
+ goto warn_and_exit;
+
+ if (i == 3)
+ e_offset = e;
+
items[i] = e->exp.X_add_number;
i++;
}
if (i <= CFI_ESC_NUM_EXP - 1)
goto warn_and_exit;
+#undef CFI_ESC_NUM_EXP
/* reg operand to DW_CFA_expression is ULEB128. For the purpose at hand,
however, the register value will be less than 128 (CFI_ESC_NUM_EXP set
to 4). See an extended comment in sframe_xlate_do_escape_expr for why
reading ULEB is okay to skip without sacrificing correctness. */
- reg = items[0];
-#undef CFI_ESC_NUM_EXP
+ unsigned int reg = items[0];
- if (reg == SFRAME_CFA_SP_REG || reg == SFRAME_CFA_FP_REG
- || (sframe_ra_tracking_p () && reg == SFRAME_CFA_RA_REG)
- || reg == sframe_xlate_ctx_get_cur_cfa_reg (xlate_ctx))
+ unsigned opcode = items[2];
+ unsigned int fp_base_reg = SFRAME_FRE_REG_INVALID;
+ bool x86_fp_deref_p = true;
+
+ if (sframe_get_abi_arch () == SFRAME_ABI_AMD64_ENDIAN_LITTLE
+ && sframe_support_flex_fde_p ()
+ && opcode == DW_OP_breg6)
+ {
+ x86_fp_deref_p = true;
+ fp_base_reg = SFRAME_CFA_FP_REG;
+ }
+
+ offset = sframe_xlate_escape_sleb128_to_offsetT (e_offset);
+
+ struct sframe_row_entry *cur_fre = xlate_ctx->cur_fre;
+ gas_assert (cur_fre);
+
+ if (x86_fp_deref_p
+ && reg == SFRAME_CFA_FP_REG
+ && sframe_fre_stack_offset_bound_p (offset, false))
+ {
+ xlate_ctx->flex_p = true;
+ sframe_fre_set_fp_track (cur_fre, offset);
+ cur_fre->fp_loc = SFRAME_FRE_ELEM_LOC_REG;
+ cur_fre->fp_reg = fp_base_reg;
+ cur_fre->fp_deref_p = true;
+ cur_fre->merge_candidate = false;
+ }
+ else if (reg == SFRAME_CFA_SP_REG || reg == SFRAME_CFA_FP_REG
+ || (sframe_ra_tracking_p () && reg == SFRAME_CFA_RA_REG)
+ || reg == sframe_xlate_ctx_get_cur_cfa_reg (xlate_ctx))
{
as_warn (_("no SFrame FDE emitted; "
".cfi_escape DW_CFA_expression with %s reg %u"),
SFRAME_XLATE_OK if OK to skip. */
static int
-sframe_xlate_do_cfi_escape (const struct sframe_xlate_ctx *xlate_ctx,
+sframe_xlate_do_cfi_escape (struct sframe_xlate_ctx *xlate_ctx,
const struct cfi_insn_data *cfi_insn)
{
const struct cfi_escape_data *e;
}
break;
+ case DW_CFA_def_cfa_expression:
+ err = sframe_xlate_do_escape_cfa_expr (xlate_ctx, cfi_insn, &warn_p);
+ break;
+
case DW_CFA_expression:
err = sframe_xlate_do_escape_expr (xlate_ctx, cfi_insn, &warn_p);
break;
if (err && get_dw_fde_signal_p (dw_fde))
{
sframe_xlate_ctx_cleanup (xlate_ctx);
+ xlate_ctx->flex_p = false;
err = SFRAME_XLATE_OK;
}