| /* The library used by gdb. |
| Copyright (C) 2014-2021 Free Software Foundation, Inc. |
| |
| This file is part of GCC. |
| |
| GCC 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, or (at your option) any later |
| version. |
| |
| GCC 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 GCC; see the file COPYING3. If not see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #include <cc1plugin-config.h> |
| #include <vector> |
| #include <string> |
| #include <sys/socket.h> |
| #include <sys/types.h> |
| #include <unistd.h> |
| #include <sys/wait.h> |
| #include <stdio.h> |
| #include <errno.h> |
| #include <sys/stat.h> |
| #include <stdlib.h> |
| #include <sstream> |
| #include "marshall-c.hh" |
| #include "rpc.hh" |
| #include "connection.hh" |
| #include "names.hh" |
| #include "callbacks.hh" |
| #include "libiberty.h" |
| #include "xregex.h" |
| #include "findcomp.hh" |
| #include "compiler-name.hh" |
| #include "intl.h" |
| |
| struct libcc1; |
| |
| class libcc1_connection; |
| |
| // The C compiler context that we hand back to our caller. |
| struct libcc1 : public gcc_c_context |
| { |
| libcc1 (const gcc_base_vtable *, const gcc_c_fe_vtable *); |
| ~libcc1 (); |
| |
| // A convenience function to print something. |
| void print (const char *str) |
| { |
| this->print_function (this->print_datum, str); |
| } |
| |
| libcc1_connection *connection; |
| |
| gcc_c_oracle_function *binding_oracle; |
| gcc_c_symbol_address_function *address_oracle; |
| void *oracle_datum; |
| |
| void (*print_function) (void *datum, const char *message); |
| void *print_datum; |
| |
| std::vector<std::string> args; |
| std::string source_file; |
| |
| /* Non-zero as an equivalent to gcc driver option "-v". */ |
| bool verbose; |
| |
| /* Compiler to set by set_triplet_regexp or set_driver_filename. */ |
| class compiler |
| { |
| protected: |
| libcc1 *self_; |
| public: |
| compiler (libcc1 *self) : self_ (self) |
| { |
| } |
| virtual char *find (std::string &compiler) const; |
| virtual ~compiler () |
| { |
| } |
| } *compilerp; |
| |
| /* Compiler to set by set_triplet_regexp. */ |
| class compiler_triplet_regexp : public compiler |
| { |
| private: |
| std::string triplet_regexp_; |
| public: |
| virtual char *find (std::string &compiler) const; |
| compiler_triplet_regexp (libcc1 *self, std::string triplet_regexp) |
| : compiler (self), triplet_regexp_ (triplet_regexp) |
| { |
| } |
| virtual ~compiler_triplet_regexp () |
| { |
| } |
| }; |
| |
| /* Compiler to set by set_driver_filename. */ |
| class compiler_driver_filename : public compiler |
| { |
| private: |
| std::string driver_filename_; |
| public: |
| virtual char *find (std::string &compiler) const; |
| compiler_driver_filename (libcc1 *self, std::string driver_filename) |
| : compiler (self), driver_filename_ (driver_filename) |
| { |
| } |
| virtual ~compiler_driver_filename () |
| { |
| } |
| }; |
| }; |
| |
| // A local subclass of connection that holds a back-pointer to the |
| // gcc_c_context object that we provide to our caller. |
| class libcc1_connection : public cc1_plugin::connection |
| { |
| public: |
| |
| libcc1_connection (int fd, int aux_fd, libcc1 *b) |
| : connection (fd, aux_fd), |
| back_ptr (b) |
| { |
| } |
| |
| virtual void print (const char *buf) |
| { |
| back_ptr->print (buf); |
| } |
| |
| libcc1 *back_ptr; |
| }; |
| |
| libcc1::libcc1 (const gcc_base_vtable *v, |
| const gcc_c_fe_vtable *cv) |
| : connection (NULL), |
| binding_oracle (NULL), |
| address_oracle (NULL), |
| oracle_datum (NULL), |
| print_function (NULL), |
| print_datum (NULL), |
| args (), |
| source_file (), |
| verbose (false), |
| compilerp (new libcc1::compiler (this)) |
| { |
| base.ops = v; |
| c_ops = cv; |
| } |
| |
| libcc1::~libcc1 () |
| { |
| delete connection; |
| delete compilerp; |
| } |
| |
| |
| |
| // Enclose these functions in an anonymous namespace because they |
| // shouldn't be exported, but they can't be static because they're |
| // used as template arguments. |
| namespace { |
| // This is a wrapper function that is called by the RPC system and |
| // that then forwards the call to the library user. Note that the |
| // return value is not used; the type cannot be 'void' due to |
| // limitations in our simple RPC. |
| int |
| c_call_binding_oracle (cc1_plugin::connection *conn, |
| enum gcc_c_oracle_request request, |
| const char *identifier) |
| { |
| libcc1 *self = ((libcc1_connection *) conn)->back_ptr; |
| |
| self->binding_oracle (self->oracle_datum, self, request, identifier); |
| return 1; |
| } |
| |
| // This is a wrapper function that is called by the RPC system and |
| // that then forwards the call to the library user. |
| gcc_address |
| c_call_symbol_address (cc1_plugin::connection *conn, const char *identifier) |
| { |
| libcc1 *self = ((libcc1_connection *) conn)->back_ptr; |
| |
| return self->address_oracle (self->oracle_datum, self, identifier); |
| } |
| } /* anonymous namespace */ |
| |
| |
| |
| static void |
| set_callbacks (struct gcc_c_context *s, |
| gcc_c_oracle_function *binding_oracle, |
| gcc_c_symbol_address_function *address_oracle, |
| void *datum) |
| { |
| libcc1 *self = (libcc1 *) s; |
| |
| self->binding_oracle = binding_oracle; |
| self->address_oracle = address_oracle; |
| self->oracle_datum = datum; |
| } |
| |
| // Instances of these rpc<> template functions are installed into the |
| // "c_vtable". These functions are parameterized by type and method |
| // name and forward the call via the connection. |
| |
| template<typename R, const char *&NAME> |
| R rpc (struct gcc_c_context *s) |
| { |
| libcc1 *self = (libcc1 *) s; |
| R result; |
| |
| if (!cc1_plugin::call (self->connection, NAME, &result)) |
| return 0; |
| return result; |
| } |
| |
| template<typename R, const char *&NAME, typename A> |
| R rpc (struct gcc_c_context *s, A arg) |
| { |
| libcc1 *self = (libcc1 *) s; |
| R result; |
| |
| if (!cc1_plugin::call (self->connection, NAME, &result, arg)) |
| return 0; |
| return result; |
| } |
| |
| template<typename R, const char *&NAME, typename A1, typename A2> |
| R rpc (struct gcc_c_context *s, A1 arg1, A2 arg2) |
| { |
| libcc1 *self = (libcc1 *) s; |
| R result; |
| |
| if (!cc1_plugin::call (self->connection, NAME, &result, arg1, arg2)) |
| return 0; |
| return result; |
| } |
| |
| template<typename R, const char *&NAME, typename A1, typename A2, typename A3> |
| R rpc (struct gcc_c_context *s, A1 arg1, A2 arg2, A3 arg3) |
| { |
| libcc1 *self = (libcc1 *) s; |
| R result; |
| |
| if (!cc1_plugin::call (self->connection, NAME, &result, arg1, arg2, arg3)) |
| return 0; |
| return result; |
| } |
| |
| template<typename R, const char *&NAME, typename A1, typename A2, typename A3, |
| typename A4> |
| R rpc (struct gcc_c_context *s, A1 arg1, A2 arg2, A3 arg3, A4 arg4) |
| { |
| libcc1 *self = (libcc1 *) s; |
| R result; |
| |
| if (!cc1_plugin::call (self->connection, NAME, &result, arg1, arg2, arg3, |
| arg4)) |
| return 0; |
| return result; |
| } |
| |
| template<typename R, const char *&NAME, typename A1, typename A2, typename A3, |
| typename A4, typename A5> |
| R rpc (struct gcc_c_context *s, A1 arg1, A2 arg2, A3 arg3, A4 arg4, A5 arg5) |
| { |
| libcc1 *self = (libcc1 *) s; |
| R result; |
| |
| if (!cc1_plugin::call (self->connection, NAME, &result, arg1, arg2, arg3, |
| arg4, arg5)) |
| return 0; |
| return result; |
| } |
| |
| template<typename R, const char *&NAME, typename A1, typename A2, typename A3, |
| typename A4, typename A5, typename A6, typename A7> |
| R rpc (struct gcc_c_context *s, A1 arg1, A2 arg2, A3 arg3, A4 arg4, A5 arg5, |
| A6 arg6, A7 arg7) |
| { |
| libcc1 *self = (libcc1 *) s; |
| R result; |
| |
| if (!cc1_plugin::call (self->connection, NAME, &result, arg1, arg2, arg3, |
| arg4, arg5, arg6, arg7)) |
| return 0; |
| return result; |
| } |
| |
| static const struct gcc_c_fe_vtable c_vtable = |
| { |
| GCC_C_FE_VERSION_0, |
| set_callbacks, |
| |
| #define GCC_METHOD0(R, N) \ |
| rpc<R, cc1_plugin::c::N>, |
| #define GCC_METHOD1(R, N, A) \ |
| rpc<R, cc1_plugin::c::N, A>, |
| #define GCC_METHOD2(R, N, A, B) \ |
| rpc<R, cc1_plugin::c::N, A, B>, |
| #define GCC_METHOD3(R, N, A, B, C) \ |
| rpc<R, cc1_plugin::c::N, A, B, C>, |
| #define GCC_METHOD4(R, N, A, B, C, D) \ |
| rpc<R, cc1_plugin::c::N, A, B, C, D>, |
| #define GCC_METHOD5(R, N, A, B, C, D, E) \ |
| rpc<R, cc1_plugin::c::N, A, B, C, D, E>, |
| #define GCC_METHOD7(R, N, A, B, C, D, E, F, G) \ |
| rpc<R, cc1_plugin::c::N, A, B, C, D, E, F, G>, |
| |
| #include "gcc-c-fe.def" |
| |
| #undef GCC_METHOD0 |
| #undef GCC_METHOD1 |
| #undef GCC_METHOD2 |
| #undef GCC_METHOD3 |
| #undef GCC_METHOD4 |
| #undef GCC_METHOD5 |
| #undef GCC_METHOD7 |
| }; |
| |
| |
| |
| // Construct an appropriate regexp to match the compiler name. |
| static std::string |
| make_regexp (const char *triplet_regexp, const char *compiler) |
| { |
| std::stringstream buf; |
| |
| buf << "^" << triplet_regexp << "-"; |
| |
| // Quote the compiler name in case it has something funny in it. |
| for (const char *p = compiler; *p; ++p) |
| { |
| switch (*p) |
| { |
| case '.': |
| case '^': |
| case '$': |
| case '*': |
| case '+': |
| case '?': |
| case '(': |
| case ')': |
| case '[': |
| case '{': |
| case '\\': |
| case '|': |
| buf << '\\'; |
| break; |
| } |
| buf << *p; |
| } |
| buf << "$"; |
| |
| return buf.str (); |
| } |
| |
| static void |
| libcc1_set_verbose (struct gcc_base_context *s, int /* bool */ verbose) |
| { |
| libcc1 *self = (libcc1 *) s; |
| |
| self->verbose = verbose != 0; |
| } |
| |
| char * |
| libcc1::compiler::find (std::string &compiler ATTRIBUTE_UNUSED) const |
| { |
| return xstrdup (_("Compiler has not been specified")); |
| } |
| |
| char * |
| libcc1::compiler_triplet_regexp::find (std::string &compiler) const |
| { |
| std::string rx = make_regexp (triplet_regexp_.c_str (), C_COMPILER_NAME); |
| if (self_->verbose) |
| fprintf (stderr, _("searching for compiler matching regex %s\n"), |
| rx.c_str()); |
| regex_t triplet; |
| int code = regcomp (&triplet, rx.c_str (), REG_EXTENDED | REG_NOSUB); |
| if (code != 0) |
| { |
| size_t len = regerror (code, &triplet, NULL, 0); |
| char err[len]; |
| |
| regerror (code, &triplet, err, len); |
| |
| return concat ("Could not compile regexp \"", |
| rx.c_str (), |
| "\": ", |
| err, |
| (char *) NULL); |
| } |
| |
| if (!find_compiler (triplet, &compiler)) |
| { |
| regfree (&triplet); |
| return concat ("Could not find a compiler matching \"", |
| rx.c_str (), |
| "\"", |
| (char *) NULL); |
| } |
| regfree (&triplet); |
| if (self_->verbose) |
| fprintf (stderr, _("found compiler %s\n"), compiler.c_str()); |
| return NULL; |
| } |
| |
| char * |
| libcc1::compiler_driver_filename::find (std::string &compiler) const |
| { |
| // Simulate fnotice by fprintf. |
| if (self_->verbose) |
| fprintf (stderr, _("using explicit compiler filename %s\n"), |
| driver_filename_.c_str()); |
| compiler = driver_filename_; |
| return NULL; |
| } |
| |
| static char * |
| libcc1_set_arguments (struct gcc_base_context *s, |
| int argc, char **argv) |
| { |
| libcc1 *self = (libcc1 *) s; |
| |
| std::string compiler; |
| char *errmsg = self->compilerp->find (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 * |
| libcc1_set_triplet_regexp (struct gcc_base_context *s, |
| const char *triplet_regexp) |
| { |
| libcc1 *self = (libcc1 *) s; |
| |
| delete self->compilerp; |
| self->compilerp = new libcc1::compiler_triplet_regexp (self, triplet_regexp); |
| return NULL; |
| } |
| |
| static char * |
| libcc1_set_driver_filename (struct gcc_base_context *s, |
| const char *driver_filename) |
| { |
| libcc1 *self = (libcc1 *) s; |
| |
| delete self->compilerp; |
| self->compilerp = new libcc1::compiler_driver_filename (self, |
| driver_filename); |
| return NULL; |
| } |
| |
| static char * |
| libcc1_set_arguments_v0 (struct gcc_base_context *s, |
| const char *triplet_regexp, |
| int argc, char **argv) |
| { |
| char *errmsg = libcc1_set_triplet_regexp (s, triplet_regexp); |
| if (errmsg != NULL) |
| return errmsg; |
| |
| return libcc1_set_arguments (s, argc, argv); |
| } |
| |
| static void |
| libcc1_set_source_file (struct gcc_base_context *s, |
| const char *file) |
| { |
| libcc1 *self = (libcc1 *) s; |
| |
| self->source_file = file; |
| } |
| |
| static void |
| libcc1_set_print_callback (struct gcc_base_context *s, |
| void (*print_function) (void *datum, |
| const char *message), |
| void *datum) |
| { |
| libcc1 *self = (libcc1 *) s; |
| |
| self->print_function = print_function; |
| self->print_datum = datum; |
| } |
| |
| static int |
| fork_exec (libcc1 *self, 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 (self->connection->send ('H') |
| && ::cc1_plugin::marshall (self->connection, GCC_C_FE_VERSION_1)) |
| result = self->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 |
| libcc1_compile (struct gcc_base_context *s, |
| const char *filename) |
| { |
| libcc1 *self = (libcc1 *) 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 ("-fplugin=libcc1plugin"); |
| char buf[100]; |
| if (snprintf (buf, sizeof (buf), "-fplugin-arg-libcc1plugin-fd=%d", fds[1]) |
| >= (long) sizeof (buf)) |
| abort (); |
| self->args.push_back (buf); |
| |
| 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->connection = new libcc1_connection (fds[0], stderr_fds[0], self); |
| |
| cc1_plugin::callback_ftype *fun |
| = cc1_plugin::callback<int, |
| enum gcc_c_oracle_request, |
| const char *, |
| c_call_binding_oracle>; |
| self->connection->add_callback ("binding_oracle", fun); |
| |
| fun = cc1_plugin::callback<gcc_address, |
| const char *, |
| c_call_symbol_address>; |
| self->connection->add_callback ("address_oracle", fun); |
| |
| 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 fork_exec (self, argv, fds, stderr_fds); |
| } |
| |
| static int |
| libcc1_compile_v0 (struct gcc_base_context *s, const char *filename, |
| int verbose) |
| { |
| libcc1_set_verbose (s, verbose); |
| return libcc1_compile (s, filename); |
| } |
| |
| static void |
| libcc1_destroy (struct gcc_base_context *s) |
| { |
| libcc1 *self = (libcc1 *) s; |
| |
| delete self; |
| } |
| |
| static const struct gcc_base_vtable vtable = |
| { |
| GCC_FE_VERSION_1, |
| libcc1_set_arguments_v0, |
| libcc1_set_source_file, |
| libcc1_set_print_callback, |
| libcc1_compile_v0, |
| libcc1_destroy, |
| libcc1_set_verbose, |
| libcc1_compile, |
| libcc1_set_arguments, |
| libcc1_set_triplet_regexp, |
| libcc1_set_driver_filename, |
| }; |
| |
| extern "C" gcc_c_fe_context_function gcc_c_fe_context; |
| |
| #ifdef __GNUC__ |
| #pragma GCC visibility push(default) |
| #endif |
| |
| extern "C" |
| struct gcc_c_context * |
| gcc_c_fe_context (enum gcc_base_api_version base_version, |
| enum gcc_c_api_version c_version) |
| { |
| if ((base_version != GCC_FE_VERSION_0 && base_version != GCC_FE_VERSION_1) |
| || (c_version != GCC_C_FE_VERSION_0 && c_version != GCC_C_FE_VERSION_1)) |
| return NULL; |
| |
| return new libcc1 (&vtable, &c_vtable); |
| } |