diff libcc1/libcc1.cc @ 111:04ced10e8804

gcc 7
author kono
date Fri, 27 Oct 2017 22:46:09 +0900
parents
children 84e7813d76e9
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libcc1/libcc1.cc	Fri Oct 27 22:46:09 2017 +0900
@@ -0,0 +1,674 @@
+/* The library used by gdb.
+   Copyright (C) 2014-2017 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 "c-compiler-name.h"
+#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);
+}