[DTrace-devel] [PATCH v3 03/19] cg: fault improvements

Nick Alcock nick.alcock at oracle.com
Thu Mar 24 18:24:29 UTC 2022


Add a label to dt_cg_probe_error, which is dropped on the first
instruction if set; eschew regset allocation on the grounds that
spilling to the stack is pointless when the regs will all be thrown away
in any case, and can trigger errors if some of the regs are not properly
initialized, as is relatively likely in an error condition.

Also add a new dt_cg_probe_error_regval, which is like dt_cg_probe_error
except the last argument is not an illegal value but a register whose
value should be emitted.  This is a bit trickier: we must move the
illegal register arg into place first because we don't use the regset
allocation code, so it might be using one of the regs we're about to
overwrite; and don't emit a move at all if we're by chance already using
that reg (the verifier doesn't like mov %r4, %r4 very much).

Signed-off-by: Nick Alcock <nick.alcock at oracle.com>
---
 libdtrace/dt_cg.c | 76 +++++++++++++++++++++++++++++++++++++----------
 1 file changed, 60 insertions(+), 16 deletions(-)

diff --git a/libdtrace/dt_cg.c b/libdtrace/dt_cg.c
index 5f86c34d7be4..ffbe5e95670f 100644
--- a/libdtrace/dt_cg.c
+++ b/libdtrace/dt_cg.c
@@ -647,14 +647,16 @@ dt_cg_epilogue(dt_pcb_t *pcb)
 }
 
 /*
- * Generate code for a fault condition.  A call is made to dt_probe_error() to
- * set the fault information.
+ * Generate code for a fault condition, possibly at a given label.  A call is
+ * made to dt_probe_error() to set the fault information.
+ *
+ * Doesn't use the regset code on the grounds that when this is executed we will
+ * never reuse any of the callers' regs in any case.
  */
 static void
-dt_cg_probe_error(dt_pcb_t *pcb, uint32_t fault, uint64_t illval)
+dt_cg_probe_error(dt_pcb_t *pcb, int lbl, uint32_t fault, uint64_t illval)
 {
 	dt_irlist_t	*dlp = &pcb->pcb_ir;
-	dt_regset_t	*drp = pcb->pcb_regs;
 	dt_ident_t	*idp;
 
 	/*
@@ -666,21 +668,63 @@ dt_cg_probe_error(dt_pcb_t *pcb, uint32_t fault, uint64_t illval)
 	 *				// call dt_probe_error
 	 *	return;			// exit
 	 */
-	if (dt_regset_xalloc_args(drp) == -1)
-		longjmp(pcb->pcb_jmpbuf, EDT_NOREG);
-	emit(dlp,  BPF_LOAD(BPF_DW, BPF_REG_1, BPF_REG_FP, DT_STK_DCTX));
+	emitl(dlp, lbl,
+		   BPF_LOAD(BPF_DW, BPF_REG_1, BPF_REG_FP, DT_STK_DCTX));
 	idp = dt_dlib_get_var(pcb->pcb_hdl, "PC");
 	assert(idp != NULL);
 	emite(dlp, BPF_MOV_IMM(BPF_REG_2, -1), idp);
 	emit(dlp,  BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, 4));
 	emit(dlp,  BPF_MOV_IMM(BPF_REG_3, fault));
 	emit(dlp,  BPF_MOV_IMM(BPF_REG_4, illval));
-	dt_regset_xalloc(drp, BPF_REG_0);
 	idp = dt_dlib_get_func(pcb->pcb_hdl, "dt_probe_error");
 	assert(idp != NULL);
 	emite(dlp, BPF_CALL_FUNC(idp->di_id), idp);
-	dt_regset_free_args(drp);
-	dt_regset_free(drp, BPF_REG_0);
+	emit(dlp,  BPF_RETURN());
+}
+
+/*
+ * Generate code for a fault condition with the illval coming from a register.
+ * Drop a label at the start.
+ *
+ * Doesn't use the regset code on the grounds that when this is executed we will
+ * only reuse one caller's reg in a stereotyped manner.
+ */
+static void
+dt_cg_probe_error_regval(dt_pcb_t *pcb, int lbl, uint32_t fault, int illreg)
+{
+	dt_irlist_t	*dlp = &pcb->pcb_ir;
+	dt_ident_t	*idp;
+
+	/*
+	 *	dt_probe_error(
+	 *		dctx,		// lddw %r1, %fp, DT_STK_DCTX
+	 *		PC,		// mov %r2, PC
+	 *		fault,		// mov %r3, fault
+	 *		illreg);	// mov %r4, %illreg
+	 *				// call dt_probe_error
+	 *	return;			// exit
+	 */
+
+	/*
+	 * Move the only pre-existing reg we need (illreg) into place first,
+	 * since we don't know which reg it is and it might perfectly well be
+	 * one we are about to blindly reuse.
+	 */
+
+	if (illreg != 4) {
+		emitl(dlp, lbl, BPF_MOV_REG(BPF_REG_4, illreg));
+		lbl = DT_LBL_NONE;
+	}
+	emitl(dlp, lbl,
+		   BPF_LOAD(BPF_DW, BPF_REG_1, BPF_REG_FP, DT_STK_DCTX));
+	idp = dt_dlib_get_var(pcb->pcb_hdl, "PC");
+	assert(idp != NULL);
+	emite(dlp, BPF_MOV_IMM(BPF_REG_2, -1), idp);
+	emit(dlp,  BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, 4));
+	emit(dlp,  BPF_MOV_IMM(BPF_REG_3, fault));
+	idp = dt_dlib_get_func(pcb->pcb_hdl, "dt_probe_error");
+	assert(idp != NULL);
+	emite(dlp, BPF_CALL_FUNC(idp->di_id), idp);
 	emit(dlp,  BPF_RETURN());
 }
 
@@ -694,7 +738,7 @@ dt_cg_check_notnull(dt_irlist_t *dlp, dt_regset_t *drp, int reg)
 	uint_t	lbl_notnull = dt_irlist_label(dlp);
 
 	emit(dlp,  BPF_BRANCH_IMM(BPF_JNE, reg, 0, lbl_notnull));
-	dt_cg_probe_error(yypcb, DTRACEFLT_BADADDR, 0);
+	dt_cg_probe_error(yypcb, DT_LBL_NONE, DTRACEFLT_BADADDR, 0);
 	emitl(dlp, lbl_notnull,
 		   BPF_NOP());
 }
@@ -1682,7 +1726,7 @@ dt_cg_act_stack(dt_pcb_t *pcb, dt_node_t *dnp, dtrace_actkind_t kind)
 	dt_regset_free_args(drp);
 	emit(dlp,  BPF_BRANCH_IMM(BPF_JSGE, BPF_REG_0, 0, lbl_valid));
 	dt_regset_free(drp, BPF_REG_0);
-	dt_cg_probe_error(pcb, DTRACEFLT_BADSTACK, 0);
+	dt_cg_probe_error(pcb, DT_LBL_NONE, DTRACEFLT_BADSTACK, 0);
 	emitl(dlp, lbl_valid,
 		   BPF_NOP());
 }
@@ -1852,7 +1896,7 @@ dt_cg_act_ustack(dt_pcb_t *pcb, dt_node_t *dnp, dtrace_actkind_t kind)
 	dt_regset_free_args(drp);
 	emit(dlp,  BPF_BRANCH_IMM(BPF_JSGE, BPF_REG_0, 0, lbl_valid));
 	dt_regset_free(drp, BPF_REG_0);
-	dt_cg_probe_error(pcb, DTRACEFLT_BADSTACK, 0);
+	dt_cg_probe_error(pcb, DT_LBL_NONE, DTRACEFLT_BADSTACK, 0);
 	emitl(dlp, lbl_valid,
 		   BPF_NOP());
 }
@@ -2522,7 +2566,7 @@ dt_cg_arglist(dt_ident_t *idp, dt_node_t *args, dt_irlist_t *dlp,
 			dt_regset_free_args(drp);
 			emit(dlp,  BPF_BRANCH_IMM(BPF_JSGE, BPF_REG_0, 0, lbl_valid));
 			dt_regset_free(drp, BPF_REG_0);
-			dt_cg_probe_error(yypcb, DTRACEFLT_BADADDR, 128 + i);
+			dt_cg_probe_error(yypcb, DT_LBL_NONE, DTRACEFLT_BADADDR, 128 + i);
 			dt_regset_xalloc(drp, BPF_REG_0);
 
 			emitl(dlp, lbl_valid,
@@ -2544,7 +2588,7 @@ dt_cg_arglist(dt_ident_t *idp, dt_node_t *args, dt_irlist_t *dlp,
 			emit(dlp,  BPF_CALL_HELPER(BPF_FUNC_probe_read));
 			dt_regset_free_args(drp);
 			emit(dlp,  BPF_BRANCH_IMM(BPF_JEQ, BPF_REG_0, 0, lbl_valid));
-			dt_cg_probe_error(yypcb, DTRACEFLT_BADADDR, 0);
+			dt_cg_probe_error(yypcb, DT_LBL_NONE, DTRACEFLT_BADADDR, 0);
 
 			emitl(dlp, lbl_valid,
 				   BPF_ALU64_IMM(BPF_ADD, treg, size));
@@ -2827,7 +2871,7 @@ dt_cg_arithmetic_op(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp,
 		/* First ensure we do not perform a division by zero. */
 		emit(dlp,  BPF_BRANCH_IMM(BPF_JNE, dnp->dn_right->dn_reg, 0,
 					  lbl_valid));
-		dt_cg_probe_error(yypcb, DTRACEFLT_DIVZERO, 0);
+		dt_cg_probe_error(yypcb, DT_LBL_NONE, DTRACEFLT_DIVZERO, 0);
 		emitl(dlp, lbl_valid,
 			   BPF_NOP());
 
-- 
2.35.1.261.g8402f930ba.dirty




More information about the DTrace-devel mailing list