diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c index 7a3493c..b505013 100644 --- a/gdb/aarch64-tdep.c +++ b/gdb/aarch64-tdep.c @@ -35,6 +35,7 @@ #include "frame-base.h" #include "trad-frame.h" #include "objfiles.h" +#include "dwarf2.h" #include "dwarf2-frame.h" #include "gdbtypes.h" #include "prologue-value.h" @@ -1046,12 +1047,30 @@ static struct value * aarch64_dwarf2_prev_register (struct frame_info *this_frame, void **this_cache, int regnum) { + struct gdbarch *gdbarch = get_frame_arch (this_frame); CORE_ADDR lr; switch (regnum) { case AARCH64_PC_REGNUM: lr = frame_unwind_register_unsigned (this_frame, AARCH64_LR_REGNUM); + /* Also fetch return address signing status, mask off the signature bits + if it's signed. */ + if (aarch64_tdep_has_pauth_p (gdbarch_tdep (gdbarch))) + { + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + int ra_state_column + = tdep->regnum.pauth_ra_state_regnum + gdbarch_num_regs (gdbarch); + CORE_ADDR ra_state = frame_unwind_register_unsigned (this_frame, + ra_state_column); + if (ra_state) + { + int cmask_num = tdep->regnum.pauth_reg_base + 1; + CORE_ADDR cmask + = frame_unwind_register_unsigned (this_frame, cmask_num); + lr = lr & ~cmask; + } + } return frame_unwind_got_constant (this_frame, regnum, lr); default: @@ -1067,6 +1086,33 @@ aarch64_dwarf2_frame_init_reg (struct gdbarch *gdbarch, int regnum, struct dwarf2_frame_state_reg *reg, struct frame_info *this_frame) { + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + + /* Init pauth registers. */ + if (aarch64_tdep_has_pauth_p (tdep)) + { + int ra_state_num + = tdep->regnum.pauth_ra_state_regnum + gdbarch_num_regs (gdbarch); + int dmask_num = tdep->regnum.pauth_reg_base; + int cmask_num = tdep->regnum.pauth_reg_base + 1; + + if (regnum == ra_state_num) + { + /* Initialize RA_STATE column to zero. */ + static unsigned char exp_0 = 0x30; /* DW_OP_lit0. */ + + reg->how = DWARF2_FRAME_REG_SAVED_VAL_EXP; + reg->loc.exp = &exp_0; + reg->exp_len = 1; + return; + } + else if (regnum == cmask_num || regnum == dmask_num) + { + reg->how = DWARF2_FRAME_REG_SAME_VALUE; + return; + } + } + switch (regnum) { case AARCH64_PC_REGNUM: @@ -1079,6 +1125,40 @@ aarch64_dwarf2_frame_init_reg (struct gdbarch *gdbarch, int regnum, } } +/* Implement the execute_dwarf_cfa_vendor_op method. */ + +static bool +aarch64_execute_dwarf_cfa_vendor_op (struct gdbarch *gdbarch, gdb_byte op, + struct dwarf2_frame_state *fs) +{ + struct dwarf2_frame_state_reg *ra_state_column; + static unsigned char exp_0 = 0x30; /* DW_OP_lit0. */ + static unsigned char exp_1 = 0x31; /* DW_OP_lit1. */ + + /* Only DW_CFA_GNU_window_save is expected on AArch64. */ + if (op != DW_CFA_GNU_window_save) + return false; + + + /* Allocate RA_STATE column if it's not allocated yet. */ + dwarf2_frame_state_alloc_regs (&fs->regs, AARCH64_DWARF_PAUTH_RA_STATE + 1); + + /* Toggle the status of RA_STATE column between 0 and 1. This column is + therefore using value expression rule as currently it's the only rule to + represent constant. */ + ra_state_column = &(fs->regs.reg[AARCH64_DWARF_PAUTH_RA_STATE]); + ra_state_column->how = DWARF2_FRAME_REG_SAVED_VAL_EXP; + + if (ra_state_column->loc.exp == NULL || ra_state_column->loc.exp == &exp_0) + ra_state_column->loc.exp = &exp_1; + else + ra_state_column->loc.exp = &exp_0; + + ra_state_column->exp_len = 1; + + return true; +} + /* When arguments must be pushed onto the stack, they go on in reverse order. The code below implements a FILO (stack) to do this. */ @@ -1777,6 +1857,8 @@ aarch64_vnb_type (struct gdbarch *gdbarch) static int aarch64_dwarf_reg_to_regnum (struct gdbarch *gdbarch, int reg) { + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + if (reg >= AARCH64_DWARF_X0 && reg <= AARCH64_DWARF_X0 + 30) return AARCH64_X0_REGNUM + reg - AARCH64_DWARF_X0; @@ -1786,6 +1868,16 @@ aarch64_dwarf_reg_to_regnum (struct gdbarch *gdbarch, int reg) if (reg >= AARCH64_DWARF_V0 && reg <= AARCH64_DWARF_V0 + 31) return AARCH64_V0_REGNUM + reg - AARCH64_DWARF_V0; + if (aarch64_tdep_has_pauth_p (tdep)) + { + if (reg >= AARCH64_DWARF_PAUTH_DMASK && reg <= AARCH64_DWARF_PAUTH_CMASK) + return tdep->regnum.pauth_reg_base + reg - AARCH64_DWARF_PAUTH_DMASK; + + if (reg == AARCH64_DWARF_PAUTH_RA_STATE) + /* AARCH64_PAUTH_RA_STATE_REGNUM is a pseudo register. */ + return tdep->regnum.pauth_ra_state_regnum + gdbarch_num_regs (gdbarch); + } + return -1; } @@ -2120,6 +2212,8 @@ aarch64_gen_return_address (struct gdbarch *gdbarch, static const char * aarch64_pseudo_register_name (struct gdbarch *gdbarch, int regnum) { + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + static const char *const q_name[] = { "q0", "q1", "q2", "q3", @@ -2197,6 +2291,21 @@ aarch64_pseudo_register_name (struct gdbarch *gdbarch, int regnum) if (regnum >= AARCH64_B0_REGNUM && regnum < AARCH64_B0_REGNUM + 32) return b_name[regnum - AARCH64_B0_REGNUM]; + if (aarch64_tdep_has_pauth_p (tdep)) + { + /* RA_STATE is the first pseudo register for pointer authentication + feature. */ + int ra_state = tdep->regnum.pauth_ra_state_regnum; + + /* RA_STATE is a pseudo register whose value will be initialized and + referenced only during frame unwinding. GDB should not read its + value from low level methods. In some functions, GDB will read a + register if it has a name. For example, mi_cmd_trace_frame_collected. + Therefore, we make RA_STATE anonymous to avoid such reading. */ + if (regnum == ra_state) + return ""; + } + internal_error (__FILE__, __LINE__, _("aarch64_pseudo_register_name: bad register number %d"), regnum); @@ -2207,6 +2316,8 @@ aarch64_pseudo_register_name (struct gdbarch *gdbarch, int regnum) static struct type * aarch64_pseudo_register_type (struct gdbarch *gdbarch, int regnum) { + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + regnum -= gdbarch_num_regs (gdbarch); if (regnum >= AARCH64_Q0_REGNUM && regnum < AARCH64_Q0_REGNUM + 32) @@ -2224,6 +2335,10 @@ aarch64_pseudo_register_type (struct gdbarch *gdbarch, int regnum) if (regnum >= AARCH64_B0_REGNUM && regnum < AARCH64_B0_REGNUM + 32) return aarch64_vnb_type (gdbarch); + if (aarch64_tdep_has_pauth_p (tdep) + && regnum == tdep->regnum.pauth_ra_state_regnum) + return builtin_type (gdbarch)->builtin_uint64; + internal_error (__FILE__, __LINE__, _("aarch64_pseudo_register_type: bad register number %d"), regnum); @@ -2235,6 +2350,8 @@ static int aarch64_pseudo_register_reggroup_p (struct gdbarch *gdbarch, int regnum, struct reggroup *group) { + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + regnum -= gdbarch_num_regs (gdbarch); if (regnum >= AARCH64_Q0_REGNUM && regnum < AARCH64_Q0_REGNUM + 32) @@ -2249,6 +2366,11 @@ aarch64_pseudo_register_reggroup_p (struct gdbarch *gdbarch, int regnum, return group == all_reggroup || group == vector_reggroup; else if (regnum >= AARCH64_B0_REGNUM && regnum < AARCH64_B0_REGNUM + 32) return group == all_reggroup || group == vector_reggroup; + /* Don't assign RA_STATE to any group. This pseudo register is supposed to be + used during unwinding only. Don't display it for "info register". */ + if (aarch64_tdep_has_pauth_p (tdep) + && regnum == tdep->regnum.pauth_ra_state_regnum) + return 0; return group == all_reggroup; } @@ -2891,6 +3013,7 @@ aarch64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) int num_pseudo_regs = 0; struct aarch64_optional_regnum *regnum; int first_pauth_regnum = -1; + int pauth_ra_state_regnum = -1; /* Ensure we always have a target descriptor. */ if (!tdesc_has_registers (tdesc)) @@ -2939,6 +3062,7 @@ aarch64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) if (feature_pauth) { first_pauth_regnum = num_regs; + pauth_ra_state_regnum = num_pseudo_regs; /* Validate the descriptor provides the mandatory PAUTH registers and allocate their numbers. */ for (i = 0; i < ARRAY_SIZE (aarch64_pauth_register_names); i++) @@ -2948,6 +3072,7 @@ aarch64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) aarch64_pauth_register_names[i]); num_regs += i; + num_pseudo_regs += 1; /* Count RA_STATE pseudo register. */ } if (!valid_p) @@ -2988,6 +3113,7 @@ aarch64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) tdep->jb_pc = -1; /* Longjump support not enabled by default. */ tdep->jb_elt_size = 8; tdep->regnum.pauth_reg_base = first_pauth_regnum; + tdep->regnum.pauth_ra_state_regnum = pauth_ra_state_regnum; set_gdbarch_push_dummy_call (gdbarch, aarch64_push_dummy_call); set_gdbarch_frame_align (gdbarch, aarch64_frame_align); @@ -3059,6 +3185,9 @@ aarch64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) gdbarch_init_osabi (info, gdbarch); dwarf2_frame_set_init_reg (gdbarch, aarch64_dwarf2_frame_init_reg); + /* Register DWARF CFA vendor handler. */ + set_gdbarch_execute_dwarf_cfa_vendor_op (gdbarch, + aarch64_execute_dwarf_cfa_vendor_op); /* Add some default predicates. */ frame_unwind_append_unwinder (gdbarch, &aarch64_stub_unwind); diff --git a/gdb/aarch64-tdep.h b/gdb/aarch64-tdep.h index 5ece079..88b77b8 100644 --- a/gdb/aarch64-tdep.h +++ b/gdb/aarch64-tdep.h @@ -29,6 +29,9 @@ struct regset; /* AArch64 Dwarf register numbering. */ #define AARCH64_DWARF_X0 0 #define AARCH64_DWARF_SP 31 +#define AARCH64_DWARF_PAUTH_RA_STATE 34 +#define AARCH64_DWARF_PAUTH_DMASK 35 +#define AARCH64_DWARF_PAUTH_CMASK 36 #define AARCH64_DWARF_V0 64 /* Register numbers of various important registers. */ @@ -80,6 +83,8 @@ struct aarch64_optional_regnum { /* First pauth register number. */ int pauth_reg_base; + /* pauth ra_state register number. */ + int pauth_ra_state_regnum; }; /* Target-dependent structure in gdbarch. */