diff options
Diffstat (limited to 'gdb/compile/compile-c-support.c')
-rw-r--r-- | gdb/compile/compile-c-support.c | 399 |
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; +} |