diff options
Diffstat (limited to 'libcc1/gdbctx.hh')
-rw-r--r-- | libcc1/gdbctx.hh | 253 |
1 files changed, 251 insertions, 2 deletions
diff --git a/libcc1/gdbctx.hh b/libcc1/gdbctx.hh index 1c8d87d..4a48381 100644 --- a/libcc1/gdbctx.hh +++ b/libcc1/gdbctx.hh @@ -23,16 +23,38 @@ along with GCC; see the file COPYING3. If not see namespace cc1_plugin { // The compiler context that we hand back to our caller. + // Due to this, the entire implementation is in this header. template<typename T> struct base_gdb_plugin : public T { - explicit base_gdb_plugin (const gcc_base_vtable *v) + base_gdb_plugin (const char *plugin_name_, const char *base_name, + int version) : verbose (false), + plugin_name (plugin_name_), + fe_version (version), + compiler_name (base_name), compilerp (new compiler (verbose)) { - this->base.ops = v; + vtable = + { + GCC_FE_VERSION_1, + do_set_arguments_v0, + do_set_source_file, + do_set_print_callback, + do_compile_v0, + do_destroy, + do_set_verbose, + do_compile, + do_set_arguments, + do_set_triplet_regexp, + do_set_driver_filename, + }; + + this->base.ops = &vtable; } + virtual ~base_gdb_plugin () = default; + // A convenience function to print something. void print (const char *str) { @@ -53,6 +75,10 @@ namespace cc1_plugin connection.reset (new local_connection (fd, aux_fd, this)); } + // This is called just before compilation begins. It should set + // any needed callbacks on the connection. + virtual void add_callbacks () = 0; + // A local subclass of connection that holds a back-pointer to the // context object that we provide to our caller. class local_connection : public cc1_plugin::connection @@ -84,7 +110,230 @@ namespace cc1_plugin /* Non-zero as an equivalent to gcc driver option "-v". */ bool verbose; + const char *plugin_name; + int fe_version; + + const char *compiler_name; std::unique_ptr<cc1_plugin::compiler> compilerp; + + private: + + struct gcc_base_vtable vtable; + + static inline base_gdb_plugin<T> * + get_self (gcc_base_context *s) + { + T *sub = (T *) s; + return static_cast<base_gdb_plugin<T> *> (sub); + } + + static void + do_set_verbose (struct gcc_base_context *s, int /* bool */ verbose) + { + base_gdb_plugin<T> *self = get_self (s); + + self->set_verbose (verbose != 0); + } + + static char * + do_set_arguments (struct gcc_base_context *s, + int argc, char **argv) + { + base_gdb_plugin<T> *self = get_self (s); + + std::string compiler; + char *errmsg = self->compilerp->find (self->compiler_name, compiler); + if (errmsg != NULL) + return errmsg; + + self->args.push_back (compiler); + + for (int i = 0; i < argc; ++i) + self->args.push_back (argv[i]); + + return NULL; + } + + static char * + do_set_triplet_regexp (struct gcc_base_context *s, + const char *triplet_regexp) + { + base_gdb_plugin<T> *self = get_self (s); + + self->compilerp.reset + (new cc1_plugin::compiler_triplet_regexp (self->verbose, + triplet_regexp)); + return NULL; + } + + static char * + do_set_driver_filename (struct gcc_base_context *s, + const char *driver_filename) + { + base_gdb_plugin<T> *self = get_self (s); + + self->compilerp.reset + (new cc1_plugin::compiler_driver_filename (self->verbose, + driver_filename)); + return NULL; + } + + static char * + do_set_arguments_v0 (struct gcc_base_context *s, + const char *triplet_regexp, + int argc, char **argv) + { + char *errmsg = do_set_triplet_regexp (s, triplet_regexp); + if (errmsg != NULL) + return errmsg; + + return do_set_arguments (s, argc, argv); + } + + static void + do_set_source_file (struct gcc_base_context *s, + const char *file) + { + base_gdb_plugin<T> *self = get_self (s); + + self->source_file = file; + } + + static void + do_set_print_callback (struct gcc_base_context *s, + void (*print_function) (void *datum, + const char *message), + void *datum) + { + base_gdb_plugin<T> *self = get_self (s); + + self->print_function = print_function; + self->print_datum = datum; + } + + int fork_exec (char **argv, int spair_fds[2], int stderr_fds[2]) + { + pid_t child_pid = fork (); + + if (child_pid == -1) + { + close (spair_fds[0]); + close (spair_fds[1]); + close (stderr_fds[0]); + close (stderr_fds[1]); + return 0; + } + + if (child_pid == 0) + { + // Child. + dup2 (stderr_fds[1], 1); + dup2 (stderr_fds[1], 2); + close (stderr_fds[0]); + close (stderr_fds[1]); + close (spair_fds[0]); + + execvp (argv[0], argv); + _exit (127); + } + else + { + // Parent. + close (spair_fds[1]); + close (stderr_fds[1]); + + cc1_plugin::status result = cc1_plugin::FAIL; + if (connection->send ('H') + && ::cc1_plugin::marshall (connection.get (), fe_version)) + result = connection->wait_for_query (); + + close (spair_fds[0]); + close (stderr_fds[0]); + + while (true) + { + int status; + + if (waitpid (child_pid, &status, 0) == -1) + { + if (errno != EINTR) + return 0; + } + + if (!WIFEXITED (status) || WEXITSTATUS (status) != 0) + return 0; + break; + } + + if (!result) + return 0; + return 1; + } + } + + static int + do_compile (struct gcc_base_context *s, + const char *filename) + { + base_gdb_plugin<T> *self = get_self (s); + + int fds[2]; + if (socketpair (AF_UNIX, SOCK_STREAM, 0, fds) != 0) + { + self->print ("could not create socketpair\n"); + return 0; + } + + int stderr_fds[2]; + if (pipe (stderr_fds) != 0) + { + self->print ("could not create pipe\n"); + close (fds[0]); + close (fds[1]); + return 0; + } + + self->args.push_back (std::string ("-fplugin=") + self->plugin_name); + self->args.push_back (std::string ("-fplugin-arg-") + self->plugin_name + + "-fd=" + std::to_string (fds[1])); + + self->args.push_back (self->source_file); + self->args.push_back ("-c"); + self->args.push_back ("-o"); + self->args.push_back (filename); + if (self->verbose) + self->args.push_back ("-v"); + + self->set_connection (fds[0], stderr_fds[0]); + + self->add_callbacks (); + + char **argv = new (std::nothrow) char *[self->args.size () + 1]; + if (argv == NULL) + return 0; + + for (unsigned int i = 0; i < self->args.size (); ++i) + argv[i] = const_cast<char *> (self->args[i].c_str ()); + argv[self->args.size ()] = NULL; + + return self->fork_exec (argv, fds, stderr_fds); + } + + static int + do_compile_v0 (struct gcc_base_context *s, const char *filename, + int verbose) + { + do_set_verbose (s, verbose); + return do_compile (s, filename); + } + + static void + do_destroy (struct gcc_base_context *s) + { + base_gdb_plugin<T> *self = get_self (s); + + delete self; + } }; // Instances of this rpc<> template function are installed into the |