diff --git a/gdb/ChangeLog b/gdb/ChangeLog index a55c12a666..d28471e3ea 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,15 @@ +2019-06-28 Sergio Durigan Junior + + PR breakpoints/24541 + * gdbarch.c: Regenerate. + * gdbarch.h: Regenerate. + * gdbarch.sh: Add 'stap_adjust_register'. + * i386-tdep.c: Include ''. + (i386_stap_adjust_register): New function. + (i386_elf_init_abi): Register 'i386_stap_adjust_register'. + * stap-probe.c (stap_parse_register_operand): Call + 'gdbarch_stap_adjust_register'. + 2019-06-28 Sergio Durigan Junior PR python/24742 diff --git a/gdb/gdbarch.c b/gdb/gdbarch.c index a0c169d74d..cc7d0ace66 100644 --- a/gdb/gdbarch.c +++ b/gdb/gdbarch.c @@ -324,6 +324,7 @@ struct gdbarch const char * stap_gdb_register_suffix; gdbarch_stap_is_single_operand_ftype *stap_is_single_operand; gdbarch_stap_parse_special_token_ftype *stap_parse_special_token; + gdbarch_stap_adjust_register_ftype *stap_adjust_register; gdbarch_dtrace_parse_probe_argument_ftype *dtrace_parse_probe_argument; gdbarch_dtrace_probe_is_enabled_ftype *dtrace_probe_is_enabled; gdbarch_dtrace_enable_probe_ftype *dtrace_enable_probe; @@ -687,6 +688,7 @@ verify_gdbarch (struct gdbarch *gdbarch) /* Skip verify of stap_gdb_register_suffix, invalid_p == 0 */ /* Skip verify of stap_is_single_operand, has predicate. */ /* Skip verify of stap_parse_special_token, has predicate. */ + /* Skip verify of stap_adjust_register, has predicate. */ /* Skip verify of dtrace_parse_probe_argument, has predicate. */ /* Skip verify of dtrace_probe_is_enabled, has predicate. */ /* Skip verify of dtrace_enable_probe, has predicate. */ @@ -1396,6 +1398,12 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file) fprintf_unfiltered (file, "gdbarch_dump: stack_frame_destroyed_p = <%s>\n", host_address_to_string (gdbarch->stack_frame_destroyed_p)); + fprintf_unfiltered (file, + "gdbarch_dump: gdbarch_stap_adjust_register_p() = %d\n", + gdbarch_stap_adjust_register_p (gdbarch)); + fprintf_unfiltered (file, + "gdbarch_dump: stap_adjust_register = <%s>\n", + host_address_to_string (gdbarch->stap_adjust_register)); fprintf_unfiltered (file, "gdbarch_dump: stap_gdb_register_prefix = %s\n", pstring (gdbarch->stap_gdb_register_prefix)); @@ -4515,6 +4523,30 @@ set_gdbarch_stap_parse_special_token (struct gdbarch *gdbarch, gdbarch->stap_parse_special_token = stap_parse_special_token; } +int +gdbarch_stap_adjust_register_p (struct gdbarch *gdbarch) +{ + gdb_assert (gdbarch != NULL); + return gdbarch->stap_adjust_register != NULL; +} + +void +gdbarch_stap_adjust_register (struct gdbarch *gdbarch, struct stap_parse_info *p, std::string ®name, int regnum) +{ + gdb_assert (gdbarch != NULL); + gdb_assert (gdbarch->stap_adjust_register != NULL); + if (gdbarch_debug >= 2) + fprintf_unfiltered (gdb_stdlog, "gdbarch_stap_adjust_register called\n"); + gdbarch->stap_adjust_register (gdbarch, p, regname, regnum); +} + +void +set_gdbarch_stap_adjust_register (struct gdbarch *gdbarch, + gdbarch_stap_adjust_register_ftype stap_adjust_register) +{ + gdbarch->stap_adjust_register = stap_adjust_register; +} + int gdbarch_dtrace_parse_probe_argument_p (struct gdbarch *gdbarch) { diff --git a/gdb/gdbarch.h b/gdb/gdbarch.h index 7ebd365a31..0857d2f302 100644 --- a/gdb/gdbarch.h +++ b/gdb/gdbarch.h @@ -1350,6 +1350,36 @@ typedef int (gdbarch_stap_parse_special_token_ftype) (struct gdbarch *gdbarch, s extern int gdbarch_stap_parse_special_token (struct gdbarch *gdbarch, struct stap_parse_info *p); extern void set_gdbarch_stap_parse_special_token (struct gdbarch *gdbarch, gdbarch_stap_parse_special_token_ftype *stap_parse_special_token); +/* Perform arch-dependent adjustments to a register name. + + In very specific situations, it may be necessary for the register + name present in a SystemTap probe's argument to be handled in a + special way. For example, on i386, GCC may over-optimize the + register allocation and use smaller registers than necessary. In + such cases, the client that is reading and evaluating the SystemTap + probe (ourselves) will need to actually fetch values from the wider + version of the register in question. + + To illustrate the example, consider the following probe argument + (i386): + + 4@%ax + + This argument says that its value can be found at the %ax register, + which is a 16-bit register. However, the argument's prefix says + that its type is "uint32_t", which is 32-bit in size. Therefore, in + this case, GDB should actually fetch the probe's value from register + %eax, not %ax. In this scenario, this function would actually + replace the register name from %ax to %eax. + + The rationale for this can be found at PR breakpoints/24541. */ + +extern int gdbarch_stap_adjust_register_p (struct gdbarch *gdbarch); + +typedef void (gdbarch_stap_adjust_register_ftype) (struct gdbarch *gdbarch, struct stap_parse_info *p, std::string ®name, int regnum); +extern void gdbarch_stap_adjust_register (struct gdbarch *gdbarch, struct stap_parse_info *p, std::string ®name, int regnum); +extern void set_gdbarch_stap_adjust_register (struct gdbarch *gdbarch, gdbarch_stap_adjust_register_ftype *stap_adjust_register); + /* DTrace related functions. The expression to compute the NARTGth+1 argument to a DTrace USDT probe. NARG must be >= 0. */ diff --git a/gdb/gdbarch.sh b/gdb/gdbarch.sh index 59493d8c21..f3d1bf489a 100755 --- a/gdb/gdbarch.sh +++ b/gdb/gdbarch.sh @@ -1030,6 +1030,31 @@ M;int;stap_is_single_operand;const char *s;s # parser), and should advance the buffer pointer (p->arg). M;int;stap_parse_special_token;struct stap_parse_info *p;p +# Perform arch-dependent adjustments to a register name. +# +# In very specific situations, it may be necessary for the register +# name present in a SystemTap probe's argument to be handled in a +# special way. For example, on i386, GCC may over-optimize the +# register allocation and use smaller registers than necessary. In +# such cases, the client that is reading and evaluating the SystemTap +# probe (ourselves) will need to actually fetch values from the wider +# version of the register in question. +# +# To illustrate the example, consider the following probe argument +# (i386): +# +# 4@%ax +# +# This argument says that its value can be found at the %ax register, +# which is a 16-bit register. However, the argument's prefix says +# that its type is "uint32_t", which is 32-bit in size. Therefore, in +# this case, GDB should actually fetch the probe's value from register +# %eax, not %ax. In this scenario, this function would actually +# replace the register name from %ax to %eax. +# +# The rationale for this can be found at PR breakpoints/24541. +M;void;stap_adjust_register;struct stap_parse_info *p, std::string \®name, int regnum;p, regname, regnum + # DTrace related functions. # The expression to compute the NARTGth+1 argument to a DTrace USDT probe. diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c index c542edf28a..00c1f8d749 100644 --- a/gdb/i386-tdep.c +++ b/gdb/i386-tdep.c @@ -64,6 +64,7 @@ #include "parser-defs.h" #include #include +#include /* Register names. */ @@ -4385,6 +4386,32 @@ i386_stap_parse_special_token (struct gdbarch *gdbarch, return 0; } +/* Implementation of 'gdbarch_stap_adjust_register', as defined in + gdbarch.h. */ + +static void +i386_stap_adjust_register (struct gdbarch *gdbarch, struct stap_parse_info *p, + std::string ®name, int regnum) +{ + static const std::unordered_set reg_assoc + = { "ax", "bx", "cx", "dx", + "si", "di", "bp", "sp" }; + + if (register_size (gdbarch, regnum) >= TYPE_LENGTH (p->arg_type)) + { + /* If we're dealing with a register whose size is greater or + equal than the size specified by the "[-]N@" prefix, then we + don't need to do anything. */ + return; + } + + if (reg_assoc.find (regname) != reg_assoc.end ()) + { + /* Use the extended version of the register. */ + regname = "e" + regname; + } +} + /* gdbarch gnu_triplet_regexp method. Both arches are acceptable as GDB always @@ -4433,6 +4460,8 @@ i386_elf_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) i386_stap_is_single_operand); set_gdbarch_stap_parse_special_token (gdbarch, i386_stap_parse_special_token); + set_gdbarch_stap_adjust_register (gdbarch, + i386_stap_adjust_register); set_gdbarch_in_indirect_branch_thunk (gdbarch, i386_in_indirect_branch_thunk); diff --git a/gdb/stap-probe.c b/gdb/stap-probe.c index e5a901b4bf..aa1c8144d8 100644 --- a/gdb/stap-probe.c +++ b/gdb/stap-probe.c @@ -762,13 +762,38 @@ stap_parse_register_operand (struct stap_parse_info *p) regname += gdb_reg_suffix; } + int regnum = user_reg_map_name_to_regnum (gdbarch, regname.c_str (), + regname.size ()); + /* Is this a valid register name? */ - if (user_reg_map_name_to_regnum (gdbarch, - regname.c_str (), - regname.size ()) == -1) + if (regnum == -1) error (_("Invalid register name `%s' on expression `%s'."), regname.c_str (), p->saved_arg); + /* Check if there's any special treatment that the arch-specific + code would like to perform on the register name. */ + if (gdbarch_stap_adjust_register_p (gdbarch)) + { + std::string oldregname = regname; + + gdbarch_stap_adjust_register (gdbarch, p, regname, regnum); + + if (regname != oldregname) + { + /* This is just a check we perform to make sure that the + arch-dependent code has provided us with a valid + register name. */ + regnum = user_reg_map_name_to_regnum (gdbarch, regname.c_str (), + regname.size ()); + + if (regnum == -1) + internal_error (__FILE__, __LINE__, + _("Invalid register name '%s' after replacing it" + " (previous name was '%s')"), + regname.c_str (), oldregname.c_str ()); + } + } + write_exp_elt_opcode (&p->pstate, OP_REGISTER); str.ptr = regname.c_str (); str.length = regname.size ();