diff options
author | Keith Seitz <keiths@redhat.com> | 2018-08-29 15:12:24 -0700 |
---|---|---|
committer | Keith Seitz <keiths@redhat.com> | 2018-08-29 15:12:24 -0700 |
commit | 078a020797210f4d8f9491bbfe16ec31e9efb652 (patch) | |
tree | 278f541a2e88d33fcc3c4f782b5265a477abb540 /gdb/compile | |
parent | fcaad03cc027ec2cdf7f2cff70d792867d43c57f (diff) | |
download | gdb-078a020797210f4d8f9491bbfe16ec31e9efb652.zip gdb-078a020797210f4d8f9491bbfe16ec31e9efb652.tar.gz gdb-078a020797210f4d8f9491bbfe16ec31e9efb652.tar.bz2 |
C++ compile support
This patch adds *basic* support for C++ to the compile feature. It does
most simple type conversions, including everything that C compile does and
your basic "with-classes" type of C++.
I've written a new compile-support.exp support file which adds a new test
facility for automating and simplifying "compile print" vs "compile code"
testing. See testsuite/lib/compile-support.exp and CompileExpression
for more on that. The tests use this facility extensively.
This initial support has several glaring omissions:
- No template support at all
I have follow-on patches for this, but they add much complexity
to this "basic" support. Consequently, they will be submitted separately.
- Cannot print functions
The code template needs tweaking, and I simply haven't gotten to it yet.
- So-called "special function" support is not included
Using constructors, destructors, operators, etc will not work. I have
follow-on patches for that, but they require some work because of the
recent churn in symbol searching.
- There are several test suite references to "compile/1234" bugs.
I will file bugs and update the test suite's bug references before pushing
these patches.
The test suite started as a copy of the original C-language support, but
I have written tests to exercise the basic functionality of the plug-in.
I've added a new option for outputting debug messages for C++ type-conversion
("debug compile-cplus-types").
gdb/ChangeLog:
* 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.
gdb/testsuite/ChangeLog:
* 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.
doc/ChangeLog:
* gdb.texinfo (Compiling and injecting code in GDB): Document
set/show "compile-oracle" and "compile-cplus-types" commands.
Diffstat (limited to 'gdb/compile')
-rw-r--r-- | gdb/compile/compile-c-support.c | 205 | ||||
-rw-r--r-- | gdb/compile/compile-cplus-symbols.c | 493 | ||||
-rw-r--r-- | gdb/compile/compile-cplus-types.c | 1427 | ||||
-rw-r--r-- | gdb/compile/compile-cplus.h | 205 | ||||
-rw-r--r-- | gdb/compile/compile-internal.h | 4 | ||||
-rw-r--r-- | gdb/compile/compile-object-load.c | 7 | ||||
-rw-r--r-- | gdb/compile/compile.c | 1 | ||||
-rw-r--r-- | gdb/compile/gcc-cp-plugin.h | 85 |
8 files changed, 2399 insertions, 28 deletions
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; +}; |