diff options
Diffstat (limited to 'gdb/compile')
-rw-r--r-- | gdb/compile/compile-c-support.c | 399 | ||||
-rw-r--r-- | gdb/compile/compile-c-symbols.c | 759 | ||||
-rw-r--r-- | gdb/compile/compile-c-types.c | 438 | ||||
-rw-r--r-- | gdb/compile/compile-internal.h | 147 | ||||
-rw-r--r-- | gdb/compile/compile-loc2c.c | 1148 | ||||
-rw-r--r-- | gdb/compile/compile-object-load.c | 588 | ||||
-rw-r--r-- | gdb/compile/compile-object-load.h | 39 | ||||
-rw-r--r-- | gdb/compile/compile-object-run.c | 138 | ||||
-rw-r--r-- | gdb/compile/compile-object-run.h | 24 | ||||
-rw-r--r-- | gdb/compile/compile.c | 651 | ||||
-rw-r--r-- | gdb/compile/compile.h | 102 |
11 files changed, 4433 insertions, 0 deletions
diff --git a/gdb/compile/compile-c-support.c b/gdb/compile/compile-c-support.c new file mode 100644 index 0000000..48a81cb --- /dev/null +++ b/gdb/compile/compile-c-support.c @@ -0,0 +1,399 @@ +/* C language support for compilation. + + Copyright (C) 2014 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.h" +#include "gdb-dlfcn.h" +#include "c-lang.h" +#include "macrotab.h" +#include "macroscope.h" +#include "regcache.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. */ + +char * +c_get_range_decl_name (const struct dynamic_prop *prop) +{ + return xstrprintf ("__gdb_prop_%s", host_address_to_string (prop)); +} + + + +#define STR(x) #x +#define STRINGIFY(x) STR(x) + +/* 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. */ + +static gcc_c_fe_context_function * +load_libcc (void) +{ + void *handle; + gcc_c_fe_context_function *func; + + /* gdb_dlopen will call error () on an error, so no need to check + value. */ + handle = gdb_dlopen (STRINGIFY (GCC_C_FE_LIBCC)); + func = (gcc_c_fe_context_function *) gdb_dlsym (handle, + STRINGIFY (GCC_C_FE_CONTEXT)); + + if (func == NULL) + error (_("could not find symbol %s in library %s"), + STRINGIFY (GCC_C_FE_CONTEXT), + STRINGIFY (GCC_C_FE_LIBCC)); + return func; +} + +/* 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. */ + +struct compile_instance * +c_get_compile_context (void) +{ + static gcc_c_fe_context_function *func; + + struct gcc_c_context *context; + + if (func == NULL) + { + func = load_libcc (); + gdb_assert (func != NULL); + } + + context = (*func) (GCC_FE_VERSION_0, GCC_C_FE_VERSION_0); + if (context == NULL) + error (_("The loaded version of GCC does not support the required version " + "of the API.")); + + return new_compile_instance (context); +} + + + +/* Write one macro definition. */ + +static void +print_one_macro (const char *name, const struct macro_definition *macro, + struct macro_source_file *source, int line, + void *user_data) +{ + struct ui_file *file = user_data; + + /* Don't print command-line defines. They will be supplied another + way. */ + if (line == 0) + return; + + fprintf_filtered (file, "#define %s", 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", 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) +{ + 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, print_one_macro, file); +} + +/* Helper function to construct a header scope for a block of code. + Takes a scope argument which selects the correct header to + insert into BUF. */ + +static 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_RAW_SCOPE: + break; + default: + gdb_assert_not_reached (_("Unknown compiler scope reached.")); + } +} + +/* Helper function to construct a footer scope for a block of code. + Takes a scope argument which selects the correct footer to + insert into BUF. */ + +static void +add_code_footer (enum compile_i_scope_types type, struct ui_file *buf) +{ + switch (type) + { + case COMPILE_I_SIMPLE_SCOPE: + fputs_unfiltered ("}\n", buf); + break; + case COMPILE_I_RAW_SCOPE: + break; + default: + gdb_assert_not_reached (_("Unknown compiler scope reached.")); + } +} + +/* 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 unsigned char *registers_used) +{ + int i; + int seen = 0; + + fputs_unfiltered ("struct " COMPILE_I_SIMPLE_REGISTER_STRUCT_TAG " {\n", + stream); + + if (registers_used != NULL) + for (i = 0; i < gdbarch_num_regs (gdbarch); ++i) + { + if (registers_used[i]) + { + struct type *regtype = check_typedef (register_type (gdbarch, i)); + char *regname = compile_register_name_mangled (gdbarch, i); + struct cleanup *cleanups = make_cleanup (xfree, regname); + + 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 (TYPE_CODE (regtype)) + { + case TYPE_CODE_PTR: + fprintf_filtered (stream, "void *%s", regname); + break; + + case TYPE_CODE_INT: + { + const char *mode + = c_get_mode_for_size (TYPE_LENGTH (regtype)); + + if (mode != NULL) + { + if (TYPE_UNSIGNED (regtype)) + fputs_unfiltered ("unsigned ", stream); + fprintf_unfiltered (stream, + "int %s" + " __attribute__ ((__mode__(__%s__)))", + regname, + mode); + break; + } + } + + /* Fall through. */ + + default: + fprintf_unfiltered (stream, + " unsigned char %s[%d]" + " __attribute__((__aligned__(" + "__BIGGEST_ALIGNMENT__)))", + regname, + TYPE_LENGTH (regtype)); + } + fputs_unfiltered (";\n", stream); + + do_cleanups (cleanups); + } + } + + if (!seen) + fputs_unfiltered (" char " COMPILE_I_SIMPLE_REGISTER_DUMMY ";\n", + stream); + + fputs_unfiltered ("};\n\n", stream); +} + +/* 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, GDBARCH is the architecture to use when + computing above, EXPR_BLOCK denotes the block relevant contextually + to the inferior when the expression was created, and EXPR_PC + indicates the value of $PC. */ + +char * +c_compute_program (struct compile_instance *inst, + const char *input, + struct gdbarch *gdbarch, + const struct block *expr_block, + CORE_ADDR expr_pc) +{ + struct ui_file *buf, *var_stream = NULL; + char *code; + struct cleanup *cleanup; + struct compile_c_instance *context = (struct compile_c_instance *) inst; + + buf = mem_fileopen (); + cleanup = make_cleanup_ui_file_delete (buf); + + write_macro_definitions (expr_block, expr_pc, 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 (inst->scope != COMPILE_I_RAW_SCOPE) + { + unsigned char *registers_used; + int i; + + /* 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. */ + var_stream = mem_fileopen (); + make_cleanup_ui_file_delete (var_stream); + registers_used = generate_c_for_variable_locations (context, + var_stream, gdbarch, + expr_block, expr_pc); + make_cleanup (xfree, registers_used); + + generate_register_struct (buf, gdbarch, registers_used); + + fputs_unfiltered ("typedef unsigned int" + " __attribute__ ((__mode__(__pointer__)))" + " __gdb_uintptr;\n", + buf); + fputs_unfiltered ("typedef int" + " __attribute__ ((__mode__(__pointer__)))" + " __gdb_intptr;\n", + buf); + + // Iterate all log2 sizes in bytes supported by c_get_mode_for_size. + for (i = 0; i < 4; ++i) + { + const char *mode = c_get_mode_for_size (1 << i); + + gdb_assert (mode != NULL); + fprintf_unfiltered (buf, + "typedef int" + " __attribute__ ((__mode__(__%s__)))" + " __gdb_int_%s;\n", + mode, mode); + } + } + + add_code_header (inst->scope, buf); + + if (inst->scope == COMPILE_I_SIMPLE_SCOPE) + { + ui_file_put (var_stream, ui_file_write_for_put, buf); + fputs_unfiltered ("#pragma GCC user_expression\n", 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 (inst->scope != COMPILE_I_RAW_SCOPE) + fputs_unfiltered ("{\n", buf); + + fputs_unfiltered ("#line 1 \"gdb command line\"\n", buf); + fputs_unfiltered (input, buf); + fputs_unfiltered ("\n", buf); + + /* For larger user expressions the automatic semicolons may be + confusing. */ + if (strchr (input, '\n') == NULL) + fputs_unfiltered (";\n", buf); + + if (inst->scope != COMPILE_I_RAW_SCOPE) + fputs_unfiltered ("}\n", buf); + + add_code_footer (inst->scope, buf); + code = ui_file_xstrdup (buf, NULL); + do_cleanups (cleanup); + return code; +} diff --git a/gdb/compile/compile-c-symbols.c b/gdb/compile/compile-c-symbols.c new file mode 100644 index 0000000..b489c74 --- /dev/null +++ b/gdb/compile/compile-c-symbols.c @@ -0,0 +1,759 @@ +/* Convert symbols from GDB to GCC + + Copyright (C) 2014 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 "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" + + + +/* Object of this type are stored in the compiler's symbol_err_map. */ + +struct symbol_error +{ + /* The symbol. */ + + const struct symbol *sym; + + /* The error message to emit. This is malloc'd and owned by the + hash table. */ + + char *message; +}; + +/* Hash function for struct symbol_error. */ + +static hashval_t +hash_symbol_error (const void *a) +{ + const struct symbol_error *se = a; + + return htab_hash_pointer (se->sym); +} + +/* Equality function for struct symbol_error. */ + +static int +eq_symbol_error (const void *a, const void *b) +{ + const struct symbol_error *sea = a; + const struct symbol_error *seb = b; + + return sea->sym == seb->sym; +} + +/* Deletion function for struct symbol_error. */ + +static void +del_symbol_error (void *a) +{ + struct symbol_error *se = a; + + xfree (se->message); + xfree (se); +} + +/* Associate SYMBOL with some error text. */ + +static void +insert_symbol_error (htab_t hash, const struct symbol *sym, const char *text) +{ + struct symbol_error e; + void **slot; + + e.sym = sym; + slot = htab_find_slot (hash, &e, INSERT); + if (*slot == NULL) + { + struct symbol_error *e = XNEW (struct symbol_error); + + e->sym = sym; + e->message = xstrdup (text); + *slot = e; + } +} + +/* Emit the error message corresponding to SYM, if one exists, and + arrange for it not to be emitted again. */ + +static void +error_symbol_once (struct compile_c_instance *context, + const struct symbol *sym) +{ + struct symbol_error search; + struct symbol_error *err; + char *message; + + if (context->symbol_err_map == NULL) + return; + + search.sym = sym; + err = htab_find (context->symbol_err_map, &search); + if (err == NULL || err->message == NULL) + return; + + message = err->message; + err->message = NULL; + make_cleanup (xfree, message); + error (_("%s"), message); +} + + + +/* Compute the name of the pointer representing a local symbol's + address. */ + +static char * +symbol_substitution_name (struct symbol *sym) +{ + return concat ("__", SYMBOL_NATURAL_NAME (sym), "_ptr", (char *) NULL); +} + +/* Convert a given symbol, SYM, to the compiler's representation. + CONTEXT 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 (struct compile_c_instance *context, + struct symbol *sym, + int is_global, + int is_local) +{ + gcc_type sym_type; + const char *filename = SYMBOL_SYMTAB (sym)->filename; + unsigned short line = SYMBOL_LINE (sym); + + error_symbol_once (context, sym); + + if (SYMBOL_CLASS (sym) == LOC_LABEL) + sym_type = 0; + else + sym_type = convert_type (context, SYMBOL_TYPE (sym)); + + if (SYMBOL_DOMAIN (sym) == STRUCT_DOMAIN) + { + /* Binding a tag, so we don't need to build a decl. */ + C_CTX (context)->c_ops->tagbind (C_CTX (context), + SYMBOL_NATURAL_NAME (sym), + sym_type, filename, line); + } + else + { + gcc_decl decl; + enum gcc_c_symbol_kind kind; + CORE_ADDR addr = 0; + char *symbol_name = NULL; + + switch (SYMBOL_CLASS (sym)) + { + case LOC_TYPEDEF: + kind = GCC_C_SYMBOL_TYPEDEF; + break; + + case LOC_LABEL: + kind = GCC_C_SYMBOL_LABEL; + addr = SYMBOL_VALUE_ADDRESS (sym); + break; + + case LOC_BLOCK: + kind = GCC_C_SYMBOL_FUNCTION; + addr = BLOCK_START (SYMBOL_BLOCK_VALUE (sym)); + break; + + case LOC_CONST: + if (TYPE_CODE (SYMBOL_TYPE (sym)) == TYPE_CODE_ENUM) + { + /* Already handled by convert_enum. */ + return; + } + C_CTX (context)->c_ops->build_constant (C_CTX (context), sym_type, + SYMBOL_NATURAL_NAME (sym), + SYMBOL_VALUE (sym), + filename, line); + return; + + case LOC_CONST_BYTES: + error (_("Unsupported LOC_CONST_BYTES for symbol \"%s\"."), + SYMBOL_PRINT_NAME (sym)); + + case LOC_UNDEF: + internal_error (__FILE__, __LINE__, _("LOC_UNDEF found for \"%s\"."), + SYMBOL_PRINT_NAME (sym)); + + case LOC_COMMON_BLOCK: + error (_("Fortran common block is unsupported for compilation " + "evaluaton of symbol \"%s\"."), + SYMBOL_PRINT_NAME (sym)); + + case LOC_OPTIMIZED_OUT: + error (_("Symbol \"%s\" cannot be used for compilation evaluation " + "as it is optimized out."), + SYMBOL_PRINT_NAME (sym)); + + 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)); + /* 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 = NULL; + + if (symbol_read_needs_frame (sym)) + { + frame = get_selected_frame (NULL); + if (frame == NULL) + error (_("Symbol \"%s\" cannot be used because " + "there is no selected frame"), + SYMBOL_PRINT_NAME (sym)); + } + + val = read_var_value (sym, 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)); + + kind = GCC_C_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_C_SYMBOL_VARIABLE; + symbol_name = symbol_substitution_name (sym); + break; + + case LOC_STATIC: + kind = GCC_C_SYMBOL_VARIABLE; + addr = SYMBOL_VALUE_ADDRESS (sym); + 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 (context->base.scope != COMPILE_I_RAW_SCOPE + || symbol_name == NULL) + { + decl = C_CTX (context)->c_ops->build_decl (C_CTX (context), + SYMBOL_NATURAL_NAME (sym), + kind, + sym_type, + symbol_name, addr, + filename, line); + + C_CTX (context)->c_ops->bind (C_CTX (context), decl, is_global); + } + + xfree (symbol_name); + } +} + +/* 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 (struct compile_c_instance *context, const char *identifier, + struct symbol *sym, domain_enum domain) +{ + const struct block *static_block, *found_block; + int is_local_symbol; + + found_block = block_found; + + /* 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" + } + */ + + static_block = block_static_block (found_block); + /* STATIC_BLOCK is NULL if FOUND_BLOCK is the global block. */ + is_local_symbol = (found_block != static_block && static_block != NULL); + if (is_local_symbol) + { + struct symbol *global_sym; + + global_sym = lookup_symbol (identifier, NULL, domain, NULL); + /* If the outer symbol is in the static block, we ignore it, as + it cannot be referenced. */ + if (global_sym != NULL + && block_found != block_static_block (block_found)) + { + if (compile_debug) + fprintf_unfiltered (gdb_stdout, + "gcc_convert_symbol \"%s\": global symbol\n", + identifier); + convert_one_symbol (context, global_sym, 1, 0); + } + } + + if (compile_debug) + fprintf_unfiltered (gdb_stdout, + "gcc_convert_symbol \"%s\": local symbol\n", + identifier); + convert_one_symbol (context, sym, 0, 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 (struct compile_c_instance *context, + struct bound_minimal_symbol bmsym) +{ + struct minimal_symbol *msym = bmsym.minsym; + struct objfile *objfile = bmsym.objfile; + struct type *type; + enum gcc_c_symbol_kind kind; + gcc_type sym_type; + gcc_decl decl; + CORE_ADDR addr; + + /* 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_C_SYMBOL_FUNCTION; + break; + + case mst_text_gnu_ifunc: + type = objfile_type (objfile)->nodebug_text_gnu_ifunc_symbol; + kind = GCC_C_SYMBOL_FUNCTION; + break; + + case mst_data: + case mst_file_data: + case mst_bss: + case mst_file_bss: + type = objfile_type (objfile)->nodebug_data_symbol; + kind = GCC_C_SYMBOL_VARIABLE; + break; + + case mst_slot_got_plt: + type = objfile_type (objfile)->nodebug_got_plt_symbol; + kind = GCC_C_SYMBOL_FUNCTION; + break; + + default: + type = objfile_type (objfile)->nodebug_unknown_symbol; + kind = GCC_C_SYMBOL_VARIABLE; + break; + } + + sym_type = convert_type (context, type); + addr = MSYMBOL_VALUE_ADDRESS (objfile, msym); + decl = C_CTX (context)->c_ops->build_decl (C_CTX (context), + MSYMBOL_NATURAL_NAME (msym), + kind, sym_type, NULL, addr, + NULL, 0); + C_CTX (context)->c_ops->bind (C_CTX (context), decl, 1 /* is_global */); +} + +/* See compile-internal.h. */ + +void +gcc_convert_symbol (void *datum, + struct gcc_c_context *gcc_context, + enum gcc_c_oracle_request request, + const char *identifier) +{ + struct compile_c_instance *context = datum; + domain_enum domain; + volatile struct gdb_exception e; + int found = 0; + + switch (request) + { + case GCC_C_ORACLE_SYMBOL: + domain = VAR_DOMAIN; + break; + case GCC_C_ORACLE_TAG: + domain = STRUCT_DOMAIN; + break; + case GCC_C_ORACLE_LABEL: + domain = LABEL_DOMAIN; + break; + default: + gdb_assert_not_reached ("Unrecognized oracle request."); + } + + /* We can't allow exceptions to escape out of this callback. Safest + is to simply emit a gcc error. */ + TRY_CATCH (e, RETURN_MASK_ALL) + { + struct symbol *sym; + + sym = lookup_symbol (identifier, context->base.block, domain, NULL); + if (sym != NULL) + { + convert_symbol_sym (context, identifier, sym, domain); + found = 1; + } + else if (domain == VAR_DOMAIN) + { + struct bound_minimal_symbol bmsym; + + bmsym = lookup_minimal_symbol (identifier, NULL, NULL); + if (bmsym.minsym != NULL) + { + convert_symbol_bmsym (context, bmsym); + found = 1; + } + } + } + + if (e.reason < 0) + C_CTX (context)->c_ops->error (C_CTX (context), e.message); + + if (compile_debug && !found) + fprintf_unfiltered (gdb_stdout, + "gcc_convert_symbol \"%s\": lookup_symbol failed\n", + identifier); + return; +} + +/* See compile-internal.h. */ + +gcc_address +gcc_symbol_address (void *datum, struct gcc_c_context *gcc_context, + const char *identifier) +{ + struct compile_c_instance *context = datum; + volatile struct gdb_exception e; + gcc_address result = 0; + int found = 0; + + /* We can't allow exceptions to escape out of this callback. Safest + is to simply emit a gcc error. */ + TRY_CATCH (e, RETURN_MASK_ERROR) + { + struct symbol *sym; + + /* We only need global functions here. */ + sym = lookup_symbol (identifier, NULL, VAR_DOMAIN, NULL); + if (sym != NULL && SYMBOL_CLASS (sym) == LOC_BLOCK) + { + if (compile_debug) + fprintf_unfiltered (gdb_stdout, + "gcc_symbol_address \"%s\": full symbol\n", + identifier); + result = BLOCK_START (SYMBOL_BLOCK_VALUE (sym)); + found = 1; + } + else + { + struct bound_minimal_symbol msym; + + msym = lookup_bound_minimal_symbol (identifier); + if (msym.minsym != NULL) + { + if (compile_debug) + fprintf_unfiltered (gdb_stdout, + "gcc_symbol_address \"%s\": minimal " + "symbol\n", + identifier); + result = BMSYMBOL_VALUE_ADDRESS (msym); + found = 1; + } + } + } + + if (e.reason < 0) + C_CTX (context)->c_ops->error (C_CTX (context), e.message); + + if (compile_debug && !found) + fprintf_unfiltered (gdb_stdout, + "gcc_symbol_address \"%s\": failed\n", + identifier); + return result; +} + + + +/* A hash function for symbol names. */ + +static hashval_t +hash_symname (const void *a) +{ + const struct symbol *sym = a; + + return htab_hash_string (SYMBOL_NATURAL_NAME (sym)); +} + +/* A comparison function for hash tables that just looks at symbol + names. */ + +static int +eq_symname (const void *a, const void *b) +{ + const struct symbol *syma = a; + const struct symbol *symb = b; + + return strcmp (SYMBOL_NATURAL_NAME (syma), SYMBOL_NATURAL_NAME (symb)) == 0; +} + +/* If a symbol with the same name as SYM is already in HASHTAB, return + 1. Otherwise, add SYM to HASHTAB and return 0. */ + +static int +symbol_seen (htab_t hashtab, struct symbol *sym) +{ + void **slot; + + slot = htab_find_slot (hashtab, sym, INSERT); + if (*slot != NULL) + return 1; + + *slot = sym; + return 0; +} + +/* Generate C code to compute the length of a VLA. */ + +static void +generate_vla_size (struct compile_c_instance *compiler, + struct ui_file *stream, + struct gdbarch *gdbarch, + unsigned char *registers_used, + CORE_ADDR pc, + struct type *type, + struct symbol *sym) +{ + type = check_typedef (type); + + if (TYPE_CODE (type) == TYPE_CODE_REF) + type = check_typedef (TYPE_TARGET_TYPE (type)); + + switch (TYPE_CODE (type)) + { + case TYPE_CODE_RANGE: + { + if (TYPE_HIGH_BOUND_KIND (type) == PROP_LOCEXPR + || TYPE_HIGH_BOUND_KIND (type) == PROP_LOCLIST) + { + const struct dynamic_prop *prop = &TYPE_RANGE_DATA (type)->high; + char *name = c_get_range_decl_name (prop); + struct cleanup *cleanup = make_cleanup (xfree, name); + + dwarf2_compile_property_to_c (stream, name, + gdbarch, registers_used, + prop, pc, sym); + do_cleanups (cleanup); + } + } + break; + + case TYPE_CODE_ARRAY: + generate_vla_size (compiler, stream, gdbarch, registers_used, pc, + TYPE_INDEX_TYPE (type), sym); + generate_vla_size (compiler, stream, gdbarch, registers_used, pc, + TYPE_TARGET_TYPE (type), sym); + break; + + case TYPE_CODE_UNION: + case TYPE_CODE_STRUCT: + { + int i; + + for (i = 0; i < TYPE_NFIELDS (type); ++i) + if (!field_is_static (&TYPE_FIELD (type, i))) + generate_vla_size (compiler, stream, gdbarch, registers_used, pc, + TYPE_FIELD_TYPE (type, i), sym); + } + break; + } +} + +/* Generate C code to compute the address of SYM. */ + +static void +generate_c_for_for_one_variable (struct compile_c_instance *compiler, + struct ui_file *stream, + struct gdbarch *gdbarch, + unsigned char *registers_used, + CORE_ADDR pc, + struct symbol *sym) +{ + volatile struct gdb_exception e; + + TRY_CATCH (e, RETURN_MASK_ERROR) + { + if (is_dynamic_type (SYMBOL_TYPE (sym))) + { + struct ui_file *size_file = mem_fileopen (); + struct cleanup *cleanup = make_cleanup_ui_file_delete (size_file); + + generate_vla_size (compiler, size_file, gdbarch, registers_used, pc, + SYMBOL_TYPE (sym), sym); + ui_file_put (size_file, ui_file_write_for_put, stream); + + do_cleanups (cleanup); + } + + if (SYMBOL_COMPUTED_OPS (sym) != NULL) + { + char *generated_name = symbol_substitution_name (sym); + struct cleanup *cleanup = make_cleanup (xfree, generated_name); + /* We need to emit to a temporary buffer in case an error + occurs in the middle. */ + struct ui_file *local_file = mem_fileopen (); + + make_cleanup_ui_file_delete (local_file); + SYMBOL_COMPUTED_OPS (sym)->generate_c_location (sym, local_file, + gdbarch, + registers_used, + pc, generated_name); + ui_file_put (local_file, ui_file_write_for_put, stream); + + do_cleanups (cleanup); + } + else + { + switch (SYMBOL_CLASS (sym)) + { + case LOC_REGISTER: + case LOC_ARG: + case LOC_REF_ARG: + case LOC_REGPARM_ADDR: + case LOC_LOCAL: + error (_("Local symbol unhandled when generating C code.")); + + case LOC_COMPUTED: + gdb_assert_not_reached (_("LOC_COMPUTED variable " + "missing a method.")); + + default: + /* Nothing to do for all other cases, as they don't represent + local variables. */ + break; + } + } + } + + if (e.reason >= 0) + return; + + if (compiler->symbol_err_map == NULL) + compiler->symbol_err_map = htab_create_alloc (10, + hash_symbol_error, + eq_symbol_error, + del_symbol_error, + xcalloc, + xfree); + insert_symbol_error (compiler->symbol_err_map, sym, e.message); +} + +/* See compile-internal.h. */ + +unsigned char * +generate_c_for_variable_locations (struct compile_c_instance *compiler, + struct ui_file *stream, + struct gdbarch *gdbarch, + const struct block *block, + CORE_ADDR pc) +{ + struct cleanup *cleanup, *outer; + htab_t symhash; + const struct block *static_block = block_static_block (block); + unsigned char *registers_used; + + /* If we're already in the static or global block, there is nothing + to write. */ + if (static_block == NULL || block == static_block) + return NULL; + + registers_used = XCNEWVEC (unsigned char, gdbarch_num_regs (gdbarch)); + outer = make_cleanup (xfree, registers_used); + + /* Ensure that a given name is only entered once. This reflects the + reality of shadowing. */ + symhash = htab_create_alloc (1, hash_symname, eq_symname, NULL, + xcalloc, xfree); + cleanup = make_cleanup_htab_delete (symhash); + + while (1) + { + struct symbol *sym; + struct block_iterator iter; + + /* Iterate over symbols in this block, generating code to + compute the location of each local variable. */ + for (sym = block_iterator_first (block, &iter); + sym != NULL; + sym = block_iterator_next (&iter)) + { + if (!symbol_seen (symhash, sym)) + generate_c_for_for_one_variable (compiler, stream, gdbarch, + registers_used, pc, sym); + } + + /* If we just finished the outermost block of a function, we're + done. */ + if (BLOCK_FUNCTION (block) != NULL) + break; + block = BLOCK_SUPERBLOCK (block); + } + + do_cleanups (cleanup); + discard_cleanups (outer); + return registers_used; +} diff --git a/gdb/compile/compile-c-types.c b/gdb/compile/compile-c-types.c new file mode 100644 index 0000000..78ae9a8 --- /dev/null +++ b/gdb/compile/compile-c-types.c @@ -0,0 +1,438 @@ +/* Convert types from GDB to GCC + + Copyright (C) 2014 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 "gdbtypes.h" +#include "compile-internal.h" +#include "gdb_assert.h" + +/* An object that maps a gdb type to a gcc type. */ + +struct type_map_instance +{ + /* The gdb type. */ + + struct type *type; + + /* The corresponding gcc type handle. */ + + gcc_type gcc_type; +}; + +/* Hash a type_map_instance. */ + +static hashval_t +hash_type_map_instance (const void *p) +{ + const struct type_map_instance *inst = p; + + return htab_hash_pointer (inst->type); +} + +/* Check two type_map_instance objects for equality. */ + +static int +eq_type_map_instance (const void *a, const void *b) +{ + const struct type_map_instance *insta = a; + const struct type_map_instance *instb = b; + + return insta->type == instb->type; +} + + + +/* Insert an entry into the type map associated with CONTEXT that maps + from the gdb type TYPE to the gcc type GCC_TYPE. It is ok for a + given type to be inserted more than once, provided that the exact + same association is made each time. This simplifies how type + caching works elsewhere in this file -- see how struct type caching + is handled. */ + +static void +insert_type (struct compile_c_instance *context, struct type *type, + gcc_type gcc_type) +{ + struct type_map_instance inst, *add; + void **slot; + + inst.type = type; + inst.gcc_type = gcc_type; + slot = htab_find_slot (context->type_map, &inst, INSERT); + + add = *slot; + /* The type might have already been inserted in order to handle + recursive types. */ + gdb_assert (add == NULL || add->gcc_type == gcc_type); + + if (add == NULL) + { + add = XNEW (struct type_map_instance); + *add = inst; + *slot = add; + } +} + +/* Convert a pointer type to its gcc representation. */ + +static gcc_type +convert_pointer (struct compile_c_instance *context, struct type *type) +{ + gcc_type target = convert_type (context, TYPE_TARGET_TYPE (type)); + + return C_CTX (context)->c_ops->build_pointer_type (C_CTX (context), + target); +} + +/* Convert an array type to its gcc representation. */ + +static gcc_type +convert_array (struct compile_c_instance *context, struct type *type) +{ + gcc_type element_type; + struct type *range = TYPE_INDEX_TYPE (type); + + element_type = convert_type (context, TYPE_TARGET_TYPE (type)); + + if (TYPE_LOW_BOUND_KIND (range) != PROP_CONST) + return C_CTX (context)->c_ops->error (C_CTX (context), + _("array type with non-constant" + " lower bound is not supported")); + if (TYPE_LOW_BOUND (range) != 0) + return C_CTX (context)->c_ops->error (C_CTX (context), + _("cannot convert array type with " + "non-zero lower bound to C")); + + if (TYPE_HIGH_BOUND_KIND (range) == PROP_LOCEXPR + || TYPE_HIGH_BOUND_KIND (range) == PROP_LOCLIST) + { + gcc_type result; + char *upper_bound; + + if (TYPE_VECTOR (type)) + return C_CTX (context)->c_ops->error (C_CTX (context), + _("variably-sized vector type" + " is not supported")); + + upper_bound = c_get_range_decl_name (&TYPE_RANGE_DATA (range)->high); + result = C_CTX (context)->c_ops->build_vla_array_type (C_CTX (context), + element_type, + upper_bound); + xfree (upper_bound); + return result; + } + 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 C_CTX (context)->c_ops->build_vector_type (C_CTX (context), + element_type, + count); + return C_CTX (context)->c_ops->build_array_type (C_CTX (context), + element_type, count); + } +} + +/* Convert a struct or union type to its gcc representation. */ + +static gcc_type +convert_struct_or_union (struct compile_c_instance *context, struct type *type) +{ + int i; + gcc_type result; + + /* First we create the resulting type and enter it into our hash + table. This lets recursive types work. */ + if (TYPE_CODE (type) == TYPE_CODE_STRUCT) + result = C_CTX (context)->c_ops->build_record_type (C_CTX (context)); + else + { + gdb_assert (TYPE_CODE (type) == TYPE_CODE_UNION); + result = C_CTX (context)->c_ops->build_union_type (C_CTX (context)); + } + insert_type (context, type, result); + + for (i = 0; i < TYPE_NFIELDS (type); ++i) + { + gcc_type field_type; + unsigned long bitsize = TYPE_FIELD_BITSIZE (type, i); + + field_type = convert_type (context, TYPE_FIELD_TYPE (type, i)); + if (bitsize == 0) + bitsize = 8 * TYPE_LENGTH (TYPE_FIELD_TYPE (type, i)); + C_CTX (context)->c_ops->build_add_field (C_CTX (context), result, + TYPE_FIELD_NAME (type, i), + field_type, + bitsize, + TYPE_FIELD_BITPOS (type, i)); + } + + C_CTX (context)->c_ops->finish_record_or_union (C_CTX (context), result, + TYPE_LENGTH (type)); + return result; +} + +/* Convert an enum type to its gcc representation. */ + +static gcc_type +convert_enum (struct compile_c_instance *context, struct type *type) +{ + gcc_type int_type, result; + int i; + struct gcc_c_context *ctx = C_CTX (context); + + int_type = ctx->c_ops->int_type (ctx, + TYPE_UNSIGNED (type), + TYPE_LENGTH (type)); + + result = ctx->c_ops->build_enum_type (ctx, int_type); + for (i = 0; i < TYPE_NFIELDS (type); ++i) + { + ctx->c_ops->build_add_enum_constant (ctx, + result, + TYPE_FIELD_NAME (type, i), + TYPE_FIELD_ENUMVAL (type, i)); + } + + ctx->c_ops->finish_enum_type (ctx, result); + + return result; +} + +/* Convert a function type to its gcc representation. */ + +static gcc_type +convert_func (struct compile_c_instance *context, struct type *type) +{ + int i; + gcc_type result, return_type; + struct gcc_type_array array; + int is_varargs = TYPE_VARARGS (type) || !TYPE_PROTOTYPED (type); + + /* This approach means we can't make self-referential function + types. Those are impossible in C, though. */ + return_type = convert_type (context, TYPE_TARGET_TYPE (type)); + + array.n_elements = TYPE_NFIELDS (type); + array.elements = XNEWVEC (gcc_type, TYPE_NFIELDS (type)); + for (i = 0; i < TYPE_NFIELDS (type); ++i) + array.elements[i] = convert_type (context, TYPE_FIELD_TYPE (type, i)); + + result = C_CTX (context)->c_ops->build_function_type (C_CTX (context), + return_type, + &array, is_varargs); + xfree (array.elements); + + return result; +} + +/* Convert an integer type to its gcc representation. */ + +static gcc_type +convert_int (struct compile_c_instance *context, struct type *type) +{ + return C_CTX (context)->c_ops->int_type (C_CTX (context), + TYPE_UNSIGNED (type), + TYPE_LENGTH (type)); +} + +/* Convert a floating-point type to its gcc representation. */ + +static gcc_type +convert_float (struct compile_c_instance *context, struct type *type) +{ + return C_CTX (context)->c_ops->float_type (C_CTX (context), + TYPE_LENGTH (type)); +} + +/* Convert the 'void' type to its gcc representation. */ + +static gcc_type +convert_void (struct compile_c_instance *context, struct type *type) +{ + return C_CTX (context)->c_ops->void_type (C_CTX (context)); +} + +/* Convert a boolean type to its gcc representation. */ + +static gcc_type +convert_bool (struct compile_c_instance *context, struct type *type) +{ + return C_CTX (context)->c_ops->bool_type (C_CTX (context)); +} + +/* Convert a qualified type to its gcc representation. */ + +static gcc_type +convert_qualified (struct compile_c_instance *context, struct type *type) +{ + struct type *unqual = make_unqualified_type (type); + gcc_type unqual_converted; + int quals = 0; + + unqual_converted = convert_type (context, unqual); + + if (TYPE_CONST (type)) + quals |= GCC_QUALIFIER_CONST; + if (TYPE_VOLATILE (type)) + quals |= GCC_QUALIFIER_VOLATILE; + if (TYPE_RESTRICT (type)) + quals |= GCC_QUALIFIER_RESTRICT; + + return C_CTX (context)->c_ops->build_qualified_type (C_CTX (context), + unqual_converted, + quals); +} + +/* Convert a complex type to its gcc representation. */ + +static gcc_type +convert_complex (struct compile_c_instance *context, struct type *type) +{ + gcc_type base = convert_type (context, TYPE_TARGET_TYPE (type)); + + return C_CTX (context)->c_ops->build_complex_type (C_CTX (context), base); +} + +/* 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. */ + +static gcc_type +convert_type_basic (struct compile_c_instance *context, struct type *type) +{ + /* 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 convert_qualified (context, type); + + switch (TYPE_CODE (type)) + { + case TYPE_CODE_PTR: + return convert_pointer (context, type); + + case TYPE_CODE_ARRAY: + return convert_array (context, type); + + case TYPE_CODE_STRUCT: + case TYPE_CODE_UNION: + return convert_struct_or_union (context, type); + + case TYPE_CODE_ENUM: + return convert_enum (context, type); + + case TYPE_CODE_FUNC: + return convert_func (context, type); + + case TYPE_CODE_INT: + return convert_int (context, type); + + case TYPE_CODE_FLT: + return convert_float (context, type); + + case TYPE_CODE_VOID: + return convert_void (context, type); + + case TYPE_CODE_BOOL: + return convert_bool (context, type); + + case TYPE_CODE_COMPLEX: + return convert_complex (context, type); + } + + return C_CTX (context)->c_ops->error (C_CTX (context), + _("cannot convert gdb type " + "to gcc type")); +} + +/* See compile-internal.h. */ + +gcc_type +convert_type (struct compile_c_instance *context, struct type *type) +{ + struct type_map_instance inst, *found; + gcc_type result; + + /* We don't ever have to deal with typedefs in this code, because + those are only needed as symbols by the C compiler. */ + CHECK_TYPEDEF (type); + + inst.type = type; + found = htab_find (context->type_map, &inst); + if (found != NULL) + return found->gcc_type; + + result = convert_type_basic (context, type); + insert_type (context, type, result); + return result; +} + + + +/* Delete the compiler instance C. */ + +static void +delete_instance (struct compile_instance *c) +{ + struct compile_c_instance *context = (struct compile_c_instance *) c; + + context->base.fe->ops->destroy (context->base.fe); + htab_delete (context->type_map); + if (context->symbol_err_map != NULL) + htab_delete (context->symbol_err_map); + xfree (context); +} + +/* See compile-internal.h. */ + +struct compile_instance * +new_compile_instance (struct gcc_c_context *fe) +{ + struct compile_c_instance *result = XCNEW (struct compile_c_instance); + + result->base.fe = &fe->base; + result->base.destroy = delete_instance; + result->base.gcc_target_options = ("-std=gnu11" + /* Otherwise the .o file may need + "_Unwind_Resume" and + "__gcc_personality_v0". */ + " -fno-exceptions"); + + result->type_map = htab_create_alloc (10, hash_type_map_instance, + eq_type_map_instance, + xfree, xcalloc, xfree); + + fe->c_ops->set_callbacks (fe, gcc_convert_symbol, + gcc_symbol_address, result); + + return &result->base; +} diff --git a/gdb/compile/compile-internal.h b/gdb/compile/compile-internal.h new file mode 100644 index 0000000..cb8d3d1 --- /dev/null +++ b/gdb/compile/compile-internal.h @@ -0,0 +1,147 @@ +/* Header file for GDB compile command and supporting functions. + Copyright (C) 2014 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_INTERNAL_H +#define GDB_COMPILE_INTERNAL_H + +#include "hashtab.h" +#include "gcc-c-interface.h" + +/* Debugging flag for the "compile" family of commands. */ + +extern int compile_debug; + +struct block; + +/* An object of this type holds state associated with a given + compilation job. */ + +struct compile_instance +{ + /* The GCC front end. */ + + struct gcc_base_context *fe; + + /* The "scope" of this compilation. */ + + enum compile_i_scope_types scope; + + /* The block in which an expression is being parsed. */ + + const struct block *block; + + /* Specify "-std=gnu11", "-std=gnu++11" or similar. These options are put + after CU's DW_AT_producer compilation options to override them. */ + + const char *gcc_target_options; + + /* How to destroy this object. */ + + void (*destroy) (struct compile_instance *); +}; + +/* A subclass of compile_instance that is specific to the C front + end. */ +struct compile_c_instance +{ + /* Base class. Note that the base class vtable actually points to a + gcc_c_fe_vtable. */ + + struct compile_instance base; + + /* Map from gdb types to gcc types. */ + + htab_t type_map; + + /* Map from gdb symbols to gcc error messages to emit. */ + + htab_t symbol_err_map; +}; + +/* A helper macro that takes a compile_c_instance and returns its + corresponding gcc_c_context. */ + +#define C_CTX(I) ((struct gcc_c_context *) ((I)->base.fe)) + +/* Define header and footers for different scopes. */ + +/* A simple scope just declares a function named "_gdb_expr", takes no + arguments and returns no value. */ + +#define COMPILE_I_SIMPLE_REGISTER_STRUCT_TAG "__gdb_regs" +#define COMPILE_I_SIMPLE_REGISTER_ARG_NAME "__regs" +#define COMPILE_I_SIMPLE_REGISTER_DUMMY "_dummy" + +/* 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. Returned pointer is + never NULL. Returned pointer needs to be deallocated by xfree. */ + +extern char *compile_register_name_mangled (struct gdbarch *gdbarch, + int regnum); + +/* Convert compiler source register name to register number of + GDBARCH. Returned value is always >= 0, function throws an error + for non-matching REG_NAME. */ + +extern int compile_register_name_demangle (struct gdbarch *gdbarch, + const char *reg_name); + +/* Convert a gdb type, TYPE, to a GCC type. CONTEXT is used to do the + actual conversion. The new GCC type is returned. */ + +struct type; +extern gcc_type convert_type (struct compile_c_instance *context, + struct type *type); + +/* A callback suitable for use as the GCC C symbol oracle. */ + +extern gcc_c_oracle_function gcc_convert_symbol; + +/* A callback suitable for use as the GCC C address oracle. */ + +extern gcc_c_symbol_address_function gcc_symbol_address; + +/* Instantiate a GDB object holding state for the GCC context FE. The + new object is returned. */ + +extern struct compile_instance *new_compile_instance (struct gcc_c_context *fe); + +/* Emit code to compute the address for all the local variables in + scope at PC in BLOCK. Returns a malloc'd vector, indexed by gdb + register number, where each element indicates if the corresponding + register is needed to compute a local variable. */ + +extern unsigned char *generate_c_for_variable_locations + (struct compile_c_instance *compiler, + struct ui_file *stream, + struct gdbarch *gdbarch, + const struct block *block, + CORE_ADDR pc); + +/* Get the GCC mode attribute value for a given type size. */ + +extern const char *c_get_mode_for_size (int size); + +/* Given a dynamic property, return an xmallocd name that is used to + represent its size. The result must be freed by the caller. The + contents of the resulting string will be the same each time for + each call with the same argument. */ + +struct dynamic_prop; +extern char *c_get_range_decl_name (const struct dynamic_prop *prop); + +#endif /* GDB_COMPILE_INTERNAL_H */ diff --git a/gdb/compile/compile-loc2c.c b/gdb/compile/compile-loc2c.c new file mode 100644 index 0000000..e31d44d --- /dev/null +++ b/gdb/compile/compile-loc2c.c @@ -0,0 +1,1148 @@ +/* Convert a DWARF location expression to C + + Copyright (C) 2014 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 "dwarf2.h" +#include "dwarf2expr.h" +#include "dwarf2loc.h" +#include "ui-file.h" +#include "utils.h" +#include "compile-internal.h" +#include "compile.h" +#include "block.h" +#include "dwarf2-frame.h" +#include "gdb_vecs.h" +#include "value.h" + + + +/* Information about a given instruction. */ + +struct insn_info +{ + /* Stack depth at entry. */ + + unsigned int depth; + + /* Whether this instruction has been visited. */ + + unsigned int visited : 1; + + /* Whether this instruction needs a label. */ + + unsigned int label : 1; + + /* Whether this instruction is DW_OP_GNU_push_tls_address. This is + a hack until we can add a feature to glibc to let us properly + generate code for TLS. */ + + unsigned int is_tls : 1; +}; + +/* A helper function for compute_stack_depth that does the work. This + examines the DWARF expression starting from START and computes + stack effects. + + NEED_TEMPVAR is an out parameter which is set if this expression + needs a special temporary variable to be emitted (see the code + generator). + INFO is an array of insn_info objects, indexed by offset from the + start of the DWARF expression. + TO_DO is a list of bytecodes which must be examined; it may be + added to by this function. + BYTE_ORDER and ADDR_SIZE describe this bytecode in the obvious way. + OP_PTR and OP_END are the bounds of the DWARF expression. */ + +static void +compute_stack_depth_worker (int start, int *need_tempvar, + struct insn_info *info, + VEC (int) **to_do, + enum bfd_endian byte_order, unsigned int addr_size, + const gdb_byte *op_ptr, const gdb_byte *op_end) +{ + const gdb_byte * const base = op_ptr; + int stack_depth; + + op_ptr += start; + gdb_assert (info[start].visited); + stack_depth = info[start].depth; + + while (op_ptr < op_end) + { + enum dwarf_location_atom op = *op_ptr; + uint64_t reg; + int64_t offset; + int ndx = op_ptr - base; + +#define SET_CHECK_DEPTH(WHERE) \ + if (info[WHERE].visited) \ + { \ + if (info[WHERE].depth != stack_depth) \ + error (_("inconsistent stack depths")); \ + } \ + else \ + { \ + /* Stack depth not set, so set it. */ \ + info[WHERE].visited = 1; \ + info[WHERE].depth = stack_depth; \ + } + + SET_CHECK_DEPTH (ndx); + + ++op_ptr; + + switch (op) + { + case DW_OP_lit0: + case DW_OP_lit1: + case DW_OP_lit2: + case DW_OP_lit3: + case DW_OP_lit4: + case DW_OP_lit5: + case DW_OP_lit6: + case DW_OP_lit7: + case DW_OP_lit8: + case DW_OP_lit9: + case DW_OP_lit10: + case DW_OP_lit11: + case DW_OP_lit12: + case DW_OP_lit13: + case DW_OP_lit14: + case DW_OP_lit15: + case DW_OP_lit16: + case DW_OP_lit17: + case DW_OP_lit18: + case DW_OP_lit19: + case DW_OP_lit20: + case DW_OP_lit21: + case DW_OP_lit22: + case DW_OP_lit23: + case DW_OP_lit24: + case DW_OP_lit25: + case DW_OP_lit26: + case DW_OP_lit27: + case DW_OP_lit28: + case DW_OP_lit29: + case DW_OP_lit30: + case DW_OP_lit31: + ++stack_depth; + break; + + case DW_OP_addr: + op_ptr += addr_size; + ++stack_depth; + break; + + case DW_OP_const1u: + case DW_OP_const1s: + op_ptr += 1; + ++stack_depth; + break; + case DW_OP_const2u: + case DW_OP_const2s: + op_ptr += 2; + ++stack_depth; + break; + case DW_OP_const4u: + case DW_OP_const4s: + op_ptr += 4; + ++stack_depth; + break; + case DW_OP_const8u: + case DW_OP_const8s: + op_ptr += 8; + ++stack_depth; + break; + case DW_OP_constu: + case DW_OP_consts: + op_ptr = safe_read_sleb128 (op_ptr, op_end, &offset); + ++stack_depth; + break; + + case DW_OP_reg0: + case DW_OP_reg1: + case DW_OP_reg2: + case DW_OP_reg3: + case DW_OP_reg4: + case DW_OP_reg5: + case DW_OP_reg6: + case DW_OP_reg7: + case DW_OP_reg8: + case DW_OP_reg9: + case DW_OP_reg10: + case DW_OP_reg11: + case DW_OP_reg12: + case DW_OP_reg13: + case DW_OP_reg14: + case DW_OP_reg15: + case DW_OP_reg16: + case DW_OP_reg17: + case DW_OP_reg18: + case DW_OP_reg19: + case DW_OP_reg20: + case DW_OP_reg21: + case DW_OP_reg22: + case DW_OP_reg23: + case DW_OP_reg24: + case DW_OP_reg25: + case DW_OP_reg26: + case DW_OP_reg27: + case DW_OP_reg28: + case DW_OP_reg29: + case DW_OP_reg30: + case DW_OP_reg31: + ++stack_depth; + break; + + case DW_OP_regx: + op_ptr = safe_read_uleb128 (op_ptr, op_end, ®); + ++stack_depth; + break; + + case DW_OP_breg0: + case DW_OP_breg1: + case DW_OP_breg2: + case DW_OP_breg3: + case DW_OP_breg4: + case DW_OP_breg5: + case DW_OP_breg6: + case DW_OP_breg7: + case DW_OP_breg8: + case DW_OP_breg9: + case DW_OP_breg10: + case DW_OP_breg11: + case DW_OP_breg12: + case DW_OP_breg13: + case DW_OP_breg14: + case DW_OP_breg15: + case DW_OP_breg16: + case DW_OP_breg17: + case DW_OP_breg18: + case DW_OP_breg19: + case DW_OP_breg20: + case DW_OP_breg21: + case DW_OP_breg22: + case DW_OP_breg23: + case DW_OP_breg24: + case DW_OP_breg25: + case DW_OP_breg26: + case DW_OP_breg27: + case DW_OP_breg28: + case DW_OP_breg29: + case DW_OP_breg30: + case DW_OP_breg31: + op_ptr = safe_read_sleb128 (op_ptr, op_end, &offset); + ++stack_depth; + break; + case DW_OP_bregx: + { + op_ptr = safe_read_uleb128 (op_ptr, op_end, ®); + op_ptr = safe_read_sleb128 (op_ptr, op_end, &offset); + ++stack_depth; + } + break; + case DW_OP_fbreg: + op_ptr = safe_read_sleb128 (op_ptr, op_end, &offset); + ++stack_depth; + break; + + case DW_OP_dup: + ++stack_depth; + break; + + case DW_OP_drop: + --stack_depth; + break; + + case DW_OP_pick: + ++op_ptr; + ++stack_depth; + break; + + case DW_OP_rot: + case DW_OP_swap: + *need_tempvar = 1; + break; + + case DW_OP_over: + ++stack_depth; + break; + + case DW_OP_abs: + case DW_OP_neg: + case DW_OP_not: + case DW_OP_deref: + break; + + case DW_OP_deref_size: + ++op_ptr; + break; + + case DW_OP_plus_uconst: + op_ptr = safe_read_uleb128 (op_ptr, op_end, ®); + break; + + case DW_OP_div: + case DW_OP_shra: + case DW_OP_and: + case DW_OP_minus: + case DW_OP_mod: + case DW_OP_mul: + case DW_OP_or: + case DW_OP_plus: + case DW_OP_shl: + case DW_OP_shr: + case DW_OP_xor: + case DW_OP_le: + case DW_OP_ge: + case DW_OP_eq: + case DW_OP_lt: + case DW_OP_gt: + case DW_OP_ne: + --stack_depth; + break; + + case DW_OP_call_frame_cfa: + ++stack_depth; + break; + + case DW_OP_GNU_push_tls_address: + info[ndx].is_tls = 1; + break; + + case DW_OP_skip: + offset = extract_signed_integer (op_ptr, 2, byte_order); + op_ptr += 2; + offset = op_ptr + offset - base; + /* If the destination has not been seen yet, add it to the + to-do list. */ + if (!info[offset].visited) + VEC_safe_push (int, *to_do, offset); + SET_CHECK_DEPTH (offset); + info[offset].label = 1; + /* We're done with this line of code. */ + return; + + case DW_OP_bra: + offset = extract_signed_integer (op_ptr, 2, byte_order); + op_ptr += 2; + offset = op_ptr + offset - base; + --stack_depth; + /* If the destination has not been seen yet, add it to the + to-do list. */ + if (!info[offset].visited) + VEC_safe_push (int, *to_do, offset); + SET_CHECK_DEPTH (offset); + info[offset].label = 1; + break; + + case DW_OP_nop: + break; + + default: + error (_("unhandled DWARF op: %s"), get_DW_OP_name (op)); + } + } + + gdb_assert (op_ptr == op_end); + +#undef SET_CHECK_DEPTH +} + +/* Compute the maximum needed stack depth of a DWARF expression, and + some other information as well. + + BYTE_ORDER and ADDR_SIZE describe this bytecode in the obvious way. + NEED_TEMPVAR is an out parameter which is set if this expression + needs a special temporary variable to be emitted (see the code + generator). + IS_TLS is an out parameter which is set if this expression refers + to a TLS variable. + OP_PTR and OP_END are the bounds of the DWARF expression. + INITIAL_DEPTH is the initial depth of the DWARF expression stack. + INFO is an array of insn_info objects, indexed by offset from the + start of the DWARF expression. + + This returns the maximum stack depth. */ + +static int +compute_stack_depth (enum bfd_endian byte_order, unsigned int addr_size, + int *need_tempvar, int *is_tls, + const gdb_byte *op_ptr, const gdb_byte *op_end, + int initial_depth, + struct insn_info **info) +{ + unsigned char *set; + struct cleanup *outer_cleanup, *cleanup; + VEC (int) *to_do = NULL; + int stack_depth, i; + + *info = XCNEWVEC (struct insn_info, op_end - op_ptr); + outer_cleanup = make_cleanup (xfree, *info); + + cleanup = make_cleanup (VEC_cleanup (int), &to_do); + + VEC_safe_push (int, to_do, 0); + (*info)[0].depth = initial_depth; + (*info)[0].visited = 1; + + while (!VEC_empty (int, to_do)) + { + int ndx = VEC_pop (int, to_do); + + compute_stack_depth_worker (ndx, need_tempvar, *info, &to_do, + byte_order, addr_size, + op_ptr, op_end); + } + + stack_depth = 0; + *is_tls = 0; + for (i = 0; i < op_end - op_ptr; ++i) + { + if ((*info)[i].depth > stack_depth) + stack_depth = (*info)[i].depth; + if ((*info)[i].is_tls) + *is_tls = 1; + } + + do_cleanups (cleanup); + discard_cleanups (outer_cleanup); + return stack_depth + 1; +} + + + +#define GCC_UINTPTR "__gdb_uintptr" +#define GCC_INTPTR "__gdb_intptr" + +/* Emit code to push a constant. */ + +static void +push (int indent, struct ui_file *stream, ULONGEST l) +{ + fprintfi_filtered (indent, stream, "__gdb_stack[++__gdb_tos] = %s;\n", + hex_string (l)); +} + +/* Emit code to push an arbitrary expression. This works like + printf. */ + +static void +pushf (int indent, struct ui_file *stream, const char *format, ...) +{ + va_list args; + + fprintfi_filtered (indent, stream, "__gdb_stack[__gdb_tos + 1] = "); + va_start (args, format); + vfprintf_filtered (stream, format, args); + va_end (args); + fprintf_filtered (stream, ";\n"); + + fprintfi_filtered (indent, stream, "++__gdb_tos;\n"); +} + +/* Emit code for a unary expression -- one which operates in-place on + the top-of-stack. This works like printf. */ + +static void +unary (int indent, struct ui_file *stream, const char *format, ...) +{ + va_list args; + + fprintfi_filtered (indent, stream, "__gdb_stack[__gdb_tos] = "); + va_start (args, format); + vfprintf_filtered (stream, format, args); + va_end (args); + fprintf_filtered (stream, ";\n"); +} + +/* Emit code for a unary expression -- one which uses the top two + stack items, popping the topmost one. This works like printf. */ + +static void +binary (int indent, struct ui_file *stream, const char *format, ...) +{ + va_list args; + + fprintfi_filtered (indent, stream, "__gdb_stack[__gdb_tos - 1] = "); + va_start (args, format); + vfprintf_filtered (stream, format, args); + va_end (args); + fprintf_filtered (stream, ";\n"); + fprintfi_filtered (indent, stream, "--__gdb_tos;\n"); +} + +/* Print the name of a label given its "SCOPE", an arbitrary integer + used for uniqueness, and its TARGET, the bytecode offset + corresponding to the label's point of definition. */ + +static void +print_label (struct ui_file *stream, unsigned int scope, int target) +{ + fprintf_filtered (stream, "__label_%u_%s", + scope, pulongest (target)); +} + +/* Emit code that pushes a register's address on the stack. + REGISTERS_USED is an out parameter which is updated to note which + register was needed by this expression. */ + +static void +pushf_register_address (int indent, struct ui_file *stream, + unsigned char *registers_used, + struct gdbarch *gdbarch, int regnum) +{ + char *regname = compile_register_name_mangled (gdbarch, regnum); + struct cleanup *cleanups = make_cleanup (xfree, regname); + + registers_used[regnum] = 1; + pushf (indent, stream, "&" COMPILE_I_SIMPLE_REGISTER_ARG_NAME "->%s", + regname); + + do_cleanups (cleanups); +} + +/* Emit code that pushes a register's value on the stack. + REGISTERS_USED is an out parameter which is updated to note which + register was needed by this expression. OFFSET is added to the + register's value before it is pushed. */ + +static void +pushf_register (int indent, struct ui_file *stream, + unsigned char *registers_used, + struct gdbarch *gdbarch, int regnum, uint64_t offset) +{ + char *regname = compile_register_name_mangled (gdbarch, regnum); + struct cleanup *cleanups = make_cleanup (xfree, regname); + + registers_used[regnum] = 1; + if (offset == 0) + pushf (indent, stream, COMPILE_I_SIMPLE_REGISTER_ARG_NAME "->%s", + regname); + else + pushf (indent, stream, COMPILE_I_SIMPLE_REGISTER_ARG_NAME "->%s + %s", + regname, hex_string (offset)); + + do_cleanups (cleanups); +} + +/* Compile a DWARF expression to C code. + + INDENT is the indentation level to use. + STREAM is the stream where the code should be written. + + TYPE_NAME names the type of the result of the DWARF expression. + For locations this is "void *" but for array bounds it will be an + integer type. + + RESULT_NAME is the name of a variable in the resulting C code. The + result of the expression will be assigned to this variable. + + SYM is the symbol corresponding to this expression. + PC is the location at which the expression is being evaluated. + ARCH is the architecture to use. + + REGISTERS_USED is an out parameter which is updated to note which + registers were needed by this expression. + + ADDR_SIZE is the DWARF address size to use. + + OPT_PTR and OP_END are the bounds of the DWARF expression. + + If non-NULL, INITIAL points to an initial value to write to the + stack. If NULL, no initial value is written. + + PER_CU is the per-CU object used for looking up various other + things. */ + +static void +do_compile_dwarf_expr_to_c (int indent, struct ui_file *stream, + const char *type_name, + const char *result_name, + struct symbol *sym, CORE_ADDR pc, + struct gdbarch *arch, + unsigned char *registers_used, + unsigned int addr_size, + const gdb_byte *op_ptr, const gdb_byte *op_end, + CORE_ADDR *initial, + struct dwarf2_per_cu_data *per_cu) +{ + /* We keep a counter so that labels and other objects we create have + unique names. */ + static unsigned int scope; + + enum bfd_endian byte_order = gdbarch_byte_order (arch); + const gdb_byte * const base = op_ptr; + int need_tempvar = 0; + int is_tls = 0; + struct cleanup *cleanup; + struct insn_info *info; + int stack_depth; + + ++scope; + + fprintfi_filtered (indent, stream, "%s%s;\n", type_name, result_name); + fprintfi_filtered (indent, stream, "{\n"); + indent += 2; + + stack_depth = compute_stack_depth (byte_order, addr_size, + &need_tempvar, &is_tls, + op_ptr, op_end, initial != NULL, + &info); + cleanup = make_cleanup (xfree, info); + + /* This is a hack until we can add a feature to glibc to let us + properly generate code for TLS. You might think we could emit + the address in the ordinary course of translating + DW_OP_GNU_push_tls_address, but since the operand appears on the + stack, it is relatively hard to find, and the idea of calling + target_translate_tls_address with OFFSET==0 and then adding the + offset by hand seemed too hackish. */ + if (is_tls) + { + struct frame_info *frame = get_selected_frame (NULL); + struct value *val; + + if (frame == NULL) + error (_("Symbol \"%s\" cannot be used because " + "there is no selected frame"), + SYMBOL_PRINT_NAME (sym)); + + val = read_var_value (sym, 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)); + + warning (_("Symbol \"%s\" is thread-local and currently can only " + "be referenced from the current thread in " + "compiled code."), + SYMBOL_PRINT_NAME (sym)); + + fprintfi_filtered (indent, stream, "%s = %s;\n", + result_name, + core_addr_to_string (value_address (val))); + fprintfi_filtered (indent - 2, stream, "}\n"); + do_cleanups (cleanup); + return; + } + + fprintfi_filtered (indent, stream, GCC_UINTPTR " __gdb_stack[%d];\n", + stack_depth); + + if (need_tempvar) + fprintfi_filtered (indent, stream, GCC_UINTPTR " __gdb_tmp;\n"); + fprintfi_filtered (indent, stream, "int __gdb_tos = -1;\n"); + + if (initial != NULL) + pushf (indent, stream, core_addr_to_string (*initial)); + + while (op_ptr < op_end) + { + enum dwarf_location_atom op = *op_ptr; + uint64_t uoffset, reg; + int64_t offset; + + print_spaces (indent - 2, stream); + if (info[op_ptr - base].label) + { + print_label (stream, scope, op_ptr - base); + fprintf_filtered (stream, ":;"); + } + fprintf_filtered (stream, "/* %s */\n", get_DW_OP_name (op)); + + /* This is handy for debugging the generated code: + fprintf_filtered (stream, "if (__gdb_tos != %d) abort ();\n", + (int) info[op_ptr - base].depth - 1); + */ + + ++op_ptr; + + switch (op) + { + case DW_OP_lit0: + case DW_OP_lit1: + case DW_OP_lit2: + case DW_OP_lit3: + case DW_OP_lit4: + case DW_OP_lit5: + case DW_OP_lit6: + case DW_OP_lit7: + case DW_OP_lit8: + case DW_OP_lit9: + case DW_OP_lit10: + case DW_OP_lit11: + case DW_OP_lit12: + case DW_OP_lit13: + case DW_OP_lit14: + case DW_OP_lit15: + case DW_OP_lit16: + case DW_OP_lit17: + case DW_OP_lit18: + case DW_OP_lit19: + case DW_OP_lit20: + case DW_OP_lit21: + case DW_OP_lit22: + case DW_OP_lit23: + case DW_OP_lit24: + case DW_OP_lit25: + case DW_OP_lit26: + case DW_OP_lit27: + case DW_OP_lit28: + case DW_OP_lit29: + case DW_OP_lit30: + case DW_OP_lit31: + push (indent, stream, op - DW_OP_lit0); + break; + + case DW_OP_addr: + op_ptr += addr_size; + /* Some versions of GCC emit DW_OP_addr before + DW_OP_GNU_push_tls_address. In this case the value is an + index, not an address. We don't support things like + branching between the address and the TLS op. */ + if (op_ptr >= op_end || *op_ptr != DW_OP_GNU_push_tls_address) + uoffset += dwarf2_per_cu_text_offset (per_cu); + push (indent, stream, uoffset); + break; + + case DW_OP_const1u: + push (indent, stream, + extract_unsigned_integer (op_ptr, 1, byte_order)); + op_ptr += 1; + break; + case DW_OP_const1s: + push (indent, stream, + extract_signed_integer (op_ptr, 1, byte_order)); + op_ptr += 1; + break; + case DW_OP_const2u: + push (indent, stream, + extract_unsigned_integer (op_ptr, 2, byte_order)); + op_ptr += 2; + break; + case DW_OP_const2s: + push (indent, stream, + extract_signed_integer (op_ptr, 2, byte_order)); + op_ptr += 2; + break; + case DW_OP_const4u: + push (indent, stream, + extract_unsigned_integer (op_ptr, 4, byte_order)); + op_ptr += 4; + break; + case DW_OP_const4s: + push (indent, stream, + extract_signed_integer (op_ptr, 4, byte_order)); + op_ptr += 4; + break; + case DW_OP_const8u: + push (indent, stream, + extract_unsigned_integer (op_ptr, 8, byte_order)); + op_ptr += 8; + break; + case DW_OP_const8s: + push (indent, stream, + extract_signed_integer (op_ptr, 8, byte_order)); + op_ptr += 8; + break; + case DW_OP_constu: + op_ptr = safe_read_uleb128 (op_ptr, op_end, &uoffset); + push (indent, stream, uoffset); + break; + case DW_OP_consts: + op_ptr = safe_read_sleb128 (op_ptr, op_end, &offset); + push (indent, stream, offset); + break; + + case DW_OP_reg0: + case DW_OP_reg1: + case DW_OP_reg2: + case DW_OP_reg3: + case DW_OP_reg4: + case DW_OP_reg5: + case DW_OP_reg6: + case DW_OP_reg7: + case DW_OP_reg8: + case DW_OP_reg9: + case DW_OP_reg10: + case DW_OP_reg11: + case DW_OP_reg12: + case DW_OP_reg13: + case DW_OP_reg14: + case DW_OP_reg15: + case DW_OP_reg16: + case DW_OP_reg17: + case DW_OP_reg18: + case DW_OP_reg19: + case DW_OP_reg20: + case DW_OP_reg21: + case DW_OP_reg22: + case DW_OP_reg23: + case DW_OP_reg24: + case DW_OP_reg25: + case DW_OP_reg26: + case DW_OP_reg27: + case DW_OP_reg28: + case DW_OP_reg29: + case DW_OP_reg30: + case DW_OP_reg31: + dwarf_expr_require_composition (op_ptr, op_end, "DW_OP_regx"); + pushf_register_address (indent, stream, registers_used, arch, + dwarf2_reg_to_regnum_or_error (arch, + op - DW_OP_reg0)); + break; + + case DW_OP_regx: + op_ptr = safe_read_uleb128 (op_ptr, op_end, ®); + dwarf_expr_require_composition (op_ptr, op_end, "DW_OP_regx"); + pushf_register_address (indent, stream, registers_used, arch, + dwarf2_reg_to_regnum_or_error (arch, reg)); + break; + + case DW_OP_breg0: + case DW_OP_breg1: + case DW_OP_breg2: + case DW_OP_breg3: + case DW_OP_breg4: + case DW_OP_breg5: + case DW_OP_breg6: + case DW_OP_breg7: + case DW_OP_breg8: + case DW_OP_breg9: + case DW_OP_breg10: + case DW_OP_breg11: + case DW_OP_breg12: + case DW_OP_breg13: + case DW_OP_breg14: + case DW_OP_breg15: + case DW_OP_breg16: + case DW_OP_breg17: + case DW_OP_breg18: + case DW_OP_breg19: + case DW_OP_breg20: + case DW_OP_breg21: + case DW_OP_breg22: + case DW_OP_breg23: + case DW_OP_breg24: + case DW_OP_breg25: + case DW_OP_breg26: + case DW_OP_breg27: + case DW_OP_breg28: + case DW_OP_breg29: + case DW_OP_breg30: + case DW_OP_breg31: + op_ptr = safe_read_sleb128 (op_ptr, op_end, &offset); + pushf_register (indent, stream, registers_used, arch, + dwarf2_reg_to_regnum_or_error (arch, + op - DW_OP_breg0), + offset); + break; + case DW_OP_bregx: + { + op_ptr = safe_read_uleb128 (op_ptr, op_end, ®); + op_ptr = safe_read_sleb128 (op_ptr, op_end, &offset); + pushf_register (indent, stream, registers_used, arch, + dwarf2_reg_to_regnum_or_error (arch, reg), offset); + } + break; + case DW_OP_fbreg: + { + const gdb_byte *datastart; + size_t datalen; + const struct block *b; + struct symbol *framefunc; + char fb_name[50]; + + b = block_for_pc (pc); + + if (!b) + error (_("No block found for address")); + + framefunc = block_linkage_function (b); + + if (!framefunc) + error (_("No function found for block")); + + func_get_frame_base_dwarf_block (framefunc, pc, + &datastart, &datalen); + + op_ptr = safe_read_sleb128 (op_ptr, op_end, &offset); + + /* Generate a unique-enough name, in case the frame base + is computed multiple times in this expression. */ + xsnprintf (fb_name, sizeof (fb_name), "__frame_base_%ld", + (long) (op_ptr - base)); + + do_compile_dwarf_expr_to_c (indent, stream, + "void *", fb_name, + sym, pc, + arch, registers_used, addr_size, + datastart, datastart + datalen, + NULL, per_cu); + + pushf (indent, stream, "%s + %s", fb_name, hex_string (offset)); + } + break; + + case DW_OP_dup: + pushf (indent, stream, "__gdb_stack[__gdb_tos]"); + break; + + case DW_OP_drop: + fprintfi_filtered (indent, stream, "--__gdb_tos;\n"); + break; + + case DW_OP_pick: + offset = *op_ptr++; + pushf (indent, stream, "__gdb_stack[__gdb_tos - %d]", offset); + break; + + case DW_OP_swap: + fprintfi_filtered (indent, stream, + "__gdb_tmp = __gdb_stack[__gdb_tos - 1];\n"); + fprintfi_filtered (indent, stream, + "__gdb_stack[__gdb_tos - 1] = " + "__gdb_stack[__gdb_tos];\n"); + fprintfi_filtered (indent, stream, ("__gdb_stack[__gdb_tos] = " + "__gdb_tmp;\n")); + break; + + case DW_OP_over: + pushf (indent, stream, "__gdb_stack[__gdb_tos - 1]"); + break; + + case DW_OP_rot: + fprintfi_filtered (indent, stream, ("__gdb_tmp = " + "__gdb_stack[__gdb_tos];\n")); + fprintfi_filtered (indent, stream, + "__gdb_stack[__gdb_tos] = " + "__gdb_stack[__gdb_tos - 1];\n"); + fprintfi_filtered (indent, stream, + "__gdb_stack[__gdb_tos - 1] = " + "__gdb_stack[__gdb_tos -2];\n"); + fprintfi_filtered (indent, stream, "__gdb_stack[__gdb_tos - 2] = " + "__gdb_tmp;\n"); + break; + + case DW_OP_deref: + case DW_OP_deref_size: + { + int size; + const char *mode; + + if (op == DW_OP_deref_size) + size = *op_ptr++; + else + size = addr_size; + + mode = c_get_mode_for_size (size); + if (mode == NULL) + error (_("Unsupported size %d in %s"), + size, get_DW_OP_name (op)); + + /* Cast to a pointer of the desired type, then + dereference. */ + fprintfi_filtered (indent, stream, + "__gdb_stack[__gdb_tos] = " + "*((__gdb_int_%s *) " + "__gdb_stack[__gdb_tos]);\n", + mode); + } + break; + + case DW_OP_abs: + unary (indent, stream, + "((" GCC_INTPTR ") __gdb_stack[__gdb_tos]) < 0 ? " + "-__gdb_stack[__gdb_tos] : __gdb_stack[__gdb_tos]"); + break; + + case DW_OP_neg: + unary (indent, stream, "-__gdb_stack[__gdb_tos]"); + break; + + case DW_OP_not: + unary (indent, stream, "~__gdb_stack[__gdb_tos]"); + break; + + case DW_OP_plus_uconst: + op_ptr = safe_read_uleb128 (op_ptr, op_end, ®); + unary (indent, stream, "__gdb_stack[__gdb_tos] + %s", + hex_string (reg)); + break; + + case DW_OP_div: + binary (indent, stream, ("((" GCC_INTPTR + ") __gdb_stack[__gdb_tos-1]) / ((" + GCC_INTPTR ") __gdb_stack[__gdb_tos])")); + break; + + case DW_OP_shra: + binary (indent, stream, + "((" GCC_INTPTR ") __gdb_stack[__gdb_tos-1]) >> " + "__gdb_stack[__gdb_tos]"); + break; + +#define BINARY(OP) \ + binary (indent, stream, ("__gdb_stack[__gdb_tos-1] " #OP \ + " __gdb_stack[__gdb_tos]")); \ + break + + case DW_OP_and: + BINARY (&); + case DW_OP_minus: + BINARY (-); + case DW_OP_mod: + BINARY (%); + case DW_OP_mul: + BINARY (*); + case DW_OP_or: + BINARY (|); + case DW_OP_plus: + BINARY (+); + case DW_OP_shl: + BINARY (<<); + case DW_OP_shr: + BINARY (>>); + case DW_OP_xor: + BINARY (^); +#undef BINARY + +#define COMPARE(OP) \ + binary (indent, stream, \ + "(((" GCC_INTPTR ") __gdb_stack[__gdb_tos-1]) " #OP \ + " ((" GCC_INTPTR \ + ") __gdb_stack[__gdb_tos]))"); \ + break + + case DW_OP_le: + COMPARE (<=); + case DW_OP_ge: + COMPARE (>=); + case DW_OP_eq: + COMPARE (==); + case DW_OP_lt: + COMPARE (<); + case DW_OP_gt: + COMPARE (>); + case DW_OP_ne: + COMPARE (!=); +#undef COMPARE + + case DW_OP_call_frame_cfa: + { + int regnum; + CORE_ADDR text_offset; + LONGEST off; + const gdb_byte *cfa_start, *cfa_end; + + if (dwarf2_fetch_cfa_info (arch, pc, per_cu, + ®num, &off, + &text_offset, &cfa_start, &cfa_end)) + { + /* Register. */ + pushf_register (indent, stream, registers_used, arch, regnum, + off); + } + else + { + /* Another expression. */ + char cfa_name[50]; + + /* Generate a unique-enough name, in case the CFA is + computed multiple times in this expression. */ + xsnprintf (cfa_name, sizeof (cfa_name), + "__cfa_%ld", (long) (op_ptr - base)); + + do_compile_dwarf_expr_to_c (indent, stream, + "void *", cfa_name, + sym, pc, arch, registers_used, + addr_size, + cfa_start, cfa_end, + &text_offset, per_cu); + pushf (indent, stream, cfa_name); + } + } + + break; + + case DW_OP_skip: + offset = extract_signed_integer (op_ptr, 2, byte_order); + op_ptr += 2; + fprintfi_filtered (indent, stream, "goto "); + print_label (stream, scope, op_ptr + offset - base); + fprintf_filtered (stream, ";\n"); + break; + + case DW_OP_bra: + offset = extract_signed_integer (op_ptr, 2, byte_order); + op_ptr += 2; + fprintfi_filtered (indent, stream, + "if ((( " GCC_INTPTR + ") __gdb_stack[__gdb_tos--]) != 0) goto "); + print_label (stream, scope, op_ptr + offset - base); + fprintf_filtered (stream, ";\n"); + break; + + case DW_OP_nop: + break; + + default: + error (_("unhandled DWARF op: %s"), get_DW_OP_name (op)); + } + } + + fprintfi_filtered (indent, stream, "%s = (%s) __gdb_stack[__gdb_tos];\n", + result_name, type_name); + fprintfi_filtered (indent - 2, stream, "}\n"); + + do_cleanups (cleanup); +} + +/* See compile.h. */ + +void +compile_dwarf_expr_to_c (struct ui_file *stream, const char *result_name, + struct symbol *sym, CORE_ADDR pc, + struct gdbarch *arch, unsigned char *registers_used, + unsigned int addr_size, + const gdb_byte *op_ptr, const gdb_byte *op_end, + struct dwarf2_per_cu_data *per_cu) +{ + do_compile_dwarf_expr_to_c (2, stream, "void *", result_name, sym, pc, + arch, registers_used, addr_size, op_ptr, op_end, + NULL, per_cu); +} + +/* See compile.h. */ + +void +compile_dwarf_bounds_to_c (struct ui_file *stream, + const char *result_name, + const struct dynamic_prop *prop, + struct symbol *sym, CORE_ADDR pc, + struct gdbarch *arch, unsigned char *registers_used, + unsigned int addr_size, + const gdb_byte *op_ptr, const gdb_byte *op_end, + struct dwarf2_per_cu_data *per_cu) +{ + do_compile_dwarf_expr_to_c (2, stream, "unsigned long ", result_name, + sym, pc, arch, registers_used, + addr_size, op_ptr, op_end, NULL, per_cu); +} diff --git a/gdb/compile/compile-object-load.c b/gdb/compile/compile-object-load.c new file mode 100644 index 0000000..eedc9fa --- /dev/null +++ b/gdb/compile/compile-object-load.c @@ -0,0 +1,588 @@ +/* Load module for 'compile' command. + + Copyright (C) 2014 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-object-load.h" +#include "compile-internal.h" +#include "command.h" +#include "objfiles.h" +#include "gdbcore.h" +#include "readline/tilde.h" +#include "bfdlink.h" +#include "gdbcmd.h" +#include "regcache.h" +#include "inferior.h" +#include "compile.h" +#include "arch-utils.h" + +/* Helper data for setup_sections. */ + +struct setup_sections_data +{ + /* Size of all recent sections with matching LAST_PROT. */ + CORE_ADDR last_size; + + /* First section matching LAST_PROT. */ + asection *last_section_first; + + /* Memory protection like the prot parameter of gdbarch_infcall_mmap. */ + unsigned last_prot; + + /* Maximum of alignments of all sections matching LAST_PROT. + This value is always at least 1. This value is always a power of 2. */ + CORE_ADDR last_max_alignment; +}; + +/* Place all ABFD sections next to each other obeying all constraints. */ + +static void +setup_sections (bfd *abfd, asection *sect, void *data_voidp) +{ + struct setup_sections_data *data = data_voidp; + CORE_ADDR alignment; + unsigned prot; + + if (sect != NULL) + { + /* It is required by later bfd_get_relocated_section_contents. */ + if (sect->output_section == NULL) + sect->output_section = sect; + + if ((bfd_get_section_flags (abfd, sect) & SEC_ALLOC) == 0) + return; + + // Make the memory always readable. + prot = GDB_MMAP_PROT_READ; + if ((bfd_get_section_flags (abfd, sect) & SEC_READONLY) == 0) + prot |= GDB_MMAP_PROT_WRITE; + if ((bfd_get_section_flags (abfd, sect) & SEC_CODE) != 0) + prot |= GDB_MMAP_PROT_EXEC; + + if (compile_debug) + fprintf_unfiltered (gdb_stdout, + "module \"%s\" section \"%s\" size %s prot %u\n", + bfd_get_filename (abfd), + bfd_get_section_name (abfd, sect), + paddress (target_gdbarch (), + bfd_get_section_size (sect)), + prot); + } + else + prot = -1; + + if (sect == NULL + || (data->last_prot != prot && bfd_get_section_size (sect) != 0)) + { + CORE_ADDR addr; + asection *sect_iter; + + if (data->last_size != 0) + { + addr = gdbarch_infcall_mmap (target_gdbarch (), data->last_size, + data->last_prot); + if (compile_debug) + fprintf_unfiltered (gdb_stdout, + "allocated %s bytes at %s prot %u\n", + paddress (target_gdbarch (), data->last_size), + paddress (target_gdbarch (), addr), + data->last_prot); + } + else + addr = 0; + + if ((addr & (data->last_max_alignment - 1)) != 0) + error (_("Inferior compiled module address %s " + "is not aligned to BFD required %s."), + paddress (target_gdbarch (), addr), + paddress (target_gdbarch (), data->last_max_alignment)); + + for (sect_iter = data->last_section_first; sect_iter != sect; + sect_iter = sect_iter->next) + if ((bfd_get_section_flags (abfd, sect_iter) & SEC_ALLOC) != 0) + bfd_set_section_vma (abfd, sect_iter, + addr + bfd_get_section_vma (abfd, sect_iter)); + + data->last_size = 0; + data->last_section_first = sect; + data->last_prot = prot; + data->last_max_alignment = 1; + } + + if (sect == NULL) + return; + + alignment = ((CORE_ADDR) 1) << bfd_get_section_alignment (abfd, sect); + data->last_max_alignment = max (data->last_max_alignment, alignment); + + data->last_size = (data->last_size + alignment - 1) & -alignment; + + bfd_set_section_vma (abfd, sect, data->last_size); + + data->last_size += bfd_get_section_size (sect); + data->last_size = (data->last_size + alignment - 1) & -alignment; +} + +/* Helper for link_callbacks callbacks vector. */ + +static bfd_boolean +link_callbacks_multiple_definition (struct bfd_link_info *link_info, + struct bfd_link_hash_entry *h, bfd *nbfd, + asection *nsec, bfd_vma nval) +{ + bfd *abfd = link_info->input_bfds; + + if (link_info->allow_multiple_definition) + return TRUE; + warning (_("Compiled module \"%s\": multiple symbol definitions: %s\n"), + bfd_get_filename (abfd), h->root.string); + return FALSE; +} + +/* Helper for link_callbacks callbacks vector. */ + +static bfd_boolean +link_callbacks_warning (struct bfd_link_info *link_info, const char *xwarning, + const char *symbol, bfd *abfd, asection *section, + bfd_vma address) +{ + warning (_("Compiled module \"%s\" section \"%s\": warning: %s\n"), + bfd_get_filename (abfd), bfd_get_section_name (abfd, section), + xwarning); + /* Maybe permit running as a module? */ + return FALSE; +} + +/* Helper for link_callbacks callbacks vector. */ + +static bfd_boolean +link_callbacks_undefined_symbol (struct bfd_link_info *link_info, + const char *name, bfd *abfd, asection *section, + bfd_vma address, bfd_boolean is_fatal) +{ + warning (_("Cannot resolve relocation to \"%s\" " + "from compiled module \"%s\" section \"%s\"."), + name, bfd_get_filename (abfd), bfd_get_section_name (abfd, section)); + return FALSE; +} + +/* Helper for link_callbacks callbacks vector. */ + +static bfd_boolean +link_callbacks_reloc_overflow (struct bfd_link_info *link_info, + struct bfd_link_hash_entry *entry, + const char *name, const char *reloc_name, + bfd_vma addend, bfd *abfd, asection *section, + bfd_vma address) +{ + /* TRUE is required for intra-module relocations. */ + return TRUE; +} + +/* Helper for link_callbacks callbacks vector. */ + +static bfd_boolean +link_callbacks_reloc_dangerous (struct bfd_link_info *link_info, + const char *message, bfd *abfd, + asection *section, bfd_vma address) +{ + warning (_("Compiled module \"%s\" section \"%s\": dangerous " + "relocation: %s\n"), + bfd_get_filename (abfd), bfd_get_section_name (abfd, section), + message); + return FALSE; +} + +/* Helper for link_callbacks callbacks vector. */ + +static bfd_boolean +link_callbacks_unattached_reloc (struct bfd_link_info *link_info, + const char *name, bfd *abfd, asection *section, + bfd_vma address) +{ + warning (_("Compiled module \"%s\" section \"%s\": unattached " + "relocation: %s\n"), + bfd_get_filename (abfd), bfd_get_section_name (abfd, section), + name); + return FALSE; +} + +/* Helper for link_callbacks callbacks vector. */ + +static void +link_callbacks_einfo (const char *fmt, ...) +{ + struct cleanup *cleanups; + va_list ap; + char *str; + + va_start (ap, fmt); + str = xstrvprintf (fmt, ap); + va_end (ap); + cleanups = make_cleanup (xfree, str); + + warning (_("Compile module: warning: %s\n"), str); + + do_cleanups (cleanups); +} + +/* Helper for bfd_get_relocated_section_contents. + Only these symbols are set by bfd_simple_get_relocated_section_contents + but bfd/ seems to use even the NULL ones without checking them first. */ + +static const struct bfd_link_callbacks link_callbacks = +{ + NULL, /* add_archive_element */ + link_callbacks_multiple_definition, /* multiple_definition */ + NULL, /* multiple_common */ + NULL, /* add_to_set */ + NULL, /* constructor */ + link_callbacks_warning, /* warning */ + link_callbacks_undefined_symbol, /* undefined_symbol */ + link_callbacks_reloc_overflow, /* reloc_overflow */ + link_callbacks_reloc_dangerous, /* reloc_dangerous */ + link_callbacks_unattached_reloc, /* unattached_reloc */ + NULL, /* notice */ + link_callbacks_einfo, /* einfo */ + NULL, /* info */ + NULL, /* minfo */ + NULL, /* override_segment_assignment */ +}; + +struct link_hash_table_cleanup_data +{ + bfd *abfd; + bfd *link_next; +}; + +/* Cleanup callback for struct bfd_link_info. */ + +static void +link_hash_table_free (void *d) +{ + struct link_hash_table_cleanup_data *data = d; + + if (data->abfd->is_linker_output) + (*data->abfd->link.hash->hash_table_free) (data->abfd); + data->abfd->link.next = data->link_next; +} + +/* Relocate and store into inferior memory each section SECT of ABFD. */ + +static void +copy_sections (bfd *abfd, asection *sect, void *data) +{ + asymbol **symbol_table = data; + bfd_byte *sect_data, *sect_data_got; + struct cleanup *cleanups; + struct bfd_link_info link_info; + struct bfd_link_order link_order; + CORE_ADDR inferior_addr; + struct link_hash_table_cleanup_data cleanup_data; + + if ((bfd_get_section_flags (abfd, sect) & (SEC_ALLOC | SEC_LOAD)) + != (SEC_ALLOC | SEC_LOAD)) + return; + + if (bfd_get_section_size (sect) == 0) + return; + + /* Mostly a copy of bfd_simple_get_relocated_section_contents which GDB + cannot use as it does not report relocations to undefined symbols. */ + memset (&link_info, 0, sizeof (link_info)); + link_info.output_bfd = abfd; + link_info.input_bfds = abfd; + link_info.input_bfds_tail = &abfd->link.next; + + cleanup_data.abfd = abfd; + cleanup_data.link_next = abfd->link.next; + + abfd->link.next = NULL; + link_info.hash = bfd_link_hash_table_create (abfd); + + cleanups = make_cleanup (link_hash_table_free, &cleanup_data); + link_info.callbacks = &link_callbacks; + + memset (&link_order, 0, sizeof (link_order)); + link_order.next = NULL; + link_order.type = bfd_indirect_link_order; + link_order.offset = 0; + link_order.size = bfd_get_section_size (sect); + link_order.u.indirect.section = sect; + + sect_data = xmalloc (bfd_get_section_size (sect)); + make_cleanup (xfree, sect_data); + + sect_data_got = bfd_get_relocated_section_contents (abfd, &link_info, + &link_order, sect_data, + FALSE, symbol_table); + + if (sect_data_got == NULL) + error (_("Cannot map compiled module \"%s\" section \"%s\": %s"), + bfd_get_filename (abfd), bfd_get_section_name (abfd, sect), + bfd_errmsg (bfd_get_error ())); + gdb_assert (sect_data_got == sect_data); + + inferior_addr = bfd_get_section_vma (abfd, sect); + if (0 != target_write_memory (inferior_addr, sect_data, + bfd_get_section_size (sect))) + error (_("Cannot write compiled module \"%s\" section \"%s\" " + "to inferior memory range %s-%s."), + bfd_get_filename (abfd), bfd_get_section_name (abfd, sect), + paddress (target_gdbarch (), inferior_addr), + paddress (target_gdbarch (), + inferior_addr + bfd_get_section_size (sect))); + + do_cleanups (cleanups); +} + +/* Fetch the type of first parameter of GCC_FE_WRAPPER_FUNCTION. + Return NULL if GCC_FE_WRAPPER_FUNCTION has no parameters. + Throw an error otherwise. */ + +static struct type * +get_regs_type (struct objfile *objfile) +{ + struct symbol *func_sym; + struct type *func_type, *regsp_type, *regs_type; + + func_sym = lookup_global_symbol_from_objfile (objfile, + GCC_FE_WRAPPER_FUNCTION, + VAR_DOMAIN); + if (func_sym == NULL) + error (_("Cannot find function \"%s\" in compiled module \"%s\"."), + GCC_FE_WRAPPER_FUNCTION, objfile_name (objfile)); + + func_type = SYMBOL_TYPE (func_sym); + if (TYPE_CODE (func_type) != TYPE_CODE_FUNC) + error (_("Invalid type code %d of function \"%s\" in compiled " + "module \"%s\"."), + TYPE_CODE (func_type), GCC_FE_WRAPPER_FUNCTION, + objfile_name (objfile)); + + /* No register parameter present. */ + if (TYPE_NFIELDS (func_type) == 0) + return NULL; + + if (TYPE_NFIELDS (func_type) != 1) + error (_("Invalid %d parameters of function \"%s\" in compiled " + "module \"%s\"."), + TYPE_NFIELDS (func_type), GCC_FE_WRAPPER_FUNCTION, + objfile_name (objfile)); + + regsp_type = check_typedef (TYPE_FIELD_TYPE (func_type, 0)); + if (TYPE_CODE (regsp_type) != TYPE_CODE_PTR) + error (_("Invalid type code %d of first parameter of function \"%s\" " + "in compiled module \"%s\"."), + TYPE_CODE (regsp_type), GCC_FE_WRAPPER_FUNCTION, + objfile_name (objfile)); + + regs_type = check_typedef (TYPE_TARGET_TYPE (regsp_type)); + if (TYPE_CODE (regs_type) != TYPE_CODE_STRUCT) + error (_("Invalid type code %d of dereferenced first parameter " + "of function \"%s\" in compiled module \"%s\"."), + TYPE_CODE (regs_type), GCC_FE_WRAPPER_FUNCTION, + objfile_name (objfile)); + + return regs_type; +} + +/* Store all inferior registers required by REGS_TYPE to inferior memory + starting at inferior address REGS_BASE. */ + +static void +store_regs (struct type *regs_type, CORE_ADDR regs_base) +{ + struct gdbarch *gdbarch = target_gdbarch (); + struct regcache *regcache = get_thread_regcache (inferior_ptid); + int fieldno; + + for (fieldno = 0; fieldno < TYPE_NFIELDS (regs_type); fieldno++) + { + const char *reg_name = TYPE_FIELD_NAME (regs_type, fieldno); + ULONGEST reg_bitpos = TYPE_FIELD_BITPOS (regs_type, fieldno); + ULONGEST reg_bitsize = TYPE_FIELD_BITSIZE (regs_type, fieldno); + ULONGEST reg_offset; + struct type *reg_type = check_typedef (TYPE_FIELD_TYPE (regs_type, + fieldno)); + ULONGEST reg_size = TYPE_LENGTH (reg_type); + int regnum; + struct value *regval; + CORE_ADDR inferior_addr; + + if (strcmp (reg_name, COMPILE_I_SIMPLE_REGISTER_DUMMY) == 0) + continue; + + if ((reg_bitpos % 8) != 0 || reg_bitsize != 0) + error (_("Invalid register \"%s\" position %s bits or size %s bits"), + reg_name, pulongest (reg_bitpos), pulongest (reg_bitsize)); + reg_offset = reg_bitpos / 8; + + if (TYPE_CODE (reg_type) != TYPE_CODE_INT + && TYPE_CODE (reg_type) != TYPE_CODE_PTR) + error (_("Invalid register \"%s\" type code %d"), reg_name, + TYPE_CODE (reg_type)); + + regnum = compile_register_name_demangle (gdbarch, reg_name); + + regval = value_from_register (reg_type, regnum, get_current_frame ()); + if (value_optimized_out (regval)) + error (_("Register \"%s\" is optimized out."), reg_name); + if (!value_entirely_available (regval)) + error (_("Register \"%s\" is not available."), reg_name); + + inferior_addr = regs_base + reg_offset; + if (0 != target_write_memory (inferior_addr, value_contents (regval), + reg_size)) + error (_("Cannot write register \"%s\" to inferior memory at %s."), + reg_name, paddress (gdbarch, inferior_addr)); + } +} + +/* Load OBJECT_FILE into inferior memory. Throw an error otherwise. + 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. */ + +struct compile_module * +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, func_addr, regs_addr; + struct bound_minimal_symbol bmsym; + long storage_needed; + asymbol **symbol_table, **symp; + long number_of_symbols, missing_symbols; + 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; + char *filename, **matching; + struct objfile *objfile; + + filename = tilde_expand (object_file); + cleanups = make_cleanup (xfree, filename); + + abfd = gdb_bfd_open (filename, gnutarget, -1); + if (abfd == NULL) + error (_("\"%s\": could not open as compiled module: %s"), + filename, bfd_errmsg (bfd_get_error ())); + make_cleanup_bfd_unref (abfd); + + if (!bfd_check_format_matches (abfd, bfd_object, &matching)) + error (_("\"%s\": not in loadable format: %s"), + filename, gdb_bfd_errmsg (bfd_get_error (), matching)); + + if ((bfd_get_file_flags (abfd) & (EXEC_P | DYNAMIC)) != 0) + error (_("\"%s\": not in object format."), filename); + + setup_sections_data.last_size = 0; + setup_sections_data.last_section_first = abfd->sections; + setup_sections_data.last_prot = -1; + setup_sections_data.last_max_alignment = 1; + bfd_map_over_sections (abfd, setup_sections, &setup_sections_data); + setup_sections (abfd, NULL, &setup_sections_data); + + storage_needed = bfd_get_symtab_upper_bound (abfd); + if (storage_needed < 0) + error (_("Cannot read symbols of compiled module \"%s\": %s"), + filename, bfd_errmsg (bfd_get_error ())); + + /* SYMFILE_VERBOSE is not passed even if FROM_TTY, user is not interested in + "Reading symbols from ..." message for automatically generated file. */ + objfile = symbol_file_add_from_bfd (abfd, filename, 0, NULL, 0, NULL); + cleanups_free_objfile = make_cleanup_free_objfile (objfile); + + bmsym = lookup_minimal_symbol_text (GCC_FE_WRAPPER_FUNCTION, objfile); + if (bmsym.minsym == NULL || MSYMBOL_TYPE (bmsym.minsym) == mst_file_text) + error (_("Could not find symbol \"%s\" of compiled module \"%s\"."), + GCC_FE_WRAPPER_FUNCTION, filename); + func_addr = BMSYMBOL_VALUE_ADDRESS (bmsym); + + /* The memory may be later needed + by bfd_generic_get_relocated_section_contents + called from default_symfile_relocate. */ + symbol_table = obstack_alloc (&objfile->objfile_obstack, storage_needed); + number_of_symbols = bfd_canonicalize_symtab (abfd, symbol_table); + if (number_of_symbols < 0) + error (_("Cannot parse symbols of compiled module \"%s\": %s"), + filename, bfd_errmsg (bfd_get_error ())); + + missing_symbols = 0; + for (symp = symbol_table; symp < symbol_table + number_of_symbols; symp++) + { + asymbol *sym = *symp; + + if (sym->flags != 0) + continue; + if (compile_debug) + fprintf_unfiltered (gdb_stdout, + "lookup undefined ELF symbol \"%s\"\n", + sym->name); + sym->flags = BSF_GLOBAL; + sym->section = bfd_abs_section_ptr; + if (strcmp (sym->name, "_GLOBAL_OFFSET_TABLE_") == 0) + { + sym->value = 0; + continue; + } + bmsym = lookup_minimal_symbol (sym->name, NULL, NULL); + switch (bmsym.minsym == NULL + ? mst_unknown : MSYMBOL_TYPE (bmsym.minsym)) + { + case mst_text: + sym->value = BMSYMBOL_VALUE_ADDRESS (bmsym); + break; + default: + warning (_("Could not find symbol \"%s\" " + "for compiled module \"%s\"."), + sym->name, filename); + missing_symbols++; + } + } + if (missing_symbols) + error (_("%ld symbols were missing, cannot continue."), missing_symbols); + + bfd_map_over_sections (abfd, copy_sections, symbol_table); + + regs_type = get_regs_type (objfile); + if (regs_type == NULL) + regs_addr = 0; + else + { + /* Use read-only non-executable memory protection. */ + regs_addr = gdbarch_infcall_mmap (target_gdbarch (), + TYPE_LENGTH (regs_type), + GDB_MMAP_PROT_READ); + gdb_assert (regs_addr != 0); + store_regs (regs_type, regs_addr); + } + + discard_cleanups (cleanups_free_objfile); + do_cleanups (cleanups); + + retval = xmalloc (sizeof (*retval)); + retval->objfile = objfile; + retval->source_file = xstrdup (source_file); + retval->func_addr = func_addr; + retval->regs_addr = regs_addr; + return retval; +} diff --git a/gdb/compile/compile-object-load.h b/gdb/compile/compile-object-load.h new file mode 100644 index 0000000..850111e --- /dev/null +++ b/gdb/compile/compile-object-load.h @@ -0,0 +1,39 @@ +/* Header file to load module for 'compile' command. + Copyright (C) 2014 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_OBJECT_LOAD_H +#define GDB_COMPILE_OBJECT_LOAD_H + +struct compile_module +{ + /* objfile for the compiled module. */ + struct objfile *objfile; + + /* .c file OBJFILE was built from. It needs to be xfree-d. */ + char *source_file; + + /* Inferior function address. */ + CORE_ADDR func_addr; + + /* Inferior registers address or NULL if the inferior function does not + require any. */ + CORE_ADDR regs_addr; +}; + +extern struct compile_module *compile_object_load (const char *object_file, + const char *source_file); + +#endif /* GDB_COMPILE_OBJECT_LOAD_H */ diff --git a/gdb/compile/compile-object-run.c b/gdb/compile/compile-object-run.c new file mode 100644 index 0000000..b7c4c4d --- /dev/null +++ b/gdb/compile/compile-object-run.c @@ -0,0 +1,138 @@ +/* Call module for 'compile' command. + + Copyright (C) 2014 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-object-run.h" +#include "value.h" +#include "infcall.h" +#include "objfiles.h" +#include "compile-internal.h" +#include "dummy-frame.h" + +/* Helper for do_module_cleanup. */ + +struct do_module_cleanup +{ + /* Boolean to set true upon a call of do_module_cleanup. + The pointer may be NULL. */ + int *executedp; + + /* .c file OBJFILE was built from. It needs to be xfree-d. */ + char *source_file; + + /* objfile_name of our objfile. */ + char objfile_name_string[1]; +}; + +/* Cleanup everything after the inferior function dummy frame gets + discarded. */ + +static dummy_frame_dtor_ftype do_module_cleanup; +static void +do_module_cleanup (void *arg) +{ + struct do_module_cleanup *data = arg; + struct objfile *objfile; + + if (data->executedp != NULL) + *data->executedp = 1; + + ALL_OBJFILES (objfile) + if ((objfile->flags & OBJF_USERLOADED) == 0 + && (strcmp (objfile_name (objfile), data->objfile_name_string) == 0)) + { + free_objfile (objfile); + + /* It may be a bit too pervasive in this dummy_frame dtor callback. */ + clear_symtab_users (0); + + break; + } + + /* Delete the .c file. */ + unlink (data->source_file); + xfree (data->source_file); + + /* Delete the .o file. */ + unlink (data->objfile_name_string); + xfree (data); +} + +/* Perform inferior call of MODULE. This function may throw an error. + This function may leave files referenced by MODULE on disk until + the inferior call dummy frame is discarded. This function may throw errors. + Thrown errors and left MODULE files are unrelated events. Caller must no + longer touch MODULE's memory after this function has been called. */ + +void +compile_object_run (struct compile_module *module) +{ + struct value *func_val; + struct frame_id dummy_id; + struct cleanup *cleanups; + struct do_module_cleanup *data; + volatile struct gdb_exception ex; + const char *objfile_name_s = objfile_name (module->objfile); + int dtor_found, executed = 0; + CORE_ADDR func_addr = module->func_addr; + CORE_ADDR regs_addr = module->regs_addr; + + data = xmalloc (sizeof (*data) + strlen (objfile_name_s)); + data->executedp = &executed; + data->source_file = xstrdup (module->source_file); + strcpy (data->objfile_name_string, objfile_name_s); + + xfree (module->source_file); + xfree (module); + + TRY_CATCH (ex, RETURN_MASK_ERROR) + { + func_val = value_from_pointer + (builtin_type (target_gdbarch ())->builtin_func_ptr, + func_addr); + + if (regs_addr == 0) + call_function_by_hand_dummy (func_val, 0, NULL, + do_module_cleanup, data); + else + { + struct value *arg_val; + + arg_val = value_from_pointer + (builtin_type (target_gdbarch ())->builtin_func_ptr, + regs_addr); + call_function_by_hand_dummy (func_val, 1, &arg_val, + do_module_cleanup, data); + } + } + dtor_found = find_dummy_frame_dtor (do_module_cleanup, data); + if (!executed) + data->executedp = NULL; + if (ex.reason >= 0) + gdb_assert (!dtor_found && executed); + else + { + /* In the case od DTOR_FOUND or in the case of EXECUTED nothing + needs to be done. */ + gdb_assert (!(dtor_found && executed)); + if (!dtor_found && !executed) + do_module_cleanup (data); + throw_exception (ex); + } +} diff --git a/gdb/compile/compile-object-run.h b/gdb/compile/compile-object-run.h new file mode 100644 index 0000000..71ba077 --- /dev/null +++ b/gdb/compile/compile-object-run.h @@ -0,0 +1,24 @@ +/* Header file to call module for 'compile' command. + Copyright (C) 2014 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_OBJECT_RUN_H +#define GDB_COMPILE_OBJECT_RUN_H + +#include "compile-object-load.h" + +extern void compile_object_run (struct compile_module *module); + +#endif /* GDB_COMPILE_OBJECT_RUN_H */ diff --git a/gdb/compile/compile.c b/gdb/compile/compile.c new file mode 100644 index 0000000..6d3d16e --- /dev/null +++ b/gdb/compile/compile.c @@ -0,0 +1,651 @@ +/* General Compile and inject code + + Copyright (C) 2014 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 "interps.h" +#include "ui-out.h" +#include "command.h" +#include "cli/cli-script.h" +#include "cli/cli-utils.h" +#include "completer.h" +#include "gdbcmd.h" +#include "compile.h" +#include "compile-internal.h" +#include "compile-object-load.h" +#include "compile-object-run.h" +#include "language.h" +#include "frame.h" +#include "source.h" +#include "block.h" +#include "arch-utils.h" +#include "filestuff.h" +#include "target.h" +#include "osabi.h" + + + +/* Initial filename for temporary files. */ + +#define TMP_PREFIX "/tmp/gdbobj-" + +/* Hold "compile" commands. */ + +static struct cmd_list_element *compile_command_list; + +/* Debug flag for "compile" commands. */ + +int compile_debug; + +/* Implement "show debug compile". */ + +static void +show_compile_debug (struct ui_file *file, int from_tty, + struct cmd_list_element *c, const char *value) +{ + fprintf_filtered (file, _("Compile debugging is %s.\n"), value); +} + + + +/* Check *ARG for a "-raw" or "-r" argument. Return 0 if not seen. + Return 1 if seen and update *ARG. */ + +static int +check_raw_argument (char **arg) +{ + *arg = skip_spaces (*arg); + + if (arg != NULL + && (check_for_argument (arg, "-raw", sizeof ("-raw") - 1) + || check_for_argument (arg, "-r", sizeof ("-r") - 1))) + return 1; + return 0; +} + +/* Handle the input from the 'compile file' command. The "compile + file" command is used to evaluate an expression contained in a file + that may contain calls to the GCC compiler. */ + +static void +compile_file_command (char *arg, int from_tty) +{ + enum compile_i_scope_types scope = COMPILE_I_SIMPLE_SCOPE; + char *buffer; + struct cleanup *cleanup; + + cleanup = make_cleanup_restore_integer (&interpreter_async); + interpreter_async = 0; + + /* Check the user did not just <enter> after command. */ + if (arg == NULL) + error (_("You must provide a filename for this command.")); + + /* Check if a raw (-r|-raw) argument is provided. */ + if (arg != NULL && check_raw_argument (&arg)) + { + scope = COMPILE_I_RAW_SCOPE; + arg = skip_spaces (arg); + } + + /* After processing arguments, check there is a filename at the end + of the command. */ + if (arg[0] == '\0') + error (_("You must provide a filename with the raw option set.")); + + if (arg[0] == '-') + error (_("Unknown argument specified.")); + + arg = skip_spaces (arg); + arg = gdb_abspath (arg); + make_cleanup (xfree, arg); + buffer = xstrprintf ("#include \"%s\"\n", arg); + make_cleanup (xfree, buffer); + eval_compile_command (NULL, buffer, scope); + do_cleanups (cleanup); +} + +/* Handle the input from the 'compile code' command. The + "compile code" command is used to evaluate 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_code_command (char *arg, int from_tty) +{ + struct cleanup *cleanup; + enum compile_i_scope_types scope = COMPILE_I_SIMPLE_SCOPE; + + cleanup = make_cleanup_restore_integer (&interpreter_async); + interpreter_async = 0; + + if (arg != NULL && check_raw_argument (&arg)) + { + scope = COMPILE_I_RAW_SCOPE; + arg = skip_spaces (arg); + } + + arg = skip_spaces (arg); + + if (arg != NULL && !check_for_argument (&arg, "--", sizeof ("--") - 1)) + { + if (arg[0] == '-') + error (_("Unknown argument specified.")); + } + + if (arg && *arg) + eval_compile_command (NULL, arg, scope); + else + { + struct command_line *l = get_command_line (compile_control, ""); + + make_cleanup_free_command_lines (&l); + l->control_u.compile.scope = scope; + execute_control_command_untraced (l); + } + + do_cleanups (cleanup); +} + +/* A cleanup function to remove a directory and all its contents. */ + +static void +do_rmdir (void *arg) +{ + const char *dir = arg; + char *zap; + + gdb_assert (strncmp (dir, TMP_PREFIX, strlen (TMP_PREFIX)) == 0); + zap = concat ("rm -rf ", dir, (char *) NULL); + system (zap); +} + +/* Return the name of the temporary directory to use for .o files, and + arrange for the directory to be removed at shutdown. */ + +static const char * +get_compile_file_tempdir (void) +{ + static char *tempdir_name; + +#define TEMPLATE TMP_PREFIX "XXXXXX" + char tname[sizeof (TEMPLATE)]; + + if (tempdir_name != NULL) + return tempdir_name; + + strcpy (tname, TEMPLATE); +#undef TEMPLATE + tempdir_name = mkdtemp (tname); + if (tempdir_name == NULL) + perror_with_name (_("Could not make temporary directory")); + + tempdir_name = xstrdup (tempdir_name); + make_final_cleanup (do_rmdir, tempdir_name); + return tempdir_name; +} + +/* Compute the names of source and object files to use. The names are + allocated by malloc and should be freed by the caller. */ + +static void +get_new_file_names (char **source_file, char **object_file) +{ + static int seq; + const char *dir = get_compile_file_tempdir (); + + ++seq; + *source_file = xstrprintf ("%s%sout%d.c", dir, SLASH_STRING, seq); + *object_file = xstrprintf ("%s%sout%d.o", dir, SLASH_STRING, seq); +} + +/* Get the block and PC at which to evaluate an expression. */ + +static const struct block * +get_expr_block_and_pc (CORE_ADDR *pc) +{ + const struct block *block = get_selected_block (pc); + + if (block == NULL) + { + struct symtab_and_line cursal = get_current_source_symtab_and_line (); + + if (cursal.symtab) + block = BLOCKVECTOR_BLOCK (SYMTAB_BLOCKVECTOR (cursal.symtab), + STATIC_BLOCK); + if (block != NULL) + *pc = BLOCK_START (block); + } + else + *pc = BLOCK_START (block); + + return block; +} + +/* Call gdb_buildargv, set its result for S into *ARGVP but calculate also the + number of parsed arguments into *ARGCP. If gdb_buildargv has returned NULL + then *ARGCP is set to zero. */ + +static void +build_argc_argv (const char *s, int *argcp, char ***argvp) +{ + *argvp = gdb_buildargv (s); + *argcp = countargv (*argvp); +} + +/* String for 'set compile-args' and 'show compile-args'. */ +static char *compile_args; + +/* Parsed form of COMPILE_ARGS. COMPILE_ARGS_ARGV is NULL terminated. */ +static int compile_args_argc; +static char **compile_args_argv; + +/* Implement 'set compile-args'. */ + +static void +set_compile_args (char *args, int from_tty, struct cmd_list_element *c) +{ + freeargv (compile_args_argv); + build_argc_argv (compile_args, &compile_args_argc, &compile_args_argv); +} + +/* Implement 'show compile-args'. */ + +static void +show_compile_args (struct ui_file *file, int from_tty, + struct cmd_list_element *c, const char *value) +{ + fprintf_filtered (file, _("Compile command command-line arguments " + "are \"%s\".\n"), + value); +} + +/* Append ARGC and ARGV (as parsed by build_argc_argv) to *ARGCP and *ARGVP. + ARGCP+ARGVP can be zero+NULL and also ARGC+ARGV can be zero+NULL. */ + +static void +append_args (int *argcp, char ***argvp, int argc, char **argv) +{ + int argi; + + *argvp = xrealloc (*argvp, (*argcp + argc + 1) * sizeof (**argvp)); + + for (argi = 0; argi < argc; argi++) + (*argvp)[(*argcp)++] = xstrdup (argv[argi]); + (*argvp)[(*argcp)] = NULL; +} + +/* Return DW_AT_producer parsed for get_selected_frame () (if any). + Return NULL otherwise. + + GCC already filters its command-line arguments only for the suitable ones to + put into DW_AT_producer - see GCC function gen_producer_string. */ + +static const char * +get_selected_pc_producer_options (void) +{ + CORE_ADDR pc = get_frame_pc (get_selected_frame (NULL)); + struct compunit_symtab *symtab = find_pc_compunit_symtab (pc); + const char *cs; + + if (symtab == NULL || symtab->producer == NULL + || strncmp (symtab->producer, "GNU ", strlen ("GNU ")) != 0) + return NULL; + + cs = symtab->producer; + while (*cs != 0 && *cs != '-') + cs = skip_spaces_const (skip_to_space_const (cs)); + if (*cs != '-') + return NULL; + return cs; +} + +/* Produce final vector of GCC compilation options. First element is target + size ("-m64", "-m32" etc.), optionally followed by DW_AT_producer options + and then compile-args string GDB variable. */ + +static void +get_args (const struct compile_instance *compiler, struct gdbarch *gdbarch, + int *argcp, char ***argvp) +{ + const char *cs_producer_options; + int argc_compiler; + char **argv_compiler; + + build_argc_argv (gdbarch_gcc_target_options (gdbarch), + argcp, argvp); + + cs_producer_options = get_selected_pc_producer_options (); + if (cs_producer_options != NULL) + { + int argc_producer; + char **argv_producer; + + build_argc_argv (cs_producer_options, &argc_producer, &argv_producer); + append_args (argcp, argvp, argc_producer, argv_producer); + freeargv (argv_producer); + } + + build_argc_argv (compiler->gcc_target_options, + &argc_compiler, &argv_compiler); + append_args (argcp, argvp, argc_compiler, argv_compiler); + freeargv (argv_compiler); + + append_args (argcp, argvp, compile_args_argc, compile_args_argv); +} + +/* A cleanup function to destroy a gdb_gcc_instance. */ + +static void +cleanup_compile_instance (void *arg) +{ + struct compile_instance *inst = arg; + + inst->destroy (inst); +} + +/* A cleanup function to unlink a file. */ + +static void +cleanup_unlink_file (void *arg) +{ + const char *filename = arg; + + unlink (filename); +} + +/* A helper function suitable for use as the "print_callback" in the + compiler object. */ + +static void +print_callback (void *ignore, const char *message) +{ + fputs_filtered (message, gdb_stderr); +} + +/* Process the compilation request. On success it returns the object + file name and *SOURCE_FILEP is set to source file name. On an + error condition, error () is called. The caller is responsible for + freeing both strings. */ + +static char * +compile_to_object (struct command_line *cmd, char *cmd_string, + enum compile_i_scope_types scope, + char **source_filep) +{ + char *code; + char *source_file, *object_file; + struct compile_instance *compiler; + struct cleanup *cleanup, *inner_cleanup; + const struct block *expr_block; + CORE_ADDR trash_pc, expr_pc; + int argc; + char **argv; + int ok; + FILE *src; + struct gdbarch *gdbarch = get_current_arch (); + const char *os_rx; + const char *arch_rx; + char *triplet_rx; + char *error_message; + + if (!target_has_execution) + error (_("The program must be running for the compile command to "\ + "work.")); + + expr_block = get_expr_block_and_pc (&trash_pc); + expr_pc = get_frame_address_in_block (get_selected_frame (NULL)); + + /* Set up instance and context for the compiler. */ + if (current_language->la_get_compile_instance == NULL) + error (_("No compiler support for this language.")); + compiler = current_language->la_get_compile_instance (); + cleanup = make_cleanup (cleanup_compile_instance, compiler); + + compiler->fe->ops->set_print_callback (compiler->fe, print_callback, NULL); + + compiler->scope = scope; + compiler->block = expr_block; + + /* From the provided expression, build a scope to pass to the + compiler. */ + if (cmd != NULL) + { + struct ui_file *stream = mem_fileopen (); + struct command_line *iter; + + make_cleanup_ui_file_delete (stream); + for (iter = cmd->body_list[0]; iter; iter = iter->next) + { + fputs_unfiltered (iter->line, stream); + fputs_unfiltered ("\n", stream); + } + + code = ui_file_xstrdup (stream, NULL); + make_cleanup (xfree, code); + } + else if (cmd_string != NULL) + code = cmd_string; + else + error (_("Neither a simple expression, or a multi-line specified.")); + + code = current_language->la_compute_program (compiler, code, gdbarch, + expr_block, expr_pc); + make_cleanup (xfree, code); + if (compile_debug) + fprintf_unfiltered (gdb_stdout, "debug output:\n\n%s", code); + + os_rx = osabi_triplet_regexp (gdbarch_osabi (gdbarch)); + arch_rx = gdbarch_gnu_triplet_regexp (gdbarch); + triplet_rx = concat (arch_rx, "-[^-]*-", os_rx, (char *) NULL); + make_cleanup (xfree, triplet_rx); + + /* Set compiler command-line arguments. */ + get_args (compiler, gdbarch, &argc, &argv); + make_cleanup_freeargv (argv); + + error_message = compiler->fe->ops->set_arguments (compiler->fe, triplet_rx, + argc, argv); + if (error_message != NULL) + { + make_cleanup (xfree, error_message); + error ("%s", error_message); + } + + if (compile_debug) + { + int argi; + + fprintf_unfiltered (gdb_stdout, "Passing %d compiler options:\n", argc); + for (argi = 0; argi < argc; argi++) + fprintf_unfiltered (gdb_stdout, "Compiler option %d: <%s>\n", + argi, argv[argi]); + } + + get_new_file_names (&source_file, &object_file); + inner_cleanup = make_cleanup (xfree, source_file); + make_cleanup (xfree, object_file); + + src = gdb_fopen_cloexec (source_file, "w"); + if (src == NULL) + perror_with_name (_("Could not open source file for writing")); + make_cleanup (cleanup_unlink_file, source_file); + if (fputs (code, src) == EOF) + perror_with_name (_("Could not write to source file")); + fclose (src); + + if (compile_debug) + fprintf_unfiltered (gdb_stdout, "source file produced: %s\n\n", + source_file); + + /* Call the compiler and start the compilation process. */ + compiler->fe->ops->set_source_file (compiler->fe, source_file); + + if (!compiler->fe->ops->compile (compiler->fe, object_file, + compile_debug)) + error (_("Compilation failed.")); + + if (compile_debug) + fprintf_unfiltered (gdb_stdout, "object file produced: %s\n\n", + object_file); + + discard_cleanups (inner_cleanup); + do_cleanups (cleanup); + *source_filep = source_file; + return object_file; +} + +/* The "compile" prefix command. */ + +static void +compile_command (char *args, int from_tty) +{ + /* If a sub-command is not specified to the compile prefix command, + assume it is a direct code compilation. */ + compile_code_command (args, from_tty); +} + +/* See compile.h. */ + +void +eval_compile_command (struct command_line *cmd, char *cmd_string, + enum compile_i_scope_types scope) +{ + char *object_file, *source_file; + + object_file = compile_to_object (cmd, cmd_string, scope, &source_file); + if (object_file != NULL) + { + struct cleanup *cleanup_xfree, *cleanup_unlink; + struct compile_module *compile_module; + + cleanup_xfree = make_cleanup (xfree, object_file); + make_cleanup (xfree, source_file); + cleanup_unlink = make_cleanup (cleanup_unlink_file, object_file); + make_cleanup (cleanup_unlink_file, source_file); + compile_module = compile_object_load (object_file, source_file); + discard_cleanups (cleanup_unlink); + do_cleanups (cleanup_xfree); + compile_object_run (compile_module); + } +} + +/* See compile/compile-internal.h. */ + +char * +compile_register_name_mangled (struct gdbarch *gdbarch, int regnum) +{ + const char *regname = gdbarch_register_name (gdbarch, regnum); + + return xstrprintf ("__%s", regname); +} + +/* See compile/compile-internal.h. */ + +int +compile_register_name_demangle (struct gdbarch *gdbarch, + const char *regname) +{ + int regnum; + + if (regname[0] != '_' || regname[1] != '_') + error (_("Invalid register name \"%s\"."), regname); + regname += 2; + + for (regnum = 0; regnum < gdbarch_num_regs (gdbarch); regnum++) + if (strcmp (regname, gdbarch_register_name (gdbarch, regnum)) == 0) + return regnum; + + error (_("Cannot find gdbarch register \"%s\"."), regname); +} + +extern initialize_file_ftype _initialize_compile; + +void +_initialize_compile (void) +{ + struct cmd_list_element *c = NULL; + + add_prefix_cmd ("compile", class_obscure, compile_command, + _("\ +Command to compile source code and inject it into the inferior."), + &compile_command_list, "compile ", 1, &cmdlist); + add_com_alias ("expression", "compile", class_obscure, 0); + + add_cmd ("code", class_obscure, compile_code_command, + _("\ +Compile, inject, and execute code.\n\ +\n\ +Usage: compile code [-r|-raw] [--] [CODE]\n\ +-r|-raw: Suppress automatic 'void _gdb_expr () { CODE }' wrapping.\n\ +--: Do not parse any options beyond this delimiter. All text to the\n\ + right will be treated as source code.\n\ +\n\ +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."), + &compile_command_list); + + c = add_cmd ("file", class_obscure, compile_file_command, + _("\ +Evaluate a file containing source code.\n\ +\n\ +Usage: compile file [-r|-raw] [filename]\n\ +-r|-raw: Suppress automatic 'void _gdb_expr () { CODE }' wrapping."), + &compile_command_list); + set_cmd_completer (c, filename_completer); + + add_setshow_boolean_cmd ("compile", class_maintenance, &compile_debug, _("\ +Set compile command debugging."), _("\ +Show compile command debugging."), _("\ +When on, compile command debugging is enabled."), + NULL, show_compile_debug, + &setdebuglist, &showdebuglist); + + add_setshow_string_cmd ("compile-args", class_support, + &compile_args, + _("Set compile command GCC command-line arguments"), + _("Show compile command GCC command-line arguments"), + _("\ +Use options like -I (include file directory) or ABI settings.\n\ +String quoting is parsed like in shell, for example:\n\ + -mno-align-double \"-I/dir with a space/include\""), + set_compile_args, show_compile_args, &setlist, &showlist); + + /* Override flags possibly coming from DW_AT_producer. */ + compile_args = xstrdup ("-O0 -gdwarf-4" + /* We use -fPIC Otherwise GDB would need to reserve space large enough for + any object file in the inferior in advance to get the final address when + to link the object file to and additionally the default system linker + script would need to be modified so that one can specify there the + absolute target address. */ + " -fPIC" + /* We don't want warnings. */ + " -w" + /* Override CU's possible -fstack-protector-strong. */ + " -fno-stack-protector" + ); + set_compile_args (compile_args, 0, NULL); +} diff --git a/gdb/compile/compile.h b/gdb/compile/compile.h new file mode 100644 index 0000000..486361f --- /dev/null +++ b/gdb/compile/compile.h @@ -0,0 +1,102 @@ +/* Header file for Compile and inject module. + + Copyright (C) 2014 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_H +#define GDB_COMPILE_H + +struct ui_file; +struct gdbarch; +struct dwarf2_per_cu_data; +struct symbol; +struct dynamic_prop; + +/* Public function that is called from compile_control case in the + expression command. GDB returns either a CMD, or a CMD_STRING, but + never both. */ + +extern void eval_compile_command (struct command_line *cmd, char *cmd_string, + enum compile_i_scope_types scope); + +/* Compile a DWARF location expression to C, suitable for use by the + compiler. + + STREAM is the stream where the code should be written. + + RESULT_NAME is the name of a variable in the resulting C code. The + result of the expression will be assigned to this variable. + + SYM is the symbol corresponding to this expression. + PC is the location at which the expression is being evaluated. + ARCH is the architecture to use. + + REGISTERS_USED is an out parameter which is updated to note which + registers were needed by this expression. + + ADDR_SIZE is the DWARF address size to use. + + OPT_PTR and OP_END are the bounds of the DWARF expression. + + PER_CU is the per-CU object used for looking up various other + things. */ + +extern void compile_dwarf_expr_to_c (struct ui_file *stream, + const char *result_name, + struct symbol *sym, + CORE_ADDR pc, + struct gdbarch *arch, + unsigned char *registers_used, + unsigned int addr_size, + const gdb_byte *op_ptr, + const gdb_byte *op_end, + struct dwarf2_per_cu_data *per_cu); + +/* Compile a DWARF bounds expression to C, suitable for use by the + compiler. + + STREAM is the stream where the code should be written. + + RESULT_NAME is the name of a variable in the resulting C code. The + result of the expression will be assigned to this variable. + + PROP is the dynamic property for which we're compiling. + + SYM is the symbol corresponding to this expression. + PC is the location at which the expression is being evaluated. + ARCH is the architecture to use. + + REGISTERS_USED is an out parameter which is updated to note which + registers were needed by this expression. + + ADDR_SIZE is the DWARF address size to use. + + OPT_PTR and OP_END are the bounds of the DWARF expression. + + PER_CU is the per-CU object used for looking up various other + things. */ + +extern void compile_dwarf_bounds_to_c (struct ui_file *stream, + const char *result_name, + const struct dynamic_prop *prop, + struct symbol *sym, CORE_ADDR pc, + struct gdbarch *arch, + unsigned char *registers_used, + unsigned int addr_size, + const gdb_byte *op_ptr, + const gdb_byte *op_end, + struct dwarf2_per_cu_data *per_cu); + +#endif /* GDB_COMPILE_H */ |