| /* Parse expressions for GDB. |
| |
| Copyright (C) 1986-2021 Free Software Foundation, Inc. |
| |
| Modified from expread.y by the Department of Computer Science at the |
| State University of New York at Buffalo, 1991. |
| |
| This file is part of GDB. |
| |
| This program is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program. If not, see <http://www.gnu.org/licenses/>. */ |
| |
| /* Parse an expression from text in a string, |
| and return the result as a struct expression pointer. |
| That structure contains arithmetic operations in reverse polish, |
| with constants represented by operations that are followed by special data. |
| See expression.h for the details of the format. |
| What is important here is that it can be built up sequentially |
| during the process of parsing; the lower levels of the tree always |
| come first in the result. */ |
| |
| #include "defs.h" |
| #include <ctype.h> |
| #include "arch-utils.h" |
| #include "symtab.h" |
| #include "gdbtypes.h" |
| #include "frame.h" |
| #include "expression.h" |
| #include "value.h" |
| #include "command.h" |
| #include "language.h" |
| #include "parser-defs.h" |
| #include "gdbcmd.h" |
| #include "symfile.h" /* for overlay functions */ |
| #include "inferior.h" |
| #include "target-float.h" |
| #include "block.h" |
| #include "source.h" |
| #include "objfiles.h" |
| #include "user-regs.h" |
| #include <algorithm> |
| #include "gdbsupport/gdb_optional.h" |
| #include "c-exp.h" |
| |
| static unsigned int expressiondebug = 0; |
| static void |
| show_expressiondebug (struct ui_file *file, int from_tty, |
| struct cmd_list_element *c, const char *value) |
| { |
| fprintf_filtered (file, _("Expression debugging is %s.\n"), value); |
| } |
| |
| |
| /* True if an expression parser should set yydebug. */ |
| bool parser_debug; |
| |
| static void |
| show_parserdebug (struct ui_file *file, int from_tty, |
| struct cmd_list_element *c, const char *value) |
| { |
| fprintf_filtered (file, _("Parser debugging is %s.\n"), value); |
| } |
| |
| |
| static expression_up parse_exp_in_context (const char **, CORE_ADDR, |
| const struct block *, int, |
| bool, innermost_block_tracker *, |
| expr_completion_state *); |
| |
| /* Documented at it's declaration. */ |
| |
| void |
| innermost_block_tracker::update (const struct block *b, |
| innermost_block_tracker_types t) |
| { |
| if ((m_types & t) != 0 |
| && (m_innermost_block == NULL |
| || contained_in (b, m_innermost_block))) |
| m_innermost_block = b; |
| } |
| |
| |
| |
| /* Return the type of MSYMBOL, a minimal symbol of OBJFILE. If |
| ADDRESS_P is not NULL, set it to the MSYMBOL's resolved |
| address. */ |
| |
| type * |
| find_minsym_type_and_address (minimal_symbol *msymbol, |
| struct objfile *objfile, |
| CORE_ADDR *address_p) |
| { |
| bound_minimal_symbol bound_msym = {msymbol, objfile}; |
| struct obj_section *section = msymbol->obj_section (objfile); |
| enum minimal_symbol_type type = MSYMBOL_TYPE (msymbol); |
| |
| bool is_tls = (section != NULL |
| && section->the_bfd_section->flags & SEC_THREAD_LOCAL); |
| |
| /* The minimal symbol might point to a function descriptor; |
| resolve it to the actual code address instead. */ |
| CORE_ADDR addr; |
| if (is_tls) |
| { |
| /* Addresses of TLS symbols are really offsets into a |
| per-objfile/per-thread storage block. */ |
| addr = MSYMBOL_VALUE_RAW_ADDRESS (bound_msym.minsym); |
| } |
| else if (msymbol_is_function (objfile, msymbol, &addr)) |
| { |
| if (addr != BMSYMBOL_VALUE_ADDRESS (bound_msym)) |
| { |
| /* This means we resolved a function descriptor, and we now |
| have an address for a code/text symbol instead of a data |
| symbol. */ |
| if (MSYMBOL_TYPE (msymbol) == mst_data_gnu_ifunc) |
| type = mst_text_gnu_ifunc; |
| else |
| type = mst_text; |
| section = NULL; |
| } |
| } |
| else |
| addr = BMSYMBOL_VALUE_ADDRESS (bound_msym); |
| |
| if (overlay_debugging) |
| addr = symbol_overlayed_address (addr, section); |
| |
| if (is_tls) |
| { |
| /* Skip translation if caller does not need the address. */ |
| if (address_p != NULL) |
| *address_p = target_translate_tls_address (objfile, addr); |
| return objfile_type (objfile)->nodebug_tls_symbol; |
| } |
| |
| if (address_p != NULL) |
| *address_p = addr; |
| |
| switch (type) |
| { |
| case mst_text: |
| case mst_file_text: |
| case mst_solib_trampoline: |
| return objfile_type (objfile)->nodebug_text_symbol; |
| |
| case mst_text_gnu_ifunc: |
| return objfile_type (objfile)->nodebug_text_gnu_ifunc_symbol; |
| |
| case mst_data: |
| case mst_file_data: |
| case mst_bss: |
| case mst_file_bss: |
| return objfile_type (objfile)->nodebug_data_symbol; |
| |
| case mst_slot_got_plt: |
| return objfile_type (objfile)->nodebug_got_plt_symbol; |
| |
| default: |
| return objfile_type (objfile)->nodebug_unknown_symbol; |
| } |
| } |
| |
| /* See parser-defs.h. */ |
| |
| void |
| parser_state::mark_struct_expression (expr::structop_base_operation *op) |
| { |
| gdb_assert (parse_completion |
| && (m_completion_state.expout_tag_completion_type |
| == TYPE_CODE_UNDEF)); |
| m_completion_state.expout_last_op = op; |
| } |
| |
| /* Indicate that the current parser invocation is completing a tag. |
| TAG is the type code of the tag, and PTR and LENGTH represent the |
| start of the tag name. */ |
| |
| void |
| parser_state::mark_completion_tag (enum type_code tag, const char *ptr, |
| int length) |
| { |
| gdb_assert (parse_completion |
| && (m_completion_state.expout_tag_completion_type |
| == TYPE_CODE_UNDEF) |
| && m_completion_state.expout_completion_name == NULL |
| && m_completion_state.expout_last_op == nullptr); |
| gdb_assert (tag == TYPE_CODE_UNION |
| || tag == TYPE_CODE_STRUCT |
| || tag == TYPE_CODE_ENUM); |
| m_completion_state.expout_tag_completion_type = tag; |
| m_completion_state.expout_completion_name.reset (xstrndup (ptr, length)); |
| } |
| |
| /* See parser-defs.h. */ |
| |
| void |
| parser_state::push_c_string (int kind, struct stoken_vector *vec) |
| { |
| std::vector<std::string> data (vec->len); |
| for (int i = 0; i < vec->len; ++i) |
| data[i] = std::string (vec->tokens[i].ptr, vec->tokens[i].length); |
| |
| push_new<expr::c_string_operation> ((enum c_string_type_values) kind, |
| std::move (data)); |
| } |
| |
| /* See parser-defs.h. */ |
| |
| void |
| parser_state::push_symbol (const char *name, block_symbol sym) |
| { |
| if (sym.symbol != nullptr) |
| { |
| if (symbol_read_needs_frame (sym.symbol)) |
| block_tracker->update (sym); |
| push_new<expr::var_value_operation> (sym); |
| } |
| else |
| { |
| struct bound_minimal_symbol msymbol = lookup_bound_minimal_symbol (name); |
| if (msymbol.minsym != NULL) |
| push_new<expr::var_msym_value_operation> (msymbol); |
| else if (!have_full_symbols () && !have_partial_symbols ()) |
| error (_("No symbol table is loaded. Use the \"file\" command.")); |
| else |
| error (_("No symbol \"%s\" in current context."), name); |
| } |
| } |
| |
| /* See parser-defs.h. */ |
| |
| void |
| parser_state::push_dollar (struct stoken str) |
| { |
| struct block_symbol sym; |
| struct bound_minimal_symbol msym; |
| struct internalvar *isym = NULL; |
| std::string copy; |
| |
| /* Handle the tokens $digits; also $ (short for $0) and $$ (short for $$1) |
| and $$digits (equivalent to $<-digits> if you could type that). */ |
| |
| int negate = 0; |
| int i = 1; |
| /* Double dollar means negate the number and add -1 as well. |
| Thus $$ alone means -1. */ |
| if (str.length >= 2 && str.ptr[1] == '$') |
| { |
| negate = 1; |
| i = 2; |
| } |
| if (i == str.length) |
| { |
| /* Just dollars (one or two). */ |
| i = -negate; |
| goto handle_last; |
| } |
| /* Is the rest of the token digits? */ |
| for (; i < str.length; i++) |
| if (!(str.ptr[i] >= '0' && str.ptr[i] <= '9')) |
| break; |
| if (i == str.length) |
| { |
| i = atoi (str.ptr + 1 + negate); |
| if (negate) |
| i = -i; |
| goto handle_last; |
| } |
| |
| /* Handle tokens that refer to machine registers: |
| $ followed by a register name. */ |
| i = user_reg_map_name_to_regnum (gdbarch (), |
| str.ptr + 1, str.length - 1); |
| if (i >= 0) |
| goto handle_register; |
| |
| /* Any names starting with $ are probably debugger internal variables. */ |
| |
| copy = copy_name (str); |
| isym = lookup_only_internalvar (copy.c_str () + 1); |
| if (isym) |
| { |
| push_new<expr::internalvar_operation> (isym); |
| return; |
| } |
| |
| /* On some systems, such as HP-UX and hppa-linux, certain system routines |
| have names beginning with $ or $$. Check for those, first. */ |
| |
| sym = lookup_symbol (copy.c_str (), NULL, VAR_DOMAIN, NULL); |
| if (sym.symbol) |
| { |
| push_new<expr::var_value_operation> (sym); |
| return; |
| } |
| msym = lookup_bound_minimal_symbol (copy.c_str ()); |
| if (msym.minsym) |
| { |
| push_new<expr::var_msym_value_operation> (msym); |
| return; |
| } |
| |
| /* Any other names are assumed to be debugger internal variables. */ |
| |
| push_new<expr::internalvar_operation> |
| (create_internalvar (copy.c_str () + 1)); |
| return; |
| handle_last: |
| push_new<expr::last_operation> (i); |
| return; |
| handle_register: |
| str.length--; |
| str.ptr++; |
| push_new<expr::register_operation> (copy_name (str)); |
| block_tracker->update (expression_context_block, |
| INNERMOST_BLOCK_FOR_REGISTERS); |
| return; |
| } |
| |
| |
| |
| const char * |
| find_template_name_end (const char *p) |
| { |
| int depth = 1; |
| int just_seen_right = 0; |
| int just_seen_colon = 0; |
| int just_seen_space = 0; |
| |
| if (!p || (*p != '<')) |
| return 0; |
| |
| while (*++p) |
| { |
| switch (*p) |
| { |
| case '\'': |
| case '\"': |
| case '{': |
| case '}': |
| /* In future, may want to allow these?? */ |
| return 0; |
| case '<': |
| depth++; /* start nested template */ |
| if (just_seen_colon || just_seen_right || just_seen_space) |
| return 0; /* but not after : or :: or > or space */ |
| break; |
| case '>': |
| if (just_seen_colon || just_seen_right) |
| return 0; /* end a (nested?) template */ |
| just_seen_right = 1; /* but not after : or :: */ |
| if (--depth == 0) /* also disallow >>, insist on > > */ |
| return ++p; /* if outermost ended, return */ |
| break; |
| case ':': |
| if (just_seen_space || (just_seen_colon > 1)) |
| return 0; /* nested class spec coming up */ |
| just_seen_colon++; /* we allow :: but not :::: */ |
| break; |
| case ' ': |
| break; |
| default: |
| if (!((*p >= 'a' && *p <= 'z') || /* allow token chars */ |
| (*p >= 'A' && *p <= 'Z') || |
| (*p >= '0' && *p <= '9') || |
| (*p == '_') || (*p == ',') || /* commas for template args */ |
| (*p == '&') || (*p == '*') || /* pointer and ref types */ |
| (*p == '(') || (*p == ')') || /* function types */ |
| (*p == '[') || (*p == ']'))) /* array types */ |
| return 0; |
| } |
| if (*p != ' ') |
| just_seen_space = 0; |
| if (*p != ':') |
| just_seen_colon = 0; |
| if (*p != '>') |
| just_seen_right = 0; |
| } |
| return 0; |
| } |
| |
| |
| /* Return a null-terminated temporary copy of the name of a string token. |
| |
| Tokens that refer to names do so with explicit pointer and length, |
| so they can share the storage that lexptr is parsing. |
| When it is necessary to pass a name to a function that expects |
| a null-terminated string, the substring is copied out |
| into a separate block of storage. */ |
| |
| std::string |
| copy_name (struct stoken token) |
| { |
| return std::string (token.ptr, token.length); |
| } |
| |
| |
| /* Read an expression from the string *STRINGPTR points to, |
| parse it, and return a pointer to a struct expression that we malloc. |
| Use block BLOCK as the lexical context for variable names; |
| if BLOCK is zero, use the block of the selected stack frame. |
| Meanwhile, advance *STRINGPTR to point after the expression, |
| at the first nonwhite character that is not part of the expression |
| (possibly a null character). |
| |
| If COMMA is nonzero, stop if a comma is reached. */ |
| |
| expression_up |
| parse_exp_1 (const char **stringptr, CORE_ADDR pc, const struct block *block, |
| int comma, innermost_block_tracker *tracker) |
| { |
| return parse_exp_in_context (stringptr, pc, block, comma, false, |
| tracker, nullptr); |
| } |
| |
| /* As for parse_exp_1, except that if VOID_CONTEXT_P, then |
| no value is expected from the expression. */ |
| |
| static expression_up |
| parse_exp_in_context (const char **stringptr, CORE_ADDR pc, |
| const struct block *block, |
| int comma, bool void_context_p, |
| innermost_block_tracker *tracker, |
| expr_completion_state *cstate) |
| { |
| const struct language_defn *lang = NULL; |
| |
| if (*stringptr == 0 || **stringptr == 0) |
| error_no_arg (_("expression to compute")); |
| |
| const struct block *expression_context_block = block; |
| CORE_ADDR expression_context_pc = 0; |
| |
| innermost_block_tracker local_tracker; |
| if (tracker == nullptr) |
| tracker = &local_tracker; |
| |
| /* If no context specified, try using the current frame, if any. */ |
| if (!expression_context_block) |
| expression_context_block = get_selected_block (&expression_context_pc); |
| else if (pc == 0) |
| expression_context_pc = BLOCK_ENTRY_PC (expression_context_block); |
| else |
| expression_context_pc = pc; |
| |
| /* Fall back to using the current source static context, if any. */ |
| |
| if (!expression_context_block) |
| { |
| struct symtab_and_line cursal = get_current_source_symtab_and_line (); |
| if (cursal.symtab) |
| expression_context_block |
| = BLOCKVECTOR_BLOCK (SYMTAB_BLOCKVECTOR (cursal.symtab), |
| STATIC_BLOCK); |
| if (expression_context_block) |
| expression_context_pc = BLOCK_ENTRY_PC (expression_context_block); |
| } |
| |
| if (language_mode == language_mode_auto && block != NULL) |
| { |
| /* Find the language associated to the given context block. |
| Default to the current language if it can not be determined. |
| |
| Note that using the language corresponding to the current frame |
| can sometimes give unexpected results. For instance, this |
| routine is often called several times during the inferior |
| startup phase to re-parse breakpoint expressions after |
| a new shared library has been loaded. The language associated |
| to the current frame at this moment is not relevant for |
| the breakpoint. Using it would therefore be silly, so it seems |
| better to rely on the current language rather than relying on |
| the current frame language to parse the expression. That's why |
| we do the following language detection only if the context block |
| has been specifically provided. */ |
| struct symbol *func = block_linkage_function (block); |
| |
| if (func != NULL) |
| lang = language_def (func->language ()); |
| if (lang == NULL || lang->la_language == language_unknown) |
| lang = current_language; |
| } |
| else |
| lang = current_language; |
| |
| /* get_current_arch may reset CURRENT_LANGUAGE via select_frame. |
| While we need CURRENT_LANGUAGE to be set to LANG (for lookup_symbol |
| and others called from *.y) ensure CURRENT_LANGUAGE gets restored |
| to the value matching SELECTED_FRAME as set by get_current_arch. */ |
| |
| parser_state ps (lang, get_current_arch (), expression_context_block, |
| expression_context_pc, comma, *stringptr, |
| cstate != nullptr, tracker, void_context_p); |
| |
| scoped_restore_current_language lang_saver; |
| set_language (lang->la_language); |
| |
| try |
| { |
| lang->parser (&ps); |
| } |
| catch (const gdb_exception &except) |
| { |
| /* If parsing for completion, allow this to succeed; but if no |
| expression elements have been written, then there's nothing |
| to do, so fail. */ |
| if (! ps.parse_completion || ps.expout->op == nullptr) |
| throw; |
| } |
| |
| expression_up result = ps.release (); |
| result->op->set_outermost (); |
| |
| if (expressiondebug) |
| dump_prefix_expression (result.get (), gdb_stdlog); |
| |
| if (cstate != nullptr) |
| *cstate = std::move (ps.m_completion_state); |
| *stringptr = ps.lexptr; |
| return result; |
| } |
| |
| /* Parse STRING as an expression, and complain if this fails to use up |
| all of the contents of STRING. TRACKER, if non-null, will be |
| updated by the parser. VOID_CONTEXT_P should be true to indicate |
| that the expression may be expected to return a value with void |
| type. Parsers are free to ignore this, or to use it to help with |
| overload resolution decisions. */ |
| |
| expression_up |
| parse_expression (const char *string, innermost_block_tracker *tracker, |
| bool void_context_p) |
| { |
| expression_up exp = parse_exp_in_context (&string, 0, nullptr, 0, |
| void_context_p, |
| tracker, nullptr); |
| if (*string) |
| error (_("Junk after end of expression.")); |
| return exp; |
| } |
| |
| /* Same as parse_expression, but using the given language (LANG) |
| to parse the expression. */ |
| |
| expression_up |
| parse_expression_with_language (const char *string, enum language lang) |
| { |
| gdb::optional<scoped_restore_current_language> lang_saver; |
| if (current_language->la_language != lang) |
| { |
| lang_saver.emplace (); |
| set_language (lang); |
| } |
| |
| return parse_expression (string); |
| } |
| |
| /* Parse STRING as an expression. If parsing ends in the middle of a |
| field reference, return the type of the left-hand-side of the |
| reference; furthermore, if the parsing ends in the field name, |
| return the field name in *NAME. If the parsing ends in the middle |
| of a field reference, but the reference is somehow invalid, throw |
| an exception. In all other cases, return NULL. */ |
| |
| struct type * |
| parse_expression_for_completion (const char *string, |
| gdb::unique_xmalloc_ptr<char> *name, |
| enum type_code *code) |
| { |
| expression_up exp; |
| expr_completion_state cstate; |
| |
| try |
| { |
| exp = parse_exp_in_context (&string, 0, 0, 0, false, nullptr, &cstate); |
| } |
| catch (const gdb_exception_error &except) |
| { |
| /* Nothing, EXP remains NULL. */ |
| } |
| |
| if (exp == NULL) |
| return NULL; |
| |
| if (cstate.expout_tag_completion_type != TYPE_CODE_UNDEF) |
| { |
| *code = cstate.expout_tag_completion_type; |
| *name = std::move (cstate.expout_completion_name); |
| return NULL; |
| } |
| |
| if (cstate.expout_last_op == nullptr) |
| return nullptr; |
| |
| expr::structop_base_operation *op = cstate.expout_last_op; |
| const std::string &fld = op->get_string (); |
| *name = make_unique_xstrdup (fld.c_str ()); |
| return value_type (op->evaluate_lhs (exp.get ())); |
| } |
| |
| /* Parse floating point value P of length LEN. |
| Return false if invalid, true if valid. |
| The successfully parsed number is stored in DATA in |
| target format for floating-point type TYPE. |
| |
| NOTE: This accepts the floating point syntax that sscanf accepts. */ |
| |
| bool |
| parse_float (const char *p, int len, |
| const struct type *type, gdb_byte *data) |
| { |
| return target_float_from_string (data, type, std::string (p, len)); |
| } |
| |
| /* This function avoids direct calls to fprintf |
| in the parser generated debug code. */ |
| void |
| parser_fprintf (FILE *x, const char *y, ...) |
| { |
| va_list args; |
| |
| va_start (args, y); |
| if (x == stderr) |
| vfprintf_unfiltered (gdb_stderr, y, args); |
| else |
| { |
| fprintf_unfiltered (gdb_stderr, " Unknown FILE used.\n"); |
| vfprintf_unfiltered (gdb_stderr, y, args); |
| } |
| va_end (args); |
| } |
| |
| /* Return rue if EXP uses OBJFILE (and will become dangling when |
| OBJFILE is unloaded), otherwise return false. OBJFILE must not be |
| a separate debug info file. */ |
| |
| bool |
| exp_uses_objfile (struct expression *exp, struct objfile *objfile) |
| { |
| gdb_assert (objfile->separate_debug_objfile_backlink == NULL); |
| |
| return exp->op->uses_objfile (objfile); |
| } |
| |
| void _initialize_parse (); |
| void |
| _initialize_parse () |
| { |
| add_setshow_zuinteger_cmd ("expression", class_maintenance, |
| &expressiondebug, |
| _("Set expression debugging."), |
| _("Show expression debugging."), |
| _("When non-zero, the internal representation " |
| "of expressions will be printed."), |
| NULL, |
| show_expressiondebug, |
| &setdebuglist, &showdebuglist); |
| add_setshow_boolean_cmd ("parser", class_maintenance, |
| &parser_debug, |
| _("Set parser debugging."), |
| _("Show parser debugging."), |
| _("When non-zero, expression parser " |
| "tracing will be enabled."), |
| NULL, |
| show_parserdebug, |
| &setdebuglist, &showdebuglist); |
| } |