diff options
Diffstat (limited to 'gdb/compile/compile.c')
-rw-r--r-- | gdb/compile/compile.c | 651 |
1 files changed, 651 insertions, 0 deletions
diff --git a/gdb/compile/compile.c b/gdb/compile/compile.c new file mode 100644 index 0000000..6d3d16e --- /dev/null +++ b/gdb/compile/compile.c @@ -0,0 +1,651 @@ +/* General Compile and inject code + + Copyright (C) 2014 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include "defs.h" +#include "interps.h" +#include "ui-out.h" +#include "command.h" +#include "cli/cli-script.h" +#include "cli/cli-utils.h" +#include "completer.h" +#include "gdbcmd.h" +#include "compile.h" +#include "compile-internal.h" +#include "compile-object-load.h" +#include "compile-object-run.h" +#include "language.h" +#include "frame.h" +#include "source.h" +#include "block.h" +#include "arch-utils.h" +#include "filestuff.h" +#include "target.h" +#include "osabi.h" + + + +/* Initial filename for temporary files. */ + +#define TMP_PREFIX "/tmp/gdbobj-" + +/* Hold "compile" commands. */ + +static struct cmd_list_element *compile_command_list; + +/* Debug flag for "compile" commands. */ + +int compile_debug; + +/* Implement "show debug compile". */ + +static void +show_compile_debug (struct ui_file *file, int from_tty, + struct cmd_list_element *c, const char *value) +{ + fprintf_filtered (file, _("Compile debugging is %s.\n"), value); +} + + + +/* Check *ARG for a "-raw" or "-r" argument. Return 0 if not seen. + Return 1 if seen and update *ARG. */ + +static int +check_raw_argument (char **arg) +{ + *arg = skip_spaces (*arg); + + if (arg != NULL + && (check_for_argument (arg, "-raw", sizeof ("-raw") - 1) + || check_for_argument (arg, "-r", sizeof ("-r") - 1))) + return 1; + return 0; +} + +/* Handle the input from the 'compile file' command. The "compile + file" command is used to evaluate an expression contained in a file + that may contain calls to the GCC compiler. */ + +static void +compile_file_command (char *arg, int from_tty) +{ + enum compile_i_scope_types scope = COMPILE_I_SIMPLE_SCOPE; + char *buffer; + struct cleanup *cleanup; + + cleanup = make_cleanup_restore_integer (&interpreter_async); + interpreter_async = 0; + + /* Check the user did not just <enter> after command. */ + if (arg == NULL) + error (_("You must provide a filename for this command.")); + + /* Check if a raw (-r|-raw) argument is provided. */ + if (arg != NULL && check_raw_argument (&arg)) + { + scope = COMPILE_I_RAW_SCOPE; + arg = skip_spaces (arg); + } + + /* After processing arguments, check there is a filename at the end + of the command. */ + if (arg[0] == '\0') + error (_("You must provide a filename with the raw option set.")); + + if (arg[0] == '-') + error (_("Unknown argument specified.")); + + arg = skip_spaces (arg); + arg = gdb_abspath (arg); + make_cleanup (xfree, arg); + buffer = xstrprintf ("#include \"%s\"\n", arg); + make_cleanup (xfree, buffer); + eval_compile_command (NULL, buffer, scope); + do_cleanups (cleanup); +} + +/* Handle the input from the 'compile code' command. The + "compile code" command is used to evaluate an expression that may + contain calls to the GCC compiler. The language expected in this + compile command is the language currently set in GDB. */ + +static void +compile_code_command (char *arg, int from_tty) +{ + struct cleanup *cleanup; + enum compile_i_scope_types scope = COMPILE_I_SIMPLE_SCOPE; + + cleanup = make_cleanup_restore_integer (&interpreter_async); + interpreter_async = 0; + + if (arg != NULL && check_raw_argument (&arg)) + { + scope = COMPILE_I_RAW_SCOPE; + arg = skip_spaces (arg); + } + + arg = skip_spaces (arg); + + if (arg != NULL && !check_for_argument (&arg, "--", sizeof ("--") - 1)) + { + if (arg[0] == '-') + error (_("Unknown argument specified.")); + } + + if (arg && *arg) + eval_compile_command (NULL, arg, scope); + else + { + struct command_line *l = get_command_line (compile_control, ""); + + make_cleanup_free_command_lines (&l); + l->control_u.compile.scope = scope; + execute_control_command_untraced (l); + } + + do_cleanups (cleanup); +} + +/* A cleanup function to remove a directory and all its contents. */ + +static void +do_rmdir (void *arg) +{ + const char *dir = arg; + char *zap; + + gdb_assert (strncmp (dir, TMP_PREFIX, strlen (TMP_PREFIX)) == 0); + zap = concat ("rm -rf ", dir, (char *) NULL); + system (zap); +} + +/* Return the name of the temporary directory to use for .o files, and + arrange for the directory to be removed at shutdown. */ + +static const char * +get_compile_file_tempdir (void) +{ + static char *tempdir_name; + +#define TEMPLATE TMP_PREFIX "XXXXXX" + char tname[sizeof (TEMPLATE)]; + + if (tempdir_name != NULL) + return tempdir_name; + + strcpy (tname, TEMPLATE); +#undef TEMPLATE + tempdir_name = mkdtemp (tname); + if (tempdir_name == NULL) + perror_with_name (_("Could not make temporary directory")); + + tempdir_name = xstrdup (tempdir_name); + make_final_cleanup (do_rmdir, tempdir_name); + return tempdir_name; +} + +/* Compute the names of source and object files to use. The names are + allocated by malloc and should be freed by the caller. */ + +static void +get_new_file_names (char **source_file, char **object_file) +{ + static int seq; + const char *dir = get_compile_file_tempdir (); + + ++seq; + *source_file = xstrprintf ("%s%sout%d.c", dir, SLASH_STRING, seq); + *object_file = xstrprintf ("%s%sout%d.o", dir, SLASH_STRING, seq); +} + +/* Get the block and PC at which to evaluate an expression. */ + +static const struct block * +get_expr_block_and_pc (CORE_ADDR *pc) +{ + const struct block *block = get_selected_block (pc); + + if (block == NULL) + { + struct symtab_and_line cursal = get_current_source_symtab_and_line (); + + if (cursal.symtab) + block = BLOCKVECTOR_BLOCK (SYMTAB_BLOCKVECTOR (cursal.symtab), + STATIC_BLOCK); + if (block != NULL) + *pc = BLOCK_START (block); + } + else + *pc = BLOCK_START (block); + + return block; +} + +/* Call gdb_buildargv, set its result for S into *ARGVP but calculate also the + number of parsed arguments into *ARGCP. If gdb_buildargv has returned NULL + then *ARGCP is set to zero. */ + +static void +build_argc_argv (const char *s, int *argcp, char ***argvp) +{ + *argvp = gdb_buildargv (s); + *argcp = countargv (*argvp); +} + +/* String for 'set compile-args' and 'show compile-args'. */ +static char *compile_args; + +/* Parsed form of COMPILE_ARGS. COMPILE_ARGS_ARGV is NULL terminated. */ +static int compile_args_argc; +static char **compile_args_argv; + +/* Implement 'set compile-args'. */ + +static void +set_compile_args (char *args, int from_tty, struct cmd_list_element *c) +{ + freeargv (compile_args_argv); + build_argc_argv (compile_args, &compile_args_argc, &compile_args_argv); +} + +/* Implement 'show compile-args'. */ + +static void +show_compile_args (struct ui_file *file, int from_tty, + struct cmd_list_element *c, const char *value) +{ + fprintf_filtered (file, _("Compile command command-line arguments " + "are \"%s\".\n"), + value); +} + +/* Append ARGC and ARGV (as parsed by build_argc_argv) to *ARGCP and *ARGVP. + ARGCP+ARGVP can be zero+NULL and also ARGC+ARGV can be zero+NULL. */ + +static void +append_args (int *argcp, char ***argvp, int argc, char **argv) +{ + int argi; + + *argvp = xrealloc (*argvp, (*argcp + argc + 1) * sizeof (**argvp)); + + for (argi = 0; argi < argc; argi++) + (*argvp)[(*argcp)++] = xstrdup (argv[argi]); + (*argvp)[(*argcp)] = NULL; +} + +/* Return DW_AT_producer parsed for get_selected_frame () (if any). + Return NULL otherwise. + + GCC already filters its command-line arguments only for the suitable ones to + put into DW_AT_producer - see GCC function gen_producer_string. */ + +static const char * +get_selected_pc_producer_options (void) +{ + CORE_ADDR pc = get_frame_pc (get_selected_frame (NULL)); + struct compunit_symtab *symtab = find_pc_compunit_symtab (pc); + const char *cs; + + if (symtab == NULL || symtab->producer == NULL + || strncmp (symtab->producer, "GNU ", strlen ("GNU ")) != 0) + return NULL; + + cs = symtab->producer; + while (*cs != 0 && *cs != '-') + cs = skip_spaces_const (skip_to_space_const (cs)); + if (*cs != '-') + return NULL; + return cs; +} + +/* Produce final vector of GCC compilation options. First element is target + size ("-m64", "-m32" etc.), optionally followed by DW_AT_producer options + and then compile-args string GDB variable. */ + +static void +get_args (const struct compile_instance *compiler, struct gdbarch *gdbarch, + int *argcp, char ***argvp) +{ + const char *cs_producer_options; + int argc_compiler; + char **argv_compiler; + + build_argc_argv (gdbarch_gcc_target_options (gdbarch), + argcp, argvp); + + cs_producer_options = get_selected_pc_producer_options (); + if (cs_producer_options != NULL) + { + int argc_producer; + char **argv_producer; + + build_argc_argv (cs_producer_options, &argc_producer, &argv_producer); + append_args (argcp, argvp, argc_producer, argv_producer); + freeargv (argv_producer); + } + + build_argc_argv (compiler->gcc_target_options, + &argc_compiler, &argv_compiler); + append_args (argcp, argvp, argc_compiler, argv_compiler); + freeargv (argv_compiler); + + append_args (argcp, argvp, compile_args_argc, compile_args_argv); +} + +/* A cleanup function to destroy a gdb_gcc_instance. */ + +static void +cleanup_compile_instance (void *arg) +{ + struct compile_instance *inst = arg; + + inst->destroy (inst); +} + +/* A cleanup function to unlink a file. */ + +static void +cleanup_unlink_file (void *arg) +{ + const char *filename = arg; + + unlink (filename); +} + +/* A helper function suitable for use as the "print_callback" in the + compiler object. */ + +static void +print_callback (void *ignore, const char *message) +{ + fputs_filtered (message, gdb_stderr); +} + +/* Process the compilation request. On success it returns the object + file name and *SOURCE_FILEP is set to source file name. On an + error condition, error () is called. The caller is responsible for + freeing both strings. */ + +static char * +compile_to_object (struct command_line *cmd, char *cmd_string, + enum compile_i_scope_types scope, + char **source_filep) +{ + char *code; + char *source_file, *object_file; + struct compile_instance *compiler; + struct cleanup *cleanup, *inner_cleanup; + const struct block *expr_block; + CORE_ADDR trash_pc, expr_pc; + int argc; + char **argv; + int ok; + FILE *src; + struct gdbarch *gdbarch = get_current_arch (); + const char *os_rx; + const char *arch_rx; + char *triplet_rx; + char *error_message; + + if (!target_has_execution) + error (_("The program must be running for the compile command to "\ + "work.")); + + expr_block = get_expr_block_and_pc (&trash_pc); + expr_pc = get_frame_address_in_block (get_selected_frame (NULL)); + + /* Set up instance and context for the compiler. */ + if (current_language->la_get_compile_instance == NULL) + error (_("No compiler support for this language.")); + compiler = current_language->la_get_compile_instance (); + cleanup = make_cleanup (cleanup_compile_instance, compiler); + + compiler->fe->ops->set_print_callback (compiler->fe, print_callback, NULL); + + compiler->scope = scope; + compiler->block = expr_block; + + /* From the provided expression, build a scope to pass to the + compiler. */ + if (cmd != NULL) + { + struct ui_file *stream = mem_fileopen (); + struct command_line *iter; + + make_cleanup_ui_file_delete (stream); + for (iter = cmd->body_list[0]; iter; iter = iter->next) + { + fputs_unfiltered (iter->line, stream); + fputs_unfiltered ("\n", stream); + } + + code = ui_file_xstrdup (stream, NULL); + make_cleanup (xfree, code); + } + else if (cmd_string != NULL) + code = cmd_string; + else + error (_("Neither a simple expression, or a multi-line specified.")); + + code = current_language->la_compute_program (compiler, code, gdbarch, + expr_block, expr_pc); + make_cleanup (xfree, code); + if (compile_debug) + fprintf_unfiltered (gdb_stdout, "debug output:\n\n%s", code); + + os_rx = osabi_triplet_regexp (gdbarch_osabi (gdbarch)); + arch_rx = gdbarch_gnu_triplet_regexp (gdbarch); + triplet_rx = concat (arch_rx, "-[^-]*-", os_rx, (char *) NULL); + make_cleanup (xfree, triplet_rx); + + /* Set compiler command-line arguments. */ + get_args (compiler, gdbarch, &argc, &argv); + make_cleanup_freeargv (argv); + + error_message = compiler->fe->ops->set_arguments (compiler->fe, triplet_rx, + argc, argv); + if (error_message != NULL) + { + make_cleanup (xfree, error_message); + error ("%s", error_message); + } + + if (compile_debug) + { + int argi; + + fprintf_unfiltered (gdb_stdout, "Passing %d compiler options:\n", argc); + for (argi = 0; argi < argc; argi++) + fprintf_unfiltered (gdb_stdout, "Compiler option %d: <%s>\n", + argi, argv[argi]); + } + + get_new_file_names (&source_file, &object_file); + inner_cleanup = make_cleanup (xfree, source_file); + make_cleanup (xfree, object_file); + + src = gdb_fopen_cloexec (source_file, "w"); + if (src == NULL) + perror_with_name (_("Could not open source file for writing")); + make_cleanup (cleanup_unlink_file, source_file); + if (fputs (code, src) == EOF) + perror_with_name (_("Could not write to source file")); + fclose (src); + + if (compile_debug) + fprintf_unfiltered (gdb_stdout, "source file produced: %s\n\n", + source_file); + + /* Call the compiler and start the compilation process. */ + compiler->fe->ops->set_source_file (compiler->fe, source_file); + + if (!compiler->fe->ops->compile (compiler->fe, object_file, + compile_debug)) + error (_("Compilation failed.")); + + if (compile_debug) + fprintf_unfiltered (gdb_stdout, "object file produced: %s\n\n", + object_file); + + discard_cleanups (inner_cleanup); + do_cleanups (cleanup); + *source_filep = source_file; + return object_file; +} + +/* The "compile" prefix command. */ + +static void +compile_command (char *args, int from_tty) +{ + /* If a sub-command is not specified to the compile prefix command, + assume it is a direct code compilation. */ + compile_code_command (args, from_tty); +} + +/* See compile.h. */ + +void +eval_compile_command (struct command_line *cmd, char *cmd_string, + enum compile_i_scope_types scope) +{ + char *object_file, *source_file; + + object_file = compile_to_object (cmd, cmd_string, scope, &source_file); + if (object_file != NULL) + { + struct cleanup *cleanup_xfree, *cleanup_unlink; + struct compile_module *compile_module; + + cleanup_xfree = make_cleanup (xfree, object_file); + make_cleanup (xfree, source_file); + cleanup_unlink = make_cleanup (cleanup_unlink_file, object_file); + make_cleanup (cleanup_unlink_file, source_file); + compile_module = compile_object_load (object_file, source_file); + discard_cleanups (cleanup_unlink); + do_cleanups (cleanup_xfree); + compile_object_run (compile_module); + } +} + +/* See compile/compile-internal.h. */ + +char * +compile_register_name_mangled (struct gdbarch *gdbarch, int regnum) +{ + const char *regname = gdbarch_register_name (gdbarch, regnum); + + return xstrprintf ("__%s", regname); +} + +/* See compile/compile-internal.h. */ + +int +compile_register_name_demangle (struct gdbarch *gdbarch, + const char *regname) +{ + int regnum; + + if (regname[0] != '_' || regname[1] != '_') + error (_("Invalid register name \"%s\"."), regname); + regname += 2; + + for (regnum = 0; regnum < gdbarch_num_regs (gdbarch); regnum++) + if (strcmp (regname, gdbarch_register_name (gdbarch, regnum)) == 0) + return regnum; + + error (_("Cannot find gdbarch register \"%s\"."), regname); +} + +extern initialize_file_ftype _initialize_compile; + +void +_initialize_compile (void) +{ + struct cmd_list_element *c = NULL; + + add_prefix_cmd ("compile", class_obscure, compile_command, + _("\ +Command to compile source code and inject it into the inferior."), + &compile_command_list, "compile ", 1, &cmdlist); + add_com_alias ("expression", "compile", class_obscure, 0); + + add_cmd ("code", class_obscure, compile_code_command, + _("\ +Compile, inject, and execute code.\n\ +\n\ +Usage: compile code [-r|-raw] [--] [CODE]\n\ +-r|-raw: Suppress automatic 'void _gdb_expr () { CODE }' wrapping.\n\ +--: Do not parse any options beyond this delimiter. All text to the\n\ + right will be treated as source code.\n\ +\n\ +The source code may be specified as a simple one line expression, e.g.:\n\ +\n\ + compile code printf(\"Hello world\\n\");\n\ +\n\ +Alternatively, you can type the source code interactively.\n\ +You can invoke this mode when no argument is given to the command\n\ +(i.e.,\"compile code\" is typed with nothing after it). An\n\ +interactive prompt will be shown allowing you to enter multiple\n\ +lines of source code. Type a line containing \"end\" to indicate\n\ +the end of the source code."), + &compile_command_list); + + c = add_cmd ("file", class_obscure, compile_file_command, + _("\ +Evaluate a file containing source code.\n\ +\n\ +Usage: compile file [-r|-raw] [filename]\n\ +-r|-raw: Suppress automatic 'void _gdb_expr () { CODE }' wrapping."), + &compile_command_list); + set_cmd_completer (c, filename_completer); + + add_setshow_boolean_cmd ("compile", class_maintenance, &compile_debug, _("\ +Set compile command debugging."), _("\ +Show compile command debugging."), _("\ +When on, compile command debugging is enabled."), + NULL, show_compile_debug, + &setdebuglist, &showdebuglist); + + add_setshow_string_cmd ("compile-args", class_support, + &compile_args, + _("Set compile command GCC command-line arguments"), + _("Show compile command GCC command-line arguments"), + _("\ +Use options like -I (include file directory) or ABI settings.\n\ +String quoting is parsed like in shell, for example:\n\ + -mno-align-double \"-I/dir with a space/include\""), + set_compile_args, show_compile_args, &setlist, &showlist); + + /* Override flags possibly coming from DW_AT_producer. */ + compile_args = xstrdup ("-O0 -gdwarf-4" + /* We use -fPIC Otherwise GDB would need to reserve space large enough for + any object file in the inferior in advance to get the final address when + to link the object file to and additionally the default system linker + script would need to be modified so that one can specify there the + absolute target address. */ + " -fPIC" + /* We don't want warnings. */ + " -w" + /* Override CU's possible -fstack-protector-strong. */ + " -fno-stack-protector" + ); + set_compile_args (compile_args, 0, NULL); +} |