@@ -280,6 +280,24 @@ SUBDIR_TUI_LDFLAGS=
SUBDIR_TUI_CFLAGS= \
-DTUI=1
+#
+# GCC Compile support sub-directory definitions
+#
+SUBDIR_GCC_COMPILE_OBS = \
+ compile.o compile-c-symbols.o compile-c-types.o \
+ compile-object-load.o compile-object-run.o \
+ compile-loc2c.o compile-c-support.o
+SUBDIR_GCC_COMPILE_SRCS = \
+ compile/compile.c \
+ compile/compile-c-symbols.c \
+ compile/compile-c-types.c \
+ compile/compile-object-load.c \
+ compile/compile-object-load.h \
+ compile/compile-object-run.c \
+ compile/compile-object-run.h \
+ compile/compile-loc2c.c \
+ compile/compile-c-support.c
+
# Guile sub directory definitons for guile support.
SUBDIR_GUILE_OBS = \
@@ -850,7 +868,8 @@ SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c ada-tasks.c \
common/gdb_vecs.c common/common-utils.c common/xml-utils.c \
common/ptid.c common/buffer.c gdb-dlfcn.c common/agent.c \
common/format.c common/filestuff.c btrace.c record-btrace.c ctf.c \
- target/waitstatus.c common/print-utils.c common/rsp-low.c
+ target/waitstatus.c common/print-utils.c common/rsp-low.c \
+ $(SUBDIR_GCC_COMPILE_SRCS)
LINTFILES = $(SFILES) $(YYFILES) $(CONFIG_SRCS) init.c
@@ -872,7 +891,7 @@ nto-tdep.h serial.h \
c-lang.h d-lang.h go-lang.h frame.h event-loop.h block.h cli/cli-setshow.h \
cli/cli-decode.h cli/cli-cmds.h cli/cli-utils.h \
cli/cli-script.h macrotab.h symtab.h common/version.h \
-gnulib/import/string.in.h gnulib/import/str-two-way.h \
+compile/compile.h gnulib/import/string.in.h gnulib/import/str-two-way.h \
gnulib/import/stdint.in.h remote.h remote-notif.h gdb.h sparc-nat.h \
gdbthread.h dwarf2-frame.h dwarf2-frame-tailcall.h nbsd-nat.h dcache.h \
amd64-nat.h s390-linux-tdep.h arm-linux-tdep.h exceptions.h macroscope.h \
@@ -1033,7 +1052,8 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \
gdb_vecs.o jit.o progspace.o skip.o probe.o \
common-utils.o buffer.o ptid.o gdb-dlfcn.o common-agent.o \
format.o registry.o btrace.o record-btrace.o waitstatus.o \
- print-utils.o rsp-low.o
+ print-utils.o rsp-low.o \
+ $(SUBDIR_GCC_COMPILE_OBS)
TSOBS = inflow.o
@@ -1268,7 +1288,7 @@ test-cp-name-parser$(EXEEXT): test-cp-name-parser.o $(LIBIBERTY)
# duplicates. Files in the gdb/ directory can end up appearing in
# COMMON_OBS (as a .o file) and CONFIG_SRCS (as a .c file).
-INIT_FILES = $(COMMON_OBS) $(TSOBS) $(CONFIG_SRCS)
+INIT_FILES = $(COMMON_OBS) $(TSOBS) $(CONFIG_SRCS) $(SUBDIR_GCC_COMPILE_SRCS)
init.c: $(INIT_FILES)
@echo Making init.c
@rm -f init.c-tmp init.l-tmp
@@ -1901,6 +1921,39 @@ cli-utils.o: $(srcdir)/cli/cli-utils.c
$(COMPILE) $(srcdir)/cli/cli-utils.c
$(POSTCOMPILE)
+# GCC Compile support dependencies
+#
+# Need to explicitly specify the compile rule as make will do nothing
+# or try to compile the object file into the sub-directory.
+
+compile.o: $(srcdir)/compile/compile.c
+ $(COMPILE) $(srcdir)/compile/compile.c
+ $(POSTCOMPILE)
+
+compile-c-types.o: $(srcdir)/compile/compile-c-types.c
+ $(COMPILE) $(srcdir)/compile/compile-c-types.c
+ $(POSTCOMPILE)
+
+compile-c-symbols.o: $(srcdir)/compile/compile-c-symbols.c
+ $(COMPILE) $(srcdir)/compile/compile-c-symbols.c
+ $(POSTCOMPILE)
+
+compile-object-load.o: $(srcdir)/compile/compile-object-load.c
+ $(COMPILE) $(srcdir)/compile/compile-object-load.c
+ $(POSTCOMPILE)
+
+compile-object-run.o: $(srcdir)/compile/compile-object-run.c
+ $(COMPILE) $(srcdir)/compile/compile-object-run.c
+ $(POSTCOMPILE)
+
+compile-loc2c.o: $(srcdir)/compile/compile-loc2c.c
+ $(COMPILE) $(srcdir)/compile/compile-loc2c.c
+ $(POSTCOMPILE)
+
+compile-c-support.o: $(srcdir)/compile/compile-c-support.c
+ $(COMPILE) $(srcdir)/compile/compile-c-support.c
+ $(POSTCOMPILE)
+
#
# GDBTK sub-directory
@@ -10,6 +10,16 @@
-D data-directory
This is an alias for the --data-directory option.
+* GDB now supports the compilation and injection of source code into
+ the inferior. GDB will use a feature-capable compiler to compile
+ the source code to object code, and if successful, inject and
+ execute that code within the current context of the inferior.
+ Currently the C language is supported. The commands used to
+ interface with this new feature are:
+
+ compile code [-raw|-r] [--] [source code]
+ compile file [-raw|-r] filename
+
* GDB supports printing and modifying of variable length automatic arrays
as specified in ISO C99.
@@ -33,6 +43,15 @@ guile-repl
gr
Start a Guile interactive prompt (or "repl" for "read-eval-print loop").
+compile code [-r|-raw] [--] [source code]
+ Compile and inject into the inferior the executable object code
+ produced by compiling the provided source code.
+
+compile file [-r|-raw] filename
+ Compile and inject into the inferior the executable object code
+ produced by compiling the source code stored in the filename
+ provided.
+
info auto-load guile-scripts [regexp]
Print the list of automatically loaded Guile scripts.
@@ -13501,6 +13501,8 @@ const struct language_defn ada_language_defn = {
ada_get_symbol_name_cmp, /* la_get_symbol_name_cmp */
ada_iterate_over_symbols,
&ada_varobj_ops,
+ NULL,
+ NULL,
LANG_MAGIC
};
@@ -868,6 +868,8 @@ const struct language_defn c_language_defn =
NULL, /* la_get_symbol_name_cmp */
iterate_over_symbols,
&c_varobj_ops,
+ c_get_compile_context,
+ c_compute_program,
LANG_MAGIC
};
@@ -993,6 +995,8 @@ const struct language_defn cplus_language_defn =
NULL, /* la_get_symbol_name_cmp */
iterate_over_symbols,
&cplus_varobj_ops,
+ NULL,
+ NULL,
LANG_MAGIC
};
@@ -1036,6 +1040,8 @@ const struct language_defn asm_language_defn =
NULL, /* la_get_symbol_name_cmp */
iterate_over_symbols,
&default_varobj_ops,
+ NULL,
+ NULL,
LANG_MAGIC
};
@@ -1084,6 +1090,8 @@ const struct language_defn minimal_language_defn =
NULL, /* la_get_symbol_name_cmp */
iterate_over_symbols,
&default_varobj_ops,
+ NULL,
+ NULL,
LANG_MAGIC
};
@@ -141,5 +141,24 @@ extern int cp_is_vtbl_member (struct type *);
extern int c_textual_element_type (struct type *, char);
+/* Create a new instance of the C compiler and return it. The new
+ compiler is owned by the caller and must be freed using the destroy
+ method. This function never returns NULL, but rather throws an
+ exception on failure. This is suitable for use as the
+ la_get_compile_instance language method. */
+
+extern struct compile_instance *c_get_compile_context (void);
+
+/* This takes the user-supplied text and returns a newly malloc'd bit
+ of code to compile.
+
+ This is used as the la_compute_program language method; see that
+ for a description of the arguments. */
+
+extern char *c_compute_program (struct compile_instance *inst,
+ const char *input,
+ struct gdbarch *gdbarch,
+ const struct block *expr_block,
+ CORE_ADDR expr_pc);
#endif /* !defined (C_LANG_H) */
@@ -34,6 +34,7 @@
#include "extension.h"
#include "interps.h"
+#include "compile/compile.h"
/* Prototypes for local functions. */
@@ -90,6 +91,7 @@ multi_line_command_p (enum command_control_type type)
case while_control:
case while_stepping_control:
case commands_control:
+ case compile_control:
case python_control:
case guile_control:
return 1;
@@ -275,6 +277,19 @@ print_command_lines (struct ui_out *uiout, struct command_line *cmd,
continue;
}
+ if (list->control_type == compile_control)
+ {
+ ui_out_field_string (uiout, NULL, "compile expression");
+ ui_out_text (uiout, "\n");
+ print_command_lines (uiout, *list->body_list, 0);
+ if (depth)
+ ui_out_spaces (uiout, 2 * depth);
+ ui_out_field_string (uiout, NULL, "end");
+ ui_out_text (uiout, "\n");
+ list = list->next;
+ continue;
+ }
+
if (list->control_type == guile_control)
{
ui_out_field_string (uiout, NULL, "guile");
@@ -602,6 +617,11 @@ execute_control_command (struct command_line *cmd)
break;
}
+ case compile_control:
+ eval_compile_command (cmd, NULL, cmd->control_u.compile.scope);
+ ret = simple_control;
+ break;
+
case python_control:
case guile_control:
{
@@ -1043,6 +1063,14 @@ process_next_line (char *p, struct command_line **command, int parse_commands,
here. */
*command = build_command_line (python_control, "");
}
+ else if (p_end - p == 6 && !strncmp (p, "compile", 7))
+ {
+ /* Note that we ignore the inline "compile command" form
+ here. */
+ *command = build_command_line (compile_control, "");
+ (*command)->control_u.compile.scope = COMPILE_I_INVALID_SCOPE;
+ }
+
else if (p_end - p == 5 && !strncmp (p, "guile", 5))
{
/* Note that we ignore the inline "guile command" form here. */
@@ -1136,7 +1164,8 @@ recurse_read_control_structure (char * (*read_next_line_func) (void),
next = NULL;
val = process_next_line (read_next_line_func (), &next,
current_cmd->control_type != python_control
- && current_cmd->control_type != guile_control,
+ && current_cmd->control_type != guile_control
+ && current_cmd->control_type != compile_control,
validator, closure);
/* Just skip blanks and comments. */
new file mode 100644
@@ -0,0 +1,396 @@
+/* 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;
+ }
+
+ 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);
+
+ 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;
+}
new file mode 100644
@@ -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_ERROR)
+ {
+ 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;
+}
new file mode 100644
@@ -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;
+}
new file mode 100644
@@ -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 */
new file mode 100644
@@ -0,0 +1,1147 @@
+/* Convert a DWARF location expression to C
+
+ Copyright (C) 2014 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "defs.h"
+#include "dwarf2.h"
+#include "dwarf2expr.h"
+#include "dwarf2loc.h"
+#include "ui-file.h"
+#include "utils.h"
+#include "compile-internal.h"
+#include "compile.h"
+#include "block.h"
+#include "dwarf2-frame.h"
+#include "gdb_vecs.h"
+#include "value.h"
+
+
+
+/* Information about a given instruction. */
+
+struct insn_info
+{
+ /* Stack depth at entry. */
+
+ unsigned int depth;
+
+ /* Whether this instruction has been visited. */
+
+ unsigned int visited : 1;
+
+ /* Whether this instruction needs a label. */
+
+ unsigned int label : 1;
+
+ /* Whether this instruction is DW_OP_GNU_push_tls_address. This is
+ a hack until we can add a feature to glibc to let us properly
+ generate code for TLS. */
+
+ unsigned int is_tls : 1;
+};
+
+/* A helper function for compute_stack_depth that does the work. This
+ examines the DWARF expression starting from START and computes
+ stack effects.
+
+ NEED_TEMPVAR is an out parameter which is set if this expression
+ needs a special temporary variable to be emitted (see the code
+ generator).
+ INFO is an array of insn_info objects, indexed by offset from the
+ start of the DWARF expression.
+ TO_DO is a list of bytecodes which must be examined; it may be
+ added to by this function.
+ BYTE_ORDER and ADDR_SIZE describe this bytecode in the obvious way.
+ OP_PTR and OP_END are the bounds of the DWARF expression. */
+
+static void
+compute_stack_depth_worker (int start, int *need_tempvar,
+ struct insn_info *info,
+ VEC (int) **to_do,
+ enum bfd_endian byte_order, unsigned int addr_size,
+ const gdb_byte *op_ptr, const gdb_byte *op_end)
+{
+ const gdb_byte * const base = op_ptr;
+ int stack_depth;
+
+ op_ptr += start;
+ gdb_assert (info[start].visited);
+ stack_depth = info[start].depth;
+
+ while (op_ptr < op_end)
+ {
+ enum dwarf_location_atom op = *op_ptr;
+ uint64_t reg;
+ int64_t offset;
+ int ndx = op_ptr - base;
+
+#define SET_CHECK_DEPTH(WHERE) \
+ if (info[WHERE].visited) \
+ { \
+ if (info[WHERE].depth != stack_depth) \
+ error (_("inconsistent stack depths")); \
+ } \
+ else \
+ { \
+ /* Stack depth not set, so set it. */ \
+ info[WHERE].visited = 1; \
+ info[WHERE].depth = stack_depth; \
+ }
+
+ SET_CHECK_DEPTH (ndx);
+
+ ++op_ptr;
+
+ switch (op)
+ {
+ case DW_OP_lit0:
+ case DW_OP_lit1:
+ case DW_OP_lit2:
+ case DW_OP_lit3:
+ case DW_OP_lit4:
+ case DW_OP_lit5:
+ case DW_OP_lit6:
+ case DW_OP_lit7:
+ case DW_OP_lit8:
+ case DW_OP_lit9:
+ case DW_OP_lit10:
+ case DW_OP_lit11:
+ case DW_OP_lit12:
+ case DW_OP_lit13:
+ case DW_OP_lit14:
+ case DW_OP_lit15:
+ case DW_OP_lit16:
+ case DW_OP_lit17:
+ case DW_OP_lit18:
+ case DW_OP_lit19:
+ case DW_OP_lit20:
+ case DW_OP_lit21:
+ case DW_OP_lit22:
+ case DW_OP_lit23:
+ case DW_OP_lit24:
+ case DW_OP_lit25:
+ case DW_OP_lit26:
+ case DW_OP_lit27:
+ case DW_OP_lit28:
+ case DW_OP_lit29:
+ case DW_OP_lit30:
+ case DW_OP_lit31:
+ ++stack_depth;
+ break;
+
+ case DW_OP_addr:
+ op_ptr += addr_size;
+ ++stack_depth;
+ break;
+
+ case DW_OP_const1u:
+ case DW_OP_const1s:
+ op_ptr += 1;
+ ++stack_depth;
+ break;
+ case DW_OP_const2u:
+ case DW_OP_const2s:
+ op_ptr += 2;
+ ++stack_depth;
+ break;
+ case DW_OP_const4u:
+ case DW_OP_const4s:
+ op_ptr += 4;
+ ++stack_depth;
+ break;
+ case DW_OP_const8u:
+ case DW_OP_const8s:
+ op_ptr += 8;
+ ++stack_depth;
+ break;
+ case DW_OP_constu:
+ case DW_OP_consts:
+ op_ptr = safe_read_sleb128 (op_ptr, op_end, &offset);
+ ++stack_depth;
+ break;
+
+ case DW_OP_reg0:
+ case DW_OP_reg1:
+ case DW_OP_reg2:
+ case DW_OP_reg3:
+ case DW_OP_reg4:
+ case DW_OP_reg5:
+ case DW_OP_reg6:
+ case DW_OP_reg7:
+ case DW_OP_reg8:
+ case DW_OP_reg9:
+ case DW_OP_reg10:
+ case DW_OP_reg11:
+ case DW_OP_reg12:
+ case DW_OP_reg13:
+ case DW_OP_reg14:
+ case DW_OP_reg15:
+ case DW_OP_reg16:
+ case DW_OP_reg17:
+ case DW_OP_reg18:
+ case DW_OP_reg19:
+ case DW_OP_reg20:
+ case DW_OP_reg21:
+ case DW_OP_reg22:
+ case DW_OP_reg23:
+ case DW_OP_reg24:
+ case DW_OP_reg25:
+ case DW_OP_reg26:
+ case DW_OP_reg27:
+ case DW_OP_reg28:
+ case DW_OP_reg29:
+ case DW_OP_reg30:
+ case DW_OP_reg31:
+ ++stack_depth;
+ break;
+
+ case DW_OP_regx:
+ op_ptr = safe_read_uleb128 (op_ptr, op_end, ®);
+ ++stack_depth;
+ break;
+
+ case DW_OP_breg0:
+ case DW_OP_breg1:
+ case DW_OP_breg2:
+ case DW_OP_breg3:
+ case DW_OP_breg4:
+ case DW_OP_breg5:
+ case DW_OP_breg6:
+ case DW_OP_breg7:
+ case DW_OP_breg8:
+ case DW_OP_breg9:
+ case DW_OP_breg10:
+ case DW_OP_breg11:
+ case DW_OP_breg12:
+ case DW_OP_breg13:
+ case DW_OP_breg14:
+ case DW_OP_breg15:
+ case DW_OP_breg16:
+ case DW_OP_breg17:
+ case DW_OP_breg18:
+ case DW_OP_breg19:
+ case DW_OP_breg20:
+ case DW_OP_breg21:
+ case DW_OP_breg22:
+ case DW_OP_breg23:
+ case DW_OP_breg24:
+ case DW_OP_breg25:
+ case DW_OP_breg26:
+ case DW_OP_breg27:
+ case DW_OP_breg28:
+ case DW_OP_breg29:
+ case DW_OP_breg30:
+ case DW_OP_breg31:
+ op_ptr = safe_read_sleb128 (op_ptr, op_end, &offset);
+ ++stack_depth;
+ break;
+ case DW_OP_bregx:
+ {
+ op_ptr = safe_read_uleb128 (op_ptr, op_end, ®);
+ op_ptr = safe_read_sleb128 (op_ptr, op_end, &offset);
+ ++stack_depth;
+ }
+ break;
+ case DW_OP_fbreg:
+ op_ptr = safe_read_sleb128 (op_ptr, op_end, &offset);
+ ++stack_depth;
+ break;
+
+ case DW_OP_dup:
+ ++stack_depth;
+ break;
+
+ case DW_OP_drop:
+ --stack_depth;
+ break;
+
+ case DW_OP_pick:
+ ++op_ptr;
+ ++stack_depth;
+ break;
+
+ case DW_OP_rot:
+ case DW_OP_swap:
+ *need_tempvar = 1;
+ break;
+
+ case DW_OP_over:
+ ++stack_depth;
+ break;
+
+ case DW_OP_abs:
+ case DW_OP_neg:
+ case DW_OP_not:
+ case DW_OP_deref:
+ break;
+
+ case DW_OP_deref_size:
+ ++op_ptr;
+ break;
+
+ case DW_OP_plus_uconst:
+ op_ptr = safe_read_uleb128 (op_ptr, op_end, ®);
+ break;
+
+ case DW_OP_div:
+ case DW_OP_shra:
+ case DW_OP_and:
+ case DW_OP_minus:
+ case DW_OP_mod:
+ case DW_OP_mul:
+ case DW_OP_or:
+ case DW_OP_plus:
+ case DW_OP_shl:
+ case DW_OP_shr:
+ case DW_OP_xor:
+ case DW_OP_le:
+ case DW_OP_ge:
+ case DW_OP_eq:
+ case DW_OP_lt:
+ case DW_OP_gt:
+ case DW_OP_ne:
+ --stack_depth;
+ break;
+
+ case DW_OP_call_frame_cfa:
+ ++stack_depth;
+ break;
+
+ case DW_OP_GNU_push_tls_address:
+ info[ndx].is_tls = 1;
+ break;
+
+ case DW_OP_skip:
+ offset = extract_signed_integer (op_ptr, 2, byte_order);
+ op_ptr += 2;
+ offset = op_ptr + offset - base;
+ /* If the destination has not been seen yet, add it to the
+ to-do list. */
+ if (!info[offset].visited)
+ VEC_safe_push (int, *to_do, offset);
+ SET_CHECK_DEPTH (offset);
+ info[offset].label = 1;
+ /* We're done with this line of code. */
+ return;
+
+ case DW_OP_bra:
+ offset = extract_signed_integer (op_ptr, 2, byte_order);
+ op_ptr += 2;
+ offset = op_ptr + offset - base;
+ --stack_depth;
+ /* If the destination has not been seen yet, add it to the
+ to-do list. */
+ if (!info[offset].visited)
+ VEC_safe_push (int, *to_do, offset);
+ SET_CHECK_DEPTH (offset);
+ info[offset].label = 1;
+ break;
+
+ case DW_OP_nop:
+ break;
+
+ default:
+ error (_("unhandled DWARF op: %s"), get_DW_OP_name (op));
+ }
+ }
+
+ gdb_assert (op_ptr == op_end);
+
+#undef SET_CHECK_DEPTH
+}
+
+/* Compute the maximum needed stack depth of a DWARF expression, and
+ some other information as well.
+
+ BYTE_ORDER and ADDR_SIZE describe this bytecode in the obvious way.
+ NEED_TEMPVAR is an out parameter which is set if this expression
+ needs a special temporary variable to be emitted (see the code
+ generator).
+ IS_TLS is an out parameter which is set if this expression refers
+ to a TLS variable.
+ OP_PTR and OP_END are the bounds of the DWARF expression.
+ INITIAL_DEPTH is the initial depth of the DWARF expression stack.
+ INFO is an array of insn_info objects, indexed by offset from the
+ start of the DWARF expression.
+
+ This returns the maximum stack depth. */
+
+static int
+compute_stack_depth (enum bfd_endian byte_order, unsigned int addr_size,
+ int *need_tempvar, int *is_tls,
+ const gdb_byte *op_ptr, const gdb_byte *op_end,
+ int initial_depth,
+ struct insn_info **info)
+{
+ unsigned char *set;
+ struct cleanup *outer_cleanup, *cleanup;
+ VEC (int) *to_do = NULL;
+ int stack_depth, i;
+
+ *info = XCNEWVEC (struct insn_info, op_end - op_ptr);
+ outer_cleanup = make_cleanup (xfree, *info);
+
+ cleanup = make_cleanup (VEC_cleanup (int), &to_do);
+
+ VEC_safe_push (int, to_do, 0);
+ (*info)[0].depth = initial_depth;
+ (*info)[0].visited = 1;
+
+ while (!VEC_empty (int, to_do))
+ {
+ int ndx = VEC_pop (int, to_do);
+
+ compute_stack_depth_worker (ndx, need_tempvar, *info, &to_do,
+ byte_order, addr_size,
+ op_ptr, op_end);
+ }
+
+ stack_depth = 0;
+ *is_tls = 0;
+ for (i = 0; i < op_end - op_ptr; ++i)
+ {
+ if ((*info)[i].depth > stack_depth)
+ stack_depth = (*info)[i].depth;
+ if ((*info)[i].is_tls)
+ *is_tls = 1;
+ }
+
+ do_cleanups (cleanup);
+ discard_cleanups (outer_cleanup);
+ return stack_depth + 1;
+}
+
+
+
+#define GCC_UINTPTR "__gdb_uintptr"
+#define GCC_INTPTR "__gdb_intptr"
+
+/* Emit code to push a constant. */
+
+static void
+push (int indent, struct ui_file *stream, ULONGEST l)
+{
+ fprintfi_filtered (indent, stream, "__gdb_stack[++__gdb_tos] = %s;\n",
+ hex_string (l));
+}
+
+/* Emit code to push an arbitrary expression. This works like
+ printf. */
+
+static void
+pushf (int indent, struct ui_file *stream, const char *format, ...)
+{
+ va_list args;
+
+ fprintfi_filtered (indent, stream, "__gdb_stack[__gdb_tos + 1] = ");
+ va_start (args, format);
+ vfprintf_filtered (stream, format, args);
+ va_end (args);
+ fprintf_filtered (stream, ";\n");
+
+ fprintfi_filtered (indent, stream, "++__gdb_tos;\n");
+}
+
+/* Emit code for a unary expression -- one which operates in-place on
+ the top-of-stack. This works like printf. */
+
+static void
+unary (int indent, struct ui_file *stream, const char *format, ...)
+{
+ va_list args;
+
+ fprintfi_filtered (indent, stream, "__gdb_stack[__gdb_tos] = ");
+ va_start (args, format);
+ vfprintf_filtered (stream, format, args);
+ va_end (args);
+ fprintf_filtered (stream, ";\n");
+}
+
+/* Emit code for a unary expression -- one which uses the top two
+ stack items, popping the topmost one. This works like printf. */
+
+static void
+binary (int indent, struct ui_file *stream, const char *format, ...)
+{
+ va_list args;
+
+ fprintfi_filtered (indent, stream, "__gdb_stack[__gdb_tos - 1] = ");
+ va_start (args, format);
+ vfprintf_filtered (stream, format, args);
+ va_end (args);
+ fprintf_filtered (stream, ";\n");
+ fprintfi_filtered (indent, stream, "--__gdb_tos;\n");
+}
+
+/* Print the name of a label given its "SCOPE", an arbitrary integer
+ used for uniqueness, and its TARGET, the bytecode offset
+ corresponding to the label's point of definition. */
+
+static void
+print_label (struct ui_file *stream, unsigned int scope, int target)
+{
+ fprintf_filtered (stream, "__label_%u_%s",
+ scope, pulongest (target));
+}
+
+/* Emit code that pushes a register's address on the stack.
+ REGISTERS_USED is an out parameter which is updated to note which
+ register was needed by this expression. */
+
+static void
+pushf_register_address (int indent, struct ui_file *stream,
+ unsigned char *registers_used,
+ struct gdbarch *gdbarch, int regnum)
+{
+ char *regname = compile_register_name_mangled (gdbarch, regnum);
+ struct cleanup *cleanups = make_cleanup (xfree, regname);
+
+ registers_used[regnum] = 1;
+ pushf (indent, stream, "&" COMPILE_I_SIMPLE_REGISTER_ARG_NAME "->%s",
+ regname);
+
+ do_cleanups (cleanups);
+}
+
+/* Emit code that pushes a register's value on the stack.
+ REGISTERS_USED is an out parameter which is updated to note which
+ register was needed by this expression. OFFSET is added to the
+ register's value before it is pushed. */
+
+static void
+pushf_register (int indent, struct ui_file *stream,
+ unsigned char *registers_used,
+ struct gdbarch *gdbarch, int regnum, uint64_t offset)
+{
+ char *regname = compile_register_name_mangled (gdbarch, regnum);
+ struct cleanup *cleanups = make_cleanup (xfree, regname);
+
+ registers_used[regnum] = 1;
+ if (offset == 0)
+ pushf (indent, stream, COMPILE_I_SIMPLE_REGISTER_ARG_NAME "->%s",
+ regname);
+ else
+ pushf (indent, stream, COMPILE_I_SIMPLE_REGISTER_ARG_NAME "->%s + %s",
+ regname, hex_string (offset));
+
+ do_cleanups (cleanups);
+}
+
+/* Compile a DWARF expression to C code.
+
+ INDENT is the indentation level to use.
+ STREAM is the stream where the code should be written.
+
+ TYPE_NAME names the type of the result of the DWARF expression.
+ For locations this is "void *" but for array bounds it will be an
+ integer type.
+
+ RESULT_NAME is the name of a variable in the resulting C code. The
+ result of the expression will be assigned to this variable.
+
+ SYM is the symbol corresponding to this expression.
+ PC is the location at which the expression is being evaluated.
+ ARCH is the architecture to use.
+
+ REGISTERS_USED is an out parameter which is updated to note which
+ registers were needed by this expression.
+
+ ADDR_SIZE is the DWARF address size to use.
+
+ OPT_PTR and OP_END are the bounds of the DWARF expression.
+
+ If non-NULL, INITIAL points to an initial value to write to the
+ stack. If NULL, no initial value is written.
+
+ PER_CU is the per-CU object used for looking up various other
+ things. */
+
+static void
+do_compile_dwarf_expr_to_c (int indent, struct ui_file *stream,
+ const char *type_name,
+ const char *result_name,
+ struct symbol *sym, CORE_ADDR pc,
+ struct gdbarch *arch,
+ unsigned char *registers_used,
+ unsigned int addr_size,
+ const gdb_byte *op_ptr, const gdb_byte *op_end,
+ CORE_ADDR *initial,
+ struct dwarf2_per_cu_data *per_cu)
+{
+ /* We keep a counter so that labels and other objects we create have
+ unique names. */
+ static unsigned int scope;
+
+ enum bfd_endian byte_order = gdbarch_byte_order (arch);
+ const gdb_byte * const base = op_ptr;
+ int need_tempvar = 0;
+ int is_tls = 0;
+ struct cleanup *cleanup;
+ struct insn_info *info;
+ int stack_depth;
+
+ ++scope;
+
+ fprintfi_filtered (indent, stream, "%s%s;\n", type_name, result_name);
+ fprintfi_filtered (indent, stream, "{\n");
+ indent += 2;
+
+ stack_depth = compute_stack_depth (byte_order, addr_size,
+ &need_tempvar, &is_tls,
+ op_ptr, op_end, initial != NULL,
+ &info);
+ cleanup = make_cleanup (xfree, info);
+
+ /* This is a hack until we can add a feature to glibc to let us
+ properly generate code for TLS. You might think we could emit
+ the address in the ordinary course of translating
+ DW_OP_GNU_push_tls_address, but since the operand appears on the
+ stack, it is relatively hard to find, and the idea of calling
+ target_translate_tls_address with OFFSET==0 and then adding the
+ offset by hand seemed too hackish. */
+ if (is_tls)
+ {
+ struct frame_info *frame = get_selected_frame (NULL);
+ struct value *val;
+
+ if (frame == NULL)
+ error (_("Symbol \"%s\" cannot be used because "
+ "there is no selected frame"),
+ SYMBOL_PRINT_NAME (sym));
+
+ val = read_var_value (sym, frame);
+ if (VALUE_LVAL (val) != lval_memory)
+ error (_("Symbol \"%s\" cannot be used for compilation evaluation "
+ "as its address has not been found."),
+ SYMBOL_PRINT_NAME (sym));
+
+ warning (_("Symbol \"%s\" is thread-local and currently can only "
+ "be referenced from the current thread in "
+ "compiled code."),
+ SYMBOL_PRINT_NAME (sym));
+
+ fprintfi_filtered (indent, stream, "%s = %s;\n",
+ result_name,
+ core_addr_to_string (value_address (val)));
+ fprintfi_filtered (indent - 2, stream, "}\n");
+ do_cleanups (cleanup);
+ return;
+ }
+
+ fprintfi_filtered (indent, stream, GCC_UINTPTR " __gdb_stack[%d];\n",
+ stack_depth);
+
+ if (need_tempvar)
+ fprintfi_filtered (indent, stream, GCC_UINTPTR " __gdb_tmp;\n");
+ fprintfi_filtered (indent, stream, "int __gdb_tos = -1;\n");
+
+ if (initial != NULL)
+ pushf (indent, stream, core_addr_to_string (*initial));
+
+ while (op_ptr < op_end)
+ {
+ enum dwarf_location_atom op = *op_ptr;
+ uint64_t uoffset, reg;
+ int64_t offset;
+
+ print_spaces (indent - 2, stream);
+ if (info[op_ptr - base].label)
+ {
+ print_label (stream, scope, op_ptr - base);
+ fprintf_filtered (stream, ":;");
+ }
+ fprintf_filtered (stream, "/* %s */\n", get_DW_OP_name (op));
+
+ /* This is handy for debugging the generated code:
+ fprintf_filtered (stream, "if (__gdb_tos != %d) abort ();\n",
+ (int) info[op_ptr - base].depth - 1);
+ */
+
+ ++op_ptr;
+
+ switch (op)
+ {
+ case DW_OP_lit0:
+ case DW_OP_lit1:
+ case DW_OP_lit2:
+ case DW_OP_lit3:
+ case DW_OP_lit4:
+ case DW_OP_lit5:
+ case DW_OP_lit6:
+ case DW_OP_lit7:
+ case DW_OP_lit8:
+ case DW_OP_lit9:
+ case DW_OP_lit10:
+ case DW_OP_lit11:
+ case DW_OP_lit12:
+ case DW_OP_lit13:
+ case DW_OP_lit14:
+ case DW_OP_lit15:
+ case DW_OP_lit16:
+ case DW_OP_lit17:
+ case DW_OP_lit18:
+ case DW_OP_lit19:
+ case DW_OP_lit20:
+ case DW_OP_lit21:
+ case DW_OP_lit22:
+ case DW_OP_lit23:
+ case DW_OP_lit24:
+ case DW_OP_lit25:
+ case DW_OP_lit26:
+ case DW_OP_lit27:
+ case DW_OP_lit28:
+ case DW_OP_lit29:
+ case DW_OP_lit30:
+ case DW_OP_lit31:
+ push (indent, stream, op - DW_OP_lit0);
+ break;
+
+ case DW_OP_addr:
+ op_ptr += addr_size;
+ /* Some versions of GCC emit DW_OP_addr before
+ DW_OP_GNU_push_tls_address. In this case the value is an
+ index, not an address. We don't support things like
+ branching between the address and the TLS op. */
+ if (op_ptr >= op_end || *op_ptr != DW_OP_GNU_push_tls_address)
+ uoffset += dwarf2_per_cu_text_offset (per_cu);
+ push (indent, stream, uoffset);
+ break;
+
+ case DW_OP_const1u:
+ push (indent, stream,
+ extract_unsigned_integer (op_ptr, 1, byte_order));
+ op_ptr += 1;
+ break;
+ case DW_OP_const1s:
+ push (indent, stream,
+ extract_signed_integer (op_ptr, 1, byte_order));
+ op_ptr += 1;
+ break;
+ case DW_OP_const2u:
+ push (indent, stream,
+ extract_unsigned_integer (op_ptr, 2, byte_order));
+ op_ptr += 2;
+ break;
+ case DW_OP_const2s:
+ push (indent, stream,
+ extract_signed_integer (op_ptr, 2, byte_order));
+ op_ptr += 2;
+ break;
+ case DW_OP_const4u:
+ push (indent, stream,
+ extract_unsigned_integer (op_ptr, 4, byte_order));
+ op_ptr += 4;
+ break;
+ case DW_OP_const4s:
+ push (indent, stream,
+ extract_signed_integer (op_ptr, 4, byte_order));
+ op_ptr += 4;
+ break;
+ case DW_OP_const8u:
+ push (indent, stream,
+ extract_unsigned_integer (op_ptr, 8, byte_order));
+ op_ptr += 8;
+ break;
+ case DW_OP_const8s:
+ push (indent, stream,
+ extract_signed_integer (op_ptr, 8, byte_order));
+ op_ptr += 8;
+ break;
+ case DW_OP_constu:
+ op_ptr = safe_read_uleb128 (op_ptr, op_end, &uoffset);
+ push (indent, stream, uoffset);
+ break;
+ case DW_OP_consts:
+ op_ptr = safe_read_sleb128 (op_ptr, op_end, &offset);
+ push (indent, stream, offset);
+ break;
+
+ case DW_OP_reg0:
+ case DW_OP_reg1:
+ case DW_OP_reg2:
+ case DW_OP_reg3:
+ case DW_OP_reg4:
+ case DW_OP_reg5:
+ case DW_OP_reg6:
+ case DW_OP_reg7:
+ case DW_OP_reg8:
+ case DW_OP_reg9:
+ case DW_OP_reg10:
+ case DW_OP_reg11:
+ case DW_OP_reg12:
+ case DW_OP_reg13:
+ case DW_OP_reg14:
+ case DW_OP_reg15:
+ case DW_OP_reg16:
+ case DW_OP_reg17:
+ case DW_OP_reg18:
+ case DW_OP_reg19:
+ case DW_OP_reg20:
+ case DW_OP_reg21:
+ case DW_OP_reg22:
+ case DW_OP_reg23:
+ case DW_OP_reg24:
+ case DW_OP_reg25:
+ case DW_OP_reg26:
+ case DW_OP_reg27:
+ case DW_OP_reg28:
+ case DW_OP_reg29:
+ case DW_OP_reg30:
+ case DW_OP_reg31:
+ dwarf_expr_require_composition (op_ptr, op_end, "DW_OP_regx");
+ pushf_register_address (indent, stream, registers_used, arch,
+ dwarf2_reg_to_regnum_or_error (arch,
+ op - DW_OP_reg0));
+ break;
+
+ case DW_OP_regx:
+ op_ptr = safe_read_uleb128 (op_ptr, op_end, ®);
+ dwarf_expr_require_composition (op_ptr, op_end, "DW_OP_regx");
+ pushf_register_address (indent, stream, registers_used, arch,
+ dwarf2_reg_to_regnum_or_error (arch, reg));
+ break;
+
+ case DW_OP_breg0:
+ case DW_OP_breg1:
+ case DW_OP_breg2:
+ case DW_OP_breg3:
+ case DW_OP_breg4:
+ case DW_OP_breg5:
+ case DW_OP_breg6:
+ case DW_OP_breg7:
+ case DW_OP_breg8:
+ case DW_OP_breg9:
+ case DW_OP_breg10:
+ case DW_OP_breg11:
+ case DW_OP_breg12:
+ case DW_OP_breg13:
+ case DW_OP_breg14:
+ case DW_OP_breg15:
+ case DW_OP_breg16:
+ case DW_OP_breg17:
+ case DW_OP_breg18:
+ case DW_OP_breg19:
+ case DW_OP_breg20:
+ case DW_OP_breg21:
+ case DW_OP_breg22:
+ case DW_OP_breg23:
+ case DW_OP_breg24:
+ case DW_OP_breg25:
+ case DW_OP_breg26:
+ case DW_OP_breg27:
+ case DW_OP_breg28:
+ case DW_OP_breg29:
+ case DW_OP_breg30:
+ case DW_OP_breg31:
+ op_ptr = safe_read_sleb128 (op_ptr, op_end, &offset);
+ pushf_register (indent, stream, registers_used, arch,
+ dwarf2_reg_to_regnum_or_error (arch,
+ op - DW_OP_breg0),
+ offset);
+ break;
+ case DW_OP_bregx:
+ {
+ op_ptr = safe_read_uleb128 (op_ptr, op_end, ®);
+ op_ptr = safe_read_sleb128 (op_ptr, op_end, &offset);
+ pushf_register (indent, stream, registers_used, arch,
+ dwarf2_reg_to_regnum_or_error (arch, reg), offset);
+ }
+ break;
+ case DW_OP_fbreg:
+ {
+ const gdb_byte *datastart;
+ size_t datalen;
+ const struct block *b;
+ struct symbol *framefunc;
+ char fb_name[50];
+
+ b = block_for_pc (pc);
+
+ if (!b)
+ error (_("No block found for address"));
+
+ framefunc = block_linkage_function (b);
+
+ if (!framefunc)
+ error (_("No function found for block"));
+
+ dwarf_expr_frame_base_1 (framefunc, pc, &datastart, &datalen);
+
+ op_ptr = safe_read_sleb128 (op_ptr, op_end, &offset);
+
+ /* Generate a unique-enough name, in case the frame base
+ is computed multiple times in this expression. */
+ xsnprintf (fb_name, sizeof (fb_name), "__frame_base_%ld",
+ (long) (op_ptr - base));
+
+ do_compile_dwarf_expr_to_c (indent, stream,
+ "void *", fb_name,
+ sym, pc,
+ arch, registers_used, addr_size,
+ datastart, datastart + datalen,
+ NULL, per_cu);
+
+ pushf (indent, stream, "%s + %s", fb_name, hex_string (offset));
+ }
+ break;
+
+ case DW_OP_dup:
+ pushf (indent, stream, "__gdb_stack[__gdb_tos]");
+ break;
+
+ case DW_OP_drop:
+ fprintfi_filtered (indent, stream, "--__gdb_tos;\n");
+ break;
+
+ case DW_OP_pick:
+ offset = *op_ptr++;
+ pushf (indent, stream, "__gdb_stack[__gdb_tos - %d]", offset);
+ break;
+
+ case DW_OP_swap:
+ fprintfi_filtered (indent, stream,
+ "__gdb_tmp = __gdb_stack[__gdb_tos - 1];\n");
+ fprintfi_filtered (indent, stream,
+ "__gdb_stack[__gdb_tos - 1] = "
+ "__gdb_stack[__gdb_tos];\n");
+ fprintfi_filtered (indent, stream, ("__gdb_stack[__gdb_tos] = "
+ "__gdb_tmp;\n"));
+ break;
+
+ case DW_OP_over:
+ pushf (indent, stream, "__gdb_stack[__gdb_tos - 1]");
+ break;
+
+ case DW_OP_rot:
+ fprintfi_filtered (indent, stream, ("__gdb_tmp = "
+ "__gdb_stack[__gdb_tos];\n"));
+ fprintfi_filtered (indent, stream,
+ "__gdb_stack[__gdb_tos] = "
+ "__gdb_stack[__gdb_tos - 1];\n");
+ fprintfi_filtered (indent, stream,
+ "__gdb_stack[__gdb_tos - 1] = "
+ "__gdb_stack[__gdb_tos -2];\n");
+ fprintfi_filtered (indent, stream, "__gdb_stack[__gdb_tos - 2] = "
+ "__gdb_tmp;\n");
+ break;
+
+ case DW_OP_deref:
+ case DW_OP_deref_size:
+ {
+ int size;
+ const char *mode;
+
+ if (op == DW_OP_deref_size)
+ size = *op_ptr++;
+ else
+ size = addr_size;
+
+ mode = c_get_mode_for_size (size);
+ if (mode == NULL)
+ error (_("Unsupported size %d in %s"),
+ size, get_DW_OP_name (op));
+
+ /* Cast to a pointer of the desired type, then
+ dereference. */
+ fprintfi_filtered (indent, stream,
+ "__gdb_stack[__gdb_tos] = "
+ "*((__gdb_int_%s *) "
+ "__gdb_stack[__gdb_tos]);\n",
+ mode);
+ }
+ break;
+
+ case DW_OP_abs:
+ unary (indent, stream,
+ "((" GCC_INTPTR ") __gdb_stack[__gdb_tos]) < 0 ? "
+ "-__gdb_stack[__gdb_tos] : __gdb_stack[__gdb_tos]");
+ break;
+
+ case DW_OP_neg:
+ unary (indent, stream, "-__gdb_stack[__gdb_tos]");
+ break;
+
+ case DW_OP_not:
+ unary (indent, stream, "~__gdb_stack[__gdb_tos]");
+ break;
+
+ case DW_OP_plus_uconst:
+ op_ptr = safe_read_uleb128 (op_ptr, op_end, ®);
+ unary (indent, stream, "__gdb_stack[__gdb_tos] + %s",
+ hex_string (reg));
+ break;
+
+ case DW_OP_div:
+ binary (indent, stream, ("((" GCC_INTPTR
+ ") __gdb_stack[__gdb_tos-1]) / (("
+ GCC_INTPTR ") __gdb_stack[__gdb_tos])"));
+ break;
+
+ case DW_OP_shra:
+ binary (indent, stream,
+ "((" GCC_INTPTR ") __gdb_stack[__gdb_tos-1]) >> "
+ "__gdb_stack[__gdb_tos]");
+ break;
+
+#define BINARY(OP) \
+ binary (indent, stream, ("__gdb_stack[__gdb_tos-1] " #OP \
+ " __gdb_stack[__gdb_tos]")); \
+ break
+
+ case DW_OP_and:
+ BINARY (&);
+ case DW_OP_minus:
+ BINARY (-);
+ case DW_OP_mod:
+ BINARY (%);
+ case DW_OP_mul:
+ BINARY (*);
+ case DW_OP_or:
+ BINARY (|);
+ case DW_OP_plus:
+ BINARY (+);
+ case DW_OP_shl:
+ BINARY (<<);
+ case DW_OP_shr:
+ BINARY (>>);
+ case DW_OP_xor:
+ BINARY (^);
+#undef BINARY
+
+#define COMPARE(OP) \
+ binary (indent, stream, \
+ "(((" GCC_INTPTR ") __gdb_stack[__gdb_tos-1]) " #OP \
+ " ((" GCC_INTPTR \
+ ") __gdb_stack[__gdb_tos]))"); \
+ break
+
+ case DW_OP_le:
+ COMPARE (<=);
+ case DW_OP_ge:
+ COMPARE (>=);
+ case DW_OP_eq:
+ COMPARE (==);
+ case DW_OP_lt:
+ COMPARE (<);
+ case DW_OP_gt:
+ COMPARE (>);
+ case DW_OP_ne:
+ COMPARE (!=);
+#undef COMPARE
+
+ case DW_OP_call_frame_cfa:
+ {
+ int regnum;
+ CORE_ADDR text_offset;
+ LONGEST off;
+ const gdb_byte *cfa_start, *cfa_end;
+
+ if (dwarf2_fetch_cfa_info (arch, pc, per_cu,
+ ®num, &off,
+ &text_offset, &cfa_start, &cfa_end))
+ {
+ /* Register. */
+ pushf_register (indent, stream, registers_used, arch, regnum,
+ off);
+ }
+ else
+ {
+ /* Another expression. */
+ char cfa_name[50];
+
+ /* Generate a unique-enough name, in case the CFA is
+ computed multiple times in this expression. */
+ xsnprintf (cfa_name, sizeof (cfa_name),
+ "__cfa_%ld", (long) (op_ptr - base));
+
+ do_compile_dwarf_expr_to_c (indent, stream,
+ "void *", cfa_name,
+ sym, pc, arch, registers_used,
+ addr_size,
+ cfa_start, cfa_end,
+ &text_offset, per_cu);
+ pushf (indent, stream, cfa_name);
+ }
+ }
+
+ break;
+
+ case DW_OP_skip:
+ offset = extract_signed_integer (op_ptr, 2, byte_order);
+ op_ptr += 2;
+ fprintfi_filtered (indent, stream, "goto ");
+ print_label (stream, scope, op_ptr + offset - base);
+ fprintf_filtered (stream, ";\n");
+ break;
+
+ case DW_OP_bra:
+ offset = extract_signed_integer (op_ptr, 2, byte_order);
+ op_ptr += 2;
+ fprintfi_filtered (indent, stream,
+ "if ((( " GCC_INTPTR
+ ") __gdb_stack[__gdb_tos--]) != 0) goto ");
+ print_label (stream, scope, op_ptr + offset - base);
+ fprintf_filtered (stream, ";\n");
+ break;
+
+ case DW_OP_nop:
+ break;
+
+ default:
+ error (_("unhandled DWARF op: %s"), get_DW_OP_name (op));
+ }
+ }
+
+ fprintfi_filtered (indent, stream, "%s = (%s) __gdb_stack[__gdb_tos];\n",
+ result_name, type_name);
+ fprintfi_filtered (indent - 2, stream, "}\n");
+
+ do_cleanups (cleanup);
+}
+
+/* See compile.h. */
+
+void
+compile_dwarf_expr_to_c (struct ui_file *stream, const char *result_name,
+ struct symbol *sym, CORE_ADDR pc,
+ struct gdbarch *arch, unsigned char *registers_used,
+ unsigned int addr_size,
+ const gdb_byte *op_ptr, const gdb_byte *op_end,
+ struct dwarf2_per_cu_data *per_cu)
+{
+ do_compile_dwarf_expr_to_c (2, stream, "void *", result_name, sym, pc,
+ arch, registers_used, addr_size, op_ptr, op_end,
+ NULL, per_cu);
+}
+
+/* See compile.h. */
+
+void
+compile_dwarf_bounds_to_c (struct ui_file *stream,
+ const char *result_name,
+ const struct dynamic_prop *prop,
+ struct symbol *sym, CORE_ADDR pc,
+ struct gdbarch *arch, unsigned char *registers_used,
+ unsigned int addr_size,
+ const gdb_byte *op_ptr, const gdb_byte *op_end,
+ struct dwarf2_per_cu_data *per_cu)
+{
+ do_compile_dwarf_expr_to_c (2, stream, "unsigned long ", result_name,
+ sym, pc, arch, registers_used,
+ addr_size, op_ptr, op_end, NULL, per_cu);
+}
new file mode 100644
@@ -0,0 +1,586 @@
+/* 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"
+
+/* 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 = 4;
+ if ((bfd_get_section_flags (abfd, sect) & SEC_READONLY) == 0)
+ prot |= 2;
+ if ((bfd_get_section_flags (abfd, sect) & SEC_CODE) != 0)
+ prot |= 1;
+
+ 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), 4);
+ 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;
+}
new file mode 100644
@@ -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 */
new file mode 100644
@@ -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);
+ }
+}
new file mode 100644
@@ -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 */
new file mode 100644
@@ -0,0 +1,642 @@
+/* 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"
+
+
+
+/* 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)
+{
+ char *zap = concat ("rm -rf ", arg, (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/gdbobj-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 (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 symtab *symtab = find_pc_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,
+ _("\
+Evaluate a block of source code.\n\
+\n\
+Usage: 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: 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 to ensure that we can reference properly. Otherwise
+ on x86-64 a string constant's address might be truncated when gdb
+ loads the object; another approach would be -mcmodel=large, but
+ -fPIC seems more portable across back ends. */
+ " -fPIC"
+ /* We don't want warnings. */
+ " -w"
+ /* Override CU's possible -fstack-protector-strong. */
+ " -fno-stack-protector"
+ );
+ set_compile_args (compile_args, 0, NULL);
+}
new file mode 100644
@@ -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 */
@@ -261,6 +261,8 @@ static const struct language_defn d_language_defn =
NULL, /* la_get_symbol_name_cmp */
iterate_over_symbols,
&default_varobj_ops,
+ NULL,
+ NULL,
LANG_MAGIC
};
@@ -73,6 +73,24 @@
#include "host-defs.h"
+/* Scope types enumerator. List the types of scopes the compiler will
+ accept. */
+
+enum compile_i_scope_types
+ {
+ COMPILE_I_INVALID_SCOPE,
+
+ /* A simple scope. Wrap an expression into a simple scope that
+ takes no arguments, returns no value, and uses the generic
+ function name "_gdb_expr". */
+
+ COMPILE_I_SIMPLE_SCOPE,
+
+ /* Do not wrap the expression,
+ it has to provide function "_gdb_expr" on its own. */
+ COMPILE_I_RAW_SCOPE,
+ };
+
/* Just in case they're not defined in stdio.h. */
#ifndef SEEK_SET
@@ -416,6 +434,7 @@ enum command_control_type
if_control,
commands_control,
python_control,
+ compile_control,
guile_control,
while_stepping_control,
invalid_control
@@ -429,6 +448,15 @@ struct command_line
struct command_line *next;
char *line;
enum command_control_type control_type;
+ union
+ {
+ struct
+ {
+ enum compile_i_scope_types scope;
+ }
+ compile;
+ }
+ control_u;
/* * The number of elements in body_list. */
int body_count;
/* * For composite commands, the nested lists of commands. For
@@ -16376,6 +16376,7 @@ address, or even return prematurely from a function.
* Returning:: Returning from a function
* Calling:: Calling your program's functions
* Patching:: Patching your program
+* Compiling and Injecting Code:: Compiling and injecting code in @value{GDBN}
@end menu
@node Assignment
@@ -16765,6 +16766,231 @@ Display whether executable files and core files are opened for writing
as well as reading.
@end table
+@node Compiling and Injecting Code
+@section Compiling and injecting code in @value{GDBN}
+@cindex injecting code
+@cindex writing into executables
+@cindex compiling code
+
+@value{GDBN} supports on-demand compilation and code injection into
+programs running under @value{GDBN}. A suitable compiler must be
+installed for this functionality to be enabled. This functionality is
+implemented with the following commands.
+
+@table @code
+@kindex compile code
+@item compile code @var{source}
+@itemx compile code -@var{raw} @var{--} @var{source}
+Compile @var{source} with the compiler language set as the current
+language in @value{GDBN} (@pxref{Languages}). If compilation and
+injection is not supported with the current language specified in
+@value{GDBN}, or the compiler does not support this feature, an error
+message will be printed and the command will exit. If @var{source}
+compiles and links successfully, @value{GDBN} will load the
+object-code emitted, and execute it within the context of the
+currently selected inferior. It is important to note that the
+compiled code is executed immediately. After execution, the compiled
+code is removed from @value{GDBN} and any new types or variables you
+have defined will be deleted.
+
+The command allows you to specify source code in two ways. The simplest
+method is to provide a single line of code to the command. E.g.:
+
+@smallexample
+compile code printf ("hello world\n");
+@end smallexample
+
+If you specify options on the command line as well as source code, they
+may conflict. The @samp{@var{--}} delimiter can be used to separate options
+from actual source code. E.g.:
+
+@smallexample
+compile code -r -- printf ("hello world\n");
+@end smallexample
+
+Alternatively you can enter source code as multiple lines of text. To
+enter this mode, invoke the @samp{compile code} command without any text
+following the command. This will start the multiple-line editor and
+allow you to type as many lines of source code as required. When you
+have completed typing, enter @samp{end} on its own line to exit the
+editor.
+
+@smallexample
+compile code
+>printf ("hello\n");
+>printf ("world\n");
+>end
+@end smallexample
+
+Specifying @samp{-@var{raw}}, prohibits @value{GDBN} from wrapping the
+provided @var{source} in a callable scope. In this case, you must
+specify the entry point of the code by defining a function named
+@code{_gdb_expr_}. The @samp{-@var{raw}} code does not automatically
+access variables of the inferior, for their import you must use the
+@samp{#pragma} line first:
+
+@smallexample
+#pragma GCC user_expression
+@end smallexample
+
+@samp{#include} lines are better to be placed before the @samp{#pragma}
+line. The use of @var{raw} is considered to be expert usage, and care
+should be taken when using it.
+
+@kindex compile file
+@item compile file @var{filename}
+@itemx compile file -@var{raw} @var{filename}
+Like @code{compile code}, but take the source code from @var{filename}.
+
+@smallexample
+compile file /home/user/example.c
+@end smallexample
+
+If the filename/path contains whitespace it must be enclosed in
+quotes.
+@end table
+
+There are a few caveats to keep in mind when using the @code{compile}
+command. As the caveats are different per language, the table below
+highlights specific issues on a per language basis.
+
+@table @asis
+@item C code examples and caveats
+When the language in @value{GDBN} is set to @samp{C}, the compiler will
+attempt to compile the source code with a @samp{C} compiler. The source
+code provided to the @code{compile} command will have much the same
+access to variables and types as it normally would if it were part of
+the program currently being debugged in @value{GDBN}.
+
+Below is a sample program that forms the basis of the examples that
+follow. This program has been compiled and loaded into @value{GDBN},
+much like any other normal debugging session.
+
+@smallexample
+void function1 (void)
+@{
+ int i = 42;
+ printf ("function 1\n");
+@}
+
+void function2 (void)
+@{
+ int j = 12;
+ function1 ();
+@}
+
+int main(void)
+@{
+ int k = 6;
+ int *p;
+ function2 ();
+ return 0;
+@}
+@end smallexample
+
+For the purposes of the examples in this section, the program above has
+been compiled, loaded into @value{GDBN}, stopped at the function
+@code{main}, and @value{GDBN} is awaiting input from the user.
+
+To access variables and types for any program in @value{GDBN}, the
+program must be compiled and packaged with debug information. The
+@code{compile} command is not an exception to this rule. Without debug
+information, you can still use the @code{compile} command, but you will
+be very limited in what variables and types you can access.
+
+So with that in mind, the example above has been compiled with debug
+information enabled. The @code{compile} command will have access to
+all variables and types (except those that may have been optimized
+out). Currently, as @value{GDBN} has stopped the program in the
+@code{main} function, the @code{compile} command would have access to
+the variable @code{k}. You could invoke the @code{compile} command
+and type some source code to set the value of @code{k}. You can also
+read it, or do anything with that variable you would normally do in
+@code{C}. Be aware that changes to inferior variables in the
+@code{compile} command are persistent. In the following example:
+
+@smallexample
+compile code k = 3;
+@end smallexample
+
+The variable @code{k} is now 3. It will remain that value until
+something else in the example program changes it, or another
+@code{compile} command changes it.
+
+Normal scope and access rules apply to source code compiled and injected
+by the @code{compile} command. In the example, the variables @code{j}
+and @code{k} are not accessible; subsequent execution will bring these
+variables into scope, and later, code written and compiled with the
+@code{compile} command will be able to access them. At this point the
+program is stopped in the @code{main} function so the following example:
+
+@smallexample
+compile code j = 3;
+@end smallexample
+
+would result in a compilation error, and @value{GDBN} would print that
+error to the console.
+
+You can create variables and types with the @code{compile} command as
+part of your source code. Variables and types that are created as part
+of the @code{compile} command are not persistent, and only exist as
+long as the injected object code exists. This example is valid:
+
+@smallexample
+compile code int ff = 5; printf ("ff is %d\n", ff);
+@end smallexample
+
+However, if you were to type the following into @value{GDBN} after that
+command has completed:
+
+@smallexample
+compile code printf ("ff is %d\n'', ff);
+@end smallexample
+
+A compiler error would be raised as the variable @code{ff} no longer
+exists. Object code generated and injected by the @code{compile}
+command is removed on completion of the command. Caution is advised
+when assigning variables belonging to the program with variables created
+in the @code{compile} command. This example is valid:
+
+@smallexample
+compile code int ff = 5; k = ff;
+@end smallexample
+
+The @code{k} variable is assigned to the value of @code{ff}. The variable
+@code{k} does not require the existence of @code{ff} to maintain the value
+it has been assigned. Pointers and other types of references require
+particular care in assignment. If the source code compiled with the
+@code{compile} command changed the address of a pointer in the example
+program, perhaps to a variable created in the @code{compile} command,
+that pointer would point to an invalid location when the command exits.
+The following example would likely cause issues with your debugged
+program:
+
+@smallexample
+compile code int ff = 5; p = &ff;
+@end smallexample
+
+In this example, @code{p} would point to @code{ff} when the
+@code{compile} command is executing the source code provided to it.
+However, as variables in the (example) program persist with their
+assigned values, the variable @code{p} would point to an invalid
+location when the command exists. A general rule should be followed
+in that you should either assign @code{NULL} to any assigned pointers,
+or restore a valid location to the pointer before the command exits.
+
+Similar caution must be exercised with any types defined in
+@code{compile} command. Types defined in the @code{compile} are also
+deleted when the command exits. Therefore, if you cast a variable to a
+type defined in the @code{compile} command, care must be taken to ensure
+that any future need to resolve the type can be achieved.
+
+Variables that have been optimized away by the compiler are not
+accessible to the @code{compile} command. Access to those variables
+will generate a compiler error which @value{GDBN} will print to the
+console.
+@end table
+
@node GDB Files
@chapter @value{GDBN} Files
@@ -41,6 +41,7 @@
#include <string.h>
#include "gdb_assert.h"
+#include "compile/compile.h"
extern int dwarf2_always_disassemble;
@@ -2545,6 +2546,42 @@ dwarf2_evaluate_property (const struct dynamic_prop *prop, CORE_ADDR *value)
return 0;
}
+/* See dwarf2loc.h. */
+
+void
+dwarf2_compile_property_to_c (struct ui_file *stream,
+ const char *result_name,
+ struct gdbarch *gdbarch,
+ unsigned char *registers_used,
+ const struct dynamic_prop *prop,
+ CORE_ADDR pc,
+ struct symbol *sym)
+{
+ struct dwarf2_property_baton *baton = prop->data.baton;
+ const gdb_byte *data;
+ size_t size;
+ struct dwarf2_per_cu_data *per_cu;
+
+ if (prop->kind == PROP_LOCEXPR)
+ {
+ data = baton->locexpr.data;
+ size = baton->locexpr.size;
+ per_cu = baton->locexpr.per_cu;
+ }
+ else
+ {
+ gdb_assert (prop->kind == PROP_LOCLIST);
+
+ data = dwarf2_find_location_expression (&baton->loclist, &size, pc);
+ per_cu = baton->loclist.per_cu;
+ }
+
+ compile_dwarf_bounds_to_c (stream, result_name, prop, sym, pc,
+ gdbarch, registers_used,
+ dwarf2_per_cu_addr_size (per_cu),
+ data, data + size, per_cu);
+}
+
/* Helper functions and baton for dwarf2_loc_desc_needs_frame. */
@@ -4198,6 +4235,26 @@ locexpr_tracepoint_var_ref (struct symbol *symbol, struct gdbarch *gdbarch,
dlbaton->per_cu);
}
+/* symbol_computed_ops 'generate_c_location' method. */
+
+static void
+locexpr_generate_c_location (struct symbol *sym, struct ui_file *stream,
+ struct gdbarch *gdbarch,
+ unsigned char *registers_used,
+ CORE_ADDR pc, const char *result_name)
+{
+ struct dwarf2_locexpr_baton *dlbaton = SYMBOL_LOCATION_BATON (sym);
+ unsigned int addr_size = dwarf2_per_cu_addr_size (dlbaton->per_cu);
+
+ if (dlbaton->size == 0)
+ error (_("symbol \"%s\" is optimized out"), SYMBOL_NATURAL_NAME (sym));
+
+ compile_dwarf_expr_to_c (stream, result_name,
+ sym, pc, gdbarch, registers_used, addr_size,
+ dlbaton->data, dlbaton->data + dlbaton->size,
+ dlbaton->per_cu);
+}
+
/* The set of location functions used with the DWARF-2 expression
evaluator. */
const struct symbol_computed_ops dwarf2_locexpr_funcs = {
@@ -4206,7 +4263,8 @@ const struct symbol_computed_ops dwarf2_locexpr_funcs = {
locexpr_read_needs_frame,
locexpr_describe_location,
0, /* location_has_loclist */
- locexpr_tracepoint_var_ref
+ locexpr_tracepoint_var_ref,
+ locexpr_generate_c_location
};
@@ -4377,6 +4435,29 @@ loclist_tracepoint_var_ref (struct symbol *symbol, struct gdbarch *gdbarch,
dlbaton->per_cu);
}
+/* symbol_computed_ops 'generate_c_location' method. */
+
+static void
+loclist_generate_c_location (struct symbol *sym, struct ui_file *stream,
+ struct gdbarch *gdbarch,
+ unsigned char *registers_used,
+ CORE_ADDR pc, const char *result_name)
+{
+ struct dwarf2_loclist_baton *dlbaton = SYMBOL_LOCATION_BATON (sym);
+ unsigned int addr_size = dwarf2_per_cu_addr_size (dlbaton->per_cu);
+ const gdb_byte *data;
+ size_t size;
+
+ data = dwarf2_find_location_expression (dlbaton, &size, pc);
+ if (size == 0)
+ error (_("symbol \"%s\" is optimized out"), SYMBOL_NATURAL_NAME (sym));
+
+ compile_dwarf_expr_to_c (stream, result_name,
+ sym, pc, gdbarch, registers_used, addr_size,
+ data, data + size,
+ dlbaton->per_cu);
+}
+
/* The set of location functions used with the DWARF-2 expression
evaluator and location lists. */
const struct symbol_computed_ops dwarf2_loclist_funcs = {
@@ -4385,7 +4466,8 @@ const struct symbol_computed_ops dwarf2_loclist_funcs = {
loclist_read_needs_frame,
loclist_describe_location,
1, /* location_has_loclist */
- loclist_tracepoint_var_ref
+ loclist_tracepoint_var_ref,
+ loclist_generate_c_location
};
/* Provide a prototype to silence -Wmissing-prototypes. */
@@ -108,6 +108,27 @@ struct value *dwarf2_evaluate_loc_desc (struct type *type,
int dwarf2_evaluate_property (const struct dynamic_prop *prop,
CORE_ADDR *value);
+/* A helper for the compiler interface that compiles a single dynamic
+ property to C code.
+
+ STREAM is where the C code is to be written.
+ RESULT_NAME is the name of the generated variable.
+ GDBARCH is the architecture to use.
+ REGISTERS_USED is a bit-vector that is filled to note which
+ registers are required by the generated expression.
+ PROP is the property for which code is generated.
+ ADDRESS is the address at which the property is considered to be
+ evaluated.
+ SYM the originating symbol, used for error reporting. */
+
+void dwarf2_compile_property_to_c (struct ui_file *stream,
+ const char *result_name,
+ struct gdbarch *gdbarch,
+ unsigned char *registers_used,
+ const struct dynamic_prop *prop,
+ CORE_ADDR address,
+ struct symbol *sym);
+
CORE_ADDR dwarf2_read_addr_index (struct dwarf2_per_cu_data *per_cu,
unsigned int addr_index);
@@ -276,6 +276,8 @@ const struct language_defn f_language_defn =
NULL, /* la_get_symbol_name_cmp */
iterate_over_symbols,
&default_varobj_ops,
+ NULL,
+ NULL,
LANG_MAGIC
};
@@ -599,6 +599,8 @@ static const struct language_defn go_language_defn =
NULL,
iterate_over_symbols,
&default_varobj_ops,
+ NULL,
+ NULL,
LANG_MAGIC
};
@@ -1197,6 +1197,8 @@ const struct language_defn java_language_defn =
NULL, /* la_get_symbol_name_cmp */
iterate_over_symbols,
&java_varobj_ops,
+ NULL,
+ NULL,
LANG_MAGIC
};
@@ -829,6 +829,8 @@ const struct language_defn unknown_language_defn =
NULL, /* la_get_symbol_name_cmp */
iterate_over_symbols,
&default_varobj_ops,
+ NULL,
+ NULL,
LANG_MAGIC
};
@@ -874,6 +876,8 @@ const struct language_defn auto_language_defn =
NULL, /* la_get_symbol_name_cmp */
iterate_over_symbols,
&default_varobj_ops,
+ NULL,
+ NULL,
LANG_MAGIC
};
@@ -917,6 +921,8 @@ const struct language_defn local_language_defn =
NULL, /* la_get_symbol_name_cmp */
iterate_over_symbols,
&default_varobj_ops,
+ NULL,
+ NULL,
LANG_MAGIC
};
@@ -35,6 +35,7 @@ struct value_print_options;
struct type_print_options;
struct lang_varobj_ops;
struct parser_state;
+struct compile_instance;
#define MAX_FORTRAN_DIMS 7 /* Maximum number of F77 array dims. */
@@ -355,6 +356,36 @@ struct language_defn
/* Various operations on varobj. */
const struct lang_varobj_ops *la_varobj_ops;
+ /* If this language allows compilation from the gdb command line,
+ this method should be non-NULL. When called it should return
+ an instance of struct gcc_context appropriate to the language.
+ When defined this method must never return NULL; instead it
+ should throw an exception on failure. The returned compiler
+ instance is owned by its caller and must be deallocated by
+ calling its 'destroy' method. */
+
+ struct compile_instance *(*la_get_compile_instance) (void);
+
+ /* This method must be defined if 'la_get_gcc_context' is defined.
+ If 'la_get_gcc_context' is not defined, then this method is
+ ignored.
+
+ This takes the user-supplied text and returns a newly malloc'd
+ bit of code to compile. The caller owns the result.
+
+ INST is the compiler instance being used.
+ INPUT is the user's input text.
+ GDBARCH is the architecture to use.
+ EXPR_BLOCK is the block in which the expression is being
+ parsed.
+ EXPR_PC is the PC at which the expression is being parsed. */
+
+ char *(*la_compute_program) (struct compile_instance *inst,
+ const char *input,
+ struct gdbarch *gdbarch,
+ const struct block *expr_block,
+ CORE_ADDR expr_pc);
+
/* Add fields above this point, so the magic number is always last. */
/* Magic number for compat checking. */
@@ -394,6 +394,8 @@ const struct language_defn m2_language_defn =
NULL, /* la_get_symbol_name_cmp */
iterate_over_symbols,
&default_varobj_ops,
+ NULL,
+ NULL,
LANG_MAGIC
};
@@ -392,6 +392,8 @@ const struct language_defn objc_language_defn = {
NULL, /* la_get_symbol_name_cmp */
iterate_over_symbols,
&default_varobj_ops,
+ NULL,
+ NULL,
LANG_MAGIC
};
@@ -1139,6 +1139,8 @@ const struct language_defn opencl_language_defn =
NULL, /* la_get_symbol_name_cmp */
iterate_over_symbols,
&default_varobj_ops,
+ NULL,
+ NULL,
LANG_MAGIC
};
@@ -452,6 +452,8 @@ const struct language_defn pascal_language_defn =
NULL, /* la_get_symbol_name_cmp */
iterate_over_symbols,
&default_varobj_ops,
+ NULL,
+ NULL,
LANG_MAGIC
};
@@ -641,6 +641,21 @@ struct symbol_computed_ops
void (*tracepoint_var_ref) (struct symbol *symbol, struct gdbarch *gdbarch,
struct agent_expr *ax, struct axs_value *value);
+
+ /* Generate C code to compute the location of SYMBOL. The C code is
+ emitted to STREAM. GDBARCH is the current architecture and PC is
+ the PC at which SYMBOL's location should be evaluated.
+ REGISTERS_USED is a vector indexed by register number; the
+ generator function should set an element in this vector if the
+ corresponding register is needed by the location computation.
+ The generated C code must assign the location to a local
+ variable; this variable's name is RESULT_NAME. */
+
+ void (*generate_c_location) (struct symbol *symbol, struct ui_file *stream,
+ struct gdbarch *gdbarch,
+ unsigned char *registers_used,
+ CORE_ADDR pc, const char *result_name);
+
};
/* The methods needed to implement LOC_BLOCK for inferior functions.
new file mode 100644
@@ -0,0 +1,95 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 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/>. */
+
+// gcc -o gdb.base/compile-constvar.S -dA -S -g gdb.base/compile-constvar.c
+
+ .file "compile-constvar.c"
+ .file 1 "gdb.base/compile-constvar.c"
+ .section .debug_info,"",@progbits
+.Ldebug_info0:
+ .long .Lend-.Lstart # Length of Compilation Unit Info
+.Lstart:
+ .value 0x4 # DWARF version number
+ .long .Ldebug_abbrev0 # Offset Into Abbrev. Section
+ .byte 0x8 # Pointer Size (in bytes)
+ .uleb128 0x1 # (DIE (0xb) DW_TAG_compile_unit)
+ .long .LASF0 # DW_AT_producer: "GNU C 4.8.2 20131212 (Red Hat 4.8.2-7) -mtune=generic -march=x86-64 -g"
+ .byte 0x1 # DW_AT_language
+ .long .LASF1 # DW_AT_name: "gdb.base/compile-constvar.c"
+ .long .LASF2 # DW_AT_comp_dir: "/home/jkratoch/redhat/gdb-gdbjit/gdb/testsuite"
+ .uleb128 0x2 # (DIE (0x1d) DW_TAG_variable)
+ .long .LASF3 # DW_AT_name: "constvar"
+ .long .Linttype-.Ldebug_info0 # DW_AT_type
+ # DW_AT_external
+ .byte 0x3 # DW_AT_const_value
+.Linttype:
+ .uleb128 0x3 # (DIE (0x32) DW_TAG_base_type)
+ .byte 0x4 # DW_AT_byte_size
+ .byte 0x5 # DW_AT_encoding
+ .ascii "int\0" # DW_AT_name
+ .byte 0 # end of children of DIE 0xb
+.Lend:
+ .section .debug_abbrev,"",@progbits
+.Ldebug_abbrev0:
+ .uleb128 0x1 # (abbrev code)
+ .uleb128 0x11 # (TAG: DW_TAG_compile_unit)
+ .byte 0x1 # DW_children_yes
+ .uleb128 0x25 # (DW_AT_producer)
+ .uleb128 0xe # (DW_FORM_strp)
+ .uleb128 0x13 # (DW_AT_language)
+ .uleb128 0xb # (DW_FORM_data1)
+ .uleb128 0x3 # (DW_AT_name)
+ .uleb128 0xe # (DW_FORM_strp)
+ .uleb128 0x1b # (DW_AT_comp_dir)
+ .uleb128 0xe # (DW_FORM_strp)
+ .byte 0
+ .byte 0
+ .uleb128 0x2 # (abbrev code)
+ .uleb128 0x34 # (TAG: DW_TAG_variable)
+ .byte 0 # DW_children_no
+ .uleb128 0x3 # (DW_AT_name)
+ .uleb128 0xe # (DW_FORM_strp)
+ .uleb128 0x49 # (DW_AT_type)
+ .uleb128 0x13 # (DW_FORM_ref4)
+ .uleb128 0x3f # (DW_AT_external)
+ .uleb128 0x19 # (DW_FORM_flag_present)
+ .uleb128 0x1c # (DW_AT_const_value)
+ .uleb128 0xb # (DW_FORM_data1)
+ .byte 0
+ .byte 0
+ .uleb128 0x3 # (abbrev code)
+ .uleb128 0x24 # (TAG: DW_TAG_base_type)
+ .byte 0 # DW_children_no
+ .uleb128 0xb # (DW_AT_byte_size)
+ .uleb128 0xb # (DW_FORM_data1)
+ .uleb128 0x3e # (DW_AT_encoding)
+ .uleb128 0xb # (DW_FORM_data1)
+ .uleb128 0x3 # (DW_AT_name)
+ .uleb128 0x8 # (DW_FORM_string)
+ .byte 0
+ .byte 0
+ .byte 0
+ .section .debug_str,"MS",@progbits,1
+.LASF1:
+ .string "gdb.base/compile-constvar.c"
+.LASF3:
+ .string "constvar"
+.LASF2:
+ .string ""
+.LASF0:
+ .string "GNU C 4.8.2 20131212 (Red Hat 4.8.2-7) -mtune=generic -march=x86-64 -g"
+ .ident "GCC: (GNU) 4.8.2 20131212 (Red Hat 4.8.2-7)"
new file mode 100644
@@ -0,0 +1,18 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 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/>. */
+
+int constvar = 5;
new file mode 100644
@@ -0,0 +1,26 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 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/>. */
+
+// Make 'globalvar' lookup working.
+#pragma GCC user_expression
+
+void
+_gdb_expr (void)
+{
+ globalvar = 3;
+ globalvar += 4;
+}
new file mode 100644
@@ -0,0 +1,24 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 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/>. */
+
+int
+func_nodebug (int neg)
+{
+ return -neg;
+}
+
+int unresolved = 20;
new file mode 100644
@@ -0,0 +1,46 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 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/>. */
+
+#include <setjmp.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+// Make 'done' lookup working.
+#pragma GCC user_expression
+
+static void
+foo ()
+{
+ jmp_buf env;
+
+ switch (setjmp (env))
+ {
+ case 2:
+ done = 1;
+ return;
+ case 0:
+ longjmp (env, 2);
+ break;
+ }
+ abort ();
+}
+
+void
+_gdb_expr (void)
+{
+ foo ();
+}
new file mode 100644
@@ -0,0 +1,24 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 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/>. */
+
+static int done;
+
+int
+main (void)
+{
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,34 @@
+# Copyright 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/>.
+
+standard_testfile .c compile-setjmp-mod.c
+
+if { [prepare_for_testing ${testfile}.exp $testfile] } {
+ return -1
+}
+
+if ![runto_main] {
+ return -1
+}
+
+if {[skip_compile_feature_tests]} {
+ untested "could not find libcc1 shared library"
+ return -1
+}
+
+gdb_test_no_output "compile file -r ${srcdir}/${subdir}/$srcfile2" \
+ "compile file -r"
+
+gdb_test "p done" " = 1"
new file mode 100644
@@ -0,0 +1,26 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 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/>. */
+
+extern int globalvar;
+
+int shlibvar = 10;
+
+void
+shlib_func (void)
+{
+ globalvar = 1;
+}
new file mode 100644
@@ -0,0 +1,130 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 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/>. */
+
+#define SOME_MACRO 23
+#define ARG_MACRO(X, Y) ((X) + (Y) - 1)
+
+
+enum enum_type {
+ ONE = 1,
+ TWO = 2
+};
+
+typedef int v4 __attribute__ ((vector_size (16)));
+
+union union_type;
+
+struct struct_type {
+ char charfield;
+ unsigned char ucharfield;
+ short shortfield;
+ unsigned short ushortfield;
+ int intfield;
+ unsigned int uintfield;
+ unsigned int bitfield : 3;
+ long longfield;
+ unsigned long ulongfield;
+ enum enum_type enumfield;
+ float floatfield;
+ double doublefield;
+ const union union_type *ptrfield;
+ struct struct_type *selffield;
+ int arrayfield[5];
+ _Complex double complexfield;
+ _Bool boolfield;
+ v4 vectorfield;
+};
+
+typedef int inttypedef;
+
+union union_type {
+ int intfield;
+ inttypedef typedeffield;
+};
+
+/* volatile provides some coverage of the conversion code. */
+volatile struct struct_type struct_object;
+
+union union_type union_object;
+
+
+enum ulonger_enum_type {
+ REALLY_MINUS_1 = -1UL,
+};
+
+enum ulonger_enum_type ulonger;
+
+enum longer_enum_type {
+ MINUS_1 = -1,
+ FORCE_TO_LONG = 1L << ((8 * sizeof (long)) - 2)
+};
+
+enum longer_enum_type longer;
+
+int globalvar = 10;
+
+static void
+func_static (int addend)
+{
+ globalvar += addend;
+}
+
+void
+func_global (int subtrahend)
+{
+ globalvar -= subtrahend;
+}
+
+void
+no_args_or_locals (void)
+{
+ /* no_args_or_locals breakpoint */
+}
+
+int *intptr;
+int globalshadow = 10;
+static int staticshadow = 20;
+int externed = 7;
+
+int
+main (void)
+{
+ int localvar = 50;
+ int shadowed = 51;
+ int bound = 3;
+ int unresolved = 10;
+ int globalshadow = 100;
+ int staticshadow = 200;
+ int externed = 9;
+ int f = 0;
+
+ static int static_local = 77000;
+
+ {
+ int another_local = 7;
+ int shadowed = 52;
+ extern int unresolved;
+ extern int externed;
+
+ int vla[bound];
+
+ func_static (0); /* break-here */
+ no_args_or_locals ();
+ }
+
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,357 @@
+# Copyright 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/>.
+
+standard_testfile .c compile-shlib.c compile-constvar.S compile-nodebug.c
+
+get_compiler_info
+set options {}
+if [test_compiler_info gcc*] {
+ lappend options additional_flags=-g3
+}
+
+if { ![istarget x86_64-*-* ] || ![is_lp64_target] } {
+ verbose "Skipping x86_64 LOC_CONST test."
+ set srcfile3 ""
+}
+
+set srcfilesoptions [list ${srcfile} ${options}]
+if { $srcfile3 != "" } {
+ lappend srcfilesoptions $srcfile3 ${options}
+}
+lappend srcfilesoptions $srcfile4 "nodebug"
+if { [eval build_executable_from_specs ${testfile}.exp $testfile {$options} ${srcfilesoptions}] } {
+ return -1
+}
+
+clean_restart ${testfile}
+
+#
+# Test command without an running inferior.
+#
+gdb_test "compile code int i=2;" \
+ "The program must be running for the compile command to work.*" \
+ "Test compile code command without running inferior"
+
+gdb_test "compile int i=2;" \
+ "The program must be running for the compile command to work.*" \
+ "Test compile command without running inferior"
+
+gdb_test "compile file -r ${srcdir}/${subdir}/${testfile}-mod.c" \
+ "The program must be running for the compile command to work.*" \
+ "Test compile file command without running inferior"
+
+if ![runto_main] {
+ return -1
+}
+
+if {[skip_compile_feature_tests]} {
+ untested "could not find libcc1 shared library"
+ return -1
+}
+
+#
+# Test delimiter for code, and arguments.
+#
+
+gdb_test_no_output "compile -- f = 10" \
+ "Test abbreviations and code delimiter"
+
+gdb_test "compile f = 10;" ".*= 10;: No such file.*" \
+ "Test abbreviations and code collision"
+
+gdb_test_no_output "compile -r -- _gdb_expr(){int i = 5;}" \
+ "Test delimiter with -r"
+
+gdb_test_no_output "compile -raw -- _gdb_expr(){int i = 5;}" \
+ "Test delimiter with -raw"
+
+gdb_test "compile -- -r _gdb_expr(){int i = 5;}" \
+ ".* error: 'r' undeclared \\(first use in this function\\).*" \
+ "Test delimiter with -r after it"
+
+gdb_test "p globalvar" " = 10" "expect 10"
+
+gdb_test_no_output "compile code globalvar = 11" \
+ "set variable without trailing semicolon"
+gdb_test "p globalvar" " = 11" "check variable without trailing semicolon"
+
+gdb_test_no_output "compile code globalvar = SOME_MACRO;" \
+ "set variable from macro"
+gdb_test "p globalvar" " = 23" "expect 23"
+
+gdb_test_no_output "compile code globalvar = ARG_MACRO(0, 0);" \
+ "set variable from function-like macro"
+gdb_test "p globalvar" " = -1" "expect -1"
+
+gdb_test_no_output "compile code globalvar = 42;" "set variable"
+gdb_test "p globalvar" " = 42" "expect 42"
+
+gdb_test_no_output "compile code globalvar *= 2;" "modify variable"
+gdb_test "p globalvar" " = 84" "expect 84"
+
+gdb_test_multiple "compile code" "compile code multiline 1" { -re "\r\n>$" {} }
+gdb_test_multiple "globalvar = 10;" "compile code multiline 2" { -re "\r\n>$" {} }
+gdb_test_multiple "globalvar *= 2;" "compile code multiline 3" { -re "\r\n>$" {} }
+gdb_test_no_output "end" "compile code multiline 4"
+gdb_test "p globalvar" " = 20" "expect 20"
+
+gdb_test_no_output "compile file -r ${srcdir}/${subdir}/${testfile}-mod.c" \
+ "use external source file"
+gdb_test "p globalvar" " = 7" "expect 7"
+
+gdb_test_no_output "compile code func_static (2);" "call static function"
+gdb_test "p globalvar" " = 9" "expect 9"
+gdb_test_no_output "compile code func_global (1);" "call global function"
+gdb_test "p globalvar" " = 8" "expect 8"
+
+gdb_test_no_output \
+ "compile code globalvar = (sizeof (ulonger) == sizeof (long))" \
+ "compute size of ulonger"
+gdb_test "p globalvar" " = 1" "check size of ulonger"
+gdb_test_no_output \
+ "compile code globalvar = (sizeof (longer) == sizeof (long))" \
+ "compute size of longer"
+gdb_test "p globalvar" " = 1" "check size of longer"
+gdb_test_no_output "compile code globalvar = MINUS_1"
+gdb_test "p globalvar" " = -1" "check MINUS_1"
+
+gdb_test_no_output "compile code globalvar = static_local"
+gdb_test "p globalvar" " = 77000" "check static_local"
+
+gdb_test_no_output "compile code static int staticvar = 5; intptr = &staticvar" \
+ "keep jit in memory"
+gdb_test "p *intptr" " = 5" "expect 5"
+
+gdb_test "compile code func_doesnotexist ();" "warning: Could not find symbol \"func_doesnotexist\" for .*"
+
+gdb_test "compile code *(volatile int *) 0 = 0;" \
+ "The program being debugged was signaled while in a function called from GDB\\.\r\nGDB remains in the frame where the signal was received\\.\r\n.*" \
+ "compile code segfault first"
+gdb_test "bt" \
+ "\r\n#0 \[^\r\n\]* in _gdb_expr \[^\r\n\]*\r\n#1 <function called from gdb>\r\n.*"
+
+set test "p/x \$pc"
+set infcall_pc 0
+gdb_test_multiple $test $test {
+ -re " = (0x\[0-9a-f\]+)\r\n$gdb_prompt $" {
+ set infcall_pc $expect_out(1,string)
+ pass $test
+ }
+}
+
+gdb_test "info sym $infcall_pc" "\r\n_gdb_expr .*" "info sym found"
+gdb_test "return" "\r\n#0 main .*" "return" \
+ "Make _gdb_expr return now\\? \\(y or n\\) " "y"
+gdb_test "info sym $infcall_pc" "\r\nNo symbol matches .*" "info sym not found"
+
+gdb_test_no_output "set unwindonsignal on"
+gdb_test "compile code *(volatile int *) 0 = 0;" \
+ "The program being debugged was signaled while in a function called from GDB\\.\r\nGDB has restored the context to what it was before the call\\.\r\n.*" \
+ "compile code segfault second"
+
+gdb_breakpoint [gdb_get_line_number "break-here"]
+gdb_continue_to_breakpoint "break-here" ".* break-here .*"
+
+gdb_test "p localvar" " = 50" "expect localvar 50"
+
+gdb_test_no_output "compile code localvar = 12;" "set localvar"
+gdb_test "p localvar" " = 12" "expect 12"
+
+gdb_test_no_output "compile code localvar *= 2;" "modify localvar"
+gdb_test "p localvar" " = 24" "expect 24"
+
+gdb_test_no_output "compile code localvar = shadowed" \
+ "test shadowing"
+gdb_test "p localvar" " = 52" "expect 52"
+
+gdb_test_no_output "compile code localvar = externed"
+gdb_test "p localvar" " = 7" "test extern in inner scope"
+
+gdb_test_no_output "compile code vla\[2\] = 7"
+gdb_test "p vla\[2\]" " = 7"
+gdb_test_no_output \
+ "compile code localvar = (sizeof (vla) == bound * sizeof (vla\[0\]))"
+gdb_test "p localvar" " = 1"
+
+#
+# Test setting fields and also many different types.
+#
+
+gdb_test_no_output "compile code struct_object.selffield = &struct_object"
+gdb_test "print struct_object.selffield == &struct_object" " = 1"
+
+gdb_test_no_output "compile code struct_object.charfield = 1"
+gdb_test "print struct_object.charfield" " = 1 '\\\\001'"
+gdb_test_no_output "compile code struct_object.ucharfield = 1"
+gdb_test "print struct_object.ucharfield" " = 1 '\\\\001'"
+
+foreach {field value} {
+ shortfield -5
+ ushortfield 5
+ intfield -7
+ uintfield 7
+ bitfield 2
+ longfield -9
+ ulongfield 9
+ enumfield ONE
+ floatfield 1
+ doublefield 2
+} {
+ gdb_test_no_output "compile code struct_object.$field = $value"
+ gdb_test "print struct_object.$field" " = $value"
+}
+
+gdb_test_no_output "compile code struct_object.arrayfield\[2\] = 7"
+gdb_test "print struct_object.arrayfield" \
+ " = \\{0, 0, 7, 0, 0\\}"
+
+gdb_test_no_output "compile code struct_object.complexfield = 7 + 5i"
+gdb_test "print struct_object.complexfield" " = 7 \\+ 5 \\* I"
+
+gdb_test_no_output "compile code struct_object.boolfield = 1"
+gdb_test "print struct_object.boolfield" " = true"
+
+gdb_test_no_output "compile code struct_object.vectorfield\[2\] = 7"
+gdb_test "print struct_object.vectorfield" \
+ " = \\{0, 0, 7, 0\\}"
+
+gdb_test_no_output "compile code union_object.typedeffield = 7"
+gdb_test "print union_object.typedeffield" " = 7"
+gdb_test "print union_object.intfield" " = 7"
+
+
+# LOC_UNRESOLVED tests.
+
+gdb_test "print unresolved" " = 20"
+gdb_test "compile code globalvar = unresolved;"
+gdb_test "print globalvar" " = 20" "print unresolved value"
+
+# Test shadowing with global and static variables.
+
+gdb_test_no_output "compile code globalshadow += 1;"
+gdb_test "print globalshadow" " = 101"
+gdb_test_no_output "compile code extern int globalshadow; globalshadow += 5;"
+gdb_test "print 'compile.c'::globalshadow" " = 15"
+gdb_test "print globalshadow" " = 101" "print globalshadow second time"
+gdb_test_no_output "compile code staticshadow += 2;"
+gdb_test "print staticshadow" " = 202"
+# "extern int staticshadow;" cannot access static variable.
+
+# Raw code cannot refer to locals.
+# As it references global variable we need the #pragma.
+# For #pragma we need multiline input.
+gdb_test_multiple "compile code -r" "compile code -r multiline 1" { -re "\r\n>$" {} }
+gdb_test_multiple "#pragma GCC user_expression" "compile code -r multiline 2" { -re "\r\n>$" {} }
+gdb_test_multiple "void _gdb_expr(void) { globalshadow = 77000; }" "compile code -r multiline 3" { -re "\r\n>$" {} }
+gdb_test_no_output "end" "compile code -r multiline 4"
+gdb_test "print 'compile.c'::globalshadow" " = 77000" \
+ "check globalshadow with -r"
+
+#
+# Test the case where the registers structure would not normally have
+# any fields.
+#
+
+gdb_breakpoint [gdb_get_line_number "no_args_or_locals breakpoint"]
+gdb_continue_to_breakpoint "no_args_or_locals"
+
+gdb_test_no_output "compile code globalvar = 77;" "set variable to 77"
+gdb_test "p globalvar" " = 77" "expect 77"
+
+
+# Test reference to minimal_symbol, not (full) symbol.
+
+gdb_test_no_output "compile code globalvar = func_nodebug (75);" \
+ "call func_nodebug"
+gdb_test "p globalvar" " = -75" "expect -75"
+gdb_test_no_output \
+ "compile code int (*funcp) (int) = func_nodebug; globalvar = funcp (76);" \
+ "call func_nodebug indirectly"
+gdb_test "p globalvar" " = -76" "expect -76"
+
+
+# Test compiled module memory protection.
+
+gdb_test_no_output "set debug compile on"
+gdb_test "compile code static const int readonly = 1; *(int *) &readonly = 2;" \
+ "The program being debugged was signaled while in a function called from GDB\\.\r\nGDB has restored the context to what it was before the call\\.\r\n.*"
+gdb_test_no_output "set debug compile off"
+
+
+#
+# Some simple coverage tests.
+#
+
+gdb_test "show debug compile" "Compile debugging is .*"
+gdb_test "show compile-args" \
+ "Compile command command-line arguments are .*"
+gdb_test "compile code -z" "Unknown argument.*"
+
+gdb_test "set lang java" \
+ "Warning: the current language does not match this frame."
+gdb_test "compile code globalvar" "No compiler support for this language\."
+gdb_test_no_output "set lang auto"
+
+gdb_test_no_output "compile code union union_type newdecl_u"
+gdb_test_no_output "compile code struct struct_type newdecl_s"
+gdb_test_no_output "compile code inttypedef newdecl_i"
+
+gdb_test "compile file" \
+ "You must provide a filename for this command.*" \
+ "Test compile file without a filename"
+gdb_test "compile file -r" \
+ "You must provide a filename with the raw option set.*" \
+ "Test compile file and raw option without a filename"
+gdb_test "compile file -z" \
+ "Unknown argument.*" \
+ "Test compile file with unknown argument"
+
+
+# LOC_CONST tests.
+
+if { $srcfile3 != "" } {
+ gdb_test "p constvar" " = 3"
+ gdb_test "info addr constvar" {Symbol "constvar" is constant\.}
+
+ gdb_test "compile code globalvar = constvar;"
+ gdb_test "print globalvar" " = 3" "print constvar value"
+} else {
+ untested "print constvar value"
+}
+
+# Shared library tests.
+
+if {[skip_shlib_tests]} {
+ untested "skipping shlib tests"
+ return;
+}
+
+set libbin [standard_output_file ${testfile}-shlib.so]
+if { [gdb_compile_shlib ${srcdir}/${subdir}/${srcfile2} $libbin {debug}] != ""
+ || [build_executable $testfile ${testfile}-shlib $srcfile \
+ [list debug ldflags=$libbin]] == -1 } {
+ return -1
+}
+
+clean_restart ${testfile}-shlib
+if ![runto_main] {
+ return -1
+}
+
+gdb_test_no_output "compile code shlib_func ();" "call shared library function"
+gdb_test "p globalvar" " = 1" "expect 1"
+
+gdb_test_no_output "compile code shlibvar += 5;" "modify shared library variable"
+gdb_test "p shlibvar" " = 15" "expect 15"
new file mode 100644
@@ -0,0 +1,424 @@
+# Copyright 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/>.
+
+# Some coverage testing of DWARF operators for the compiler
+# integration.
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+if {![dwarf2_support]} {
+ return 0
+}
+
+standard_testfile dw2-ifort-parameter.c gdbjit-ops.S
+
+#
+# A port of the pr10770.c test code to the DWARF assembler format.
+#
+
+set assert_tos_non0 {
+ bra 3
+ skip -3
+}
+
+set assert_tos_0 [subst {
+ lit0
+ eq
+ $assert_tos_non0
+}]
+
+set program [subst {
+ lit0
+ nop
+ $assert_tos_0
+ lit1
+ const1u 1
+ eq
+ $assert_tos_non0
+ lit16
+ const2u 16
+ eq
+ $assert_tos_non0
+ lit31
+ const4u 31
+ ne
+ $assert_tos_0
+ lit1
+ neg
+ const1s -1
+ eq
+ $assert_tos_non0
+ lit16
+ neg
+ const2s -16
+ ne
+ $assert_tos_0
+ lit31
+ const4s -31
+ neg
+ ne
+ $assert_tos_0
+ lit7
+ dup
+ plus_uconst 2
+ lit9
+ eq
+ $assert_tos_non0
+ lit7
+ eq
+ $assert_tos_non0
+ lit20
+ lit1
+ drop
+ lit20
+ eq
+ $assert_tos_non0
+ lit17
+ lit19
+ over
+ lit17
+ eq
+ $assert_tos_non0
+ lit19
+ eq
+ $assert_tos_non0
+ lit17
+ eq
+ $assert_tos_non0
+ lit1
+ lit2
+ lit3
+ lit4
+ pick 2
+ lit2
+ eq
+ $assert_tos_non0
+ lit4
+ eq
+ $assert_tos_non0
+ lit3
+ eq
+ $assert_tos_non0
+ pick 0
+ lit2
+ eq
+ $assert_tos_non0
+ lit2
+ eq
+ $assert_tos_non0
+ lit1
+ eq
+ $assert_tos_non0
+ lit6
+ lit12
+ swap
+ lit6
+ eq
+ $assert_tos_non0
+ lit12
+ eq
+ $assert_tos_non0
+ lit7
+ lit8
+ lit9
+ rot
+ lit8
+ eq
+ $assert_tos_non0
+ lit7
+ eq
+ $assert_tos_non0
+ lit9
+ eq
+ $assert_tos_non0
+ lit7
+ abs
+ lit7
+ eq
+ $assert_tos_non0
+ const1s -123
+ abs
+ const1u 123
+ eq
+ $assert_tos_non0
+ lit3
+ lit6
+ and
+ lit2
+ eq
+ $assert_tos_non0
+ lit3
+ lit6
+ or
+ lit7
+ eq
+ $assert_tos_non0
+ lit17
+ lit2
+ minus
+ lit15
+ eq
+ $assert_tos_non0
+ # Divide is signed truncating toward zero.
+ const1s -6
+ const1s -2
+ div
+ lit3
+ eq
+ $assert_tos_non0
+ const1s -7
+ const1s 3
+ div
+ const1s -2
+ eq
+ $assert_tos_non0
+ # Modulo is unsigned.
+ const1s -6
+ const1s -4
+ mod
+ const1s -6
+ eq
+ $assert_tos_non0
+ const1s -6
+ lit4
+ mod
+ lit2
+ eq
+ $assert_tos_non0
+ lit6
+ const1s -4
+ mod
+ lit6
+ eq
+ $assert_tos_non0
+ # Signed modulo can be implemented using 'over over div mul minus'.
+ const1s -6
+ const1s -4
+ over
+ over
+ div
+ mul
+ minus
+ const1s -2
+ eq
+ $assert_tos_non0
+ const1s -7
+ lit3
+ over
+ over
+ div
+ mul
+ minus
+ const1s -1
+ eq
+ $assert_tos_non0
+ lit7
+ const1s -3
+ over
+ over
+ div
+ mul
+ minus
+ lit1
+ eq
+ $assert_tos_non0
+ lit16
+ lit31
+ plus_uconst 1
+ mul
+ const2u 512
+ eq
+ $assert_tos_non0
+ lit5
+ not
+ lit31
+ and
+ lit26
+ eq
+ $assert_tos_non0
+ lit12
+ lit31
+ plus
+ const1u 43
+ eq
+ $assert_tos_non0
+ const1s -6
+ lit2
+ plus
+ const1s -4
+ eq
+ $assert_tos_non0
+ const1s -6
+ plus_uconst 3
+ const1s -3
+ eq
+ $assert_tos_non0
+ lit16
+ lit4
+ shl
+ const2u 256
+ eq
+ $assert_tos_non0
+ lit16
+ lit3
+ shr
+ lit2
+ eq
+ $assert_tos_non0
+ const1s -16
+ lit3
+ shra
+ const1s -2
+ eq
+ $assert_tos_non0
+ lit3
+ lit6
+ xor
+ lit5
+ eq
+ $assert_tos_non0
+ lit3
+ lit6
+ le
+ $assert_tos_non0
+ lit3
+ lit3
+ le
+ $assert_tos_non0
+ lit6
+ lit3
+ le
+ $assert_tos_0
+ lit3
+ lit6
+ lt
+ $assert_tos_non0
+ lit3
+ lit3
+ lt
+ $assert_tos_0
+ lit6
+ lit3
+ lt
+ $assert_tos_0
+ lit3
+ lit6
+ ge
+ $assert_tos_0
+ lit3
+ lit3
+ ge
+ $assert_tos_non0
+ lit6
+ lit3
+ ge
+ $assert_tos_non0
+ lit3
+ lit6
+ gt
+ $assert_tos_0
+ lit3
+ lit3
+ gt
+ $assert_tos_0
+ lit6
+ lit3
+ gt
+ $assert_tos_non0
+ const1s -6
+ lit1
+ shr
+ lit0
+ gt
+ $assert_tos_non0
+ const1s -6
+ lit1
+ shra
+ lit0
+ lt
+ $assert_tos_non0
+ # Finally some result.
+ addr ptr
+}]
+
+# Make some DWARF for the test.
+set asm_file [standard_output_file $srcfile2]
+Dwarf::assemble $asm_file {
+ # Creating a CU with 4-byte addresses lets this test link on both
+ # 32- and 64-bit machines.
+ cu { addr_size 4 } {
+
+ declare_labels int_label
+ extern func_start func_end ptr
+
+ compile_unit {
+ {name file1.txt}
+ {language @DW_LANG_C}
+ {low_pc func_start addr}
+ {high_pc func_end addr}
+ } {
+ global program
+
+ int_label: base_type {
+ {name int}
+ {byte_size 4 sdata}
+ {encoding @DW_ATE_signed}
+ }
+
+ subprogram {
+ {external 1 flag}
+ {name func}
+ {low_pc func_start addr}
+ {high_pc func_end addr}
+ } {
+ formal_parameter {
+ {name param}
+ {variable_parameter 1 flag}
+ {type :$int_label}
+ {location $program SPECIAL_expr}
+ }
+
+ formal_parameter {
+ {name optimized_out}
+ {variable_parameter 1 flag}
+ {type :$int_label}
+ }
+ }
+ }
+ }
+}
+
+if { [prepare_for_testing ${testfile}.exp ${testfile} \
+ [list $srcfile $asm_file] {nodebug}] } {
+ return -1
+}
+
+if ![runto func] {
+ return -1
+}
+
+if {[skip_compile_feature_tests]} {
+ untested "could not find libcc1 shared library"
+ return -1
+}
+
+# If we have a bug, this will hang.
+gdb_test_no_output "compile code param"
+
+# We can't access optimized-out variables, but their presence should
+# not affect compilations that don't refer to them.
+gdb_test "compile code optimized_out" \
+ ".*optimized out.*Compilation failed."
new file mode 100644
@@ -0,0 +1,40 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 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/>. */
+
+#include <pthread.h>
+
+__thread int global_scope;
+
+static __thread int static_scope;
+
+int foo ()
+{
+ /* Ensure we link against pthreads even with --as-needed. */
+ pthread_testcancel();
+ return 27;
+}
+
+int
+main (void)
+{
+ static __thread int local_scope;
+
+ /* break-here */
+ foo ();
+
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,42 @@
+# Copyright 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/>. */
+
+standard_testfile .c
+
+if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" \
+ executable {debug}] != "" } {
+ return -1
+}
+
+clean_restart ${binfile}
+if ![runto_main] then {
+ fail "Can't run to main"
+ return 0
+}
+
+if {[skip_compile_feature_tests]} {
+ untested "could not find libcc1 shared library"
+ return -1
+}
+
+gdb_test "compile code local_scope = 1" \
+ "warning: .* compiled code."
+gdb_test "print local_scope" " = 1"
+gdb_test "compile code static_scope = 2" \
+ "warning: .* compiled code."
+gdb_test "print static_scope" " = 2"
+gdb_test "compile code global_scope = 3" \
+ "warning: .* compiled code."
+gdb_test "print global_scope" " = 3"
@@ -2454,6 +2454,23 @@ proc skip_libstdcxx_probe_tests {} {
return $ok
}
+# Return 1 if we should skip tests of the "compile" feature.
+# This must be invoked after the inferior has been started.
+
+proc skip_compile_feature_tests {} {
+ global gdb_prompt
+
+ set result 0
+ gdb_test_multiple "compile code -- ;" "check for working compile command" {
+ "Could not load libcc1.*\r\n$gdb_prompt $" {
+ set result 1
+ }
+ -re "\r\n$gdb_prompt $" {
+ }
+ }
+ return $result
+}
+
# Check whether we're testing with the remote or extended-remote
# targets.