diff options
author | Tom Tromey <tromey@redhat.com> | 2014-05-14 14:35:45 -0600 |
---|---|---|
committer | Jan Kratochvil <jan.kratochvil@redhat.com> | 2014-12-12 22:28:44 +0100 |
commit | bb2ec1b34e7cc8d8602512235016e74b800dac3b (patch) | |
tree | 6fb368ff4d35c37a24d434d5d7348d8dd8850ca7 /gdb/compile | |
parent | a2658febe2016f875ffb70287904b01013bcca75 (diff) | |
download | fsf-binutils-gdb-bb2ec1b34e7cc8d8602512235016e74b800dac3b.zip fsf-binutils-gdb-bb2ec1b34e7cc8d8602512235016e74b800dac3b.tar.gz fsf-binutils-gdb-bb2ec1b34e7cc8d8602512235016e74b800dac3b.tar.bz2 |
the "compile" command
This final patch adds the new "compile" command and subcommands, and
all the machinery needed to make it work.
A shared library supplied by gcc is used for all communications with
gcc. Types and most aspects of symbols are provided directly by gdb
to the compiler using this library.
gdb provides some information about the user's code using plain text.
Macros are emitted this way, and DWARF location expressions (and
bounds for VLA) are compiled to C code.
This hybrid approach was taken because, on the one hand, it is better
to provide global declarations and such on demand; but on the other
hand, for local variables, translating DWARF location expressions to C
was much simpler than exporting a full compiler API to gdb -- the same
result, only easier to implement, understand, and debug.
In the ordinary mode, the user's expression is wrapped in a dummy
function. After compilation, gdb inserts the resulting object code
into the inferior, then calls this function.
Access to local variables is provided by noting which registers are
used by location expressions, and passing a structure of register
values into the function. Writes to registers are supported by
copying out these values after the function returns.
This approach was taken so that we could eventually implement other
more interesting features based on this same infrastructure; for
example, we're planning to investigate inferior-side breakpoint
conditions.
gdb/ChangeLog
2014-12-12 Phil Muldoon <pmuldoon@redhat.com>
Jan Kratochvil <jan.kratochvil@redhat.com>
Tom Tromey <tromey@redhat.com>
* NEWS: Update.
* symtab.h (struct symbol_computed_ops) <generate_c_location>: New
field.
* p-lang.c (pascal_language_defn): Update.
* opencl-lang.c (opencl_language_defn): Update.
* objc-lang.c (objc_language_defn): Update.
* m2-lang.c (m2_language_defn): Update.
* language.h (struct language_defn) <la_get_compile_instance,
la_compute_program>: New fields.
* language.c (unknown_language_defn, auto_language_defn)
(local_language_defn): Update.
* jv-lang.c (java_language_defn): Update.
* go-lang.c (go_language_defn): Update.
* f-lang.c (f_language_defn): Update.
* dwarf2loc.h (dwarf2_compile_property_to_c): Declare.
* dwarf2loc.c (dwarf2_compile_property_to_c)
(locexpr_generate_c_location, loclist_generate_c_location): New
functions.
(dwarf2_locexpr_funcs, dwarf2_loclist_funcs): Update.
* defs.h (enum compile_i_scope_types): New.
(enum command_control_type) <compile_control>: New constant.
(struct command_line) <control_u>: New field.
* d-lang.c (d_language_defn): Update.
* compile/compile.c: New file.
* compile/compile-c-support.c: New file.
* compile/compile-c-symbols.c: New file.
* compile/compile-c-types.c: New file.
* compile/compile.h: New file.
* compile/compile-internal.h: New file.
* compile/compile-loc2c.c: New file.
* compile/compile-object-load.c: New file.
* compile/compile-object-load.h: New file.
* compile/compile-object-run.c: New file.
* compile/compile-object-run.h: New file.
* cli/cli-script.c (multi_line_command_p, print_command_lines)
(execute_control_command, process_next_line)
(recurse_read_control_structure): Handle compile_control.
* c-lang.h (c_get_compile_context, c_compute_program): Declare.
* c-lang.c (c_language_defn, cplus_language_defn)
(asm_language_defn, minimal_language_defn): Update.
* ada-lang.c (ada_language_defn): Update.
* Makefile.in (SUBDIR_GCC_COMPILE_OBS, SUBDIR_GCC_COMPILE_SRCS):
New variables.
(SFILES): Add SUBDIR_GCC_COMPILE_SRCS.
(HFILES_NO_SRCDIR): Add compile.h.
(COMMON_OBS): Add SUBDIR_GCC_COMPILE_OBS.
(INIT_FILES): Add SUBDIR_GCC_COMPILE_SRCS.
(compile.o, compile-c-types.o, compile-c-symbols.o)
(compile-object-load.o, compile-object-run.o, compile-loc2c.o)
(compile-c-support.o): New targets.
gdb/doc/ChangeLog
2014-12-12 Phil Muldoon <pmuldoon@redhat.com>
Jan Kratochvil <jan.kratochvil@redhat.com>
* gdb.texinfo (Altering): Update.
(Compiling and Injecting Code): New node.
gdb/testsuite/ChangeLog
2014-12-12 Phil Muldoon <pmuldoon@redhat.com>
Jan Kratochvil <jan.kratochvil@redhat.com>
Tom Tromey <tromey@redhat.com>
* configure.ac: Add gdb.compile/.
* configure: Regenerate.
* gdb.compile/Makefile.in: New file.
* gdb.compile/compile-ops.exp: New file.
* gdb.compile/compile-ops.c: New file.
* gdb.compile/compile-tls.c: New file.
* gdb.compile/compile-tls.exp: New file.
* gdb.compile/compile-constvar.S: New file.
* gdb.compile/compile-constvar.c: New file.
* gdb.compile/compile-mod.c: New file.
* gdb.compile/compile-nodebug.c: New file.
* gdb.compile/compile-setjmp-mod.c: New file.
* gdb.compile/compile-setjmp.c: New file.
* gdb.compile/compile-setjmp.exp: New file.
* gdb.compile/compile-shlib.c: New file.
* gdb.compile/compile.c: New file.
* gdb.compile/compile.exp: New file.
* lib/gdb.exp (skip_compile_feature_tests): New proc.
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 */ |