[PATCH 4/6] gas: scfi: allow scratch reg state to track values as well
Indu Bhagat
indu.bhagat@oracle.com
Thu Aug 8 15:38:51 GMT 2024
To accommodate the 'split sp update' code pattern in aarch64, the SCFI
machinery needs to simulate _some_ data flow. Update the scratch reg
state management logic to include tracking of both:
- register location (updated when a mov reg1, reg2 is seen)
- register value (updated when a mov reg1, imm is seen)
The SCFI machinery will continue to memorize location/value of REG_SP
and REG_FP only at this time.
This patch by itself is preparatory in nature, and does not make any
changes in the current SCFI workflow.
gas/
* scfi.c (enum reg_scratch_state): New enum.
(struct reg_scratch): New definition.
(struct scfi_state): Update data type of scratch to reg_scratchS
instead of the earlier cfi_reglocS.
(scfi_state_update_scratch_reg): New definition.
(scfi_state_scratch_reg_loc_p): Likewise.
(scfi_state_scratch_reg_get_loc): Likewise.
(verify_heuristic_traceable_stack_manipulation): Use the new
function.
(gen_scfi_ops): Likewise.
---
gas/scfi.c | 119 +++++++++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 111 insertions(+), 8 deletions(-)
diff --git a/gas/scfi.c b/gas/scfi.c
index 6c59a8eeabc..d9a5d59587d 100644
--- a/gas/scfi.c
+++ b/gas/scfi.c
@@ -62,6 +62,51 @@ struct cfi_regloc
typedef struct cfi_regloc cfi_reglocS;
+/* Register scratch state enumerators. */
+
+enum reg_scratch_state
+{
+ /* Register not available in scratch state. */
+ REG_SCRATCH_UNKNOWN,
+ /* Register location known in scratch state. */
+ REG_SCRATCH_LOC,
+ /* Rgister value known in scratch state. */
+ REG_SCRATCH_VALUE
+};
+
+/* Register scratch state.
+
+ This is used to simulate _some_ data flow between ginsns. Typically used by
+ the SCFI machinery to memorize information when following code patterns are
+ seen:
+
+ mov REG_GP (dst), REG_SP (src)
+ mov REG_SP (dst), REG_GP (src)
+
+ or
+
+ mov REG (dst), IMM
+ add REG_SP (dst), REG, IMM
+
+ etc.
+
+ Register location or value may be saved. */
+
+struct reg_scratch
+{
+ union
+ {
+ /* Location information. */
+ cfi_reglocS loc;
+ /* Value data. */
+ offsetT value;
+ } r;
+ /* Whether location or value is saved. */
+ enum reg_scratch_state state;
+};
+
+typedef struct reg_scratch reg_scratchS;
+
struct scfi_op_data
{
const char *name;
@@ -105,7 +150,7 @@ struct scfi_op
struct scfi_state
{
cfi_reglocS regs[MAX_NUM_SCFI_REGS];
- cfi_reglocS scratch[MAX_NUM_SCFI_REGS];
+ reg_scratchS scratch[MAX_NUM_SCFI_REGS];
/* Current stack size. */
offsetT stack_size;
/* Whether the stack size is known.
@@ -115,6 +160,65 @@ struct scfi_state
bool traceable_p;
};
+static void
+scfi_state_update_scratch_reg (scfi_stateS *state, unsigned int reg,
+ offsetT value, bool loc_p)
+{
+ if (reg >= MAX_NUM_SCFI_REGS)
+ return;
+
+ if (loc_p)
+ {
+ state->scratch[reg].state = REG_SCRATCH_LOC;
+ state->scratch[reg].r.loc.base = REG_CFA;
+ state->scratch[reg].r.loc.offset = value;
+ state->scratch[reg].r.loc.state = CFI_IN_REG;
+ }
+ else
+ {
+ state->scratch[reg].state = REG_SCRATCH_VALUE;
+ state->scratch[reg].r.value = value;
+ }
+}
+
+/* Whether REG has previously been saved in the scratch reg state due to
+ a mov reg, reg insn. */
+
+static bool
+scfi_state_scratch_reg_loc_p (scfi_stateS *state, unsigned int reg)
+{
+ bool loc_p = false;
+
+ if (reg >= MAX_NUM_SCFI_REGS)
+ return false;
+
+ loc_p = (state->scratch[reg].state == REG_SCRATCH_LOC
+ && state->scratch[reg].r.loc.state == CFI_IN_REG);
+
+ return loc_p;
+}
+
+/* Return the location offset (stack offset from CFA, ATM) of REG.
+ IMP: Caller _must_ ensure this is done when scfi_state_scratch_reg_loc_p ()
+ is true for this to be the intended value. */
+
+static offsetT
+scfi_state_scratch_reg_get_loc (scfi_stateS *state, unsigned int reg)
+{
+ offsetT value = 0;
+
+ if (scfi_state_scratch_reg_loc_p (state, reg))
+ {
+ /* Just a sanity check. Other possibilities are not implemented yet, as
+ they are unnecessary for SCFI, ATM. */
+ gas_assert (state->scratch[reg].r.loc.base == REG_CFA);
+
+ value = state->scratch[reg].r.loc.offset;
+ }
+
+ return value;
+}
+
/* Initialize a new SCFI op. */
static scfi_opS *
@@ -597,8 +701,7 @@ verify_heuristic_traceable_stack_manipulation (ginsnS *ginsn,
{
/* A previous mov %rsp, %reg must have been seen earlier for this to be
an OK for stack manipulation. */
- if (state->scratch[src1_reg].base != REG_CFA
- || state->scratch[src1_reg].state != CFI_IN_REG)
+ if (!scfi_state_scratch_reg_loc_p (state, src1_reg))
possibly_untraceable = true;
}
/* Check add/sub/and insn usage when CFA base register is REG_SP.
@@ -809,18 +912,18 @@ gen_scfi_ops (ginsnS *ginsn, scfi_stateS *state)
IMP: The workflow in gen_scfi_ops must keep it updated.
PS: Not taking the value from state->scratch[REG_SP] is
intentional. */
- state->scratch[dst_reg].base = REG_CFA;
- state->scratch[dst_reg].offset = -state->stack_size;
- state->scratch[dst_reg].state = CFI_IN_REG;
+ scfi_state_update_scratch_reg (state, dst_reg,
+ -state->stack_size, true);
}
else if (src1_type == GINSN_SRC_REG
&& dst_type == GINSN_DST_REG && dst_reg == REG_SP)
{
/* mov %reg, %rsp. */
/* Keep the value of REG_SP updated. */
- if (state->scratch[src1_reg].state == CFI_IN_REG)
+ if (scfi_state_scratch_reg_loc_p (state, src1_reg))
{
- state->stack_size = -state->scratch[src1_reg].offset;
+ offset = scfi_state_scratch_reg_get_loc (state, src1_reg);
+ state->stack_size = -offset;
state->traceable_p = true;
}
# if 0
--
2.43.0
More information about the Binutils
mailing list