/* SystemTap probe support for GDB.
- Copyright (C) 2012-2019 Free Software Foundation, Inc.
+ Copyright (C) 2012-2022 Free Software Foundation, Inc.
This file is part of GDB.
#include "defs.h"
#include "stap-probe.h"
#include "probe.h"
-#include "common/vec.h"
#include "ui-out.h"
#include "objfiles.h"
#include "arch-utils.h"
#include "parser-defs.h"
#include "language.h"
#include "elf-bfd.h"
+#include "expop.h"
+#include <unordered_map>
+#include "gdbsupport/hash_enum.h"
#include <ctype.h>
class stap_static_probe_ops : public static_probe_ops
{
public:
+ /* We need a user-provided constructor to placate some compilers.
+ See PR build/24937. */
+ stap_static_probe_ops ()
+ {
+ }
+
/* See probe.h. */
bool is_linespec (const char **linespecp) const override;
CORE_ADDR get_relocated_address (struct objfile *objfile) override;
/* See probe.h. */
- unsigned get_argument_count (struct frame_info *frame) override;
+ unsigned get_argument_count (struct gdbarch *gdbarch) override;
/* See probe.h. */
bool can_evaluate_arguments () const override;
STAP_OPERAND_PREC_MUL
};
-static void stap_parse_argument_1 (struct stap_parse_info *p, bool has_lhs,
- enum stap_operand_prec prec);
+static expr::operation_up stap_parse_argument_1 (struct stap_parse_info *p,
+ expr::operation_up &&lhs,
+ enum stap_operand_prec prec)
+ ATTRIBUTE_UNUSED_RESULT;
-static void stap_parse_argument_conditionally (struct stap_parse_info *p);
+static expr::operation_up stap_parse_argument_conditionally
+ (struct stap_parse_info *p) ATTRIBUTE_UNUSED_RESULT;
/* Returns true if *S is an operator, false otherwise. */
return op;
}
+typedef expr::operation_up binop_maker_ftype (expr::operation_up &&,
+ expr::operation_up &&);
+/* Map from an expression opcode to a function that can create a
+ binary operation of that type. */
+static std::unordered_map<exp_opcode, binop_maker_ftype *,
+ gdb::hash_enum<exp_opcode>> stap_maker_map;
+
+/* Helper function to create a binary operation. */
+static expr::operation_up
+stap_make_binop (enum exp_opcode opcode, expr::operation_up &&lhs,
+ expr::operation_up &&rhs)
+{
+ auto iter = stap_maker_map.find (opcode);
+ gdb_assert (iter != stap_maker_map.end ());
+ return iter->second (std::move (lhs), std::move (rhs));
+}
+
/* Given the bitness of the argument, represented by B, return the
corresponding `struct type *', or throw an error if B is
unknown. */
language (e.g., `15' is the 15th general-purpose register), but inside
GDB they have a prefix (the letter `r') appended. */
-static void
+static expr::operation_up
stap_parse_register_operand (struct stap_parse_info *p)
{
/* Simple flag to indicate whether we have seen a minus signal before
certain number. */
bool got_minus = false;
- /* Flags to indicate whether this register access is being displaced and/or
+ /* Flag to indicate whether this register access is being
indirected. */
- bool disp_p = false;
bool indirect_p = false;
struct gdbarch *gdbarch = p->gdbarch;
- /* Needed to generate the register name as a part of an expression. */
- struct stoken str;
/* Variables used to extract the register name from the probe's
argument. */
const char *start;
- char *regname;
- int len;
const char *gdb_reg_prefix = gdbarch_stap_gdb_register_prefix (gdbarch);
- int gdb_reg_prefix_len = gdb_reg_prefix ? strlen (gdb_reg_prefix) : 0;
const char *gdb_reg_suffix = gdbarch_stap_gdb_register_suffix (gdbarch);
- int gdb_reg_suffix_len = gdb_reg_suffix ? strlen (gdb_reg_suffix) : 0;
const char *reg_prefix;
const char *reg_ind_prefix;
const char *reg_suffix;
const char *reg_ind_suffix;
+ using namespace expr;
+
/* Checking for a displacement argument. */
if (*p->arg == '+')
{
++p->arg;
}
+ struct type *long_type = builtin_type (gdbarch)->builtin_long;
+ operation_up disp_op;
if (isdigit (*p->arg))
{
/* The value of the displacement. */
long displacement;
char *endp;
- disp_p = true;
displacement = strtol (p->arg, &endp, 10);
p->arg = endp;
/* Generating the expression for the displacement. */
- write_exp_elt_opcode (&p->pstate, OP_LONG);
- write_exp_elt_type (&p->pstate, builtin_type (gdbarch)->builtin_long);
- write_exp_elt_longcst (&p->pstate, displacement);
- write_exp_elt_opcode (&p->pstate, OP_LONG);
if (got_minus)
- write_exp_elt_opcode (&p->pstate, UNOP_NEG);
+ displacement = -displacement;
+ disp_op = make_operation<long_const_operation> (long_type, displacement);
}
/* Getting rid of register indirection prefix. */
p->arg += strlen (reg_ind_prefix);
}
- if (disp_p && !indirect_p)
+ if (disp_op != nullptr && !indirect_p)
error (_("Invalid register displacement syntax on expression `%s'."),
p->saved_arg);
while (isalnum (*p->arg))
++p->arg;
- len = p->arg - start;
-
- regname = (char *) alloca (len + gdb_reg_prefix_len + gdb_reg_suffix_len + 1);
- regname[0] = '\0';
+ std::string regname (start, p->arg - start);
/* We only add the GDB's register prefix/suffix if we are dealing with
a numeric register. */
- if (gdb_reg_prefix && isdigit (*start))
+ if (isdigit (*start))
{
- strncpy (regname, gdb_reg_prefix, gdb_reg_prefix_len);
- strncpy (regname + gdb_reg_prefix_len, start, len);
+ if (gdb_reg_prefix != NULL)
+ regname = gdb_reg_prefix + regname;
- if (gdb_reg_suffix)
- strncpy (regname + gdb_reg_prefix_len + len,
- gdb_reg_suffix, gdb_reg_suffix_len);
-
- len += gdb_reg_prefix_len + gdb_reg_suffix_len;
+ if (gdb_reg_suffix != NULL)
+ regname += gdb_reg_suffix;
}
- else
- strncpy (regname, start, len);
- regname[len] = '\0';
+ 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, len) == -1)
+ if (regnum == -1)
error (_("Invalid register name `%s' on expression `%s'."),
- regname, p->saved_arg);
+ 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 newregname
+ = gdbarch_stap_adjust_register (gdbarch, p, regname, regnum);
+
+ if (regname != newregname)
+ {
+ /* 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, newregname.c_str (),
+ newregname.size ());
+
+ if (regnum == -1)
+ internal_error (__FILE__, __LINE__,
+ _("Invalid register name '%s' after replacing it"
+ " (previous name was '%s')"),
+ newregname.c_str (), regname.c_str ());
+
+ regname = std::move (newregname);
+ }
+ }
- write_exp_elt_opcode (&p->pstate, OP_REGISTER);
- str.ptr = regname;
- str.length = len;
- write_exp_string (&p->pstate, str);
- write_exp_elt_opcode (&p->pstate, OP_REGISTER);
+ operation_up reg = make_operation<register_operation> (std::move (regname));
if (indirect_p)
{
- if (disp_p)
- write_exp_elt_opcode (&p->pstate, BINOP_ADD);
+ if (disp_op != nullptr)
+ reg = make_operation<add_operation> (std::move (disp_op),
+ std::move (reg));
/* Casting to the expected type. */
- write_exp_elt_opcode (&p->pstate, UNOP_CAST);
- write_exp_elt_type (&p->pstate, lookup_pointer_type (p->arg_type));
- write_exp_elt_opcode (&p->pstate, UNOP_CAST);
-
- write_exp_elt_opcode (&p->pstate, UNOP_IND);
+ struct type *arg_ptr_type = lookup_pointer_type (p->arg_type);
+ reg = make_operation<unop_cast_operation> (std::move (reg),
+ arg_ptr_type);
+ reg = make_operation<unop_ind_operation> (std::move (reg));
}
/* Getting rid of the register name suffix. */
error (_("Missing indirection suffix on expression `%s'."),
p->saved_arg);
}
+
+ return reg;
}
/* This function is responsible for parsing a single operand.
A single operand can be:
- an unary operation (e.g., `-5', `~2', or even with subexpressions
- like `-(2 + 1)')
+ like `-(2 + 1)')
- a register displacement, which will be treated as a register
- operand (e.g., `-4(%eax)' on x86)
+ operand (e.g., `-4(%eax)' on x86)
- a numeric constant, or
- a register operand (see function `stap_parse_register_operand')
unrecognized operands, allowing arch-specific parsers to be
created. */
-static void
+static expr::operation_up
stap_parse_single_operand (struct stap_parse_info *p)
{
struct gdbarch *gdbarch = p->gdbarch;
const char *int_prefix = NULL;
+ using namespace expr;
+
/* We first try to parse this token as a "special token". */
- if (gdbarch_stap_parse_special_token_p (gdbarch)
- && (gdbarch_stap_parse_special_token (gdbarch, p) != 0))
+ if (gdbarch_stap_parse_special_token_p (gdbarch))
{
- /* If the return value of the above function is not zero,
- it means it successfully parsed the special token.
-
- If it is NULL, we try to parse it using our method. */
- return;
+ operation_up token = gdbarch_stap_parse_special_token (gdbarch, p);
+ if (token != nullptr)
+ return token;
}
- if (*p->arg == '-' || *p->arg == '~' || *p->arg == '+')
+ struct type *long_type = builtin_type (gdbarch)->builtin_long;
+ operation_up result;
+ if (*p->arg == '-' || *p->arg == '~' || *p->arg == '+' || *p->arg == '!')
{
char c = *p->arg;
/* We use this variable to do a lookahead. */
error (_("Invalid operator `%c' for register displacement "
"on expression `%s'."), c, p->saved_arg);
- stap_parse_register_operand (p);
+ result = stap_parse_register_operand (p);
}
else
{
/* This is not a displacement. We skip the operator, and
deal with it when the recursion returns. */
++p->arg;
- stap_parse_argument_conditionally (p);
+ result = stap_parse_argument_conditionally (p);
if (c == '-')
- write_exp_elt_opcode (&p->pstate, UNOP_NEG);
+ result = make_operation<unary_neg_operation> (std::move (result));
else if (c == '~')
- write_exp_elt_opcode (&p->pstate, UNOP_COMPLEMENT);
+ result = (make_operation<unary_complement_operation>
+ (std::move (result)));
+ else if (c == '!')
+ result = (make_operation<unary_logical_not_operation>
+ (std::move (result)));
}
}
else if (isdigit (*p->arg))
const char *int_suffix;
/* We are dealing with a numeric constant. */
- write_exp_elt_opcode (&p->pstate, OP_LONG);
- write_exp_elt_type (&p->pstate,
- builtin_type (gdbarch)->builtin_long);
- write_exp_elt_longcst (&p->pstate, number);
- write_exp_elt_opcode (&p->pstate, OP_LONG);
+ result = make_operation<long_const_operation> (long_type, number);
p->arg = tmp;
p->saved_arg);
}
else if (stap_is_register_indirection_prefix (gdbarch, tmp, NULL))
- stap_parse_register_operand (p);
+ result = stap_parse_register_operand (p);
else
error (_("Unknown numeric token on expression `%s'."),
p->saved_arg);
number = strtol (p->arg, &endp, 10);
p->arg = endp;
- write_exp_elt_opcode (&p->pstate, OP_LONG);
- write_exp_elt_type (&p->pstate, builtin_type (gdbarch)->builtin_long);
- write_exp_elt_longcst (&p->pstate, number);
- write_exp_elt_opcode (&p->pstate, OP_LONG);
+ result = make_operation<long_const_operation> (long_type, number);
if (stap_check_integer_suffix (gdbarch, p->arg, &int_suffix))
p->arg += strlen (int_suffix);
}
else if (stap_is_register_prefix (gdbarch, p->arg, NULL)
|| stap_is_register_indirection_prefix (gdbarch, p->arg, NULL))
- stap_parse_register_operand (p);
+ result = stap_parse_register_operand (p);
else
error (_("Operator `%c' not recognized on expression `%s'."),
*p->arg, p->saved_arg);
+
+ return result;
}
/* This function parses an argument conditionally, based on single or
starts with `-', `~', `+' (i.e., unary operators), a digit, or
something recognized by `gdbarch_stap_is_single_operand'. */
-static void
+static expr::operation_up
stap_parse_argument_conditionally (struct stap_parse_info *p)
{
gdb_assert (gdbarch_stap_is_single_operand_p (p->gdbarch));
- if (*p->arg == '-' || *p->arg == '~' || *p->arg == '+' /* Unary. */
+ expr::operation_up result;
+ if (*p->arg == '-' || *p->arg == '~' || *p->arg == '+' || *p->arg == '!'
|| isdigit (*p->arg)
|| gdbarch_stap_is_single_operand (p->gdbarch, p->arg))
- stap_parse_single_operand (p);
+ result = stap_parse_single_operand (p);
else if (*p->arg == '(')
{
/* We are dealing with a parenthesized operand. It means we
p->arg = skip_spaces (p->arg);
++p->inside_paren_p;
- stap_parse_argument_1 (p, 0, STAP_OPERAND_PREC_NONE);
+ result = stap_parse_argument_1 (p, {}, STAP_OPERAND_PREC_NONE);
- --p->inside_paren_p;
+ p->arg = skip_spaces (p->arg);
if (*p->arg != ')')
- error (_("Missign close-paren on expression `%s'."),
+ error (_("Missing close-parenthesis on expression `%s'."),
p->saved_arg);
+ --p->inside_paren_p;
++p->arg;
if (p->inside_paren_p)
p->arg = skip_spaces (p->arg);
}
else
error (_("Cannot parse expression `%s'."), p->saved_arg);
+
+ return result;
}
/* Helper function for `stap_parse_argument'. Please, see its comments to
better understand what this function does. */
-static void
-stap_parse_argument_1 (struct stap_parse_info *p, bool has_lhs,
+static expr::operation_up ATTRIBUTE_UNUSED_RESULT
+stap_parse_argument_1 (struct stap_parse_info *p,
+ expr::operation_up &&lhs_in,
enum stap_operand_prec prec)
{
/* This is an operator-precedence parser.
if (p->inside_paren_p)
p->arg = skip_spaces (p->arg);
- if (!has_lhs)
+ using namespace expr;
+ operation_up lhs = std::move (lhs_in);
+ if (lhs == nullptr)
{
/* We were called without a left-side, either because this is the
first call, or because we were called to parse a parenthesized
expression. It doesn't really matter; we have to parse the
left-side in order to continue the process. */
- stap_parse_argument_conditionally (p);
+ lhs = stap_parse_argument_conditionally (p);
}
+ if (p->inside_paren_p)
+ p->arg = skip_spaces (p->arg);
+
/* Start to parse the right-side, and to "join" left and right sides
depending on the operation specified.
if (p->inside_paren_p)
p->arg = skip_spaces (p->arg);
- /* Parse the right-side of the expression. */
- stap_parse_argument_conditionally (p);
+ /* Parse the right-side of the expression.
+
+ We save whether the right-side is a parenthesized
+ subexpression because, if it is, we will have to finish
+ processing this part of the expression before continuing. */
+ bool paren_subexp = *p->arg == '(';
+
+ operation_up rhs = stap_parse_argument_conditionally (p);
+ if (p->inside_paren_p)
+ p->arg = skip_spaces (p->arg);
+ if (paren_subexp)
+ {
+ lhs = stap_make_binop (opcode, std::move (lhs), std::move (rhs));
+ continue;
+ }
/* While we still have operators, try to parse another
right-side, but using the current right-side as a left-side. */
break;
}
- /* Parse the right-side of the expression, but since we already
- have a left-side at this point, set `has_lhs' to 1. */
- stap_parse_argument_1 (p, 1, lookahead_prec);
+ /* Parse the right-side of the expression, using the current
+ right-hand-side as the left-hand-side of the new
+ subexpression. */
+ rhs = stap_parse_argument_1 (p, std::move (rhs), lookahead_prec);
+ if (p->inside_paren_p)
+ p->arg = skip_spaces (p->arg);
}
- write_exp_elt_opcode (&p->pstate, opcode);
+ lhs = stap_make_binop (opcode, std::move (lhs), std::move (rhs));
}
+
+ return lhs;
}
/* Parse a probe's argument.
struct stap_parse_info p (*arg, atype, language_def (language_c),
gdbarch);
- stap_parse_argument_1 (&p, 0, STAP_OPERAND_PREC_NONE);
+ using namespace expr;
+ operation_up result = stap_parse_argument_1 (&p, {}, STAP_OPERAND_PREC_NONE);
gdb_assert (p.inside_paren_p == 0);
/* Casting the final expression to the appropriate type. */
- write_exp_elt_opcode (&p.pstate, UNOP_CAST);
- write_exp_elt_type (&p.pstate, atype);
- write_exp_elt_opcode (&p.pstate, UNOP_CAST);
+ result = make_operation<unop_cast_operation> (std::move (result), atype);
+ p.pstate.set_operation (std::move (result));
p.arg = skip_spaces (p.arg);
*arg = p.arg;
expression_up expr = stap_parse_argument (&cur, atype, gdbarch);
- if (stap_expression_debug)
- dump_raw_expression (expr.get (), gdb_stdlog,
- "before conversion to prefix form");
-
- prefixify_expression (expr.get ());
-
if (stap_expression_debug)
dump_prefix_expression (expr.get (), gdb_stdlog);
static CORE_ADDR
relocate_address (CORE_ADDR address, struct objfile *objfile)
{
- return address + ANOFFSET (objfile->section_offsets,
- SECT_OFF_DATA (objfile));
+ return address + objfile->text_section_offset ();
}
/* Implementation of the get_relocated_address method. */
argument string. */
unsigned
-stap_probe::get_argument_count (struct frame_info *frame)
+stap_probe::get_argument_count (struct gdbarch *gdbarch)
{
- struct gdbarch *gdbarch = get_frame_arch (frame);
-
if (!m_have_parsed_args)
{
if (this->can_evaluate_arguments ())
stap_probe::evaluate_argument (unsigned n, struct frame_info *frame)
{
struct stap_probe_arg *arg;
- int pos = 0;
struct gdbarch *gdbarch = get_frame_arch (frame);
arg = this->get_arg_by_number (n, gdbarch);
- return evaluate_subexp_standard (arg->atype, arg->aexpr.get (), &pos,
- EVAL_NORMAL);
+ return evaluate_expression (arg->aexpr.get (), arg->atype);
}
/* Compile the probe's argument N (indexed from 0) to agent expression.
unsigned n)
{
struct stap_probe_arg *arg;
- union exp_element *pc;
arg = this->get_arg_by_number (n, expr->gdbarch);
- pc = arg->aexpr->elts;
- gen_expr (arg->aexpr.get (), &pc, expr, value);
+ arg->aexpr->op->generate_ax (arg->aexpr.get (), expr, value);
require_rvalue (expr, value);
value->type = arg->atype;
struct type *type = builtin_type (gdbarch)->builtin_unsigned_short;
ULONGEST value;
- if (address == 0)
- return;
-
/* Swallow errors. */
if (target_read_memory (address, bytes, TYPE_LENGTH (type)) != 0)
{
return;
}
- value = extract_unsigned_integer (bytes, TYPE_LENGTH (type),
- gdbarch_byte_order (gdbarch));
+ enum bfd_endian byte_order = type_byte_order (type);
+ value = extract_unsigned_integer (bytes, TYPE_LENGTH (type), byte_order);
/* Note that we explicitly don't worry about overflow or
underflow. */
if (set)
else
--value;
- store_unsigned_integer (bytes, TYPE_LENGTH (type),
- gdbarch_byte_order (gdbarch), value);
+ store_unsigned_integer (bytes, TYPE_LENGTH (type), byte_order, value);
if (target_write_memory (address, bytes, TYPE_LENGTH (type)) != 0)
warning (_("Could not write the value of a SystemTap semaphore."));
void
stap_probe::set_semaphore (struct objfile *objfile, struct gdbarch *gdbarch)
{
+ if (m_sem_addr == 0)
+ return;
stap_modify_semaphore (relocate_address (m_sem_addr, objfile), 1, gdbarch);
}
void
stap_probe::clear_semaphore (struct objfile *objfile, struct gdbarch *gdbarch)
{
+ if (m_sem_addr == 0)
+ return;
stap_modify_semaphore (relocate_address (m_sem_addr, objfile), 0, gdbarch);
}
{
bfd *abfd = objfile->obfd;
int size = bfd_get_arch_size (abfd) / 8;
- struct gdbarch *gdbarch = get_objfile_arch (objfile);
+ struct gdbarch *gdbarch = objfile->arch ();
struct type *ptr_type = builtin_type (gdbarch)->builtin_data_ptr;
/* Provider and the name of the probe. */
probesp->emplace_back (ret);
}
-/* Helper function which tries to find the base address of the SystemTap
- base section named STAP_BASE_SECTION_NAME. */
-
-static void
-get_stap_base_address_1 (bfd *abfd, asection *sect, void *obj)
-{
- asection **ret = (asection **) obj;
-
- if ((sect->flags & (SEC_DATA | SEC_ALLOC | SEC_HAS_CONTENTS))
- && sect->name && !strcmp (sect->name, STAP_BASE_SECTION_NAME))
- *ret = sect;
-}
-
/* Helper function which iterates over every section in the BFD file,
trying to find the base address of the SystemTap base section.
Returns 1 if found (setting BASE to the proper value), zero otherwise. */
{
asection *ret = NULL;
- bfd_map_over_sections (obfd, get_stap_base_address_1, (void *) &ret);
+ for (asection *sect : gdb_bfd_sections (obfd))
+ if ((sect->flags & (SEC_DATA | SEC_ALLOC | SEC_HAS_CONTENTS))
+ && sect->name && !strcmp (sect->name, STAP_BASE_SECTION_NAME))
+ ret = sect;
if (ret == NULL)
{
complaint (_("could not obtain base address for "
"SystemTap section on objfile `%s'."),
- obfd->filename);
+ bfd_get_filename (obfd));
return 0;
}
info_probes_for_spops (arg, from_tty, &stap_static_probe_ops);
}
+void _initialize_stap_probe ();
void
-_initialize_stap_probe (void)
+_initialize_stap_probe ()
{
all_static_probe_ops.push_back (&stap_static_probe_ops);
OBJECT matches the executable or shared library name."),
info_probes_cmdlist_get ());
+
+ using namespace expr;
+ stap_maker_map[BINOP_ADD] = make_operation<add_operation>;
+ stap_maker_map[BINOP_BITWISE_AND] = make_operation<bitwise_and_operation>;
+ stap_maker_map[BINOP_BITWISE_IOR] = make_operation<bitwise_ior_operation>;
+ stap_maker_map[BINOP_BITWISE_XOR] = make_operation<bitwise_xor_operation>;
+ stap_maker_map[BINOP_DIV] = make_operation<div_operation>;
+ stap_maker_map[BINOP_EQUAL] = make_operation<equal_operation>;
+ stap_maker_map[BINOP_GEQ] = make_operation<geq_operation>;
+ stap_maker_map[BINOP_GTR] = make_operation<gtr_operation>;
+ stap_maker_map[BINOP_LEQ] = make_operation<leq_operation>;
+ stap_maker_map[BINOP_LESS] = make_operation<less_operation>;
+ stap_maker_map[BINOP_LOGICAL_AND] = make_operation<logical_and_operation>;
+ stap_maker_map[BINOP_LOGICAL_OR] = make_operation<logical_or_operation>;
+ stap_maker_map[BINOP_LSH] = make_operation<lsh_operation>;
+ stap_maker_map[BINOP_MUL] = make_operation<mul_operation>;
+ stap_maker_map[BINOP_NOTEQUAL] = make_operation<notequal_operation>;
+ stap_maker_map[BINOP_REM] = make_operation<rem_operation>;
+ stap_maker_map[BINOP_RSH] = make_operation<rsh_operation>;
+ stap_maker_map[BINOP_SUB] = make_operation<sub_operation>;
}