| /* C/C++ language support for compilation. |
| |
| Copyright (C) 2014-2021 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-c.h" |
| #include "compile-cplus.h" |
| #include "compile.h" |
| #include "c-lang.h" |
| #include "macrotab.h" |
| #include "macroscope.h" |
| #include "regcache.h" |
| #include "gdbsupport/function-view.h" |
| #include "gdbsupport/gdb-dlfcn.h" |
| #include "gdbsupport/preprocessor.h" |
| #include "gdbarch.h" |
| |
| /* See compile-internal.h. */ |
| |
| const char * |
| c_get_mode_for_size (int size) |
| { |
| const char *mode = NULL; |
| |
| switch (size) |
| { |
| case 1: |
| mode = "QI"; |
| break; |
| case 2: |
| mode = "HI"; |
| break; |
| case 4: |
| mode = "SI"; |
| break; |
| case 8: |
| mode = "DI"; |
| break; |
| default: |
| internal_error (__FILE__, __LINE__, _("Invalid GCC mode size %d."), size); |
| } |
| |
| return mode; |
| } |
| |
| /* See compile-internal.h. */ |
| |
| std::string |
| c_get_range_decl_name (const struct dynamic_prop *prop) |
| { |
| return string_printf ("__gdb_prop_%s", host_address_to_string (prop)); |
| } |
| |
| |
| |
| /* Load the plug-in library FE_LIBCC and return the initialization function |
| FE_CONTEXT. */ |
| |
| template <typename FUNCTYPE> |
| FUNCTYPE * |
| load_libcompile (const char *fe_libcc, const char *fe_context) |
| { |
| FUNCTYPE *func; |
| |
| /* 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"), fe_context, fe_libcc); |
| |
| /* Leave the library open. */ |
| handle.release (); |
| return func; |
| } |
| |
| /* Return the compile instance associated with the current 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> |
| std::unique_ptr<compile_instance> |
| get_compile_context (const char *fe_libcc, const char *fe_context, |
| BASE_VERSION_TYPE base_version, |
| API_VERSION_TYPE api_version) |
| { |
| static FUNCTYPE *func; |
| static CTXTYPE *context; |
| |
| if (func == NULL) |
| { |
| func = load_libcompile<FUNCTYPE> (fe_libcc, fe_context); |
| gdb_assert (func != NULL); |
| } |
| |
| 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 std::unique_ptr<compile_instance> (new INSTTYPE (context)); |
| } |
| |
| /* A C-language implementation of get_compile_context. */ |
| |
| std::unique_ptr<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. */ |
| |
| std::unique_ptr<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); |
| } |
| |
| |
| |
| /* Write one macro definition. */ |
| |
| static void |
| print_one_macro (const char *name, const struct macro_definition *macro, |
| struct macro_source_file *source, int line, |
| ui_file *file) |
| { |
| /* Don't print command-line defines. They will be supplied another |
| way. */ |
| if (line == 0) |
| return; |
| |
| /* None of -Wno-builtin-macro-redefined, #undef first |
| or plain #define of the same value would avoid a warning. */ |
| fprintf_filtered (file, "#ifndef %s\n# define %s", name, name); |
| |
| if (macro->kind == macro_function_like) |
| { |
| int i; |
| |
| fputs_filtered ("(", file); |
| for (i = 0; i < macro->argc; i++) |
| { |
| fputs_filtered (macro->argv[i], file); |
| if (i + 1 < macro->argc) |
| fputs_filtered (", ", file); |
| } |
| fputs_filtered (")", file); |
| } |
| |
| fprintf_filtered (file, " %s\n#endif\n", macro->replacement); |
| } |
| |
| /* Write macro definitions at PC to FILE. */ |
| |
| static void |
| write_macro_definitions (const struct block *block, CORE_ADDR pc, |
| struct ui_file *file) |
| { |
| gdb::unique_xmalloc_ptr<struct macro_scope> scope; |
| |
| if (block != NULL) |
| scope = sal_macro_scope (find_pc_line (pc, 0)); |
| else |
| scope = default_macro_scope (); |
| if (scope == NULL) |
| scope = user_macro_scope (); |
| |
| if (scope != NULL && scope->file != NULL && scope->file->table != NULL) |
| { |
| macro_for_each_in_scope (scope->file, scope->line, |
| [&] (const char *name, |
| const macro_definition *macro, |
| macro_source_file *source, |
| int line) |
| { |
| print_one_macro (name, macro, source, line, file); |
| }); |
| } |
| } |
| |
| /* Generate a structure holding all the registers used by the function |
| we're generating. */ |
| |
| static void |
| generate_register_struct (struct ui_file *stream, struct gdbarch *gdbarch, |
| const std::vector<bool> ®isters_used) |
| { |
| int i; |
| int seen = 0; |
| |
| fputs_unfiltered ("struct " COMPILE_I_SIMPLE_REGISTER_STRUCT_TAG " {\n", |
| stream); |
| |
| if (!registers_used.empty ()) |
| for (i = 0; i < gdbarch_num_regs (gdbarch); ++i) |
| { |
| if (registers_used[i]) |
| { |
| struct type *regtype = check_typedef (register_type (gdbarch, i)); |
| std::string regname = compile_register_name_mangled (gdbarch, i); |
| |
| seen = 1; |
| |
| /* You might think we could use type_print here. However, |
| target descriptions often use types with names like |
| "int64_t", which may not be defined in the inferior |
| (and in any case would not be looked up due to the |
| #pragma business). So, we take a much simpler |
| approach: for pointer- or integer-typed registers, emit |
| the field in the most direct way; and for other |
| register types (typically flags or vectors), emit a |
| maximally-aligned array of the correct size. */ |
| |
| fputs_unfiltered (" ", stream); |
| switch (regtype->code ()) |
| { |
| case TYPE_CODE_PTR: |
| fprintf_filtered (stream, "__gdb_uintptr %s", |
| regname.c_str ()); |
| break; |
| |
| case TYPE_CODE_INT: |
| { |
| const char *mode |
| = c_get_mode_for_size (TYPE_LENGTH (regtype)); |
| |
| if (mode != NULL) |
| { |
| if (regtype->is_unsigned ()) |
| fputs_unfiltered ("unsigned ", stream); |
| fprintf_unfiltered (stream, |
| "int %s" |
| " __attribute__ ((__mode__(__%s__)))", |
| regname.c_str (), |
| mode); |
| break; |
| } |
| } |
| |
| /* Fall through. */ |
| |
| default: |
| fprintf_unfiltered (stream, |
| " unsigned char %s[%s]" |
| " __attribute__((__aligned__(" |
| "__BIGGEST_ALIGNMENT__)))", |
| regname.c_str (), |
| pulongest (TYPE_LENGTH (regtype))); |
| } |
| fputs_unfiltered (";\n", stream); |
| } |
| } |
| |
| if (!seen) |
| fputs_unfiltered (" char " COMPILE_I_SIMPLE_REGISTER_DUMMY ";\n", |
| stream); |
| |
| fputs_unfiltered ("};\n\n", stream); |
| } |
| |
| /* C-language policy to emit a push user expression pragma into BUF. */ |
| |
| struct c_push_user_expression |
| { |
| void push_user_expression (struct ui_file *buf) |
| { |
| fputs_unfiltered ("#pragma GCC user_expression\n", buf); |
| } |
| }; |
| |
| /* C-language policy to emit a pop user expression pragma into BUF. |
| For C, this is a nop. */ |
| |
| struct pop_user_expression_nop |
| { |
| void pop_user_expression (struct ui_file *buf) |
| { |
| /* Nothing to do. */ |
| } |
| }; |
| |
| /* 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 c_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: |
| /* <string.h> is needed for a memcpy call below. */ |
| fputs_unfiltered ("#include <string.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 construct a code footer for a block of code. |
| Takes a scope TYPE which selects the correct footer to insert into BUF. */ |
| |
| struct c_add_code_footer |
| { |
| void add_code_footer (enum compile_i_scope_types type, struct ui_file *buf) |
| { |
| switch (type) |
| { |
| case COMPILE_I_SIMPLE_SCOPE: |
| case COMPILE_I_PRINT_ADDRESS_SCOPE: |
| case COMPILE_I_PRINT_VALUE_SCOPE: |
| fputs_unfiltered ("}\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 c_add_input |
| { |
| void add_input (enum compile_i_scope_types type, const char *input, |
| struct ui_file *buf) |
| { |
| switch (type) |
| { |
| case COMPILE_I_PRINT_ADDRESS_SCOPE: |
| case COMPILE_I_PRINT_VALUE_SCOPE: |
| fprintf_unfiltered (buf, |
| "__auto_type " COMPILE_I_EXPR_VAL " = %s;\n" |
| "typeof (%s) *" COMPILE_I_EXPR_PTR_TYPE ";\n" |
| "memcpy (" COMPILE_I_PRINT_OUT_ARG ", %s" |
| COMPILE_I_EXPR_VAL ",\n" |
| "sizeof (*" COMPILE_I_EXPR_PTR_TYPE "));\n" |
| , input, input, |
| (type == COMPILE_I_PRINT_ADDRESS_SCOPE |
| ? "&" : "")); |
| break; |
| |
| default: |
| fputs_unfiltered (input, buf); |
| break; |
| } |
| fputs_unfiltered ("\n", buf); |
| } |
| }; |
| |
| /* 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 |
| language. |
| |
| PushUserExpressionPolicy and PopUserExpressionPolicy are used to |
| push and pop user expression pragmas to the compile plug-in. |
| |
| AddCodeHeaderPolicy and AddCodeFooterPolicy are used to add the appropriate |
| code header and footer, respectively. |
| |
| AddInputPolicy adds the actual user code. */ |
| |
| template <class CompileInstanceType, class PushUserExpressionPolicy, |
| class PopUserExpressionPolicy, class AddCodeHeaderPolicy, |
| class AddCodeFooterPolicy, class AddInputPolicy> |
| class compile_program |
| : private PushUserExpressionPolicy, private PopUserExpressionPolicy, |
| private AddCodeHeaderPolicy, private AddCodeFooterPolicy, |
| private AddInputPolicy |
| { |
| public: |
| |
| /* Construct a compile_program using the compiler instance INST |
| using the architecture given by GDBARCH. */ |
| compile_program (CompileInstanceType *inst, struct gdbarch *gdbarch) |
| : m_instance (inst), m_arch (gdbarch) |
| { |
| } |
| |
| /* Take the source code provided by the user with the 'compile' |
| command and compute the additional wrapping, macro, variable and |
| register operations needed. INPUT is the source code derived from |
| the 'compile' command, EXPR_BLOCK denotes the block relevant contextually |
| to the inferior when the expression was created, and EXPR_PC |
| indicates the value of $PC. |
| |
| Returns the text of the program to compile. */ |
| std::string compute (const char *input, const struct block *expr_block, |
| CORE_ADDR expr_pc) |
| { |
| string_file var_stream; |
| string_file buf; |
| |
| /* Do not generate local variable information for "raw" |
| compilations. In this case we aren't emitting our own function |
| and the user's code may only refer to globals. */ |
| if (m_instance->scope () != COMPILE_I_RAW_SCOPE) |
| { |
| /* Generate the code to compute variable locations, but do it |
| before generating the function header, so we can define the |
| register struct before the function body. This requires a |
| temporary stream. */ |
| std::vector<bool> registers_used |
| = generate_c_for_variable_locations (m_instance, &var_stream, m_arch, |
| expr_block, expr_pc); |
| |
| buf.puts ("typedef unsigned int" |
| " __attribute__ ((__mode__(__pointer__)))" |
| " __gdb_uintptr;\n"); |
| buf.puts ("typedef int" |
| " __attribute__ ((__mode__(__pointer__)))" |
| " __gdb_intptr;\n"); |
| |
| /* Iterate all log2 sizes in bytes supported by c_get_mode_for_size. */ |
| for (int i = 0; i < 4; ++i) |
| { |
| const char *mode = c_get_mode_for_size (1 << i); |
| |
| gdb_assert (mode != NULL); |
| buf.printf ("typedef int" |
| " __attribute__ ((__mode__(__%s__)))" |
| " __gdb_int_%s;\n", |
| mode, mode); |
| } |
| |
| generate_register_struct (&buf, m_arch, registers_used); |
| } |
| |
| AddCodeHeaderPolicy::add_code_header (m_instance->scope (), &buf); |
| |
| if (m_instance->scope () == COMPILE_I_SIMPLE_SCOPE |
| || m_instance->scope () == COMPILE_I_PRINT_ADDRESS_SCOPE |
| || m_instance->scope () == COMPILE_I_PRINT_VALUE_SCOPE) |
| { |
| buf.write (var_stream.c_str (), var_stream.size ()); |
| PushUserExpressionPolicy::push_user_expression (&buf); |
| } |
| |
| write_macro_definitions (expr_block, expr_pc, &buf); |
| |
| /* The user expression has to be in its own scope, so that "extern" |
| works properly. Otherwise gcc thinks that the "extern" |
| declaration is in the same scope as the declaration provided by |
| gdb. */ |
| if (m_instance->scope () != COMPILE_I_RAW_SCOPE) |
| buf.puts ("{\n"); |
| |
| buf.puts ("#line 1 \"gdb command line\"\n"); |
| |
| AddInputPolicy::add_input (m_instance->scope (), input, &buf); |
| |
| /* For larger user expressions the automatic semicolons may be |
| confusing. */ |
| if (strchr (input, '\n') == NULL) |
| buf.puts (";\n"); |
| |
| if (m_instance->scope () != COMPILE_I_RAW_SCOPE) |
| buf.puts ("}\n"); |
| |
| if (m_instance->scope () == COMPILE_I_SIMPLE_SCOPE |
| || m_instance->scope () == COMPILE_I_PRINT_ADDRESS_SCOPE |
| || m_instance->scope () == COMPILE_I_PRINT_VALUE_SCOPE) |
| PopUserExpressionPolicy::pop_user_expression (&buf); |
| |
| AddCodeFooterPolicy::add_code_footer (m_instance->scope (), &buf); |
| return buf.string (); |
| } |
| |
| private: |
| |
| /* The compile instance to be used for compilation and |
| type-conversion. */ |
| CompileInstanceType *m_instance; |
| |
| /* The architecture to be used. */ |
| struct gdbarch *m_arch; |
| }; |
| |
| /* 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 compute_program method for C. */ |
| |
| std::string |
| c_compute_program (compile_instance *inst, |
| const char *input, |
| struct gdbarch *gdbarch, |
| const struct block *expr_block, |
| CORE_ADDR expr_pc) |
| { |
| compile_c_instance *c_inst = static_cast<compile_c_instance *> (inst); |
| c_compile_program program (c_inst, gdbarch); |
| |
| return program.compute (input, expr_block, expr_pc); |
| } |
| |
| /* The 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); |
| } |