aboutsummaryrefslogtreecommitdiff
path: root/gdb/compile
diff options
context:
space:
mode:
Diffstat (limited to 'gdb/compile')
-rw-r--r--gdb/compile/compile-c-support.c399
-rw-r--r--gdb/compile/compile-c-symbols.c759
-rw-r--r--gdb/compile/compile-c-types.c438
-rw-r--r--gdb/compile/compile-internal.h147
-rw-r--r--gdb/compile/compile-loc2c.c1148
-rw-r--r--gdb/compile/compile-object-load.c588
-rw-r--r--gdb/compile/compile-object-load.h39
-rw-r--r--gdb/compile/compile-object-run.c138
-rw-r--r--gdb/compile/compile-object-run.h24
-rw-r--r--gdb/compile/compile.c651
-rw-r--r--gdb/compile/compile.h102
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, &reg);
+ ++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, &reg);
+ 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, &reg);
+ 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, &reg);
+ 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, &reg);
+ 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, &reg);
+ 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,
+ &regnum, &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 */