diff options
38 files changed, 4449 insertions, 30 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog index d82257e..43e22e2 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,5 +1,34 @@ 2018-08-29 Keith Seitz <keiths@redhat.com> + * Makefile.in (SUBDIR_GCC_COMPILE_SRCS): Add compile-cplus-symbols.c + and compile-cplus-types.c. + (HFILES_NO_SRCDIR): Add gcc-cp-plugin.h. + * c-lang.c (cplus_language_defn): Set C++ compile functions. + * c-lang.h (cplus_get_compile_context, cplus_compute_program): + Declare. + * compile/compile-c-support.c: Include compile-cplus.h. + (load_libcompile): Templatize. + (get_compile_context): "New" function. + (c_get_compile_context): Use get_compile_context. + (cplus_get_compile_context): New function. + (cplus_push_user_expression, cplus_pop_user_expression) + (cplus_add_code_header, cplus_add_input, cplus_compile_program) + (cplus_compute_program): Define new structs/functions. + * compile/compile-cplus-symmbols.c: New file. + * compile/compile-cplus-types.c: New file. + * compile/compile-cplus.h: New file. + * compile/compile-internal.h (debug_compile_oracle, GCC_TYPE_NONE): + Declare. + * compile/compile-object-load.c (get_out_value_type): Use + strncmp_iw when comparing symbol names. + (compile_object_load): Add mst_bss and mst_data. + * compile/compile.c (_initialize_compile): Remove + -Wno-implicit-function-declaration from `compile_args'. + * compile/gcc-cp-plugin.h: New file. + * NEWS: Mention C++ compile support and new debug options. + +2018-08-29 Keith Seitz <keiths@redhat.com> + * linespec.c (collect_info::add_symbol): Make virtual. (struct symbol_searcher_collect_info): New struct. (symbol_searcher::find_all_symbols): New method. diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 542170f..438379e 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -317,6 +317,8 @@ SUBDIR_GCC_COMPILE_SRCS = \ compile/compile-c-support.c \ compile/compile-c-symbols.c \ compile/compile-c-types.c \ + compile/compile-cplus-symbols.c \ + compile/compile-cplus-types.c \ compile/compile-loc2c.c \ compile/compile-object-load.c \ compile/compile-object-run.c @@ -1464,10 +1466,12 @@ HFILES_NO_SRCDIR = \ common/xml-utils.h \ compile/compile.h \ compile/compile-c.h \ + compile/compile-cplus.h \ compile/compile-internal.h \ compile/compile-object-load.h \ compile/compile-object-run.h \ compile/gcc-c-plugin.h \ + compile/gcc-cp-plugin.h \ config/nm-linux.h \ config/nm-nto.h \ config/djgpp/langinfo.h \ @@ -3,6 +3,14 @@ *** Changes since GDB 8.2 +* GDB now has experimental support for the compilation and injection of + C++ source code into the inferior. This beta release does not include + support for several language features, such as templates, constructors, + and operators. + + This feature requires GCC 7.1 or higher built with libcp1.so + (the C++ plug-in). + * GDB and GDBserver now support IPv6 connections. IPv6 addresses can be passed using the '[ADDRESS]:PORT' notation, or the regular 'ADDRESS:PORT' method. @@ -15,6 +23,12 @@ * New commands +set debug compile-cplus-types +show debug compile-cplus-types + Control the display of debug output about type conversion in the + C++ compile feature. Commands have no effect while compiliong + for other languages. + frame apply [all | COUNT | -COUNT | level LEVEL...] [FLAG]... COMMAND Apply a command to some frames. FLAG arguments allow to control what output to produce and how to handle diff --git a/gdb/c-lang.c b/gdb/c-lang.c index 5ad5801..6bae4c1 100644 --- a/gdb/c-lang.c +++ b/gdb/c-lang.c @@ -1017,8 +1017,8 @@ extern const struct language_defn cplus_language_defn = iterate_over_symbols, cp_search_name_hash, &cplus_varobj_ops, - NULL, - NULL, + cplus_get_compile_context, + cplus_compute_program, LANG_MAGIC }; diff --git a/gdb/c-lang.h b/gdb/c-lang.h index b7bf089..ae17abd 100644 --- a/gdb/c-lang.h +++ b/gdb/c-lang.h @@ -160,6 +160,14 @@ extern int c_textual_element_type (struct type *, char); extern compile_instance *c_get_compile_context (void); +/* Create a new instance of the C++ compiler and return it. The new + compiler is owned by the caller and must be freed using the destroy + method. This function never returns NULL, but rather throws an + exception on failure. This is suitable for use as the + la_get_compile_instance language method. */ + +extern compile_instance *cplus_get_compile_context (); + /* This takes the user-supplied text and returns a new bit of code to compile. @@ -172,4 +180,15 @@ extern std::string c_compute_program (compile_instance *inst, const struct block *expr_block, CORE_ADDR expr_pc); +/* This takes the user-supplied text and returns a new bit of code to compile. + + This is used as the la_compute_program language method; see that + for a description of the arguments. */ + +extern std::string cplus_compute_program (compile_instance *inst, + const char *input, + struct gdbarch *gdbarch, + const struct block *expr_block, + CORE_ADDR expr_pc); + #endif /* !defined (C_LANG_H) */ diff --git a/gdb/compile/compile-c-support.c b/gdb/compile/compile-c-support.c index e8993aa..5c700bb 100644 --- a/gdb/compile/compile-c-support.c +++ b/gdb/compile/compile-c-support.c @@ -1,4 +1,4 @@ -/* C language support for compilation. +/* C/C++ language support for compilation. Copyright (C) 2014-2018 Free Software Foundation, Inc. @@ -20,6 +20,7 @@ #include "defs.h" #include "compile-internal.h" #include "compile-c.h" +#include "compile-cplus.h" #include "compile.h" #include "gdb-dlfcn.h" #include "c-lang.h" @@ -67,25 +68,22 @@ c_get_range_decl_name (const struct dynamic_prop *prop) -/* Helper function for c_get_compile_context. Open the GCC front-end - shared library and return the symbol specified by the current - GCC_C_FE_CONTEXT. */ +/* Load the plug-in library FE_LIBCC and return the initialization function + FE_CONTEXT. */ -static gcc_c_fe_context_function * -load_libcc (void) +template <typename FUNCTYPE> +FUNCTYPE * +load_libcompile (const char *fe_libcc, const char *fe_context) { - gcc_c_fe_context_function *func; + FUNCTYPE *func; - /* gdb_dlopen will call error () on an error, so no need to check - value. */ - gdb_dlhandle_up handle = gdb_dlopen (STRINGIFY (GCC_C_FE_LIBCC)); - func = (gcc_c_fe_context_function *) gdb_dlsym (handle, - STRINGIFY (GCC_C_FE_CONTEXT)); + /* gdb_dlopen will call error () on an error, so no need to check + value. */ + gdb_dlhandle_up handle = gdb_dlopen (fe_libcc); + func = (FUNCTYPE *) gdb_dlsym (handle, fe_context); if (func == NULL) - error (_("could not find symbol %s in library %s"), - STRINGIFY (GCC_C_FE_CONTEXT), - STRINGIFY (GCC_C_FE_LIBCC)); + error (_("could not find symbol %s in library %s"), fe_context, fe_libcc); /* Leave the library open. */ handle.release (); @@ -93,28 +91,57 @@ load_libcc (void) } /* Return the compile instance associated with the current context. - This function calls the symbol returned from the load_libcc - function. This will provide the gcc_c_context. */ + This function calls the symbol returned from the load_libcompile + function. FE_LIBCC is the library to load. BASE_VERSION is the + base compile plug-in version we support. API_VERSION is the + API version supported. */ +template <typename INSTTYPE, typename FUNCTYPE, typename CTXTYPE, + typename BASE_VERSION_TYPE, typename API_VERSION_TYPE> compile_instance * -c_get_compile_context (void) +get_compile_context (const char *fe_libcc, const char *fe_context, + BASE_VERSION_TYPE base_version, + API_VERSION_TYPE api_version) { - static gcc_c_fe_context_function *func; - - struct gcc_c_context *context; + static FUNCTYPE *func; + static CTXTYPE *context; if (func == NULL) { - func = load_libcc (); + func = load_libcompile<FUNCTYPE> (fe_libcc, fe_context); gdb_assert (func != NULL); } - context = (*func) (GCC_FE_VERSION_0, GCC_C_FE_VERSION_0); + context = (*func) (base_version, api_version); if (context == NULL) error (_("The loaded version of GCC does not support the required version " "of the API.")); - return new compile_c_instance (context); + return new INSTTYPE (context); +} + +/* A C-language implementation of get_compile_context. */ + +compile_instance * +c_get_compile_context () +{ + return get_compile_context + <compile_c_instance, gcc_c_fe_context_function, gcc_c_context, + gcc_base_api_version, gcc_c_api_version> + (STRINGIFY (GCC_C_FE_LIBCC), STRINGIFY (GCC_C_FE_CONTEXT), + GCC_FE_VERSION_0, GCC_C_FE_VERSION_0); +} + +/* A C++-language implementation of get_compile_context. */ + +compile_instance * +cplus_get_compile_context () +{ + return get_compile_context + <compile_cplus_instance, gcc_cp_fe_context_function, gcc_cp_context, + gcc_base_api_version, gcc_cp_api_version> + (STRINGIFY (GCC_CP_FE_LIBCC), STRINGIFY (GCC_CP_FE_CONTEXT), + GCC_FE_VERSION_0, GCC_CP_FE_VERSION_0); } @@ -384,6 +411,113 @@ struct c_add_input } }; +/* C++-language policy to emit a push user expression pragma into + BUF. */ + +struct cplus_push_user_expression +{ + void push_user_expression (struct ui_file *buf) + { + fputs_unfiltered ("#pragma GCC push_user_expression\n", buf); + } +}; + +/* C++-language policy to emit a pop user expression pragma into BUF. */ + +struct cplus_pop_user_expression +{ + void pop_user_expression (struct ui_file *buf) + { + fputs_unfiltered ("#pragma GCC pop_user_expression\n", buf); + } +}; + +/* C++-language policy to construct a code header for a block of code. + Takes a scope TYPE argument which selects the correct header to + insert into BUF. */ + +struct cplus_add_code_header +{ + void add_code_header (enum compile_i_scope_types type, struct ui_file *buf) + { + switch (type) + { + case COMPILE_I_SIMPLE_SCOPE: + fputs_unfiltered ("void " + GCC_FE_WRAPPER_FUNCTION + " (struct " + COMPILE_I_SIMPLE_REGISTER_STRUCT_TAG + " *" + COMPILE_I_SIMPLE_REGISTER_ARG_NAME + ") {\n", + buf); + break; + + case COMPILE_I_PRINT_ADDRESS_SCOPE: + case COMPILE_I_PRINT_VALUE_SCOPE: + fputs_unfiltered ( + "#include <cstring>\n" + "#include <bits/move.h>\n" + "void " + GCC_FE_WRAPPER_FUNCTION + " (struct " + COMPILE_I_SIMPLE_REGISTER_STRUCT_TAG + " *" + COMPILE_I_SIMPLE_REGISTER_ARG_NAME + ", " + COMPILE_I_PRINT_OUT_ARG_TYPE + " " + COMPILE_I_PRINT_OUT_ARG + ") {\n", + buf); + break; + + case COMPILE_I_RAW_SCOPE: + break; + + default: + gdb_assert_not_reached (_("Unknown compiler scope reached.")); + } + } +}; + +/* C++-language policy to emit the user code snippet INPUT into BUF based on + the scope TYPE. */ + +struct cplus_add_input +{ + void add_input (enum compile_i_scope_types type, const char *input, + struct ui_file *buf) + { + switch (type) + { + case COMPILE_I_PRINT_VALUE_SCOPE: + case COMPILE_I_PRINT_ADDRESS_SCOPE: + fprintf_unfiltered + (buf, + /* "auto" strips ref- and cv- qualifiers, so we need to also strip + those from COMPILE_I_EXPR_PTR_TYPE. */ + "auto " COMPILE_I_EXPR_VAL " = %s;\n" + "typedef " + "std::add_pointer<std::remove_cv<decltype (%s)>::type>::type " + " __gdb_expr_ptr;\n" + "__gdb_expr_ptr " COMPILE_I_EXPR_PTR_TYPE ";\n" + "std::memcpy (" COMPILE_I_PRINT_OUT_ARG ", %s (" + COMPILE_I_EXPR_VAL "),\n" + "\tsizeof (*" COMPILE_I_EXPR_PTR_TYPE "));\n" + ,input, input, + (type == COMPILE_I_PRINT_ADDRESS_SCOPE + ? "__builtin_addressof" : "")); + break; + + default: + fputs_unfiltered (input, buf); + break; + } + fputs_unfiltered ("\n", buf); + } +}; + /* A host class representing a compile program. CompileInstanceType is the type of the compile_instance for the @@ -513,13 +647,18 @@ private: struct gdbarch *m_arch; }; -/* Type used for C program computations. */ +/* The types used for C and C++ program computations. */ typedef compile_program<compile_c_instance, c_push_user_expression, pop_user_expression_nop, c_add_code_header, c_add_code_footer, c_add_input> c_compile_program; +typedef compile_program<compile_cplus_instance, + cplus_push_user_expression, cplus_pop_user_expression, + cplus_add_code_header, c_add_code_footer, + cplus_add_input> cplus_compile_program; + /* The la_compute_program method for C. */ std::string @@ -534,3 +673,19 @@ c_compute_program (compile_instance *inst, return program.compute (input, expr_block, expr_pc); } + +/* The la_compute_program method for C++. */ + +std::string +cplus_compute_program (compile_instance *inst, + const char *input, + struct gdbarch *gdbarch, + const struct block *expr_block, + CORE_ADDR expr_pc) +{ + compile_cplus_instance *cplus_inst + = static_cast<compile_cplus_instance *> (inst); + cplus_compile_program program (cplus_inst, gdbarch); + + return program.compute (input, expr_block, expr_pc); +} diff --git a/gdb/compile/compile-cplus-symbols.c b/gdb/compile/compile-cplus-symbols.c new file mode 100644 index 0000000..0f849fe --- /dev/null +++ b/gdb/compile/compile-cplus-symbols.c @@ -0,0 +1,493 @@ +/* Convert symbols from GDB to GCC + + Copyright (C) 2014-2018 Free Software Foundation, Inc. + + 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/>. */ + + +#include "defs.h" +#include "compile-internal.h" +#include "compile-cplus.h" +#include "gdb_assert.h" +#include "symtab.h" +#include "parser-defs.h" +#include "block.h" +#include "objfiles.h" +#include "compile.h" +#include "value.h" +#include "exceptions.h" +#include "gdbtypes.h" +#include "dwarf2loc.h" +#include "cp-support.h" +#include "gdbcmd.h" +#include "compile-c.h" + +/* Convert a given symbol, SYM, to the compiler's representation. + INSTANCE is the compiler instance. IS_GLOBAL is true if the + symbol came from the global scope. IS_LOCAL is true if the symbol + came from a local scope. (Note that the two are not strictly + inverses because the symbol might have come from the static + scope.) */ + +static void +convert_one_symbol (compile_cplus_instance *instance, + struct block_symbol sym, bool is_global, bool is_local) +{ + /* Squash compiler warning. */ + gcc_type sym_type = 0; + const char *filename = symbol_symtab (sym.symbol)->filename; + unsigned short line = SYMBOL_LINE (sym.symbol); + + instance->error_symbol_once (sym.symbol); + + if (SYMBOL_CLASS (sym.symbol) == LOC_LABEL) + sym_type = 0; + else + sym_type = instance->convert_type (SYMBOL_TYPE (sym.symbol)); + + if (SYMBOL_DOMAIN (sym.symbol) == STRUCT_DOMAIN) + { + /* Nothing to do. */ + } + else + { + /* Squash compiler warning. */ + gcc_cp_symbol_kind_flags kind = GCC_CP_FLAG_BASE; + CORE_ADDR addr = 0; + std::string name; + gdb::unique_xmalloc_ptr<char> symbol_name; + + switch (SYMBOL_CLASS (sym.symbol)) + { + case LOC_TYPEDEF: + if (TYPE_CODE (SYMBOL_TYPE (sym.symbol)) == TYPE_CODE_TYPEDEF) + kind = GCC_CP_SYMBOL_TYPEDEF; + else if (TYPE_CODE (SYMBOL_TYPE (sym.symbol)) == TYPE_CODE_NAMESPACE) + return; + break; + + case LOC_LABEL: + kind = GCC_CP_SYMBOL_LABEL; + addr = SYMBOL_VALUE_ADDRESS (sym.symbol); + break; + + case LOC_BLOCK: + { + kind = GCC_CP_SYMBOL_FUNCTION; + addr = BLOCK_START (SYMBOL_BLOCK_VALUE (sym.symbol)); + if (is_global && TYPE_GNU_IFUNC (SYMBOL_TYPE (sym.symbol))) + addr = gnu_ifunc_resolve_addr (target_gdbarch (), addr); + } + break; + + case LOC_CONST: + if (TYPE_CODE (SYMBOL_TYPE (sym.symbol)) == TYPE_CODE_ENUM) + { + /* Already handled by convert_enum. */ + return; + } + instance->plugin ().build_constant + (sym_type, SYMBOL_NATURAL_NAME (sym.symbol), + SYMBOL_VALUE (sym.symbol), filename, line); + return; + + case LOC_CONST_BYTES: + error (_("Unsupported LOC_CONST_BYTES for symbol \"%s\"."), + SYMBOL_PRINT_NAME (sym.symbol)); + + case LOC_UNDEF: + internal_error (__FILE__, __LINE__, _("LOC_UNDEF found for \"%s\"."), + SYMBOL_PRINT_NAME (sym.symbol)); + + case LOC_COMMON_BLOCK: + error (_("Fortran common block is unsupported for compilation " + "evaluaton of symbol \"%s\"."), + SYMBOL_PRINT_NAME (sym.symbol)); + + case LOC_OPTIMIZED_OUT: + error (_("Symbol \"%s\" cannot be used for compilation evaluation " + "as it is optimized out."), + SYMBOL_PRINT_NAME (sym.symbol)); + + case LOC_COMPUTED: + if (is_local) + goto substitution; + /* Probably TLS here. */ + warning (_("Symbol \"%s\" is thread-local and currently can only " + "be referenced from the current thread in " + "compiled code."), + SYMBOL_PRINT_NAME (sym.symbol)); + /* FALLTHROUGH */ + case LOC_UNRESOLVED: + /* 'symbol_name' cannot be used here as that one is used only for + local variables from compile_dwarf_expr_to_c. + Global variables can be accessed by GCC only by their address, not + by their name. */ + { + struct value *val; + struct frame_info *frame = nullptr; + + if (symbol_read_needs_frame (sym.symbol)) + { + frame = get_selected_frame (nullptr); + if (frame == nullptr) + error (_("Symbol \"%s\" cannot be used because " + "there is no selected frame"), + SYMBOL_PRINT_NAME (sym.symbol)); + } + + val = read_var_value (sym.symbol, sym.block, frame); + if (VALUE_LVAL (val) != lval_memory) + error (_("Symbol \"%s\" cannot be used for compilation " + "evaluation as its address has not been found."), + SYMBOL_PRINT_NAME (sym.symbol)); + + kind = GCC_CP_SYMBOL_VARIABLE; + addr = value_address (val); + } + break; + + + case LOC_REGISTER: + case LOC_ARG: + case LOC_REF_ARG: + case LOC_REGPARM_ADDR: + case LOC_LOCAL: + substitution: + kind = GCC_CP_SYMBOL_VARIABLE; + symbol_name = c_symbol_substitution_name (sym.symbol); + break; + + case LOC_STATIC: + kind = GCC_CP_SYMBOL_VARIABLE; + addr = SYMBOL_VALUE_ADDRESS (sym.symbol); + break; + + case LOC_FINAL_VALUE: + default: + gdb_assert_not_reached ("Unreachable case in convert_one_symbol."); + } + + /* Don't emit local variable decls for a raw expression. */ + if (instance->scope () != COMPILE_I_RAW_SCOPE || symbol_name == nullptr) + { + compile_scope scope; + + /* For non-local symbols, create/push a new scope so that the + symbol is properly scoped to the plug-in. */ + if (!is_local) + { + scope + = instance->new_scope (SYMBOL_NATURAL_NAME (sym.symbol), + SYMBOL_TYPE (sym.symbol)); + if (scope.nested_type () != GCC_TYPE_NONE) + { + /* We found a symbol for this type that was defined inside + some other symbol, e.g., a class tyepdef defined. */ + return; + } + + instance->enter_scope (scope); + } + + /* Get the `raw' name of the symbol. */ + if (name.empty () && SYMBOL_NATURAL_NAME (sym.symbol) != nullptr) + name = compile_cplus_instance::decl_name + (SYMBOL_NATURAL_NAME (sym.symbol)).get (); + + /* Define the decl. */ + instance->plugin ().build_decl + ("variable", name.c_str (), kind, sym_type, + symbol_name.get (), addr, filename, line); + + /* Pop scope for non-local symbols. */ + if (!is_local) + instance->leave_scope (); + } + } +} + +/* Convert a full symbol to its gcc form. CONTEXT is the compiler to + use, IDENTIFIER is the name of the symbol, SYM is the symbol + itself, and DOMAIN is the domain which was searched. */ + +static void +convert_symbol_sym (compile_cplus_instance *instance, + const char *identifier, struct block_symbol sym, + domain_enum domain) +{ + /* If we found a symbol and it is not in the static or global + scope, then we should first convert any static or global scope + symbol of the same name. This lets this unusual case work: + + int x; // Global. + int func(void) + { + int x; + // At this spot, evaluate "extern int x; x" + } + */ + + const struct block *static_block = block_static_block (sym.block); + /* STATIC_BLOCK is NULL if FOUND_BLOCK is the global block. */ + bool is_local_symbol = (sym.block != static_block && static_block != nullptr); + if (is_local_symbol) + { + struct block_symbol global_sym; + + global_sym = lookup_symbol (identifier, nullptr, domain, nullptr); + /* If the outer symbol is in the static block, we ignore it, as + it cannot be referenced. */ + if (global_sym.symbol != nullptr + && global_sym.block != block_static_block (global_sym.block)) + { + if (compile_debug) + fprintf_unfiltered (gdb_stdlog, + "gcc_convert_symbol \"%s\": global symbol\n", + identifier); + convert_one_symbol (instance, global_sym, true, false); + } + } + + if (compile_debug) + fprintf_unfiltered (gdb_stdlog, + "gcc_convert_symbol \"%s\": local symbol\n", + identifier); + convert_one_symbol (instance, sym, false, is_local_symbol); +} + +/* Convert a minimal symbol to its gcc form. CONTEXT is the compiler + to use and BMSYM is the minimal symbol to convert. */ + +static void +convert_symbol_bmsym (compile_cplus_instance *instance, + struct bound_minimal_symbol bmsym) +{ + struct minimal_symbol *msym = bmsym.minsym; + struct objfile *objfile = bmsym.objfile; + struct type *type; + gcc_cp_symbol_kind_flags kind; + gcc_type sym_type; + CORE_ADDR addr; + + addr = MSYMBOL_VALUE_ADDRESS (objfile, msym); + + /* Conversion copied from write_exp_msymbol. */ + switch (MSYMBOL_TYPE (msym)) + { + case mst_text: + case mst_file_text: + case mst_solib_trampoline: + type = objfile_type (objfile)->nodebug_text_symbol; + kind = GCC_CP_SYMBOL_FUNCTION; + break; + + case mst_text_gnu_ifunc: + /* nodebug_text_gnu_ifunc_symbol would cause: + function return type cannot be function */ + type = objfile_type (objfile)->nodebug_text_symbol; + kind = GCC_CP_SYMBOL_FUNCTION; + addr = gnu_ifunc_resolve_addr (target_gdbarch (), addr); + break; + + case mst_data: + case mst_file_data: + case mst_bss: + case mst_file_bss: + type = objfile_type (objfile)->nodebug_data_symbol; + kind = GCC_CP_SYMBOL_VARIABLE; + break; + + case mst_slot_got_plt: + type = objfile_type (objfile)->nodebug_got_plt_symbol; + kind = GCC_CP_SYMBOL_FUNCTION; + break; + + default: + type = objfile_type (objfile)->nodebug_unknown_symbol; + kind = GCC_CP_SYMBOL_VARIABLE; + break; + } + + sym_type = instance->convert_type (type); + instance->plugin ().push_namespace (""); + instance->plugin ().build_decl + ("minsym", MSYMBOL_NATURAL_NAME (msym), kind, sym_type, nullptr, addr, + nullptr, 0); + instance->plugin ().pop_binding_level (""); +} + +/* See compile-cplus.h. */ + +void +gcc_cplus_convert_symbol (void *datum, + struct gcc_cp_context *gcc_context, + enum gcc_cp_oracle_request request ATTRIBUTE_UNUSED, + const char *identifier) +{ + if (compile_debug) + fprintf_unfiltered (gdb_stdlog, + "got oracle request for \"%s\"\n", identifier); + + bool found = false; + compile_cplus_instance *instance = (compile_cplus_instance *) datum; + + TRY + { + /* Symbol searching is a three part process unfortunately. */ + + /* First do a "standard" lookup, converting any found symbols. + This will find variables in the current scope. */ + + struct block_symbol sym + = lookup_symbol (identifier, instance->block (), VAR_DOMAIN, nullptr); + + if (sym.symbol != nullptr) + { + found = true; + convert_symbol_sym (instance, identifier, sym, VAR_DOMAIN); + } + + /* Then use linespec.c's multi-symbol search. This should find + all non-variable symbols for which we have debug info. */ + + symbol_searcher searcher; + searcher.find_all_symbols (identifier, current_language, + ALL_DOMAIN, nullptr, nullptr); + + /* Convert any found symbols. */ + for (const auto &it : searcher.matching_symbols ()) + { + /* Don't convert the symbol found above, if any, twice! */ + if (it.symbol != sym.symbol) + { + found = true; + convert_symbol_sym (instance, identifier, it, + SYMBOL_DOMAIN (it.symbol)); + } + } + + /* Finally, if no symbols have been found, fall back to minsyms. */ + if (!found) + { + for (const auto &it : searcher.matching_msymbols ()) + { + found = true; + convert_symbol_bmsym (instance, it); + } + } + } + CATCH (e, RETURN_MASK_ALL) + { + /* We can't allow exceptions to escape out of this callback. Safest + is to simply emit a gcc error. */ + instance->plugin ().error (e.message); + } + END_CATCH + + if (compile_debug && !found) + fprintf_unfiltered (gdb_stdlog, + "gcc_convert_symbol \"%s\": lookup_symbol failed\n", + identifier); + + if (compile_debug) + { + if (found) + fprintf_unfiltered (gdb_stdlog, "found type for %s\n", identifier); + else + { + fprintf_unfiltered (gdb_stdlog, "did not find type for %s\n", + identifier); + } + } + + return; +} + +/* See compile-cplus.h. */ + +gcc_address +gcc_cplus_symbol_address (void *datum, struct gcc_cp_context *gcc_context, + const char *identifier) +{ + compile_cplus_instance *instance = (compile_cplus_instance *) datum; + gcc_address result = 0; + int found = 0; + + if (compile_debug) + fprintf_unfiltered (gdb_stdlog, + "got oracle request for address of %s\n", identifier); + + /* We can't allow exceptions to escape out of this callback. Safest + is to simply emit a gcc error. */ + TRY + { + struct symbol *sym + = lookup_symbol (identifier, nullptr, VAR_DOMAIN, nullptr).symbol; + + if (sym != nullptr && SYMBOL_CLASS (sym) == LOC_BLOCK) + { + if (compile_debug) + fprintf_unfiltered (gdb_stdlog, + "gcc_symbol_address \"%s\": full symbol\n", + identifier); + result = BLOCK_START (SYMBOL_BLOCK_VALUE (sym)); + if (TYPE_GNU_IFUNC (SYMBOL_TYPE (sym))) + result = gnu_ifunc_resolve_addr (target_gdbarch (), result); + found = 1; + } + else + { + struct bound_minimal_symbol msym; + + msym = lookup_bound_minimal_symbol (identifier); + if (msym.minsym != nullptr) + { + if (compile_debug) + fprintf_unfiltered (gdb_stdlog, + "gcc_symbol_address \"%s\": minimal " + "symbol\n", + identifier); + result = BMSYMBOL_VALUE_ADDRESS (msym); + if (MSYMBOL_TYPE (msym.minsym) == mst_text_gnu_ifunc) + result = gnu_ifunc_resolve_addr (target_gdbarch (), result); + found = 1; + } + } + } + + CATCH (e, RETURN_MASK_ERROR) + { + instance->plugin ().error (e.message); + } + END_CATCH + + if (compile_debug && !found) + fprintf_unfiltered (gdb_stdlog, + "gcc_symbol_address \"%s\": failed\n", + identifier); + + if (compile_debug) + { + if (found) + fprintf_unfiltered (gdb_stdlog, "found address for %s!\n", identifier); + else + fprintf_unfiltered (gdb_stdlog, + "did not find address for %s\n", identifier); + } + + return result; +} diff --git a/gdb/compile/compile-cplus-types.c b/gdb/compile/compile-cplus-types.c new file mode 100644 index 0000000..9425fc6 --- /dev/null +++ b/gdb/compile/compile-cplus-types.c @@ -0,0 +1,1427 @@ +/* Convert types from GDB to GCC + + Copyright (C) 2014-2018 Free Software Foundation, Inc. + + 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/>. */ + + +#include "defs.h" +#include "common/preprocessor.h" +#include "gdbtypes.h" +#include "compile-internal.h" +#include "compile-cplus.h" +#include "gdb_assert.h" +#include "symtab.h" +#include "source.h" +#include "cp-support.h" +#include "cp-abi.h" +#include "symtab.h" +#include "objfiles.h" +#include "block.h" +#include "gdbcmd.h" +#include "c-lang.h" +#include "compile-c.h" /* Included for c_get_range_decl_name + et al. */ +#include <algorithm> + +/* Default compile flags for C++. */ + +const char *compile_cplus_instance::m_default_cflags = "-std=gnu++11"; + +/* Flag to enable internal debugging. */ + +static int debug_compile_cplus_types = 0; + +/* Flag to enable internal scope switching debugging. */ + +static int debug_compile_cplus_scopes = 0; + +/* Forward declarations. */ + +static gcc_type compile_cplus_convert_func (compile_cplus_instance *instance, + struct type *type, + bool strip_artificial); + +/* See description in compile-cplus.h. */ + +gdb::unique_xmalloc_ptr<char> +compile_cplus_instance::decl_name (const char *natural) +{ + if (natural == nullptr) + return nullptr; + + char *name = cp_func_name (natural); + if (name != nullptr) + return gdb::unique_xmalloc_ptr<char> (name); + + return gdb::unique_xmalloc_ptr<char> (xstrdup (natural)); +} + +/* Get the access flag for the NUM'th field of TYPE. */ + +static enum gcc_cp_symbol_kind +get_field_access_flag (const struct type *type, int num) +{ + if (TYPE_FIELD_PROTECTED (type, num)) + return GCC_CP_ACCESS_PROTECTED; + else if (TYPE_FIELD_PRIVATE (type, num)) + return GCC_CP_ACCESS_PRIVATE; + + /* GDB assumes everything else is public. */ + return GCC_CP_ACCESS_PUBLIC; +} + +/* Get the access flag for the NUM'th method of TYPE's FNI'th + fieldlist. */ + +enum gcc_cp_symbol_kind +get_method_access_flag (const struct type *type, int fni, int num) +{ + gdb_assert (TYPE_CODE (type) == TYPE_CODE_STRUCT); + + /* If this type was not declared a class, everything is public. */ + if (!TYPE_DECLARED_CLASS (type)) + return GCC_CP_ACCESS_PUBLIC; + + /* Otherwise, read accessibility from the fn_field. */ + const struct fn_field *methods = TYPE_FN_FIELDLIST1 (type, fni); + if (TYPE_FN_FIELD_PROTECTED (methods, num)) + return GCC_CP_ACCESS_PROTECTED; + else if (TYPE_FN_FIELD_PRIVATE (methods, num)) + return GCC_CP_ACCESS_PRIVATE; + else + return GCC_CP_ACCESS_PUBLIC; +} + +/* A useful debugging function to output the scope SCOPE to stdout. */ + +static void __attribute__ ((used)) +debug_print_scope (const compile_scope &scope) +{ + for (const auto &comp: scope) + { + const char *symbol = (comp.bsymbol.symbol != nullptr + ? SYMBOL_NATURAL_NAME (comp.bsymbol.symbol) + : "<none>"); + + printf_unfiltered ("\tname = %s, symbol = %s\n", comp.name.c_str (), + symbol); + } +} + +/* See description in compile-cplus.h. */ + +compile_scope +type_name_to_scope (const char *type_name, const struct block *block) +{ + compile_scope scope; + + if (type_name == nullptr) + { + /* An anonymous type. We cannot really do much here. We simply cannot + look up anonymous types easily/at all. */ + return scope; + } + + const char *p = type_name; + std::string lookup_name; + + while (*p != '\0') + { + /* Create a string token of the first component of TYPE_NAME. */ + int len = cp_find_first_component (p); + std::string s (p, len); + + /* Advance past the last token. */ + p += len; + + /* Look up the symbol and decide when to stop. */ + if (!lookup_name.empty ()) + lookup_name += "::"; + lookup_name += s; + + /* Look up the resulting name. */ + struct block_symbol bsymbol + = lookup_symbol (lookup_name.c_str (), block, VAR_DOMAIN, nullptr); + + if (bsymbol.symbol != nullptr) + { + scope_component comp = {s, bsymbol}; + + scope.push_back (comp); + + if (TYPE_CODE (SYMBOL_TYPE (bsymbol.symbol)) != TYPE_CODE_NAMESPACE) + { + /* We're done. */ + break; + } + } + + if (*p == ':') + { + ++p; + if (*p == ':') + ++p; + else + { + /* This shouldn't happen since we are not attempting to + loop over user input. This name is generated by GDB + from debug info. */ + internal_error (__FILE__, __LINE__, + _("malformed TYPE_NAME during parsing")); + } + } + } + + return scope; +} + +/* Compare two scope_components for equality. These are equal if the names + of the two components' are the same. */ + +bool +operator== (const scope_component &lhs, const scope_component &rhs) +{ + return lhs.name == rhs.name; +} + +/* Compare two scope_components for inequality. These are not equal if + the two components' names are not equal. */ + +bool +operator!= (const scope_component &lhs, const scope_component &rhs) +{ + return lhs.name != rhs.name; +} + +/* Compare two compile_scopes for equality. These are equal if they are both + contain the same number of components and each component is equal. */ + +bool +operator== (const compile_scope &lhs, const compile_scope &rhs) +{ + if (lhs.size () != rhs.size ()) + return false; + + for (int i = 0; i < lhs.size (); ++i) + { + if (lhs[i] != rhs[i]) + return false; + } + + return true; +} + +/* Compare two compile_scopes for inequality. These are inequal if they + contain unequal number of elements or if any of the components are not + the same. */ + +bool +operator!= (const compile_scope &lhs, const compile_scope &rhs) +{ + if (lhs.size () != rhs.size ()) + return true; + + for (int i = 0; i < lhs.size (); ++i) + { + if (lhs[i] != rhs[i]) + return true; + } + + return false; +} + +/* See description in compile-cplus.h. */ + +void +compile_cplus_instance::enter_scope (compile_scope &new_scope) +{ + bool must_push = m_scopes.empty () || m_scopes.back () != new_scope; + + new_scope.m_pushed = must_push; + + /* Save the new scope. */ + m_scopes.push_back (new_scope); + + if (must_push) + { + if (debug_compile_cplus_scopes) + fprintf_unfiltered (gdb_stdlog, "entering new scope %p\n", new_scope); + + /* Push the global namespace. */ + plugin ().push_namespace (""); + + /* Push all other namespaces. Note that we do not push the last + scope_component -- that's the actual type we are converting. */ + std::for_each + (new_scope.begin (), new_scope.end () - 1, + [this] (const scope_component &comp) + { + gdb_assert (TYPE_CODE (SYMBOL_TYPE (comp.bsymbol.symbol)) + == TYPE_CODE_NAMESPACE); + + const char *ns = (comp.name == CP_ANONYMOUS_NAMESPACE_STR ? nullptr + : comp.name.c_str ()); + + this->plugin ().push_namespace (ns); + }); + } + else + { + if (debug_compile_cplus_scopes) + { + fprintf_unfiltered (gdb_stdlog, "staying in current scope -- " + "scopes are identical\n"); + } + } +} + +/* See description in compile-cplus.h. */ + +void +compile_cplus_instance::leave_scope () +{ + /* Get the current scope and remove it from the internal list of + scopes. */ + compile_scope current = m_scopes.back (); + + m_scopes.pop_back (); + + if (current.m_pushed) + { + if (debug_compile_cplus_scopes) + fprintf_unfiltered (gdb_stdlog, "leaving scope %p\n", current); + + /* Pop namespaces. */ + std::for_each + (current.begin (),current.end () - 1, + [this] (const scope_component &comp) { + gdb_assert (TYPE_CODE (SYMBOL_TYPE (comp.bsymbol.symbol)) + == TYPE_CODE_NAMESPACE); + this->plugin ().pop_binding_level (comp.name.c_str ()); + }); + + /* Pop global namespace. */ + plugin ().pop_binding_level (""); + } + else + { + if (debug_compile_cplus_scopes) + fprintf_unfiltered (gdb_stdlog, + "identical scopes -- not leaving scope\n"); + } +} + +/* See description in compile-cplus.h. */ + +compile_scope +compile_cplus_instance::new_scope (const char *type_name, struct type *type) +{ + /* Break the type name into components. If TYPE was defined in some + superclass, we do not process TYPE but process the enclosing type + instead. */ + compile_scope scope = type_name_to_scope (type_name, block ()); + + if (!scope.empty ()) + { + /* Get the name of the last component, which should be the + unqualified name of the type to process. */ + scope_component &comp = scope.back (); + + if (!types_equal (type, SYMBOL_TYPE (comp.bsymbol.symbol)) + && (m_scopes.empty () + || (m_scopes.back ().back ().bsymbol.symbol + != comp.bsymbol.symbol))) + { + /* The type is defined inside another class(es). Convert that + type instead of defining this type. */ + convert_type (SYMBOL_TYPE (comp.bsymbol.symbol)); + + /* If the original type (passed in to us) is defined in a nested + class, the previous call will give us that type's gcc_type. + Upper layers are expecting to get the original type's + gcc_type! */ + get_cached_type (type, scope.m_nested_type); + return scope; + } + } + else + { + if (TYPE_NAME (type) == nullptr) + { + /* Anonymous type */ + + /* We don't have a qualified name for this to look up, but + we need a scope. We have to assume, then, that it is the same + as the current scope, if any. */ + if (!m_scopes.empty ()) + { + scope = m_scopes.back (); + scope.m_pushed = false; + } + else + scope.push_back (scope_component ()); + } + else + { + scope_component comp + = { + decl_name (TYPE_NAME (type)).get (), + lookup_symbol (TYPE_NAME (type), block (), VAR_DOMAIN, nullptr) + }; + scope.push_back (comp); + } + } + + /* There must be at least one component in the compile_scope. */ + gdb_assert (scope.size () > 0); + return scope; +} + +/* See description in compile-cplus.h. */ + +gcc_type +compile_cplus_instance::convert_reference_base + (gcc_type base, enum gcc_cp_ref_qualifiers rquals) +{ + return this->plugin ().build_reference_type (base, rquals); +} + +/* Convert a reference type to its gcc representation. */ + +static gcc_type +compile_cplus_convert_reference (compile_cplus_instance *instance, + struct type *type) +{ + gcc_type target = instance->convert_type (TYPE_TARGET_TYPE (type)); + + enum gcc_cp_ref_qualifiers quals = GCC_CP_REF_QUAL_NONE; + switch (TYPE_CODE (type)) + { + case TYPE_CODE_REF: + quals = GCC_CP_REF_QUAL_LVALUE; + break; + case TYPE_CODE_RVALUE_REF: + quals = GCC_CP_REF_QUAL_RVALUE; + break; + default: + gdb_assert_not_reached ("unexpected type code for reference type"); + } + + return instance->convert_reference_base (target, quals); +} + +/* See description in compile-cplus.h. */ + +gcc_type +compile_cplus_instance::convert_pointer_base(gcc_type target) +{ + return plugin ().build_pointer_type (target); +} + +/* Convert a pointer type to its gcc representation. */ + +static gcc_type +compile_cplus_convert_pointer (compile_cplus_instance *instance, + struct type *type) +{ + gcc_type target = instance->convert_type (TYPE_TARGET_TYPE (type)); + + return instance->convert_pointer_base (target); +} + +/* Convert an array type to its gcc representation. */ + +static gcc_type +compile_cplus_convert_array (compile_cplus_instance *instance, + struct type *type) +{ + struct type *range = TYPE_INDEX_TYPE (type); + gcc_type element_type = instance->convert_type (TYPE_TARGET_TYPE (type)); + + if (TYPE_LOW_BOUND_KIND (range) != PROP_CONST) + { + const char *s = _("array type with non-constant" + " lower bound is not supported"); + + return instance->plugin ().error (s); + } + + if (TYPE_LOW_BOUND (range) != 0) + { + const char *s = _("cannot convert array type with " + "non-zero lower bound to C"); + + return instance->plugin ().error (s); + } + + if (TYPE_HIGH_BOUND_KIND (range) == PROP_LOCEXPR + || TYPE_HIGH_BOUND_KIND (range) == PROP_LOCLIST) + { + if (TYPE_VECTOR (type)) + { + const char *s = _("variably-sized vector type is not supported"); + + return instance->plugin ().error (s); + } + + std::string upper_bound + = c_get_range_decl_name (&TYPE_RANGE_DATA (range)->high); + return instance->plugin ().build_vla_array_type (element_type, + upper_bound.c_str ()); + } + else + { + LONGEST low_bound, high_bound, count; + + if (get_array_bounds (type, &low_bound, &high_bound) == 0) + count = -1; + else + { + gdb_assert (low_bound == 0); /* Ensured above. */ + count = high_bound + 1; + } + + if (TYPE_VECTOR (type)) + return instance->plugin ().build_vector_type (element_type, count); + + return instance->plugin ().build_array_type (element_type, count); + } +} + +/* Convert a typedef of TYPE. If not GCC_CP_ACCESS_NONE, NESTED_ACCESS + will define the accessibility of the typedef definition in its + containing class. */ + +static gcc_type +compile_cplus_convert_typedef (compile_cplus_instance *instance, + struct type *type, + enum gcc_cp_symbol_kind nested_access) +{ + compile_scope scope = instance->new_scope (TYPE_NAME (type), type); + + if (scope.nested_type () != GCC_TYPE_NONE) + return scope.nested_type (); + + gdb::unique_xmalloc_ptr<char> name + = compile_cplus_instance::decl_name (TYPE_NAME (type)); + + /* Make sure the scope for this type has been pushed. */ + instance->enter_scope (scope); + + /* Convert the typedef's real type. */ + gcc_type typedef_type = instance->convert_type (check_typedef (type)); + + instance->plugin ().build_decl ("typedef", name.get (), + GCC_CP_SYMBOL_TYPEDEF | nested_access, + typedef_type, 0, 0, nullptr, 0); + + /* Completed this scope. */ + instance->leave_scope (); + return typedef_type; +} + +/* Convert types defined in TYPE. */ + +static void +compile_cplus_convert_type_defns (compile_cplus_instance *instance, + struct type *type) +{ + int i; + enum gcc_cp_symbol_kind accessibility; + + /* Convert typedefs. */ + for (i = 0; i < TYPE_TYPEDEF_FIELD_COUNT (type); ++i) + { + if (TYPE_TYPEDEF_FIELD_PROTECTED (type, i)) + accessibility = GCC_CP_ACCESS_PROTECTED; + else if (TYPE_TYPEDEF_FIELD_PRIVATE (type, i)) + accessibility = GCC_CP_ACCESS_PRIVATE; + else + accessibility = GCC_CP_ACCESS_PUBLIC; + instance->convert_type (TYPE_TYPEDEF_FIELD_TYPE (type, i), accessibility); + } + + /* Convert nested types. */ + for (i = 0; i < TYPE_NESTED_TYPES_COUNT (type); ++i) + { + if (TYPE_NESTED_TYPES_FIELD_PROTECTED (type, i)) + accessibility = GCC_CP_ACCESS_PROTECTED; + else if (TYPE_NESTED_TYPES_FIELD_PRIVATE (type, i)) + accessibility = GCC_CP_ACCESS_PRIVATE; + else + accessibility = GCC_CP_ACCESS_PUBLIC; + instance->convert_type (TYPE_NESTED_TYPES_FIELD_TYPE (type, i), + accessibility); + } +} + +/* Convert data members defined in TYPE, which should be struct/class/union + with gcc_type COMP_TYPE. */ + +static void +compile_cplus_convert_struct_or_union_members + (compile_cplus_instance *instance, struct type *type, gcc_type comp_type) +{ + for (int i = TYPE_N_BASECLASSES (type); i < TYPE_NFIELDS (type); ++i) + { + const char *field_name = TYPE_FIELD_NAME (type, i); + + if (TYPE_FIELD_IGNORE (type, i) + || TYPE_FIELD_ARTIFICIAL (type, i)) + continue; + + /* GDB records unnamed/anonymous fields with empty string names. */ + if (*field_name == '\0') + field_name = nullptr; + + gcc_type field_type + = instance->convert_type (TYPE_FIELD_TYPE (type, i)); + + if (field_is_static (&TYPE_FIELD (type, i))) + { + CORE_ADDR physaddr; + + switch (TYPE_FIELD_LOC_KIND (type, i)) + { + case FIELD_LOC_KIND_PHYSADDR: + { + physaddr = TYPE_FIELD_STATIC_PHYSADDR (type, i); + + instance->plugin ().build_decl + ("field physaddr", field_name, + (GCC_CP_SYMBOL_VARIABLE | get_field_access_flag (type, i)), + field_type, nullptr, physaddr, nullptr, 0); + } + break; + + case FIELD_LOC_KIND_PHYSNAME: + { + const char *physname = TYPE_FIELD_STATIC_PHYSNAME (type, i); + struct block_symbol sym + = lookup_symbol (physname, instance->block (), + VAR_DOMAIN, nullptr); + + if (sym.symbol == nullptr) + { + /* We didn't actually find the symbol. There's little + we can do but ignore this member. */ + continue; + } + const char *filename = symbol_symtab (sym.symbol)->filename; + unsigned int line = SYMBOL_LINE (sym.symbol); + + physaddr = SYMBOL_VALUE_ADDRESS (sym.symbol); + instance->plugin ().build_decl + ("field physname", field_name, + (GCC_CP_SYMBOL_VARIABLE| get_field_access_flag (type, i)), + field_type, nullptr, physaddr, filename, line); + } + break; + + default: + gdb_assert_not_reached + ("unexpected static field location kind"); + } + } + else + { + unsigned long bitsize = TYPE_FIELD_BITSIZE (type, i); + enum gcc_cp_symbol_kind field_flags = GCC_CP_SYMBOL_FIELD + | get_field_access_flag (type, i); + + if (bitsize == 0) + bitsize = 8 * TYPE_LENGTH (TYPE_FIELD_TYPE (type, i)); + + instance->plugin ().build_field + (field_name, field_type, field_flags, bitsize, + TYPE_FIELD_BITPOS (type, i)); + } + } +} + +/* Convert a method type to its gcc representation. */ + +static gcc_type +compile_cplus_convert_method (compile_cplus_instance *instance, + struct type *parent_type, + struct type *method_type) +{ + /* Get the actual function type of the method, the corresponding class's + type and corresponding qualifier flags. */ + gcc_type func_type = compile_cplus_convert_func (instance, method_type, true); + gcc_type class_type = instance->convert_type (parent_type); + gcc_cp_qualifiers_flags quals = (enum gcc_cp_qualifiers) 0; + + if (TYPE_CONST (method_type)) + quals |= GCC_CP_QUALIFIER_CONST; + if (TYPE_VOLATILE (method_type)) + quals |= GCC_CP_QUALIFIER_VOLATILE; + if (TYPE_RESTRICT (method_type)) + quals |= GCC_CP_QUALIFIER_RESTRICT; + + /* Not yet implemented. */ + gcc_cp_ref_qualifiers_flags rquals = GCC_CP_REF_QUAL_NONE; + + return instance->plugin ().build_method_type + (class_type, func_type, quals, rquals); +} + +/* Convert a member or method pointer represented by TYPE. */ + +static gcc_type +compile_cplus_convert_memberptr (compile_cplus_instance *instance, + struct type *type) +{ + struct type *containing_class = TYPE_SELF_TYPE (type); + + if (containing_class == nullptr) + return GCC_TYPE_NONE; + + gcc_type class_type = instance->convert_type (containing_class); + gcc_type member_type + = instance->convert_type (TYPE_TARGET_TYPE (type)); + + return instance->plugin ().build_pointer_to_member_type + (class_type, member_type); +} + +/* Convert all methods defined in TYPE, which should be a class/struct/union + with gcc_type CLASS_TYPE. */ + +static void +compile_cplus_convert_struct_or_union_methods (compile_cplus_instance *instance, + struct type *type, + gcc_type class_type) +{ + for (int i = 0; i < TYPE_NFN_FIELDS (type); ++i) + { + struct fn_field *methods = TYPE_FN_FIELDLIST1 (type, i); + gdb::unique_xmalloc_ptr<char> overloaded_name + = compile_cplus_instance::decl_name (TYPE_FN_FIELDLIST_NAME (type, i)); + + /* Loop through the fieldlist, adding decls to the compiler's + representation of the class. */ + for (int j = 0; j < TYPE_FN_FIELDLIST_LENGTH (type, i); ++j) + { + /* Skip artificial methods. */ + if (TYPE_FN_FIELD_ARTIFICIAL (methods, j)) + continue; + + gcc_cp_symbol_kind_flags sym_kind = GCC_CP_SYMBOL_FUNCTION; + gcc_type method_type; + struct block_symbol sym + = lookup_symbol (TYPE_FN_FIELD_PHYSNAME (methods, j), + instance->block (), VAR_DOMAIN, nullptr); + + if (sym.symbol == nullptr) + { + if (TYPE_FN_FIELD_VIRTUAL_P (methods, j)) + { + /* This is beyond hacky, and is really only a workaround for + detecting pure virtual methods. */ + method_type = compile_cplus_convert_method + (instance, type, TYPE_FN_FIELD_TYPE (methods, j)); + + instance->plugin ().build_decl + ("pure virtual method", overloaded_name.get (), + (sym_kind + | get_method_access_flag (type, i, j) + | GCC_CP_FLAG_VIRTUAL_FUNCTION + | GCC_CP_FLAG_PURE_VIRTUAL_FUNCTION), + method_type, nullptr, 0, nullptr, 0); + continue; + } + + /* This can happen if we have a DW_AT_declaration DIE + for the method, but no "definition"-type DIE (with + DW_AT_specification referencing the decl DIE), i.e., + the compiler has probably optimized the method away. + + In this case, all we can hope to do is issue a warning + to the user letting him know. If the user has not actually + requested using this method, things should still work. */ + warning (_("Method %s appears to be optimized out.\n" + "All references to this method will be undefined."), + TYPE_FN_FIELD_PHYSNAME (methods, j)); + continue; + } + + const char *filename = symbol_symtab (sym.symbol)->filename; + unsigned int line = SYMBOL_LINE (sym.symbol); + CORE_ADDR address = BLOCK_START (SYMBOL_BLOCK_VALUE (sym.symbol)); + const char *kind; + + if (TYPE_FN_FIELD_STATIC_P (methods, j)) + { + kind = "static method"; + method_type = compile_cplus_convert_func + (instance, TYPE_FN_FIELD_TYPE (methods, j), true); + } + else + { + kind = "method"; + method_type = (compile_cplus_convert_method + (instance, type, TYPE_FN_FIELD_TYPE (methods, j))); + } + + if (TYPE_FN_FIELD_VIRTUAL_P (methods, j)) + sym_kind |= GCC_CP_FLAG_VIRTUAL_FUNCTION; + + instance->plugin ().build_decl + (kind, overloaded_name.get (), + sym_kind | get_method_access_flag (type, i, j), + method_type, nullptr, address, filename, line); + } + } +} + +/* Convert a struct or union type to its gcc representation. If this type + was defined in another type, NESTED_ACCESS should indicate the + accessibility of this type. */ + +static gcc_type +compile_cplus_convert_struct_or_union (compile_cplus_instance *instance, + struct type *type, + enum gcc_cp_symbol_kind nested_access) +{ + const char *filename = nullptr; + unsigned short line = 0; + + /* Get the decl name of this type. */ + gdb::unique_xmalloc_ptr<char> name + = compile_cplus_instance::decl_name (TYPE_NAME (type)); + + /* Create a new scope for TYPE. */ + compile_scope scope = instance->new_scope (TYPE_NAME (type), type); + + if (scope.nested_type () != GCC_TYPE_NONE) + { + /* The type requested was actually defined inside another type, + such as a nested class definition. Return that type. */ + return scope.nested_type (); + } + + /* Push all scopes. */ + instance->enter_scope (scope); + + /* First we create the resulting type and enter it into our hash + table. This lets recursive types work. */ + + gcc_decl resuld; + if (TYPE_CODE (type) == TYPE_CODE_STRUCT) + { + const char *what = TYPE_DECLARED_CLASS (type) ? "struct" : "class"; + + resuld = instance->plugin ().build_decl + (what, name.get (), (GCC_CP_SYMBOL_CLASS | nested_access + | (TYPE_DECLARED_CLASS (type) + ? GCC_CP_FLAG_CLASS_NOFLAG + : GCC_CP_FLAG_CLASS_IS_STRUCT)), + 0, nullptr, 0, filename, line); + } + else + { + gdb_assert (TYPE_CODE (type) == TYPE_CODE_UNION); + resuld = instance->plugin ().build_decl + ("union", name.get (), GCC_CP_SYMBOL_UNION | nested_access, + 0, nullptr, 0, filename, line); + } + + gcc_type result; + if (TYPE_CODE (type) == TYPE_CODE_STRUCT) + { + struct gcc_vbase_array bases; + int num_baseclasses = TYPE_N_BASECLASSES (type); + + memset (&bases, 0, sizeof (bases)); + + if (num_baseclasses > 0) + { + bases.elements = XNEWVEC (gcc_type, num_baseclasses); + bases.flags = XNEWVEC (enum gcc_cp_symbol_kind, num_baseclasses); + bases.n_elements = num_baseclasses; + for (int i = 0; i < num_baseclasses; ++i) + { + struct type *base_type = TYPE_BASECLASS (type, i); + + bases.flags[i] = GCC_CP_SYMBOL_BASECLASS + | get_field_access_flag (type, i) + | (BASETYPE_VIA_VIRTUAL (type, i) + ? GCC_CP_FLAG_BASECLASS_VIRTUAL + : GCC_CP_FLAG_BASECLASS_NOFLAG); + bases.elements[i] = instance->convert_type (base_type); + } + } + + result = instance->plugin ().start_class_type + (name.get (), resuld, &bases, filename, line); + xfree (bases.flags); + xfree (bases.elements); + } + else + { + gdb_assert (TYPE_CODE (type) == TYPE_CODE_UNION); + result = instance->plugin ().start_class_type + (name.get (), resuld, nullptr, filename, line); + } + + instance->insert_type (type, result); + + /* Add definitions. */ + compile_cplus_convert_type_defns (instance, type); + + /* Add methods. */ + compile_cplus_convert_struct_or_union_methods (instance, type, result); + + /* Add members. */ + compile_cplus_convert_struct_or_union_members (instance, type, result); + + /* All finished. */ + instance->plugin ().finish_class_type (name.get (), TYPE_LENGTH (type)); + + /* Pop all scopes. */ + instance->leave_scope (); + return result; +} + +/* Convert an enum type to its gcc representation. If this type + was defined in another type, NESTED_ACCESS should indicate the + accessibility of this type.*/ + +static gcc_type +compile_cplus_convert_enum (compile_cplus_instance *instance, struct type *type, + enum gcc_cp_symbol_kind nested_access) +{ + int scoped_enum_p = FALSE; + + /* Create a new scope for this type. */ + compile_scope scope = instance->new_scope (TYPE_NAME (type), type); + + if (scope.nested_type () != GCC_TYPE_NONE) + { + /* The type requested was actually defined inside another type, + such as a nested class definition. Return that type. */ + return scope.nested_type (); + } + + gdb::unique_xmalloc_ptr<char> name + = compile_cplus_instance::decl_name (TYPE_NAME (type)); + + /* Push all scopes. */ + instance->enter_scope (scope); + + gcc_type int_type + = instance->plugin ().get_int_type (TYPE_UNSIGNED (type), + TYPE_LENGTH (type), nullptr); + gcc_type result + = instance->plugin ().start_enum_type (name.get (), int_type, + GCC_CP_SYMBOL_ENUM | nested_access + | (scoped_enum_p + ? GCC_CP_FLAG_ENUM_SCOPED + : GCC_CP_FLAG_ENUM_NOFLAG), + nullptr, 0); + for (int i = 0; i < TYPE_NFIELDS (type); ++i) + { + gdb::unique_xmalloc_ptr<char> fname + = compile_cplus_instance::decl_name (TYPE_FIELD_NAME (type, i)); + + if (TYPE_FIELD_LOC_KIND (type, i) != FIELD_LOC_KIND_ENUMVAL + || fname == nullptr) + continue; + + instance->plugin ().build_enum_constant (result, fname.get (), + TYPE_FIELD_ENUMVAL (type, i)); + } + + /* Finish enum definition and pop scopes. */ + instance->plugin ().finish_enum_type (result); + instance->leave_scope (); + return result; +} + +/* Convert a function type to its gcc representation. This function does + not deal with function templates. */ + +static gcc_type +compile_cplus_convert_func (compile_cplus_instance *instance, + struct type *type, bool strip_artificial) +{ + int is_varargs = TYPE_VARARGS (type); + struct type *target_type = TYPE_TARGET_TYPE (type); + + /* Functions with no debug info have no return type. Ideally we'd + want to fallback to the type of the cast just before the + function, like GDB's built-in expression parser, but we don't + have access to that type here. For now, fallback to int, like + GDB's parser used to do. */ + if (target_type == nullptr) + { + if (TYPE_OBJFILE_OWNED (type)) + target_type = objfile_type (TYPE_OWNER (type).objfile)->builtin_int; + else + target_type = builtin_type (TYPE_OWNER (type).gdbarch)->builtin_int; + warning (_("function has unknown return type; assuming int")); + } + + /* This approach means we can't make self-referential function + types. Those are impossible in C, though. */ + gcc_type return_type = instance->convert_type (target_type); + + struct gcc_type_array array = + { TYPE_NFIELDS (type), XNEWVEC (gcc_type, TYPE_NFIELDS (type)) }; + int artificials = 0; + for (int i = 0; i < TYPE_NFIELDS (type); ++i) + { + if (strip_artificial && TYPE_FIELD_ARTIFICIAL (type, i)) + { + --array.n_elements; + ++artificials; + } + else + { + array.elements[i - artificials] + = instance->convert_type (TYPE_FIELD_TYPE (type, i)); + } + } + + /* We omit setting the argument types to `void' to be a little flexible + with some minsyms like printf (compile-cplus.exp has examples). */ + gcc_type result = instance->plugin ().build_function_type + (return_type, &array, is_varargs); + xfree (array.elements); + return result; +} + +/* Convert an integer type to its gcc representation. */ + +static gcc_type +compile_cplus_convert_int (compile_cplus_instance *instance, struct type *type) +{ + if (TYPE_NOSIGN (type)) + { + gdb_assert (TYPE_LENGTH (type) == 1); + return instance->plugin ().get_char_type (); + } + + return instance->plugin ().get_int_type + (TYPE_UNSIGNED (type), TYPE_LENGTH (type), TYPE_NAME (type)); +} + +/* Convert a floating-point type to its gcc representation. */ + +static gcc_type +compile_cplus_convert_float (compile_cplus_instance *instance, + struct type *type) +{ + return instance->plugin ().get_float_type + (TYPE_LENGTH (type), TYPE_NAME (type)); +} + +/* Convert the 'void' type to its gcc representation. */ + +static gcc_type +compile_cplus_convert_void (compile_cplus_instance *instance, struct type *type) +{ + return instance->plugin ().get_void_type (); +} + +/* Convert a boolean type to its gcc representation. */ + +static gcc_type +compile_cplus_convert_bool (compile_cplus_instance *instance, struct type *type) +{ + return instance->plugin ().get_bool_type (); +} + +/* See description in compile-cplus.h. */ + +gcc_type +compile_cplus_instance::convert_qualified_base (gcc_type base, + gcc_cp_qualifiers_flags quals) +{ + gcc_type result = base; + + if (quals != GCC_CP_REF_QUAL_NONE) + result = plugin ().build_qualified_type (base, quals); + + return result; +} + +/* See description in compile-cplus.h. */ + +static gcc_type +compile_cplus_convert_qualified (compile_cplus_instance *instance, + struct type *type) +{ + struct type *unqual = make_unqualified_type (type); + gcc_cp_qualifiers_flags quals = (enum gcc_cp_qualifiers) 0; + gcc_type unqual_converted = instance->convert_type (unqual); + + if (TYPE_CONST (type)) + quals |= GCC_CP_QUALIFIER_CONST; + if (TYPE_VOLATILE (type)) + quals |= GCC_CP_QUALIFIER_VOLATILE; + if (TYPE_RESTRICT (type)) + quals |= GCC_CP_QUALIFIER_RESTRICT; + + return instance->convert_qualified_base (unqual_converted, quals); +} + +/* Convert a complex type to its gcc representation. */ + +static gcc_type +compile_cplus_convert_complex (compile_cplus_instance *instance, + struct type *type) +{ + gcc_type base = instance->convert_type (TYPE_TARGET_TYPE (type)); + + return instance->plugin ().build_complex_type (base); +} + +/* Convert a namespace of TYPE. */ + +static gcc_type +compile_cplus_convert_namespace (compile_cplus_instance *instance, + struct type *type) +{ + compile_scope scope = instance->new_scope (TYPE_NAME (type), type); + gdb::unique_xmalloc_ptr<char> name + = compile_cplus_instance::decl_name (TYPE_NAME (type)); + + /* Push scope. */ + instance->enter_scope (scope); + + /* Convert this namespace. */ + instance->plugin ().push_namespace (name.get ()); + instance->plugin ().pop_binding_level (name.get ()); + + /* Pop scope. */ + instance->leave_scope (); + + /* Namespaces are non-cacheable types. */ + return GCC_TYPE_NONE; +} + +/* A helper function which knows how to convert most types from their + gdb representation to the corresponding gcc form. This examines + the TYPE and dispatches to the appropriate conversion function. It + returns the gcc type. + + If the type was defined in another type, NESTED_ACCESS should indicate the + accessibility of this type. */ + +static gcc_type +convert_type_cplus_basic (compile_cplus_instance *instance, + struct type *type, + enum gcc_cp_symbol_kind nested_access) +{ + /* If we are converting a qualified type, first convert the + unqualified type and then apply the qualifiers. */ + if ((TYPE_INSTANCE_FLAGS (type) & (TYPE_INSTANCE_FLAG_CONST + | TYPE_INSTANCE_FLAG_VOLATILE + | TYPE_INSTANCE_FLAG_RESTRICT)) != 0) + return compile_cplus_convert_qualified (instance, type); + + switch (TYPE_CODE (type)) + { + case TYPE_CODE_REF: + case TYPE_CODE_RVALUE_REF: + return compile_cplus_convert_reference (instance, type); + + case TYPE_CODE_PTR: + return compile_cplus_convert_pointer (instance, type); + + case TYPE_CODE_ARRAY: + return compile_cplus_convert_array (instance, type); + + case TYPE_CODE_STRUCT: + case TYPE_CODE_UNION: + return + compile_cplus_convert_struct_or_union (instance, type, nested_access); + + case TYPE_CODE_ENUM: + return compile_cplus_convert_enum (instance, type, nested_access); + + case TYPE_CODE_FUNC: + return compile_cplus_convert_func (instance, type, false); + + case TYPE_CODE_METHOD: + return + compile_cplus_convert_method (instance, TYPE_SELF_TYPE (type), type); + + case TYPE_CODE_MEMBERPTR: + case TYPE_CODE_METHODPTR: + return compile_cplus_convert_memberptr (instance, type); + break; + + case TYPE_CODE_INT: + return compile_cplus_convert_int (instance, type); + + case TYPE_CODE_FLT: + return compile_cplus_convert_float (instance, type); + + case TYPE_CODE_VOID: + return compile_cplus_convert_void (instance, type); + + case TYPE_CODE_BOOL: + return compile_cplus_convert_bool (instance, type); + + case TYPE_CODE_COMPLEX: + return compile_cplus_convert_complex (instance, type); + + case TYPE_CODE_NAMESPACE: + return compile_cplus_convert_namespace (instance, type); + + case TYPE_CODE_TYPEDEF: + return compile_cplus_convert_typedef (instance, type, nested_access); + + default: + break; + } + + std::string s = string_printf (_("unhandled TYPE_CODE %d"), + TYPE_CODE (type)); + + return instance->plugin ().error (s.c_str ()); +} + +gcc_type +compile_cplus_instance::convert_type (struct type *type, + enum gcc_cp_symbol_kind nested_access) +{ + /* Check if TYPE has already been converted. */ + gcc_type result; + if (get_cached_type (type, result)) + return result; + + /* It is the first time this type has been seen -- convert it + and cache it, if appropriate.. */ + result = convert_type_cplus_basic (this, type, nested_access); + if (result != GCC_TYPE_NONE) + insert_type (type, result); + return result; +} + +void +compile_cplus_instance::gcc_cplus_enter_scope + (void *datum, struct gcc_cp_context *gcc_context) +{ +} + +void +compile_cplus_instance::gcc_cplus_leave_scope + (void *datum, struct gcc_cp_context *gcc_context) +{ +} + + + +/* Plug-in forwards. */ + +/* C++ plug-in wrapper. */ + +/* A result printer for plug-in calls that return a gcc_type or + gcc_decl. */ + +static void +compile_cplus_debug_output_1 (gcc_type arg) +{ + fprintf_unfiltered (gdb_stdlog, "%lld", arg); +} + +static void +compile_cplus_debug_output_1 (const char *arg) +{ + if (arg == nullptr) + fputs_unfiltered ("NULL", gdb_stdlog); + else + fputs_unfiltered (arg, gdb_stdlog); +} + +static void +compile_cplus_debug_output () +{ +} + +template <typename T> +static void +compile_cplus_debug_output_1 (const T *arg) +{ +} + +template <typename T, typename... Targs> +static void +compile_cplus_debug_output (T arg, Targs... Args) +{ + compile_cplus_debug_output_1 (arg); + fputc_unfiltered (' ', gdb_stdlog); + compile_cplus_debug_output (Args...); +} + +#define FORWARD(OP,...) m_context->cp_ops->OP(m_context, ##__VA_ARGS__) +#define OUTPUT_DEBUG_RESULT(R) \ + if (debug_compile_cplus_types) \ + { \ + fputs_unfiltered (": ", gdb_stdlog); \ + compile_cplus_debug_output (R); \ + fputc_unfiltered ('\n', gdb_stdlog); \ + } \ + +#define GCC_METHOD0(R, N) \ + R gcc_cp_plugin::N () const \ + { \ + if (debug_compile_cplus_types) \ + compile_cplus_debug_output (STRINGIFY (N)); \ + auto result = FORWARD (N); \ + OUTPUT_DEBUG_RESULT (result); \ + return result; \ + } +#define GCC_METHOD1(R, N, A) \ + R gcc_cp_plugin::N (A a) const \ + { \ + if (debug_compile_cplus_types) \ + compile_cplus_debug_output (STRINGIFY (N), a); \ + auto result = FORWARD (N, a); \ + OUTPUT_DEBUG_RESULT (result); \ + return result; \ + } +#define GCC_METHOD2(R, N, A, B) \ + R gcc_cp_plugin::N (A a, B b) const \ + { \ + if (debug_compile_cplus_types) \ + compile_cplus_debug_output (STRINGIFY (N), a, b); \ + auto result = FORWARD (N, a, b); \ + OUTPUT_DEBUG_RESULT (result); \ + return result; \ + } +#define GCC_METHOD3(R, N, A, B, C) \ + R gcc_cp_plugin::N (A a, B b, C c) const \ + { \ + if (debug_compile_cplus_types) \ + compile_cplus_debug_output (STRINGIFY (N), a, b, c); \ + auto result = FORWARD (N, a, b, c); \ + OUTPUT_DEBUG_RESULT (result); \ + return result; \ + } +#define GCC_METHOD4(R, N, A, B, C, D) \ + R gcc_cp_plugin::N (A a, B b, C c, D d) const \ + { \ + if (debug_compile_cplus_types) \ + compile_cplus_debug_output (STRINGIFY (N), a, b, c, d); \ + auto result = FORWARD (N, a, b, c, d); \ + OUTPUT_DEBUG_RESULT (result); \ + return result; \ + } +#define GCC_METHOD5(R, N, A, B, C, D, E) \ + R gcc_cp_plugin::N (A a, B b, C c, D d, E e) const \ + { \ + if (debug_compile_cplus_types) \ + compile_cplus_debug_output (STRINGIFY (N), a, b, c, d, e); \ + auto result = FORWARD (N, a, b, c, d, e); \ + OUTPUT_DEBUG_RESULT (result); \ + return result; \ + } +#define GCC_METHOD7(R, N, A, B, C, D, E, F, G) \ + R gcc_cp_plugin::N (A a, B b, C c, D d, E e, F f, G g) const \ + { \ + if (debug_compile_cplus_types) \ + compile_cplus_debug_output (STRINGIFY (N), a, b, c, d, e, f, g); \ + auto result = FORWARD (N, a, b, c, d, e, f, g); \ + OUTPUT_DEBUG_RESULT (result); \ + return result; \ + } + +#include "gcc-cp-fe.def" + +#undef GCC_METHOD0 +#undef GCC_METHOD1 +#undef GCC_METHOD2 +#undef GCC_METHOD3 +#undef GCC_METHOD4 +#undef GCC_METHOD5 +#undef GCC_METHOD7 +#undef FORWARD +#undef OUTPUT_DEBUG_RESULT + +gcc_expr +gcc_cp_plugin::build_decl (const char *debug_decltype, const char *name, + enum gcc_cp_symbol_kind sym_kind, gcc_type sym_type, + const char *substitution_name, gcc_address address, + const char *filename, unsigned int line_number) +{ + if (debug_compile_cplus_types) + fprintf_unfiltered (gdb_stdlog, "<%s> ", debug_decltype); + + return build_decl (name, sym_kind, sym_type, substitution_name, + address, filename, line_number); +} + +gcc_type +gcc_cp_plugin::start_class_type (const char *debug_name, gcc_decl typedecl, + const struct gcc_vbase_array *base_classes, + const char *filename, unsigned int line_number) +{ + if (debug_compile_cplus_types) + fprintf_unfiltered (gdb_stdlog, "<%s> ", debug_name); + + return start_class_type (typedecl, base_classes, filename, line_number); +} + +int +gcc_cp_plugin::finish_class_type (const char *debug_name, + unsigned long size_in_bytes) +{ + if (debug_compile_cplus_types) + fprintf_unfiltered (gdb_stdlog, "<%s> ", debug_name); + + return finish_class_type (size_in_bytes); +} + +int +gcc_cp_plugin::pop_binding_level (const char *debug_name) +{ + if (debug_compile_cplus_types) + fprintf_unfiltered (gdb_stdlog, "<%s> ", debug_name); + + return pop_binding_level (); +} + +void +_initialize_compile_cplus_types () +{ + add_setshow_boolean_cmd ("compile-cplus-types", no_class, + &debug_compile_cplus_types, _("\ +Set debugging of C++ compile type conversion."), _("\ +Show debugging of C++ compile type conversion."), _("\ +When enabled debugging messages are printed during C++ type conversion for\n\ +the compile commands."), + nullptr, + nullptr, + &setdebuglist, + &showdebuglist); + + add_setshow_boolean_cmd ("compile-cplus-scopes", no_class, + &debug_compile_cplus_scopes, _("\ +Set debugging of C++ compile scopes."), _("\ +Show debugging of C++ compile scopes."), _("\ +When enabled debugging messages are printed about definition scopes during\n\ +C++ type conversion for the compile commands."), + nullptr, + nullptr, + &setdebuglist, + &showdebuglist); +} diff --git a/gdb/compile/compile-cplus.h b/gdb/compile/compile-cplus.h new file mode 100644 index 0000000..7baa57d --- /dev/null +++ b/gdb/compile/compile-cplus.h @@ -0,0 +1,205 @@ +/* Header file for GDB compile C++ language support. + Copyright (C) 2016-2018 Free Software Foundation, Inc. + + 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/>. */ + +#ifndef GDB_COMPILE_CPLUS_H +#define GDB_COMPILE_CPLUS_H + +#include "common/enum-flags.h" +#include "gcc-cp-plugin.h" + +struct type; +struct block; + +/* enum-flags wrapper */ +DEF_ENUM_FLAGS_TYPE (enum gcc_cp_qualifiers, gcc_cp_qualifiers_flags); +DEF_ENUM_FLAGS_TYPE (enum gcc_cp_ref_qualifiers, gcc_cp_ref_qualifiers_flags); +DEF_ENUM_FLAGS_TYPE (enum gcc_cp_symbol_kind, gcc_cp_symbol_kind_flags); + +class compile_cplus_instance; + +/* A single component of a type's scope. Type names are broken into + "components," a series of unqualified names comprising the type name, + e.g., "namespace1", "namespace2", "myclass". */ + +struct scope_component +{ + /* The unqualified name of this scope. */ + std::string name; + + /* The block symbol for this type/scope. */ + struct block_symbol bsymbol; +}; + +/* Comparison operators for scope_components. */ + +bool operator== (const scope_component &lhs, const scope_component &rhs); +bool operator!= (const scope_component &lhs, const scope_component &rhs); + + +/* A single compiler scope used to define a type. + + A compile_scope is a list of scope_components, where all leading + scope_components are namespaces, followed by a single non-namespace + type component (the actual type we are converting). */ + +class compile_scope : private std::vector<scope_component> +{ +public: + + using std::vector<scope_component>::push_back; + using std::vector<scope_component>::pop_back; + using std::vector<scope_component>::back; + using std::vector<scope_component>::empty; + using std::vector<scope_component>::size; + using std::vector<scope_component>::begin; + using std::vector<scope_component>::end; + using std::vector<scope_component>::operator[]; + + compile_scope () + : m_nested_type (GCC_TYPE_NONE), m_pushed (false) + { + } + + /* Return the gcc_type of the type if it is a nested definition. + Returns GCC_TYPE_NONE if this type was not nested. */ + gcc_type nested_type () + { + return m_nested_type; + } + +private: + + /* compile_cplus_instance is a friend class so that it can set the + following private members when compile_scopes are created. */ + friend compile_cplus_instance; + + /* If the type was actually a nested type, this will hold that nested + type after the scope is pushed. */ + gcc_type m_nested_type; + + /* If true, this scope was pushed to the compiler and all namespaces + must be popped when leaving the scope. */ + bool m_pushed; +}; + +/* Comparison operators for compile_scopes. */ + +bool operator== (const compile_scope &lhs, const compile_scope &rhs); +bool operator!= (const compile_scope &lhs, const compile_scope &rhs); + +/* Convert TYPENAME into a vector of namespace and top-most/super + composite scopes. + + For example, for the input "Namespace::classB::classInner", the + resultant vector will contain the tokens "Namespace" and + "classB". */ + +compile_scope type_name_to_scope (const char *type_name, + const struct block *block); + +/* A callback suitable for use as the GCC C++ symbol oracle. */ + +extern gcc_cp_oracle_function gcc_cplus_convert_symbol; + +/* A callback suitable for use as the GCC C++ address oracle. */ + +extern gcc_cp_symbol_address_function gcc_cplus_symbol_address; + +/* A subclass of compile_instance that is specific to the C++ front + end. */ + +class compile_cplus_instance : public compile_instance +{ +public: + + explicit compile_cplus_instance (struct gcc_cp_context *gcc_cp) + : compile_instance (&gcc_cp->base, m_default_cflags), + m_plugin (gcc_cp) + { + m_plugin.set_callbacks (gcc_cplus_convert_symbol, + gcc_cplus_symbol_address, + gcc_cplus_enter_scope, gcc_cplus_leave_scope, + this); + } + + /* Convert a gdb type, TYPE, to a GCC type. + + If this type was defined in another type, NESTED_ACCESS should indicate + the accessibility of this type (or GCC_CP_ACCESS_NONE if not a nested + type). GCC_CP_ACCESS_NONE is the default nested access. + + The new GCC type is returned. */ + gcc_type convert_type + (struct type *type, + enum gcc_cp_symbol_kind nested_access = GCC_CP_ACCESS_NONE); + + /* Return a handle for the GCC plug-in. */ + gcc_cp_plugin &plugin () { return m_plugin; } + + /* Factory method to create a new scope based on TYPE with name TYPE_NAME. + [TYPE_NAME could be TYPE_NAME or SYMBOL_NATURAL_NAME.] + + If TYPE is a nested or local definition, nested_type () will return + the gcc_type of the conversion. + + Otherwise, nested_type () is GCC_TYPE_NONE. */ + compile_scope new_scope (const char *type_name, struct type *type); + + /* Enter the given NEW_SCOPE. */ + void enter_scope (compile_scope &scope); + + /* Leave the current scope. */ + void leave_scope (); + + /* Add the qualifiers given by QUALS to BASE. */ + gcc_type convert_qualified_base (gcc_type base, + gcc_cp_qualifiers_flags quals); + + /* Convert TARGET into a pointer type. */ + gcc_type convert_pointer_base (gcc_type target); + + /* Convert BASE into a reference type. RQUALS describes the reference. */ + gcc_type convert_reference_base (gcc_type base, + enum gcc_cp_ref_qualifiers rquals); + + /* Return the declaration name of the symbol named NATURAL. + This returns a name with no function arguments or template parameters, + suitable for passing to the compiler plug-in. */ + static gdb::unique_xmalloc_ptr<char> decl_name (const char *natural); + +private: + + /* Callbacks suitable for use as the GCC C++ enter/leave scope requests. */ + static gcc_cp_enter_leave_user_expr_scope_function gcc_cplus_enter_scope; + static gcc_cp_enter_leave_user_expr_scope_function gcc_cplus_leave_scope; + + /* Default compiler flags for C++. */ + static const char *m_default_cflags; + + /* The GCC plug-in. */ + gcc_cp_plugin m_plugin; + + /* A list of scopes we are processing. */ + std::vector<compile_scope> m_scopes; +}; + +/* Get the access flag for the NUM'th method of TYPE's FNI'th + fieldlist. */ + +enum gcc_cp_symbol_kind get_method_access_flag (const struct type *type, + int fni, int num); + +#endif /* GDB_COMPILE_CPLUS_H */ diff --git a/gdb/compile/compile-internal.h b/gdb/compile/compile-internal.h index c8d2d2f..a6e7330 100644 --- a/gdb/compile/compile-internal.h +++ b/gdb/compile/compile-internal.h @@ -164,6 +164,10 @@ protected: #define COMPILE_I_EXPR_VAL "__gdb_expr_val" #define COMPILE_I_EXPR_PTR_TYPE "__gdb_expr_ptr_type" +/* A "type" to indicate a NULL type. */ + +const gcc_type GCC_TYPE_NONE = (gcc_type) -1; + /* Call gdbarch_register_name (GDBARCH, REGNUM) and convert its result to a form suitable for the compiler source. The register names should not clash with inferior defined macros. */ diff --git a/gdb/compile/compile-object-load.c b/gdb/compile/compile-object-load.c index a83f95d..873750b 100644 --- a/gdb/compile/compile-object-load.c +++ b/gdb/compile/compile-object-load.c @@ -459,7 +459,8 @@ get_out_value_type (struct symbol *func_sym, struct objfile *objfile, if (function != NULL && (BLOCK_SUPERBLOCK (function_block) == BLOCKVECTOR_BLOCK (bv, STATIC_BLOCK)) - && (strcmp (SYMBOL_LINKAGE_NAME (function), GCC_FE_WRAPPER_FUNCTION) + && (strcmp_iw (SYMBOL_LINKAGE_NAME (function), + GCC_FE_WRAPPER_FUNCTION) == 0)) break; } @@ -478,7 +479,7 @@ get_out_value_type (struct symbol *func_sym, struct objfile *objfile, gdb_ptr_type = check_typedef (gdb_ptr_type); if (TYPE_CODE (gdb_ptr_type) != TYPE_CODE_PTR) error (_("Type of \"%s\" is not a pointer"), COMPILE_I_EXPR_PTR_TYPE); - gdb_type_from_ptr = TYPE_TARGET_TYPE (gdb_ptr_type); + gdb_type_from_ptr = check_typedef (TYPE_TARGET_TYPE (gdb_ptr_type)); if (types_deeply_equal (gdb_type, gdb_type_from_ptr)) { @@ -741,6 +742,8 @@ compile_object_load (const compile_file_names &file_names, ? mst_unknown : MSYMBOL_TYPE (bmsym.minsym)) { case mst_text: + case mst_bss: + case mst_data: sym->value = BMSYMBOL_VALUE_ADDRESS (bmsym); if (compile_debug) fprintf_unfiltered (gdb_stdlog, diff --git a/gdb/compile/compile.c b/gdb/compile/compile.c index 20d81cb..6d51006 100644 --- a/gdb/compile/compile.c +++ b/gdb/compile/compile.c @@ -995,7 +995,6 @@ String quoting is parsed like in shell, for example:\n\ " -fPIE" /* We want warnings, except for some commonly happening for GDB commands. */ " -Wall " - " -Wno-implicit-function-declaration" " -Wno-unused-but-set-variable" " -Wno-unused-variable" /* Override CU's possible -fstack-protector-strong. */ diff --git a/gdb/compile/gcc-cp-plugin.h b/gdb/compile/gcc-cp-plugin.h new file mode 100644 index 0000000..1b72726 --- /dev/null +++ b/gdb/compile/gcc-cp-plugin.h @@ -0,0 +1,85 @@ +/* GCC C++ plug-in wrapper for GDB. + + Copyright (C) 2018 Free Software Foundation, Inc. + + 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/>. */ + +/* A class representing the GCC C++ plug-in. */ + +#include "gcc-cp-interface.h" + +class gcc_cp_plugin +{ +public: + + explicit gcc_cp_plugin (struct gcc_cp_context *gcc_cp) + : m_context (gcc_cp) + { + } + + /* Set the oracle callbacks to be used by the compiler plug-in. */ + void set_callbacks (gcc_cp_oracle_function *binding_oracle, + gcc_cp_symbol_address_function *address_oracle, + gcc_cp_enter_leave_user_expr_scope_function *enter_scope, + gcc_cp_enter_leave_user_expr_scope_function *leave_scope, + void *datum) + { + m_context->cp_ops->set_callbacks (m_context, binding_oracle, + address_oracle, enter_scope, leave_scope, + datum); + } + + /* Returns the interface version of the compiler plug-in. */ + int version () const { return m_context->cp_ops->cp_version; } + +#define GCC_METHOD0(R, N) R N () const; +#define GCC_METHOD1(R, N, A) R N (A) const; +#define GCC_METHOD2(R, N, A, B) R N (A, B) const; +#define GCC_METHOD3(R, N, A, B, C) R N (A, B, C) const; +#define GCC_METHOD4(R, N, A, B, C, D) R N (A, B, C, D) const; +#define GCC_METHOD5(R, N, A, B, C, D, E) R N (A, B, C, D, E) const; +#define GCC_METHOD7(R, N, A, B, C, D, E, F, G) R N (A, B, C, D, E, F, G) const; + +#include "gcc-cp-fe.def" + +#undef GCC_METHOD0 +#undef GCC_METHOD1 +#undef GCC_METHOD2 +#undef GCC_METHOD3 +#undef GCC_METHOD4 +#undef GCC_METHOD5 +#undef GCC_METHOD7 + + /* Special overloads of plug-in methods with added debugging information. */ + + gcc_expr build_decl (const char *debug_decltype, const char *name, + enum gcc_cp_symbol_kind sym_kind, gcc_type sym_type, + const char *substitution_name, gcc_address address, + const char *filename, unsigned int line_number); + + gcc_type start_class_type (const char *debug_name, gcc_decl typedecl, + const struct gcc_vbase_array *base_classes, + const char *filename, unsigned int line_number); + + int finish_class_type (const char *debug_name, unsigned long size_in_bytes); + + int pop_binding_level (const char *debug_name); + +private: + + /* The GCC C++ context. */ + struct gcc_cp_context *m_context; +}; diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog index c637d39..58e01e4 100644 --- a/gdb/doc/ChangeLog +++ b/gdb/doc/ChangeLog @@ -1,3 +1,8 @@ +2018-08-29 Keith Seitz <keiths@redhat.com> + + * gdb.texinfo (Compiling and injecting code in GDB): Document + set/show "compile-oracle" and "compile-cplus-types" commands. + 2018-08-22 Jan Vrany <jan.vrany@fit.cvut.cz> * gdb.texinfo (The -stack-list-frames Command): Update description diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 5068c0a..f2d1155 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -18689,6 +18689,16 @@ injecting the code. The default is off. @item show debug compile Displays the current state of displaying @value{GDBN} process of compiling and injecting the code. + +@anchor{set debug compile-cplus-types} +@item set debug compile-cplus-types +@cindex compile C@t{++} type conversion +Turns on or off the display of C@t{++} type conversion debugging information. +The default is off. + +@item show debug compile-cplus-types +Displays the current state of displaying debugging information for +C@t{++} type conversion. @end table @subsection Compilation options for the @code{compile} command diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index 5145fd6..0164558 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,28 @@ +2018-08-29 Keith Seitz <keiths@redhat.com> + + * gdb.compile/compile-cplus-anonymous.cc: New file. + * gdb.compile/compile-cplus-anonymous.exp: New file. + * gdb.compile/compile-cplus-array-decay.cc: New file. + * gdb.compile/compile-cplus-array-decay.exp: New file. + * gdb.compile/compile-cplus-inherit.cc: New file. + * gdb.compile/compile-cplus-inherit.exp: New file. + * gdb.compile/compile-cplus-member.cc: New file. + * gdb.compile/compile-cplus-member.exp: New file. + * gdb.compile/compile-cplus-method.cc: New file. + * gdb.compile/compile-cplus-method.exp: New file. + * gdb.compile/compile-cplus-mod.c: "New" file. + * gdb.compile/compile-cplus-namespace.cc: New file. + * gdb.compile/compile-cplus-namespace.exp: New file. + * gdb.compile/compile-cplus-nested.cc: New file. + * gdb.compile/compile-cplus-nested.exp: New file. + * gdb.compile/compile-cplus-print.c: "New" file. + * gdb.compile/compile-cplus-print.exp: "New" file. + * gdb.compile/compile-cplus-virtual.cc: New file. + * gdb.compile/compile-cplus-virtual.exp: New file. + * gdb.compile/compile-cplus.c: "New" file. + * gdb.compile/compile-cplus.exp: "New" file. + * lib/compile-support.exp: New file. + 2018-08-16 Gary Benson <gbenson@redhat.com> PR gdb/13000: diff --git a/gdb/testsuite/gdb.compile/compile-cplus-anonymous.cc b/gdb/testsuite/gdb.compile/compile-cplus-anonymous.cc new file mode 100644 index 0000000..95c4430 --- /dev/null +++ b/gdb/testsuite/gdb.compile/compile-cplus-anonymous.cc @@ -0,0 +1,76 @@ +/* Copyright 2015-2018 Free Software Foundation, Inc. + + 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/>. */ + +namespace { + static enum {ABC = 1, DEF, GHI, JKL} anon_e = GHI; + static union + { + char aa; + int bb; + float ff; + double dd; + void *pp; + } anon_u = { 'a' }; + + static struct + { + char *ptr; + int len; + struct + { + unsigned MAGIC; + }; + union + { + int ua; + char *ub; + }; + } anon_s = {"abracadabra", 11, 0xdead, 0xbeef}; + + struct A + { + A () : e (AA) + { + this->u.b = 0; + this->s.ptr = "hello"; + this->s.len = 5; + } + + enum {AA = 10, BB, CC, DD} e; + union + { + char a; + int b; + float f; + double d; + void *p; + } u; + struct + { + char *ptr; + int len; + } s; + }; +}; + +int +main () +{ + A a; + int var = 1234; + + return a.u.b + a.s.len + static_cast<int> (a.e) + + static_cast<int> (anon_e) + anon_u.bb + anon_s.len; // break here +} diff --git a/gdb/testsuite/gdb.compile/compile-cplus-anonymous.exp b/gdb/testsuite/gdb.compile/compile-cplus-anonymous.exp new file mode 100644 index 0000000..ebfb941 --- /dev/null +++ b/gdb/testsuite/gdb.compile/compile-cplus-anonymous.exp @@ -0,0 +1,64 @@ +# Copyright 2015-2018 Free Software Foundation, Inc. + +# 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/>. + +# Anonymous type conversion tests for GDB's C++ compile feature. + +load_lib compile-support.exp + +standard_testfile .cc + +if {[skip_cplus_tests]} { + untested "skipping C++ tests" + return +} + +if {[prepare_for_testing $testfile $testfile $srcfile \ + {debug nowarnings c++}]} { + return -1 +} + +if {![runto_main]} { + untested "could not run to main" + return -1 +} + +if {[skip_compile_feature_tests]} { + untested \ + "compile command not supported (could not find libcc1 shared library?)" + return -1 +} + +gdb_breakpoint [gdb_get_line_number "break here" $srcfile] +gdb_continue_to_breakpoint "testing location" + +# Reminder, "var" is an integer; all these types get converted to `int'. +CompileExpression::new "var" +CompileExpression::test "anon_e" {(3|GHI)} +CompileExpression::test "anon_u.aa" {97( 'a')?} +CompileExpression::test "a.u.b" 0 +CompileExpression::test "a.s.len" 5 +CompileExpression::test "a.e" {(10|A::AA)} +CompileExpression::test "(*a.s.ptr != 'h')" (0|false) +CompileExpression::test "A::BB" {(11|A::BB)} +CompileExpression::test "ABC" {(1|ABC)} +CompileExpression::test "DEF" {(2|DEF)} +CompileExpression::test "GHI" {(3|GHI)} +CompileExpression::test "JKL" {(4|JKL)} + +set k "compile/23588 *-*-*" +CompileExpression::test "anon_s.len" 11 -kfail $k +CompileExpression::test "anon_s.MAGIC" "57005" -kfail $k +CompileExpression::test "anon_s.ua" "48879" -kfail $k +CompileExpression::test "(*anon_s.ptr == 'a')" (1|true) -kfail $k diff --git a/gdb/testsuite/gdb.compile/compile-cplus-array-decay.cc b/gdb/testsuite/gdb.compile/compile-cplus-array-decay.cc new file mode 100644 index 0000000..da51a65 --- /dev/null +++ b/gdb/testsuite/gdb.compile/compile-cplus-array-decay.cc @@ -0,0 +1,31 @@ +/* Copyright 2017-2018 Free Software Foundation, Inc. + + 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/>. */ + +static const char *g_string_initializer = "hello"; + +int +main () +{ + int integers[10]; + const char *strings[10]; + + for (auto &i : integers) + i = 0; + + for (auto &i : strings) + i = g_string_initializer; + + return 0; // break here +} diff --git a/gdb/testsuite/gdb.compile/compile-cplus-array-decay.exp b/gdb/testsuite/gdb.compile/compile-cplus-array-decay.exp new file mode 100644 index 0000000..fb3e63f --- /dev/null +++ b/gdb/testsuite/gdb.compile/compile-cplus-array-decay.exp @@ -0,0 +1,50 @@ +# Copyright 2017-2018 Free Software Foundation, Inc. + +# 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/>. + +# Test whether GDB's C++ compile feature is decaying arrays into pointers. + +load_lib compile-support.exp + +standard_testfile .cc + +if {[skip_cplus_tests]} { + untested "skipping C++ tests" + return +} + +if {[prepare_for_testing $testfile $testfile $srcfile \ + {debug nowarnings c++}]} { + return -1 +} + +if {![runto_main]} { + untested "could not run to main" + return -1 +} + +if {[skip_compile_feature_tests]} { + untested \ + "compile command not supported (could not find libcc1 shared library?)" + return -1 +} + +gdb_breakpoint [gdb_get_line_number "break here" $srcfile] +gdb_continue_to_breakpoint "testing location" + +gdb_test "compile print integers" " = \\{0, 0, 0, 0, 0, 0, 0, 0, 0, 0\\}" + +gdb_test "compile print strings" " = \\{$hex \"hello\", $hex \"hello\",\ + $hex \"hello\", $hex \"hello\", $hex \"hello\", $hex \"hello\",\ + $hex \"hello\", $hex \"hello\", $hex \"hello\", $hex \"hello\"\\}" diff --git a/gdb/testsuite/gdb.compile/compile-cplus-inherit.cc b/gdb/testsuite/gdb.compile/compile-cplus-inherit.cc new file mode 100644 index 0000000..1d93f9c --- /dev/null +++ b/gdb/testsuite/gdb.compile/compile-cplus-inherit.cc @@ -0,0 +1,58 @@ +/* Copyright 2015-2018 Free Software Foundation, Inc. + + 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/>. */ + +struct A +{ + A () : a_ (1) {} + int do_it (int amount) { return a_ + amount; } + + int a_; +}; + +struct B +{ + B () : b_ (2) {} + int do_it (int amount) { return b_ - amount; } + + int b_; +}; + +struct C +{ + C () : c_ (3) {} + int do_it (int amount) { return c_ * amount; } + + int c_; +}; + +struct D : public A, B, C +{ + D () : d_ (4) {} + + int d_; +}; + +int +main () +{ + D d; + int var = 1234; + + var = d.A::do_it (1) + + d.B::do_it (2) + + d.C::do_it (3); // break here + + return 0; +} diff --git a/gdb/testsuite/gdb.compile/compile-cplus-inherit.exp b/gdb/testsuite/gdb.compile/compile-cplus-inherit.exp new file mode 100644 index 0000000..1978cb5 --- /dev/null +++ b/gdb/testsuite/gdb.compile/compile-cplus-inherit.exp @@ -0,0 +1,53 @@ +# Copyright 2015-2018 Free Software Foundation, Inc. + +# 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/>. + +# Inheritance tests for GDB's C++ compile feature. + +load_lib compile-support.exp + +standard_testfile .cc + +if {[skip_cplus_tests]} { + untested "skipping C++ tests" + return +} + +if {[prepare_for_testing $testfile $testfile $srcfile \ + {debug nowarnings c++}]} { + return -1 +} + +if {![runto_main]} { + untested "could not run to main" + return -1 +} + +if {[skip_compile_feature_tests]} { + untested \ + "compile command not supported (could not find libcc1 shared library?)" + return -1 +} + +gdb_breakpoint [gdb_get_line_number "break here" $srcfile] +gdb_continue_to_breakpoint "testing location" + +CompileExpression::new "var" +CompileExpression::test "d.a_" 1 +CompileExpression::test "d.b_" 2 +CompileExpression::test "d.c_" 3 +CompileExpression::test "d.d_" 4 +CompileExpression::test "d.A::do_it (1)" 2 +CompileExpression::test "d.B::do_it (1)" 1 +CompileExpression::test "d.C::do_it (1)" 3 diff --git a/gdb/testsuite/gdb.compile/compile-cplus-member.cc b/gdb/testsuite/gdb.compile/compile-cplus-member.cc new file mode 100644 index 0000000..d742318 --- /dev/null +++ b/gdb/testsuite/gdb.compile/compile-cplus-member.cc @@ -0,0 +1,83 @@ +/* Copyright 2015-2018 Free Software Foundation, Inc. + + 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/>. */ + +class A; +static int get_values (const A& a); + +enum myenum {E_A = 10, E_B, E_C, E_D, E_E}; + +namespace N { + typedef enum {NA = 20, NB, NC, ND} ANON_NE; +} + +namespace { + typedef enum {AA = 40, AB, AC, AD} ANON_E; +} + +ANON_E g_e = AC; + +class A +{ +public: + typedef int ATYPE; + + A () : public_ (1), protected_ (N::NB), private_ (3) {} + ATYPE public_; + static const myenum s_public_; + friend ATYPE get_values (const A&); + +protected: + N::ANON_NE protected_; + static N::ANON_NE s_protected_; + +private: + ATYPE private_; + static myenum s_private_; +}; + +const myenum A::s_public_ = E_A; +N::ANON_NE A::s_protected_ = N::NA; +myenum A::s_private_ = E_C; + +static A::ATYPE +get_values (const A& a) +{ + A::ATYPE val; + + val = a.public_ + a.private_; // 1 + 3 + if (a.protected_ == N::NB) // + 21 + val += 21; + if (a.s_public_ == E_A) // +10 + val += 10; + if (a.s_protected_ == N::NA) // +20 + val += 20; + if (a.s_private_ == E_C) // +30 + val += 30; + if (g_e == AC) // +40 + val += 40; + return val; // = 125 +} + +typedef int A::*PMI; + +int +main () +{ + A a; + int var = 1234; + PMI pmi = &A::public_; + + return a.*pmi + get_values (a); // break here +} diff --git a/gdb/testsuite/gdb.compile/compile-cplus-member.exp b/gdb/testsuite/gdb.compile/compile-cplus-member.exp new file mode 100644 index 0000000..f6255df --- /dev/null +++ b/gdb/testsuite/gdb.compile/compile-cplus-member.exp @@ -0,0 +1,76 @@ +# Copyright 2015-2018 Free Software Foundation, Inc. + +# 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/>. + +# Member tests for GDB's C++ compile feature. + +load_lib compile-support.exp + +standard_testfile .cc + +if {[skip_cplus_tests]} { + untested "skipping C++ tests" + return +} + +if {[prepare_for_testing $testfile $testfile $srcfile \ + {debug nowarnings c++}]} { + return -1 +} + +if {![runto_main]} { + untested "could not run to main" + return -1 +} + +if {[skip_compile_feature_tests]} { + untested \ + "compile command not supported (could not find libcc1 shared library?)" + return -1 +} + +gdb_breakpoint [gdb_get_line_number "break here" $srcfile] +gdb_continue_to_breakpoint "testing location" + +CompileExpression::new "var" +CompileExpression::test "a.public_" 1 +CompileExpression::test "a.protected_" {(21|N::NB)} +CompileExpression::test "a.private_" 3 +CompileExpression::test "A::s_public_" {(10|E_A)} +CompileExpression::test "A::s_protected_" {(20|N::NA)} +CompileExpression::test "A::s_private_" {(12|E_C)} +CompileExpression::test "A::ATYPE i = 10; var = i;" 10 -explicit +CompileExpression::test "get_values (a)" 125 +CompileExpression::test "myenum me = E_B; var = me;" 11 -explicit +CompileExpression::test "A::s_protected_ = N::NB; var = A::s_protected_;" \ + 21 -explicit +CompileExpression::test "A::s_private_ = E_B; var = A::s_private_;" 11 -explicit +CompileExpression::test "N::ANON_NE ae = N::ND; var = ae;" 23 -explicit +CompileExpression::test {a.*pmi} 1 +CompileExpression::test {a.public_ = 2; var = a.*pmi; a.public_ = 1} 2 -explicit +CompileExpression::test "g_e" {(42|AC)} + +# Test some compilation failures +set failed {\r\nCompilation failed\.} +gdb_test "compile code a.s_public_ = E_B" \ + ".*assignment of read-only variable 'A::s_public_'$failed" + +gdb_test "compile code get_values ()" \ + ".*too few arguments to function.*$failed" + +gdb_test "compile code ATYPE i;" \ + ".*.ATYPE. was not declared in this scope$failed" + +gdb_test "compile code N::ANON_NE nse = E_A" \ + ".*cannot convert.*$failed" diff --git a/gdb/testsuite/gdb.compile/compile-cplus-method.cc b/gdb/testsuite/gdb.compile/compile-cplus-method.cc new file mode 100644 index 0000000..3993493 --- /dev/null +++ b/gdb/testsuite/gdb.compile/compile-cplus-method.cc @@ -0,0 +1,91 @@ +/* Copyright 2015-2018 Free Software Foundation, Inc. + + 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/>. */ + +class A; +static int get_value (const A* a); + +class A +{ +public: + typedef int ATYPE; + + A () : a_ (21) {} + ATYPE get_var () { return a_; } + ATYPE get_var (unsigned long a) { return 100; } + ATYPE get_var (ATYPE a) { return 101; } + ATYPE get_var (float a) { return 102; } + ATYPE get_var (void *a) { return 103;} + ATYPE get_var (A& lr) { return 104; } + ATYPE get_var (A const& lr) { return 105; } + + ATYPE get_var1 (int n) { return a_ << n; } + ATYPE get_var2 (int incr, unsigned n) { return (a_ + incr) << n; } + + static ATYPE get_1 (int a) { return a + 1; } + static ATYPE get_2 (int a, int b) { return a + b + 2; } + + friend ATYPE get_value (const A*); + +private: + ATYPE a_; +}; + +static A::ATYPE +get_value (A::ATYPE a) +{ + return a; +} + +static A::ATYPE +get_value (const A* a) +{ + return a->a_; +} + +static A::ATYPE +get_value () +{ + return 200; +} + +typedef int (A::*PMF) (A::ATYPE); + +int +main () +{ + A *a = new A (); + int var = 1234; + float f = 1.23; + unsigned long ul = 0xdeadbeef; + A const* ac = a; + + PMF pmf = &A::get_var; + PMF *pmf_p = &pmf; + + var -= a->get_var (); // break here + var -= a->get_var (1); + var -= a->get_var (ul); + var -= a->get_var (f); + var -= a->get_var (a); + var -= a->get_var (*a); + var -= a->get_var (*ac); + var -= a->get_var1 (1); + var -= a->get_var2 (1, 2); + var += (a->*pmf) (1); + var -= (a->**pmf_p) (1); + + return var - A::get_1 (1) + A::get_2 (1, 2) + get_value () + + get_value (get_value ()) + get_value (a); +} diff --git a/gdb/testsuite/gdb.compile/compile-cplus-method.exp b/gdb/testsuite/gdb.compile/compile-cplus-method.exp new file mode 100644 index 0000000..3a5bb82 --- /dev/null +++ b/gdb/testsuite/gdb.compile/compile-cplus-method.exp @@ -0,0 +1,67 @@ +# Copyright 2015-2018 Free Software Foundation, Inc. + +# 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/>. + +# Method tests for GDB's C++ compile feature. + +load_lib compile-support.exp + +standard_testfile .cc + +if {[skip_cplus_tests]} { + untested "skipping C++ tests" + return +} + +if {[prepare_for_testing $testfile $testfile $srcfile \ + {debug nowarnings c++}]} { + return -1 +} + +if {![runto_main]} { + untested "could not run to main" + return -1 +} + +if {[skip_compile_feature_tests]} { + untested \ + "compile command not supported (could not find libcc1 shared library?)" + return -1 +} + +gdb_breakpoint [gdb_get_line_number "break here" $srcfile] +gdb_continue_to_breakpoint "testing location" + +CompileExpression::new "var" +CompileExpression::test "a->get_var ()" 21 +CompileExpression::test "a->get_var (static_cast<unsigned long> (1))" 100 +CompileExpression::test "a->get_var (static_cast<int> (1))" 101 +CompileExpression::test "a->get_var (static_cast<float> (1))" 102 +CompileExpression::test "a->get_var (static_cast<void *> (a))" 103 +CompileExpression::test "a->get_var (*a)" 104 +CompileExpression::test "a->get_var (*ac)" 105 +CompileExpression::test "a->get_var1 (1)" 42 +CompileExpression::test "a->get_var2 (1, 2)" 88 +CompileExpression::test "A::get_1 (1)" 2 +CompileExpression::test "A::get_2 (1, 2)" 5 +CompileExpression::test "A::get_1 (a->get_var ())" 22 +CompileExpression::test "a->get_var1 (a->get_var () - 16)" 672 +CompileExpression::test "a->get_var2 (a->get_var (), A::get_1 (2))" 336 +CompileExpression::test "get_value ()" 200 +CompileExpression::test "get_value (a)" 21 +CompileExpression::test "get_value (get_value ())" 200 +CompileExpression::test {(a->*pmf) (1)} 101 +CompileExpression::test \ + {pmf = &A::get_var1; var = (a->*pmf) (2); pmf = &A::get_var} 84 -explicit +CompileExpression::test {(a->**pmf_p) (1)} 101 diff --git a/gdb/testsuite/gdb.compile/compile-cplus-mod.c b/gdb/testsuite/gdb.compile/compile-cplus-mod.c new file mode 100644 index 0000000..617f2cb --- /dev/null +++ b/gdb/testsuite/gdb.compile/compile-cplus-mod.c @@ -0,0 +1,28 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2014-2018 Free Software Foundation, Inc. + + 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/>. */ + +extern "C" void +_gdb_expr () +{ + // Make 'globalvar' lookup working. +#pragma GCC push_user_expression + + globalvar = 3; + globalvar += 4; + +#pragma GCC pop_user_expression +} diff --git a/gdb/testsuite/gdb.compile/compile-cplus-namespace.cc b/gdb/testsuite/gdb.compile/compile-cplus-namespace.cc new file mode 100644 index 0000000..c64077a --- /dev/null +++ b/gdb/testsuite/gdb.compile/compile-cplus-namespace.cc @@ -0,0 +1,52 @@ +/* Copyright 2015-2018 Free Software Foundation, Inc. + + 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/>. */ + +namespace N1 +{ + namespace N2 + { + namespace N3 + { + namespace N4 + { + static int n4static = 400; + + struct S4 + { + static int s4static; + int s4int_; + S4 () : s4int_ (4) {}; + ~S4 () { --s4static; } + + int get_var () { return s4int_; } + static int get_svar () { return s4static; } + }; + int S4::s4static = 40; + } + } + } +} + +int +main () +{ + using namespace N1::N2::N3::N4; + + S4 s4; + int var = 1234; + + var += s4.s4int_; /* break here */ + return S4::get_svar () - 10 * s4.get_var (); +} diff --git a/gdb/testsuite/gdb.compile/compile-cplus-namespace.exp b/gdb/testsuite/gdb.compile/compile-cplus-namespace.exp new file mode 100644 index 0000000..768a707 --- /dev/null +++ b/gdb/testsuite/gdb.compile/compile-cplus-namespace.exp @@ -0,0 +1,51 @@ +# Copyright 2015-2018 Free Software Foundation, Inc. + +# 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/>. + +# Namespace tests for GDB's C++ compile feature. + +load_lib compile-support.exp + +standard_testfile .cc + +if {[skip_cplus_tests]} { + untested "skipping C++ tests" + return +} + +if {[prepare_for_testing $testfile $testfile $srcfile \ + {debug nowarnings c++}]} { + return -1 +} + +if {![runto_main]} { + untested "could not run to main" + return -1 +} + +if {[skip_compile_feature_tests]} { + untested \ + "compile command not supported (could not find libcc1 shared library?)" + return -1 +} + +gdb_breakpoint [gdb_get_line_number "break here" $srcfile] +gdb_continue_to_breakpoint "testing location" + +CompileExpression::new "var" +CompileExpression::test "N1::N2::N3::N4::n4static" 400 +CompileExpression::test "N1::N2::N3::N4::S4::s4static" 40 +CompileExpression::test "s4.s4int_" 4 +CompileExpression::test "N1::N2::N3::N4::S4::get_svar ()" 40 +CompileExpression::test "s4.get_var ()" 4 diff --git a/gdb/testsuite/gdb.compile/compile-cplus-nested.cc b/gdb/testsuite/gdb.compile/compile-cplus-nested.cc new file mode 100644 index 0000000..60b79cc --- /dev/null +++ b/gdb/testsuite/gdb.compile/compile-cplus-nested.cc @@ -0,0 +1,58 @@ +/* Copyright 2015-2018 Free Software Foundation, Inc. + + 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/>. */ + +class A +{ +public: + A () : a_ (1) {} + int get (); + +protected: + int a_; + +private: + /* It is important to not /not/ use the nested class definition in A. + This exercises a different path through the code. */ + struct Inner1 + { + int a_; + Inner1 () : a_ (2) {} + + struct Inner2 + { + int a_; + Inner2 () : a_ (3) {} + }; + }; +}; + +int +A::get () +{ + A::Inner1 i1; + A::Inner1::Inner2 i2; + + return i1.a_ + i2.a_; // break here +} + +int var = 1234; + +int +main () +{ + A a; + + return a.get (); +} diff --git a/gdb/testsuite/gdb.compile/compile-cplus-nested.exp b/gdb/testsuite/gdb.compile/compile-cplus-nested.exp new file mode 100644 index 0000000..3b5ba20 --- /dev/null +++ b/gdb/testsuite/gdb.compile/compile-cplus-nested.exp @@ -0,0 +1,53 @@ +# Copyright 2015-2018 Free Software Foundation, Inc. + +# 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/>. + +# Nested type tests for GDB's C++ compile feature. + +load_lib compile-support.exp + +standard_testfile .cc + +if {[skip_cplus_tests]} { + untested "skipping C++ tests" + return +} + +if {[prepare_for_testing $testfile $testfile $srcfile \ + {debug nowarnings c++}]} { + return -1 +} + +if {![runto_main]} { + untested "could not run to main" + return -1 +} + +if {[skip_compile_feature_tests]} { + untested \ + "compile command not supported (could not find libcc1 shared library?)" + return -1 +} + +gdb_breakpoint [gdb_get_line_number "break here" $srcfile] +gdb_continue_to_breakpoint "testing location" + +CompileExpression::new "var" +CompileExpression::test "i1.a_" 2 +CompileExpression::test "i2.a_" 3 +CompileExpression::test "A::Inner1 *i1p = &i1; var = i1p->a_;" 2 -explicit +CompileExpression::test "A::Inner1::Inner2 *i2p = &i2; var = i2p->a_;" 3 \ + -explicit +CompileExpression::test "A::Inner1 &r1 = i1; var = r1.a_;" 2 -explicit +CompileExpression::test "A::Inner1::Inner2 &r2 = i2; var = r2.a_;" 3 -explicit diff --git a/gdb/testsuite/gdb.compile/compile-cplus-print.c b/gdb/testsuite/gdb.compile/compile-cplus-print.c new file mode 100644 index 0000000..a1f8d04 --- /dev/null +++ b/gdb/testsuite/gdb.compile/compile-cplus-print.c @@ -0,0 +1,32 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2015-2018 Free Software Foundation, Inc. + + 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/>. */ + +#include <stdlib.h> + +int varint = 10; +int vararray[] = { 1, 2, 3, 4, 5 }; +int *vararrayp = vararray; +struct object +{ + int field; +} varobject = { 1 }; + +int +main () +{ + return 0; +} diff --git a/gdb/testsuite/gdb.compile/compile-cplus-print.exp b/gdb/testsuite/gdb.compile/compile-cplus-print.exp new file mode 100644 index 0000000..5b84b14 --- /dev/null +++ b/gdb/testsuite/gdb.compile/compile-cplus-print.exp @@ -0,0 +1,79 @@ +# Copyright 2015-2018 Free Software Foundation, Inc. + +# 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/>. + +load_lib compile-support.exp + +standard_testfile + +get_compiler_info +set options {} +if [test_compiler_info gcc*] { + lappend options additional_flags=-g3 + lappend options additional_flags=-std=gnu++11 + lappend options c++ +} + +set srcfilesoptions [list ${srcfile} ${options}] + +if { [eval build_executable_from_specs ${testfile}.exp $testfile {$options} ${srcfilesoptions}] } { + return -1 +} + +clean_restart ${testfile} + +if {[skip_compile_feature_tests]} { + untested "compile command not supported (could not find libcc1 shared library?)" + return -1 +} + +gdb_test_no_output "set language c++" \ + "Set language to C++" + +if ![runto_main] { + return -1 +} + +gdb_test "compile print varint" " = 10" +gdb_test "compile print vararray" " = \\{1, 2, 3, 4, 5\\}" + +setup_kfail compile/23586 *-*-* +gdb_test "compile print main" " = \\{int \\(void\\)\\} 0x\[0-9a-f\]+" + +setup_kfail compile/23587 *-*-* +set test "compile print *vararray@3" +gdb_test_multiple $test $test { + -re " = \\{1, 2, 3\\}\r\n$gdb_prompt $" { + pass $test + } + -re "warning: .*All references to this method will be undefined\.\r\n" { + exp_continue + } +} + +setup_kfail compile/23587 *-*-* +set test "compile print *vararrayp@3" +gdb_test_multiple $test $test { + -re " = \\{1, 2, 3\\}\r\n$gdb_prompt $" { + pass $test + } + -re "warning: .*All references to this method will be undefined\.\r\n" { + exp_continue + } +} + +gdb_test "compile print/x 256" " = 0x100" +gdb_test {print $} " = 256" + +gdb_test "compile print varobject" { = {field = 1}} diff --git a/gdb/testsuite/gdb.compile/compile-cplus-virtual.cc b/gdb/testsuite/gdb.compile/compile-cplus-virtual.cc new file mode 100644 index 0000000..f3317a3 --- /dev/null +++ b/gdb/testsuite/gdb.compile/compile-cplus-virtual.cc @@ -0,0 +1,54 @@ +/* Copyright 2015-2018 Free Software Foundation, Inc. + + 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/>. */ + +struct A +{ + virtual int doit () { return 1; } + virtual int doit3 () { return -3; } + virtual int doit2 () = 0; +}; + +struct B : virtual A +{ + int doit () { return 2; } + int doit2 () { return 22; } +}; + +struct C : virtual A +{ + int doit () { return 3; } + int doit2 () { return 33; } +}; + +struct D : B, C +{ + int doit () { return 4; } + int doit2 () { return 44; } +}; + +int +main () +{ + int var = 1234; + B b; + C c; + D d; + A *ap = &d; + + var = (b.doit () + c.doit () + d.doit () + d.doit3 () + + ap->doit () + ap->doit2 ()); // break here + + return 0; +} diff --git a/gdb/testsuite/gdb.compile/compile-cplus-virtual.exp b/gdb/testsuite/gdb.compile/compile-cplus-virtual.exp new file mode 100644 index 0000000..3a3e7b9 --- /dev/null +++ b/gdb/testsuite/gdb.compile/compile-cplus-virtual.exp @@ -0,0 +1,71 @@ +# Copyright 2015-2018 Free Software Foundation, Inc. + +# 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/>. + +# Virtual method/inheritance tests for GDB's C++ compile feature. + +load_lib compile-support.exp + +standard_testfile .cc + +if {[skip_cplus_tests]} { + untested "skipping C++ tests" + return +} + +if {[prepare_for_testing $testfile $testfile $srcfile \ + {debug nowarnings c++}]} { + return -1 +} + +if {![runto_main]} { + untested "could not run to main" + return -1 +} + +if {[skip_compile_feature_tests]} { + untested \ + "compile command not supported (could not find libcc1 shared library?)" + return -1 +} + +gdb_breakpoint [gdb_get_line_number "break here" $srcfile] +gdb_continue_to_breakpoint "testing location" + +CompileExpression::new "var" +CompileExpression::test "b.doit ()" 2 +CompileExpression::test "c.doit ()" 3 +CompileExpression::test "d.doit ()" 4 +CompileExpression::test "ap->doit ()" 4 +CompileExpression::test "b.doit2 ()" 22 +CompileExpression::test "c.doit2 ()" 33 +CompileExpression::test "d.doit2 ()" 44 +CompileExpression::test "ap->doit2 ()" 44 +CompileExpression::test "b.doit3 ()" -3 +CompileExpression::test "c.doit3 ()" -3 +CompileExpression::test "d.doit3 ()" -3 + +# These two tests are "disabled". They represent new/future features. +# CompileExpression::test \ + [concat "struct ABC {int doit2() { return 3333; }} abc;" \ + "var = abc.doit2()"] \ + 3333 -explicit +# CompileExpression::test \ + [concat "struct ABC : A {int doit2() { return 4444; }} abc;" \ + "var = abc.doit2()"] \ + 4444 -explicit + +# Test some error conditions +gdb_test "compile code A a;" \ + ".*cannot declare variable .a. to be of abstract type.*Compilation failed." diff --git a/gdb/testsuite/gdb.compile/compile-cplus.c b/gdb/testsuite/gdb.compile/compile-cplus.c new file mode 100644 index 0000000..558f1b6 --- /dev/null +++ b/gdb/testsuite/gdb.compile/compile-cplus.c @@ -0,0 +1,241 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2014-2018 Free Software Foundation, Inc. + + 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/>. */ + +#include <stdbool.h> +#include <iostream> + +#define SOME_MACRO 23 +#define ARG_MACRO(X, Y) ((X) + (Y) - 1) + + +enum enum_type { + ONE = 1, + TWO = 2 +}; + +typedef int v4 __attribute__ ((vector_size (16))); + +union union_type; + +struct struct_type { + char charfield; + unsigned char ucharfield; + short shortfield; + unsigned short ushortfield; + int intfield; + unsigned int uintfield; + unsigned int bitfield : 3; + long longfield; + unsigned long ulongfield; + enum enum_type enumfield; + float floatfield; + double doublefield; + const union union_type *ptrfield; + struct struct_type *selffield; + int arrayfield[5]; + _Complex double complexfield; + _Bool boolfield; + v4 vectorfield; +}; + +typedef int inttypedef; + +union union_type { + int intfield; + inttypedef typedeffield; +}; + +/* volatile provides some coverage of the conversion code. */ +volatile struct struct_type struct_object; + +union union_type union_object; + + +enum ulonger_enum_type { + REALLY_MINUS_1 = -1UL, +}; + +enum ulonger_enum_type ulonger; + +enum longer_enum_type { + MINUS_1 = -1, + FORCE_TO_LONG = 1L << ((8 * sizeof (long)) - 2) +}; + +enum longer_enum_type longer; + +int globalvar = 10; + +static void +func_static (int addend) +{ + globalvar += addend; +} + +void +func_global (int subtrahend) +{ + globalvar -= subtrahend; +} + +void +no_args_or_locals () +{ + /* no_args_or_locals breakpoint */ +} + +int *intptr; +int globalshadow = 10; +static int staticshadow = 20; +int externed = 7; + +class Base +{ + virtual int pure_virt () = 0; + public: + int return_value () {return a;} + private: + int a = 1; + int b = 2; +}; + +class Base2 +{ + virtual int non_pure () {return 84;} + public: + int return_value () {return b;} + private: + int a = 3; + int b = 4; +}; + +class Base3 +{ + public: + int return_value () {return b;} + private: + int b = 5; +}; + + +class Multiple : public Base, public Base2 +{ + int pure_virt () + { + int a = Base::return_value (); + return a + 42; + } +}; +//struct foo { foo(); virtual ~foo(); }; struct bar : virtual foo { bar(); ~bar(); }; struct baz : bar {}; bar::bar() {} bar::~bar() {} bar t; baz u; +struct VirtualOnly +{ + VirtualOnly(); + virtual ~VirtualOnly()=0; +}; + +VirtualOnly::VirtualOnly () +{ +} + +VirtualOnly::~VirtualOnly () +{ +} + +struct VirtualBase : virtual VirtualOnly +{ + int z = 22; + VirtualBase (); + ~VirtualBase (); +}; + +struct VirtualBase2 : VirtualBase {}; + +VirtualBase::VirtualBase () +{ + z = 24; +} + +VirtualBase::~VirtualBase () +{ + z = 22; +} + +class Foo +{ + int var; + static const int public_static_var = 12; + + private: + int private_var = 0; + int private_method (); + + public: + int public_var = 0; + int public_method (); + void set_private_var (int); +}; + +void Foo::set_private_var (int i) +{ + private_var = i; +} + +int Foo::private_method () +{ + return private_var; +} + +int Foo::public_method () +{ + return public_var; +} + +int +main () +{ + int localvar = 50; + int shadowed = 51; + int bound = 3; + int unresolved = 10; + int globalshadow = 100; + int staticshadow = 200; + int externed = 9; + int f = 0; + int var = 0; + Foo foovar; + Multiple *multivar = new Multiple; + VirtualBase vbase; + VirtualBase2 vbase2; + static int static_local = 77000; + + foovar.public_var = 42; + foovar.set_private_var (42); + multivar->Base2::return_value(); + { + int another_local = 7; + int shadowed = 52; + extern int unresolved; + extern int externed; + + int vla[bound]; + + func_static (0); /* break-here */ + no_args_or_locals (); + } + + return 0; +} diff --git a/gdb/testsuite/gdb.compile/compile-cplus.exp b/gdb/testsuite/gdb.compile/compile-cplus.exp new file mode 100644 index 0000000..5406e29 --- /dev/null +++ b/gdb/testsuite/gdb.compile/compile-cplus.exp @@ -0,0 +1,347 @@ +# Copyright 2014-2018 Free Software Foundation, Inc. + +# 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/>. + +load_lib compile-support.exp + +standard_testfile .c compile-shlib.c compile-constvar.S compile-nodebug.c + +get_compiler_info +set options {} +if [test_compiler_info gcc*] { + lappend options additional_flags=-g3 + lappend options additional_flags=-std=gnu++11 + lappend options c++ +} + +if { ![istarget x86_64-*-* ] || ![is_lp64_target] } { + verbose "Skipping x86_64 LOC_CONST test." + set srcfile3 "" +} + +set srcfilesoptions [list ${srcfile} ${options}] +if { $srcfile3 != "" } { + lappend srcfilesoptions $srcfile3 ${options} +} +lappend srcfilesoptions $srcfile4 "nodebug c++" +if { [eval build_executable_from_specs ${testfile}.exp $testfile {$options} ${srcfilesoptions}] } { + return -1 +} + +clean_restart ${testfile} + +# +# FIXME: Right now, for C++ we just duplicate the C tests, but force +# the language to C++ +# +gdb_test_no_output "set language c++" \ + "Set language to C++" + +if ![runto_main] { + return -1 +} + +if {[skip_compile_feature_tests]} { + untested "compile command not supported (could not find libcc1 shared library?)" + return -1 +} + +# +# Test delimiter for code, and arguments. +# + + +gdb_test_no_output "compile code globalvar = SOME_MACRO;" \ + "set variable from macro" +gdb_test "p globalvar" " = 23" "expect 23" + +gdb_test_no_output "compile code globalvar = ARG_MACRO(0, 0);" \ + "set variable from function-like macro" +gdb_test "p globalvar" " = -1" "expect -1" + +gdb_test_no_output "compile code globalvar = 42;" "set variable" +gdb_test "p globalvar" " = 42" "expect 42" + +gdb_test_no_output "compile code globalvar *= 2;" "modify variable" +gdb_test "p globalvar" " = 84" "expect 84" + +gdb_test_no_output "compile file -r ${srcdir}/${subdir}/${testfile}-mod.c" \ + "use external source file" +gdb_test "p globalvar" " = 7" "expect 7" + +gdb_test_no_output "compile code func_static (2);" "call static function" +gdb_test "p globalvar" " = 9" "expect 9" +gdb_test_no_output "compile code func_global (1);" "call global function" +gdb_test "p globalvar" " = 8" "expect 8" + +gdb_test_no_output \ + "compile code globalvar = (sizeof (ulonger) == sizeof (long))" \ + "compute size of ulonger" +gdb_test "p globalvar" " = 1" "check size of ulonger" +gdb_test_no_output \ + "compile code globalvar = (sizeof (longer) == sizeof (long))" \ + "compute size of longer" +gdb_test "p globalvar" " = 1" "check size of longer" +gdb_test_no_output "compile code globalvar = MINUS_1" +gdb_test "p globalvar" " = -1" "check MINUS_1" + +gdb_test_no_output "compile code globalvar = static_local" +gdb_test "p globalvar" " = 77000" "check static_local" + +gdb_test_no_output \ + "compile code static int staticvar = 5; intptr = &staticvar" \ + "do not keep jit in memory" +gdb_test "p *intptr" "Cannot access memory at address 0x\[0-9a-f\]+" \ + "expect 5" + +gdb_test "compile code func_doesnotexist ();" "error: \'func_doesnotexist\' was not declared in this scope.*" + +gdb_test "compile code *(volatile int *) 0 = 0;" \ + "The program being debugged was signaled while in a function called from GDB\\.\r\nGDB remains in the frame where the signal was received\\.\r\n.*" \ + "compile code segfault first" +gdb_test "bt" \ + "\r\n#0 \[^\r\n\]* in _gdb_expr \[^\r\n\]*\r\n#1 <function called from gdb>\r\n.*" + +set test "p/x \$pc" +set infcall_pc 0 +gdb_test_multiple $test $test { + -re " = (0x\[0-9a-f\]+)\r\n$gdb_prompt $" { + set infcall_pc $expect_out(1,string) + pass $test + } +} + +gdb_test "info sym $infcall_pc" "\r\n_gdb_expr.*" "info sym found" +gdb_test "return" "\r\n#0 main .*" "return" \ + "Make _gdb_expr\\(__gdb_regs\\*\\) return now\\? \\(y or n\\) " "y" +gdb_test "info sym $infcall_pc" "\r\nNo symbol matches .*" "info sym not found" + +gdb_test_no_output "set unwindonsignal on" +gdb_test "compile code *(volatile int *) 0 = 0;" \ + "The program being debugged was signaled while in a function called from GDB\\.\r\nGDB has restored the context to what it was before the call\\.\r\n.*" \ + "compile code segfault second" + +gdb_breakpoint [gdb_get_line_number "break-here"] +gdb_continue_to_breakpoint "break-here" ".* break-here .*" + +# C++ Specific tests. +## Public methods and members + +gdb_test "print foovar.public_var" "42" \ + "Test compile code foovar.public_var = 42 setting." +gdb_test_no_output "compile code foovar.public_var = 43;" \ + "set foobar.public_var to 43" +gdb_test "print foovar.public_var" "43" \ + "Test compile code foovar.public_var = 43 setting." +gdb_test "print foovar.public_method ()" "43" \ + "Test compile code foovar.public_method = 43 setting." + +## Private methods and members +gdb_test_no_output "compile code foovar.set_private_var (84);" \ + "Call class function to set private_var" +gdb_test "print foovar.private_var" "84" \ + "Test compile code foovar.set_private_var = 84 setting." +gdb_test_no_output "compile code foovar.private_var = 85" \ + "Directly set a private member in GDB compile5" +gdb_test "print foovar.private_var" "85" \ + "Test compile code foovar.set_private_var = 85 setting." + +## Simple inheritance +CompileExpression::new "var" +CompileExpression::test "class Baz: public Foo {public: int z = 12;}; Baz bazvar; bazvar.z = 24; var = bazvar.z" 24 -explicit +## Multiple inheritance +CompileExpression::test "class MI: public Base, public Base2 {int pure_virt () {return 42;}}; MI MIVar; var = MIVar.pure_virt();" 42 -explicit +CompileExpression::test "class MI: public Base, public Base2 {int pure_virt () {return Base::return_value() + 42;}}; MI MIVar; var = MIVar.pure_virt();" 43 -explicit +CompileExpression::test "class Base3 {public: int z = 99;}; class MI: public Base, public Base3 {int pure_virt () {return Base3::z + 42;}}; MI MIVar; var = MIVar.pure_virt();" 141 -explicit + +gdb_test "p localvar" " = 50" "expect localvar 50" + +gdb_test_no_output "compile code localvar = 12;" "set localvar" +gdb_test "p localvar" " = 12" "expect 12" + +gdb_test_no_output "compile code localvar *= 2;" "modify localvar" +gdb_test "p localvar" " = 24" "expect 24" + +gdb_test_no_output "compile code localvar = shadowed" \ + "test shadowing" +gdb_test "p localvar" " = 52" "expect 52" + +gdb_test_no_output "compile code localvar = externed" +gdb_test "p localvar" " = 7" "test extern in inner scope" + +gdb_test_no_output "compile code vla\[2\] = 7" +gdb_test "p vla\[2\]" " = 7" +gdb_test_no_output \ + "compile code localvar = (sizeof (vla) == bound * sizeof (vla\[0\]))" +gdb_test "p localvar" " = 1" + +# +# Test setting fields and also many different types. +# + +gdb_test_no_output "compile code struct_object.selffield = (struct_type*)&struct_object" +gdb_test "print struct_object.selffield == &struct_object" " = true" + +gdb_test_no_output "compile code struct_object.charfield = 1" +gdb_test "print struct_object.charfield" " = 1 '\\\\001'" +gdb_test_no_output "compile code struct_object.ucharfield = 1" +gdb_test "print struct_object.ucharfield" " = 1 '\\\\001'" + +foreach {field value} { + shortfield -5 + ushortfield 5 + intfield -7 + uintfield 7 + bitfield 2 + longfield -9 + ulongfield 9 + enumfield ONE + floatfield 1 + doublefield 2 +} { + gdb_test_no_output "compile code struct_object.$field = $value" + gdb_test "print struct_object.$field" " = $value" +} + +gdb_test_no_output "compile code struct_object.arrayfield\[2\] = 7" +gdb_test "print struct_object.arrayfield" \ + " = \\{0, 0, 7, 0, 0\\}" + +gdb_test_no_output "compile code struct_object.complexfield = 7 + 5i" +gdb_test "print struct_object.complexfield" " = 7 \\+ 5 \\* I" + +gdb_test_no_output "compile code struct_object.boolfield = 1" +gdb_test "print struct_object.boolfield" " = true" + +gdb_test_no_output "compile code struct_object.vectorfield\[2\] = 7" +gdb_test "print struct_object.vectorfield" \ + " = \\{0, 0, 7, 0\\}" + +gdb_test_no_output "compile code union_object.typedeffield = 7" +gdb_test "print union_object.typedeffield" " = 7" +gdb_test "print union_object.intfield" " = 7" + + +# LOC_UNRESOLVED tests. + +gdb_test "print unresolved" " = 20" +gdb_test "compile code globalvar = unresolved;" +gdb_test "print globalvar" " = 20" "print unresolved value" + +# Test shadowing with global and static variables. + +gdb_test_no_output "compile code globalshadow += 1;" +gdb_test "print globalshadow" " = 101" +gdb_test_no_output "compile code extern int globalshadow; globalshadow += 5;" +gdb_test "print 'compile-cplus.c'::globalshadow" " = 15" +gdb_test "print globalshadow" " = 101" "print globalshadow second time" +gdb_test_no_output "compile code staticshadow += 2;" +gdb_test "print staticshadow" " = 202" +# "extern int staticshadow;" cannot access static variable. + +# Raw code cannot refer to locals. +# As it references global variable we need the #pragma. +# For #pragma we need multiline input. +gdb_test_multiple "compile code -r" "compile code -r multiline 1" { -re "\r\n>$" {} } +gdb_test_multiple "void _gdb_expr(void) {" "compile code -r multiline 2" { -re "\r\n>$" {} } +gdb_test_multiple "#pragma GCC push_user_expression" "compile code -r multiline 3" { -re "\r\n>$" {} } +gdb_test_multiple " globalshadow = 77000;" "compile code -r multiline 4" { -re "\r\n>$" {} } +gdb_test_multiple "#pragma GCC pop_user_expression" "compile code -r multiline 5" { -re "\r\n>$" {} } +gdb_test_multiple "}" "compile code -r multiline 6" { -re "\r\n>$" {} } +gdb_test_no_output "end" "compile code -r multiline 7" +gdb_test "print 'compile-cplus.c'::globalshadow" " = 77000" \ + "check globalshadow with -r" + +# Test GOT vs. resolving jit function pointers. + +gdb_test_no_output "compile -raw -- extern \"C\" void abort(); int func(){return 21;} void _gdb_expr(){int (*funcp)()=func; if (funcp()!=21) abort();}" \ + "pointer to jit function" + +# +# Test the case where the registers structure would not normally have +# any fields. +# + +gdb_breakpoint [gdb_get_line_number "no_args_or_locals breakpoint"] +gdb_continue_to_breakpoint "no_args_or_locals" + +gdb_test_no_output "compile code globalvar = 77;" "set variable to 77" +gdb_test "p globalvar" " = 77" "expect 77" + + +# Test reference to minimal_symbol, not (full) symbol. + +setup_kfail compile/23585 *-*-* +gdb_test_no_output "compile code globalvar = func_nodebug (75);" \ + "call func_nodebug" + +setup_kfail compile/23585 *-*-* +gdb_test "p globalvar" " = -75" "expect -75" + +setup_kfail compile/23585 *-*-* +gdb_test_no_output \ + "compile code int (*funcp) (int) = (int(*)(int))func_nodebug; globalvar = funcp (76);" \ + "call func_nodebug indirectly" +setup_kfail compile/23585 *-*-* +gdb_test "p globalvar" " = -76" "expect -76" + + +# Test compiled module memory protection. + +gdb_test_no_output "set debug compile on" +gdb_test "compile code static const int readonly = 1; *(int *) &readonly = 2;" \ + "The program being debugged was signaled while in a function called from GDB\\.\r\nGDB has restored the context to what it was before the call\\.\r\n.*" +gdb_test_no_output "set debug compile off" + + +# +# Some simple coverage tests. +# + +gdb_test "show debug compile" "Compile debugging is .*" +gdb_test "show compile-args" \ + "Compile command command-line arguments are .*" +gdb_test "compile code -z" "Unknown argument.*" + +gdb_test "set lang rust" \ + "Warning: the current language does not match this frame." +gdb_test "compile code globalvar" "No compiler support for language rust\." +gdb_test_no_output "set lang auto" + +gdb_test_no_output "compile code union union_type newdecl_u" +gdb_test_no_output "compile code struct struct_type newdecl_s" +gdb_test_no_output "compile code inttypedef newdecl_i" + +gdb_test "compile file" \ + "You must provide a filename for this command.*" \ + "Test compile file without a filename" +gdb_test "compile file -r" \ + "You must provide a filename with the raw option set.*" \ + "Test compile file and raw option without a filename" +gdb_test "compile file -z" \ + "Unknown argument.*" \ + "Test compile file with unknown argument" + + +# LOC_CONST tests. + +if { $srcfile3 != "" } { + gdb_test "p constvar" " = 3" + gdb_test "info addr constvar" {Symbol "constvar" is constant\.} + + gdb_test_no_output "compile code globalvar = constvar;" + gdb_test "print globalvar" " = 3" "print constvar value" +} else { + untested "print constvar value" +} diff --git a/gdb/testsuite/lib/compile-support.exp b/gdb/testsuite/lib/compile-support.exp new file mode 100644 index 0000000..b784c35 --- /dev/null +++ b/gdb/testsuite/lib/compile-support.exp @@ -0,0 +1,227 @@ +# Copyright 2015-2018 Free Software Foundation, Inc. + +# 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/>. + +# Generic/oft used support routines for testing GDB's compile feature. + +# Return 1 if we should skip tests of the "compile" feature. +# This must be invoked after the inferior has been started. + +proc skip_compile_feature_tests {} { + global gdb_prompt + + set result 0 + gdb_test_multiple "compile code -- ;" "check for working compile command" { + "Could not load libcc1.*\r\n$gdb_prompt $" { + set result 1 + } + -re "Command not supported on this host\\..*\r\n$gdb_prompt $" { + set result 1 + } + -re "\r\n$gdb_prompt $" { + } + } + return $result +} + +# This namespace provides some convenience functions for running +# "compile code" and "compile print" tests. +# +# Exported functions are defined inline below. +# +# General usage: +# +# Start a new session, noting that the variable "var" will be used for +# "compile code" expressions. This variable /must/ exist in the stopped +# location. +# +# CompileExpression::new "var" +# +# Test the implicit expression "foo;" with result/value 3. +# CompileExpression::test "foo" 3 +# ---> Runs the following tests (name of tests ignored for illustration) +# gdb_test_no_output "compile code var = foo" +# gdb_test "p var" "= 3" +# gdb_test "compile print foo;" "= 3" +# +# Test the explicit expression "a = function (3); var = a;" with the result 21. +# CompileExpression::test "a = function (3); var = a;" 21 -explicit +# ---> Runs the following tests (name of tests ignored for illustration) +# gdb_test_no_output "compile code a = function (3); var = a;" +# gdb_test "p var" "= 21" +# +# Additional option flags may be passed to test to control the behavior +# of the test harness: +# +# Pass -explicit to specify that the test uses an explicit expression, +# one which sets the value of the variable (see above). Only the code test +# will be run. +# +# Pass -value and/or -print to indicate that the value and/or print steps +# will optionally fail. Specify "xfail" or "kfail" to indicate how +# particular step will fail. These may be followed by any accepted DejaGNU +# parameters such as architecture and bug#. [See examples below.] +# +# To specify that the compile (and consequently print and value tests) is +# expected to kfail/xfail, use -kfail or -xfail with any appropriate +# DejaGNU parameters. Both options override -print and -value. +# [-xfail is given precedence over -kfail should both be given.] +# +# -value is used when a "code" test is run, specifying that the "compile +# code" and "print VAR" steps will fail in the prescribed manner. +# [If the print step generates a PASS, the test is considered invalidly +# written. VAR's value should /always/ be invalidated before a test is +# run.] +# +# -print is used to specify that an expression will fail in the prescribed +# manner when "print" test is executed. +# +# Pass "-name NAME" to set an optional test name. If not specified, +# the harness will use test names such as "compile code EXPR" and +# "result of compile code EXPR". +# +# Pass "-noprint" or "-nocode" to suppress print or code tests, respectively, +# This is useful when the expression being tested modifies the object +# being tested, e.g., "a++". +# +# These options must be passed LAST to CompileExpression::test. +# +# Examples: +# +# Both "code" and "print" tests are expected to xfail: +# CompileExpression add_imp "foo" 3 -compile {xfail *-*-*} -print {xfail *-*-*} +# +# The "print $VARIABLE" portion of the "code" test is expected to kfail +# (the actual "compile code" GDB command will succeed), but the "print" +# test should pass: +# CompileExpression add_imp "foo" 3 -value {kfail *-*-* gdb/1234} + +namespace eval ::CompileExpression { + + # The variable name to check testing results. This variable + # must be in scope when tests are run. + variable varName_ {} + + # Start a new expression list. VARNAME is the name of the variable + # that will be printed to check if the result of the test was + # successful. + proc new {varname} { + variable varName_ + + set varName_ $varname + } + + # Test an expression. + # + # See the preamble for a list of valid optional arguments. + # + # Implicit expressions will be sent to GDB in the form + # "$varName = $EXP". "p $varName" will be used to decide the pass + # or fail status of the test. + # + # Explicit expressions will be sent to GDB as-is and tested using only + # "compile code". The expression should set the value of the variable + # $varName, which is then printed to determine whether the test passed + # or failed. + # + # Unlike explicit expressions, implicit expressions are tested with both + # "compile print" and "compile code". + + proc test {exp result args} { + parse_args {{value {"" ""}} {print {"" ""}} {name ""} + {noprint} {nocode} {explicit} {xfail {"" ""}} {kfail {"" ""}}} + + if {[lindex $xfail 0] != ""} { + set l "xfail $xfail" + } elseif {[lindex $kfail 0] != ""} { + set l "kfail $kfail" + } else { + set l "" + set compile {"" ""} + } + if {$l != ""} { + set compile $l + set print $l + set value $l + } + + if {!$nocode} { + do_test_ code $exp $result $explicit $name \ + [list $compile $value $print] + } + if {!$noprint} { + do_test_ print $exp $result $explicit $name \ + [list $compile $value $print] + } + } + + # Run a compile test for CMD ("print" or "code"). + + proc do_test_ {cmd exp result is_explicit tst fail_list} { + variable varName_ + + if {![string match $cmd "code"] + && ![string match $cmd "print"]} { + error "invalid command, $cmd; should be \"print\" or \"compile\"" + } + + # Get expected result of test. Will be "" if test is + # expected to PASS. + lassign $fail_list fail_compile fail_value fail_print + + # Set a test name if one hasn't been provided. + if {$tst == ""} { + set tst "compile $cmd $exp" + } + + if {[string match $cmd "print"]} { + if {!$is_explicit} { + eval setup_failures_ $fail_print + gdb_test "compile print $exp" $result $tst + } + } else { + if {$is_explicit} { + set command "compile code $exp" + } else { + set command "compile code $varName_ = $exp" + } + eval setup_failures_ $fail_compile + gdb_test_no_output $command $tst + eval setup_failures_ $fail_value + gdb_test "p $varName_" "= $result" "result of $tst" + } + } + + # A convenience proc used to set up xfail and kfail tests. + # HOW is either xfail or kfail (case is ignored). ARGS is any + # optional architecture, bug number, or other string to pass to + # respective DejaGNU setup_$how routines. + + proc setup_failures_ {how args} { + switch -nocase $how { + xfail { + eval setup_xfail $args + } + + kfail { + eval setup_kfail $args + } + + default { + # Do nothing. Either the test is expected to PASS + # or we have an unhandled failure mode. + } + } + } +} |