aboutsummaryrefslogtreecommitdiff
path: root/gdb/compile/compile-c-support.c
diff options
context:
space:
mode:
Diffstat (limited to 'gdb/compile/compile-c-support.c')
-rw-r--r--gdb/compile/compile-c-support.c399
1 files changed, 399 insertions, 0 deletions
diff --git a/gdb/compile/compile-c-support.c b/gdb/compile/compile-c-support.c
new file mode 100644
index 0000000..48a81cb
--- /dev/null
+++ b/gdb/compile/compile-c-support.c
@@ -0,0 +1,399 @@
+/* C language support for compilation.
+
+ Copyright (C) 2014 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "defs.h"
+#include "compile-internal.h"
+#include "compile.h"
+#include "gdb-dlfcn.h"
+#include "c-lang.h"
+#include "macrotab.h"
+#include "macroscope.h"
+#include "regcache.h"
+
+/* See compile-internal.h. */
+
+const char *
+c_get_mode_for_size (int size)
+{
+ const char *mode = NULL;
+
+ switch (size)
+ {
+ case 1:
+ mode = "QI";
+ break;
+ case 2:
+ mode = "HI";
+ break;
+ case 4:
+ mode = "SI";
+ break;
+ case 8:
+ mode = "DI";
+ break;
+ default:
+ internal_error (__FILE__, __LINE__, _("Invalid GCC mode size %d."), size);
+ }
+
+ return mode;
+}
+
+/* See compile-internal.h. */
+
+char *
+c_get_range_decl_name (const struct dynamic_prop *prop)
+{
+ return xstrprintf ("__gdb_prop_%s", host_address_to_string (prop));
+}
+
+
+
+#define STR(x) #x
+#define STRINGIFY(x) STR(x)
+
+/* Helper function for c_get_compile_context. Open the GCC front-end
+ shared library and return the symbol specified by the current
+ GCC_C_FE_CONTEXT. */
+
+static gcc_c_fe_context_function *
+load_libcc (void)
+{
+ void *handle;
+ gcc_c_fe_context_function *func;
+
+ /* gdb_dlopen will call error () on an error, so no need to check
+ value. */
+ handle = gdb_dlopen (STRINGIFY (GCC_C_FE_LIBCC));
+ func = (gcc_c_fe_context_function *) gdb_dlsym (handle,
+ STRINGIFY (GCC_C_FE_CONTEXT));
+
+ if (func == NULL)
+ error (_("could not find symbol %s in library %s"),
+ STRINGIFY (GCC_C_FE_CONTEXT),
+ STRINGIFY (GCC_C_FE_LIBCC));
+ return func;
+}
+
+/* Return the compile instance associated with the current context.
+ This function calls the symbol returned from the load_libcc
+ function. This will provide the gcc_c_context. */
+
+struct compile_instance *
+c_get_compile_context (void)
+{
+ static gcc_c_fe_context_function *func;
+
+ struct gcc_c_context *context;
+
+ if (func == NULL)
+ {
+ func = load_libcc ();
+ gdb_assert (func != NULL);
+ }
+
+ context = (*func) (GCC_FE_VERSION_0, GCC_C_FE_VERSION_0);
+ if (context == NULL)
+ error (_("The loaded version of GCC does not support the required version "
+ "of the API."));
+
+ return new_compile_instance (context);
+}
+
+
+
+/* Write one macro definition. */
+
+static void
+print_one_macro (const char *name, const struct macro_definition *macro,
+ struct macro_source_file *source, int line,
+ void *user_data)
+{
+ struct ui_file *file = user_data;
+
+ /* Don't print command-line defines. They will be supplied another
+ way. */
+ if (line == 0)
+ return;
+
+ fprintf_filtered (file, "#define %s", name);
+
+ if (macro->kind == macro_function_like)
+ {
+ int i;
+
+ fputs_filtered ("(", file);
+ for (i = 0; i < macro->argc; i++)
+ {
+ fputs_filtered (macro->argv[i], file);
+ if (i + 1 < macro->argc)
+ fputs_filtered (", ", file);
+ }
+ fputs_filtered (")", file);
+ }
+
+ fprintf_filtered (file, " %s\n", macro->replacement);
+}
+
+/* Write macro definitions at PC to FILE. */
+
+static void
+write_macro_definitions (const struct block *block, CORE_ADDR pc,
+ struct ui_file *file)
+{
+ struct macro_scope *scope;
+
+ if (block != NULL)
+ scope = sal_macro_scope (find_pc_line (pc, 0));
+ else
+ scope = default_macro_scope ();
+ if (scope == NULL)
+ scope = user_macro_scope ();
+
+ if (scope != NULL && scope->file != NULL && scope->file->table != NULL)
+ macro_for_each_in_scope (scope->file, scope->line, print_one_macro, file);
+}
+
+/* Helper function to construct a header scope for a block of code.
+ Takes a scope argument which selects the correct header to
+ insert into BUF. */
+
+static void
+add_code_header (enum compile_i_scope_types type, struct ui_file *buf)
+{
+ switch (type)
+ {
+ case COMPILE_I_SIMPLE_SCOPE:
+ fputs_unfiltered ("void "
+ GCC_FE_WRAPPER_FUNCTION
+ " (struct "
+ COMPILE_I_SIMPLE_REGISTER_STRUCT_TAG
+ " *"
+ COMPILE_I_SIMPLE_REGISTER_ARG_NAME
+ ") {\n",
+ buf);
+ break;
+ case COMPILE_I_RAW_SCOPE:
+ break;
+ default:
+ gdb_assert_not_reached (_("Unknown compiler scope reached."));
+ }
+}
+
+/* Helper function to construct a footer scope for a block of code.
+ Takes a scope argument which selects the correct footer to
+ insert into BUF. */
+
+static void
+add_code_footer (enum compile_i_scope_types type, struct ui_file *buf)
+{
+ switch (type)
+ {
+ case COMPILE_I_SIMPLE_SCOPE:
+ fputs_unfiltered ("}\n", buf);
+ break;
+ case COMPILE_I_RAW_SCOPE:
+ break;
+ default:
+ gdb_assert_not_reached (_("Unknown compiler scope reached."));
+ }
+}
+
+/* Generate a structure holding all the registers used by the function
+ we're generating. */
+
+static void
+generate_register_struct (struct ui_file *stream, struct gdbarch *gdbarch,
+ const unsigned char *registers_used)
+{
+ int i;
+ int seen = 0;
+
+ fputs_unfiltered ("struct " COMPILE_I_SIMPLE_REGISTER_STRUCT_TAG " {\n",
+ stream);
+
+ if (registers_used != NULL)
+ for (i = 0; i < gdbarch_num_regs (gdbarch); ++i)
+ {
+ if (registers_used[i])
+ {
+ struct type *regtype = check_typedef (register_type (gdbarch, i));
+ char *regname = compile_register_name_mangled (gdbarch, i);
+ struct cleanup *cleanups = make_cleanup (xfree, regname);
+
+ seen = 1;
+
+ /* You might think we could use type_print here. However,
+ target descriptions often use types with names like
+ "int64_t", which may not be defined in the inferior
+ (and in any case would not be looked up due to the
+ #pragma business). So, we take a much simpler
+ approach: for pointer- or integer-typed registers, emit
+ the field in the most direct way; and for other
+ register types (typically flags or vectors), emit a
+ maximally-aligned array of the correct size. */
+
+ fputs_unfiltered (" ", stream);
+ switch (TYPE_CODE (regtype))
+ {
+ case TYPE_CODE_PTR:
+ fprintf_filtered (stream, "void *%s", regname);
+ break;
+
+ case TYPE_CODE_INT:
+ {
+ const char *mode
+ = c_get_mode_for_size (TYPE_LENGTH (regtype));
+
+ if (mode != NULL)
+ {
+ if (TYPE_UNSIGNED (regtype))
+ fputs_unfiltered ("unsigned ", stream);
+ fprintf_unfiltered (stream,
+ "int %s"
+ " __attribute__ ((__mode__(__%s__)))",
+ regname,
+ mode);
+ break;
+ }
+ }
+
+ /* Fall through. */
+
+ default:
+ fprintf_unfiltered (stream,
+ " unsigned char %s[%d]"
+ " __attribute__((__aligned__("
+ "__BIGGEST_ALIGNMENT__)))",
+ regname,
+ TYPE_LENGTH (regtype));
+ }
+ fputs_unfiltered (";\n", stream);
+
+ do_cleanups (cleanups);
+ }
+ }
+
+ if (!seen)
+ fputs_unfiltered (" char " COMPILE_I_SIMPLE_REGISTER_DUMMY ";\n",
+ stream);
+
+ fputs_unfiltered ("};\n\n", stream);
+}
+
+/* Take the source code provided by the user with the 'compile'
+ command, and compute the additional wrapping, macro, variable and
+ register operations needed. INPUT is the source code derived from
+ the 'compile' command, GDBARCH is the architecture to use when
+ computing above, EXPR_BLOCK denotes the block relevant contextually
+ to the inferior when the expression was created, and EXPR_PC
+ indicates the value of $PC. */
+
+char *
+c_compute_program (struct compile_instance *inst,
+ const char *input,
+ struct gdbarch *gdbarch,
+ const struct block *expr_block,
+ CORE_ADDR expr_pc)
+{
+ struct ui_file *buf, *var_stream = NULL;
+ char *code;
+ struct cleanup *cleanup;
+ struct compile_c_instance *context = (struct compile_c_instance *) inst;
+
+ buf = mem_fileopen ();
+ cleanup = make_cleanup_ui_file_delete (buf);
+
+ write_macro_definitions (expr_block, expr_pc, buf);
+
+ /* Do not generate local variable information for "raw"
+ compilations. In this case we aren't emitting our own function
+ and the user's code may only refer to globals. */
+ if (inst->scope != COMPILE_I_RAW_SCOPE)
+ {
+ unsigned char *registers_used;
+ int i;
+
+ /* Generate the code to compute variable locations, but do it
+ before generating the function header, so we can define the
+ register struct before the function body. This requires a
+ temporary stream. */
+ var_stream = mem_fileopen ();
+ make_cleanup_ui_file_delete (var_stream);
+ registers_used = generate_c_for_variable_locations (context,
+ var_stream, gdbarch,
+ expr_block, expr_pc);
+ make_cleanup (xfree, registers_used);
+
+ generate_register_struct (buf, gdbarch, registers_used);
+
+ fputs_unfiltered ("typedef unsigned int"
+ " __attribute__ ((__mode__(__pointer__)))"
+ " __gdb_uintptr;\n",
+ buf);
+ fputs_unfiltered ("typedef int"
+ " __attribute__ ((__mode__(__pointer__)))"
+ " __gdb_intptr;\n",
+ buf);
+
+ // Iterate all log2 sizes in bytes supported by c_get_mode_for_size.
+ for (i = 0; i < 4; ++i)
+ {
+ const char *mode = c_get_mode_for_size (1 << i);
+
+ gdb_assert (mode != NULL);
+ fprintf_unfiltered (buf,
+ "typedef int"
+ " __attribute__ ((__mode__(__%s__)))"
+ " __gdb_int_%s;\n",
+ mode, mode);
+ }
+ }
+
+ add_code_header (inst->scope, buf);
+
+ if (inst->scope == COMPILE_I_SIMPLE_SCOPE)
+ {
+ ui_file_put (var_stream, ui_file_write_for_put, buf);
+ fputs_unfiltered ("#pragma GCC user_expression\n", buf);
+ }
+
+ /* The user expression has to be in its own scope, so that "extern"
+ works properly. Otherwise gcc thinks that the "extern"
+ declaration is in the same scope as the declaration provided by
+ gdb. */
+ if (inst->scope != COMPILE_I_RAW_SCOPE)
+ fputs_unfiltered ("{\n", buf);
+
+ fputs_unfiltered ("#line 1 \"gdb command line\"\n", buf);
+ fputs_unfiltered (input, buf);
+ fputs_unfiltered ("\n", buf);
+
+ /* For larger user expressions the automatic semicolons may be
+ confusing. */
+ if (strchr (input, '\n') == NULL)
+ fputs_unfiltered (";\n", buf);
+
+ if (inst->scope != COMPILE_I_RAW_SCOPE)
+ fputs_unfiltered ("}\n", buf);
+
+ add_code_footer (inst->scope, buf);
+ code = ui_file_xstrdup (buf, NULL);
+ do_cleanups (cleanup);
+ return code;
+}