diff options
Diffstat (limited to 'gdb/compile')
-rw-r--r-- | gdb/compile/compile-c-support.c | 44 | ||||
-rw-r--r-- | gdb/compile/compile-internal.h | 4 | ||||
-rw-r--r-- | gdb/compile/compile-object-load.c | 148 | ||||
-rw-r--r-- | gdb/compile/compile-object-load.h | 8 | ||||
-rw-r--r-- | gdb/compile/compile-object-run.c | 33 | ||||
-rw-r--r-- | gdb/compile/compile.c | 83 | ||||
-rw-r--r-- | gdb/compile/compile.h | 2 |
7 files changed, 310 insertions, 12 deletions
diff --git a/gdb/compile/compile-c-support.c b/gdb/compile/compile-c-support.c index 48a17e2..39f06c6 100644 --- a/gdb/compile/compile-c-support.c +++ b/gdb/compile/compile-c-support.c @@ -190,6 +190,24 @@ add_code_header (enum compile_i_scope_types type, struct ui_file *buf) ") {\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: @@ -207,6 +225,8 @@ 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: @@ -369,7 +389,9 @@ c_compute_program (struct compile_instance *inst, add_code_header (inst->scope, buf); - if (inst->scope == COMPILE_I_SIMPLE_SCOPE) + if (inst->scope == COMPILE_I_SIMPLE_SCOPE + || inst->scope == COMPILE_I_PRINT_ADDRESS_SCOPE + || inst->scope == COMPILE_I_PRINT_VALUE_SCOPE) { ui_file_put (var_stream, ui_file_write_for_put, buf); fputs_unfiltered ("#pragma GCC user_expression\n", buf); @@ -383,7 +405,25 @@ c_compute_program (struct compile_instance *inst, fputs_unfiltered ("{\n", buf); fputs_unfiltered ("#line 1 \"gdb command line\"\n", buf); - fputs_unfiltered (input, buf); + + switch (inst->scope) + { + 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, + (inst->scope == COMPILE_I_PRINT_ADDRESS_SCOPE + ? "&" : "")); + break; + default: + fputs_unfiltered (input, buf); + break; + } + fputs_unfiltered ("\n", buf); /* For larger user expressions the automatic semicolons may be diff --git a/gdb/compile/compile-internal.h b/gdb/compile/compile-internal.h index c369d46..b1a5a88 100644 --- a/gdb/compile/compile-internal.h +++ b/gdb/compile/compile-internal.h @@ -84,6 +84,10 @@ struct compile_c_instance #define COMPILE_I_SIMPLE_REGISTER_STRUCT_TAG "__gdb_regs" #define COMPILE_I_SIMPLE_REGISTER_ARG_NAME "__regs" #define COMPILE_I_SIMPLE_REGISTER_DUMMY "_dummy" +#define COMPILE_I_PRINT_OUT_ARG_TYPE "void *" +#define COMPILE_I_PRINT_OUT_ARG "__gdb_out_param" +#define COMPILE_I_EXPR_VAL "__gdb_expr_val" +#define COMPILE_I_EXPR_PTR_TYPE "__gdb_expr_ptr_type" /* Call gdbarch_register_name (GDBARCH, REGNUM) and convert its result to a form suitable for the compiler source. The register names diff --git a/gdb/compile/compile-object-load.c b/gdb/compile/compile-object-load.c index f04b43c..ed5ef88 100644 --- a/gdb/compile/compile-object-load.c +++ b/gdb/compile/compile-object-load.c @@ -29,6 +29,7 @@ #include "regcache.h" #include "inferior.h" #include "compile.h" +#include "block.h" #include "arch-utils.h" /* Helper data for setup_sections. */ @@ -354,6 +355,115 @@ copy_sections (bfd *abfd, asection *sect, void *data) do_cleanups (cleanups); } +/* Fetch the type of COMPILE_I_EXPR_PTR_TYPE and COMPILE_I_EXPR_VAL + symbols in OBJFILE so we can calculate how much memory to allocate + for the out parameter. This avoids needing a malloc in the generated + code. Throw an error if anything fails. + GDB first tries to compile the code with COMPILE_I_PRINT_ADDRESS_SCOPE. + If it finds user tries to print an array type this function returns + NULL. Caller will then regenerate the code with + COMPILE_I_PRINT_VALUE_SCOPE, recompiles it again and finally runs it. + This is because __auto_type array-to-pointer type conversion of + COMPILE_I_EXPR_VAL which gets detected by COMPILE_I_EXPR_PTR_TYPE + preserving the array type. */ + +static struct type * +get_out_value_type (struct symbol *func_sym, struct objfile *objfile, + enum compile_i_scope_types scope) +{ + struct symbol *gdb_ptr_type_sym, *gdb_val_sym; + struct type *gdb_ptr_type, *gdb_type_from_ptr, *gdb_type; + const struct block *block; + const struct blockvector *bv; + int nblocks = 0; + int block_loop = 0; + + bv = SYMTAB_BLOCKVECTOR (func_sym->owner.symtab); + nblocks = BLOCKVECTOR_NBLOCKS (bv); + + gdb_ptr_type_sym = NULL; + for (block_loop = 0; block_loop < nblocks; block_loop++) + { + struct symbol *function; + const struct block *function_block; + + block = BLOCKVECTOR_BLOCK (bv, block_loop); + if (BLOCK_FUNCTION (block) != NULL) + continue; + gdb_val_sym = block_lookup_symbol (block, COMPILE_I_EXPR_VAL, VAR_DOMAIN); + if (gdb_val_sym == NULL) + continue; + + function_block = block; + while (function_block != BLOCKVECTOR_BLOCK (bv, STATIC_BLOCK) + && function_block != BLOCKVECTOR_BLOCK (bv, GLOBAL_BLOCK)) + { + function_block = BLOCK_SUPERBLOCK (function_block); + function = BLOCK_FUNCTION (function_block); + if (function != NULL) + break; + } + if (function != NULL + && (BLOCK_SUPERBLOCK (function_block) + == BLOCKVECTOR_BLOCK (bv, STATIC_BLOCK)) + && (strcmp (SYMBOL_LINKAGE_NAME (function), GCC_FE_WRAPPER_FUNCTION) + == 0)) + break; + } + if (block_loop == nblocks) + error (_("No \"%s\" symbol found"), COMPILE_I_EXPR_PTR_TYPE); + + gdb_type = SYMBOL_TYPE (gdb_val_sym); + CHECK_TYPEDEF (gdb_type); + + gdb_ptr_type_sym = block_lookup_symbol (block, COMPILE_I_EXPR_PTR_TYPE, + VAR_DOMAIN); + if (gdb_ptr_type_sym == NULL) + error (_("No \"%s\" symbol found"), COMPILE_I_EXPR_PTR_TYPE); + gdb_ptr_type = SYMBOL_TYPE (gdb_ptr_type_sym); + 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); + + if (types_deeply_equal (gdb_type, gdb_type_from_ptr)) + { + if (scope != COMPILE_I_PRINT_ADDRESS_SCOPE) + error (_("Expected address scope in compiled module \"%s\"."), + objfile_name (objfile)); + return gdb_type; + } + + if (TYPE_CODE (gdb_type) != TYPE_CODE_PTR) + error (_("Invalid type code %d of symbol \"%s\" " + "in compiled module \"%s\"."), + TYPE_CODE (gdb_type_from_ptr), COMPILE_I_EXPR_VAL, + objfile_name (objfile)); + + switch (TYPE_CODE (gdb_type_from_ptr)) + { + case TYPE_CODE_ARRAY: + gdb_type_from_ptr = TYPE_TARGET_TYPE (gdb_type_from_ptr); + break; + case TYPE_CODE_FUNC: + break; + default: + error (_("Invalid type code %d of symbol \"%s\" " + "in compiled module \"%s\"."), + TYPE_CODE (gdb_type_from_ptr), COMPILE_I_EXPR_PTR_TYPE, + objfile_name (objfile)); + } + if (!types_deeply_equal (gdb_type_from_ptr, + TYPE_TARGET_TYPE (gdb_type))) + error (_("Referenced types do not match for symbols \"%s\" and \"%s\" " + "in compiled module \"%s\"."), + COMPILE_I_EXPR_PTR_TYPE, COMPILE_I_EXPR_VAL, + objfile_name (objfile)); + if (scope == COMPILE_I_PRINT_ADDRESS_SCOPE) + return NULL; + return gdb_type_from_ptr; +} + /* Fetch the type of first parameter of FUNC_SYM. Return NULL if FUNC_SYM has no parameters. Throw an error otherwise. */ @@ -440,7 +550,9 @@ store_regs (struct type *regs_type, CORE_ADDR regs_base) Caller must fully dispose the return value by calling compile_object_run. SOURCE_FILE's copy is stored into the returned object. Caller should free both OBJECT_FILE and SOURCE_FILE immediatelly after this - function returns. */ + function returns. + Function returns NULL only for COMPILE_I_PRINT_ADDRESS_SCOPE when + COMPILE_I_PRINT_VALUE_SCOPE should have been used instead. */ struct compile_module * compile_object_load (const char *object_file, const char *source_file, @@ -449,7 +561,7 @@ compile_object_load (const char *object_file, const char *source_file, struct cleanup *cleanups, *cleanups_free_objfile; bfd *abfd; struct setup_sections_data setup_sections_data; - CORE_ADDR addr, regs_addr; + CORE_ADDR addr, regs_addr, out_value_addr = 0; struct symbol *func_sym; struct type *func_type; struct bound_minimal_symbol bmsym; @@ -459,7 +571,7 @@ compile_object_load (const char *object_file, const char *source_file, struct type *dptr_type = builtin_type (target_gdbarch ())->builtin_data_ptr; unsigned dptr_type_len = TYPE_LENGTH (dptr_type); struct compile_module *retval; - struct type *regs_type; + struct type *regs_type, *out_value_type = NULL; char *filename, **matching; struct objfile *objfile; int expect_parameters; @@ -521,6 +633,11 @@ compile_object_load (const char *object_file, const char *source_file, expect_parameters = 0; expect_return_type = builtin_type (target_gdbarch ())->builtin_void; break; + case COMPILE_I_PRINT_ADDRESS_SCOPE: + case COMPILE_I_PRINT_VALUE_SCOPE: + expect_parameters = 2; + expect_return_type = builtin_type (target_gdbarch ())->builtin_void; + break; default: internal_error (__FILE__, __LINE__, _("invalid scope %d"), scope); } @@ -603,6 +720,29 @@ compile_object_load (const char *object_file, const char *source_file, store_regs (regs_type, regs_addr); } + if (scope == COMPILE_I_PRINT_ADDRESS_SCOPE + || scope == COMPILE_I_PRINT_VALUE_SCOPE) + { + out_value_type = get_out_value_type (func_sym, objfile, scope); + if (out_value_type == NULL) + { + do_cleanups (cleanups); + return NULL; + } + check_typedef (out_value_type); + out_value_addr = gdbarch_infcall_mmap (target_gdbarch (), + TYPE_LENGTH (out_value_type), + (GDB_MMAP_PROT_READ + | GDB_MMAP_PROT_WRITE)); + gdb_assert (out_value_addr != 0); + if (compile_debug) + fprintf_unfiltered (gdb_stdout, + "allocated %s bytes at %s for printed value\n", + paddress (target_gdbarch (), + TYPE_LENGTH (out_value_type)), + paddress (target_gdbarch (), out_value_addr)); + } + discard_cleanups (cleanups_free_objfile); do_cleanups (cleanups); @@ -613,5 +753,7 @@ compile_object_load (const char *object_file, const char *source_file, retval->regs_addr = regs_addr; retval->scope = scope; retval->scope_data = scope_data; + retval->out_value_type = out_value_type; + retval->out_value_addr = out_value_addr; return retval; } diff --git a/gdb/compile/compile-object-load.h b/gdb/compile/compile-object-load.h index f5e887d..5afacda 100644 --- a/gdb/compile/compile-object-load.h +++ b/gdb/compile/compile-object-load.h @@ -37,6 +37,14 @@ struct compile_module /* User data for SCOPE in use. */ void *scope_data; + + /* Inferior parameter out value type or NULL if the inferior function does not + have one. */ + struct type *out_value_type; + + /* If the inferior function has an out value, this is its address. + Otherwise it is zero. */ + CORE_ADDR out_value_addr; }; extern struct compile_module *compile_object_load diff --git a/gdb/compile/compile-object-run.c b/gdb/compile/compile-object-run.c index 15d3130..771a209 100644 --- a/gdb/compile/compile-object-run.c +++ b/gdb/compile/compile-object-run.c @@ -25,6 +25,8 @@ #include "compile-internal.h" #include "dummy-frame.h" #include "block.h" +#include "valprint.h" +#include "compile.h" /* Helper for do_module_cleanup. */ @@ -41,6 +43,10 @@ struct do_module_cleanup enum compile_i_scope_types scope; void *scope_data; + /* Copy from struct compile_module. */ + struct type *out_value_type; + CORE_ADDR out_value_addr; + /* objfile_name of our objfile. */ char objfile_name_string[1]; }; @@ -56,7 +62,23 @@ do_module_cleanup (void *arg, int registers_valid) struct objfile *objfile; if (data->executedp != NULL) - *data->executedp = 1; + { + *data->executedp = 1; + + /* This code cannot be in compile_object_run as OUT_VALUE_TYPE + no longer exists there. */ + if (data->scope == COMPILE_I_PRINT_ADDRESS_SCOPE + || data->scope == COMPILE_I_PRINT_VALUE_SCOPE) + { + struct value *addr_value; + struct type *ptr_type = lookup_pointer_type (data->out_value_type); + + addr_value = value_from_pointer (ptr_type, data->out_value_addr); + + /* SCOPE_DATA would be stale unlesse EXECUTEDP != NULL. */ + compile_print_value (value_ind (addr_value), data->scope_data); + } + } ALL_OBJFILES (objfile) if ((objfile->flags & OBJF_USERLOADED) == 0 @@ -104,6 +126,8 @@ compile_object_run (struct compile_module *module) strcpy (data->objfile_name_string, objfile_name_s); data->scope = module->scope; data->scope_data = module->scope_data; + data->out_value_type = module->out_value_type; + data->out_value_addr = module->out_value_addr; xfree (module->source_file); xfree (module); @@ -133,6 +157,13 @@ compile_object_run (struct compile_module *module) (TYPE_FIELD_TYPE (func_type, current_arg), regs_addr); ++current_arg; } + if (TYPE_NFIELDS (func_type) >= 2) + { + gdb_assert (data->out_value_addr != 0); + vargs[current_arg] = value_from_pointer + (TYPE_FIELD_TYPE (func_type, current_arg), data->out_value_addr); + ++current_arg; + } gdb_assert (current_arg == TYPE_NFIELDS (func_type)); call_function_by_hand_dummy (func_val, TYPE_NFIELDS (func_type), vargs, do_module_cleanup, data); diff --git a/gdb/compile/compile.c b/gdb/compile/compile.c index ab19a5d..fbecf8c 100644 --- a/gdb/compile/compile.c +++ b/gdb/compile/compile.c @@ -38,6 +38,7 @@ #include "target.h" #include "osabi.h" #include "gdb_wait.h" +#include "valprint.h" @@ -163,6 +164,51 @@ compile_code_command (char *arg, int from_tty) do_cleanups (cleanup); } +/* Callback for compile_print_command. */ + +void +compile_print_value (struct value *val, void *data_voidp) +{ + const struct format_data *fmtp = data_voidp; + + print_value (val, fmtp); +} + +/* Handle the input from the 'compile print' command. The "compile + print" command is used to evaluate and print an expression that may + contain calls to the GCC compiler. The language expected in this + compile command is the language currently set in GDB. */ + +static void +compile_print_command (char *arg_param, int from_tty) +{ + const char *arg = arg_param; + struct cleanup *cleanup; + enum compile_i_scope_types scope = COMPILE_I_PRINT_ADDRESS_SCOPE; + struct format_data fmt; + + cleanup = make_cleanup_restore_integer (&interpreter_async); + interpreter_async = 0; + + /* Passing &FMT as SCOPE_DATA is safe as do_module_cleanup will not + touch the stale pointer if compile_object_run has already quit. */ + print_command_parse_format (&arg, "compile print", &fmt); + + if (arg && *arg) + eval_compile_command (NULL, arg, scope, &fmt); + else + { + struct command_line *l = get_command_line (compile_control, ""); + + make_cleanup_free_command_lines (&l); + l->control_u.compile.scope = scope; + l->control_u.compile.scope_data = &fmt; + execute_control_command_untraced (l); + } + + do_cleanups (cleanup); +} + /* A cleanup function to remove a directory and all its contents. */ static void @@ -576,6 +622,14 @@ eval_compile_command (struct command_line *cmd, const char *cmd_string, make_cleanup (cleanup_unlink_file, source_file); compile_module = compile_object_load (object_file, source_file, scope, scope_data); + if (compile_module == NULL) + { + gdb_assert (scope == COMPILE_I_PRINT_ADDRESS_SCOPE); + do_cleanups (cleanup_xfree); + eval_compile_command (cmd, cmd_string, + COMPILE_I_PRINT_VALUE_SCOPE, scope_data); + return; + } discard_cleanups (cleanup_unlink); do_cleanups (cleanup_xfree); compile_object_run (compile_module); @@ -637,12 +691,10 @@ The source code may be specified as a simple one line expression, e.g.:\n\ \n\ compile code printf(\"Hello world\\n\");\n\ \n\ -Alternatively, you can type the source code interactively.\n\ -You can invoke this mode when no argument is given to the command\n\ -(i.e.,\"compile code\" is typed with nothing after it). An\n\ -interactive prompt will be shown allowing you to enter multiple\n\ -lines of source code. Type a line containing \"end\" to indicate\n\ -the end of the source code."), +Alternatively, you can type a multiline expression by invoking\n\ +this command with no argument. GDB will then prompt for the\n\ +expression interactively; type a line containing \"end\" to\n\ +indicate the end of the expression."), &compile_command_list); c = add_cmd ("file", class_obscure, compile_file_command, @@ -654,6 +706,25 @@ Usage: compile file [-r|-raw] [filename]\n\ &compile_command_list); set_cmd_completer (c, filename_completer); + add_cmd ("print", class_obscure, compile_print_command, + _("\ +Evaluate EXPR by using the compiler and print result.\n\ +\n\ +Usage: compile print[/FMT] [EXPR]\n\ +\n\ +The expression may be specified on the same line as the command, e.g.:\n\ +\n\ + compile print i\n\ +\n\ +Alternatively, you can type a multiline expression by invoking\n\ +this command with no argument. GDB will then prompt for the\n\ +expression interactively; type a line containing \"end\" to\n\ +indicate the end of the expression.\n\ +\n\ +EXPR may be preceded with /FMT, where FMT is a format letter\n\ +but no count or size letter (see \"x\" command)."), + &compile_command_list); + add_setshow_boolean_cmd ("compile", class_maintenance, &compile_debug, _("\ Set compile command debugging."), _("\ Show compile command debugging."), _("\ diff --git a/gdb/compile/compile.h b/gdb/compile/compile.h index a973167..6e108c7 100644 --- a/gdb/compile/compile.h +++ b/gdb/compile/compile.h @@ -101,4 +101,6 @@ extern void compile_dwarf_bounds_to_c (struct ui_file *stream, const gdb_byte *op_end, struct dwarf2_per_cu_data *per_cu); +extern void compile_print_value (struct value *val, void *data_voidp); + #endif /* GDB_COMPILE_H */ |