From patchwork Fri Jul 18 18:55:04 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tom Tromey X-Patchwork-Id: 371700 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 1845714012E for ; Sat, 19 Jul 2014 04:55:35 +1000 (EST) DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:from :to:subject:references:date:in-reply-to:message-id:mime-version :content-type; q=dns; s=default; b=wUL8afUWbrCry0ITXtj9gOpVmUA04 f2zERm8q7hl1SfcrzAgoyO4yms0R+YMOi8YqeUpls9kJsO0R7wccdO6cyRbiTiPd CLW54C60MS9wLS1m+anuEagmtzyIuj/5c65yk/05ZPYurYPe+iOuzMX71m0H+kxz O4GoVW8mQuXkCw= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:from :to:subject:references:date:in-reply-to:message-id:mime-version :content-type; s=default; bh=9p5V0pTxK4nGOODHdjYcvOfPMHw=; b=mgH ISq82GWFmgMR+bY96Tvauheunxo0sfXdeyEjucSnkrEWFhh4rokpBHYsJaLAlZOa vNBlQa27HeVNWTnMjQvumZpv7prgvs5EKKlnmRAoR765l9bCghjKntEOIufeZtte xqJi70Xo+mq/edx38/Wr5XUz/9EVWg+5HkTwhEXI= Received: (qmail 5636 invoked by alias); 18 Jul 2014 18:55:24 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 5537 invoked by uid 89); 18 Jul 2014 18:55:20 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-2.0 required=5.0 tests=AWL, BAYES_00, KAM_STOCKGEN, RP_MATCHES_RCVD, SPF_HELO_PASS, SPF_PASS autolearn=no version=3.3.2 X-HELO: mx1.redhat.com Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (AES256-GCM-SHA384 encrypted) ESMTPS; Fri, 18 Jul 2014 18:55:13 +0000 Received: from int-mx11.intmail.prod.int.phx2.redhat.com (int-mx11.intmail.prod.int.phx2.redhat.com [10.5.11.24]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id s6IItCEK028244 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK) for ; Fri, 18 Jul 2014 14:55:12 -0400 Received: from barimba (ovpn-113-27.phx2.redhat.com [10.3.113.27]) by int-mx11.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id s6IIt57h028958 (version=TLSv1/SSLv3 cipher=AES128-GCM-SHA256 bits=128 verify=NO); Fri, 18 Jul 2014 14:55:06 -0400 From: Tom Tromey To: gcc-patches@gcc.gnu.org Subject: Re: [PATCH 5/5] add libcc1 References: <1400254001-12038-1-git-send-email-tromey@redhat.com> <87oayx4l0x.fsf@fleche.redhat.com> <87bntobp1f.fsf@fleche.redhat.com> Date: Fri, 18 Jul 2014 12:55:04 -0600 In-Reply-To: <87bntobp1f.fsf@fleche.redhat.com> (Tom Tromey's message of "Thu, 19 Jun 2014 14:52:12 -0600") Message-ID: <8738dyv6nr.fsf@fleche.redhat.com> User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/24.3 (gnu/linux) MIME-Version: 1.0 >>>>> "Tom" == Tom Tromey writes: Tom> I've edited this one down by removing the auto-generated stuff , and Tom> then compressed it. Tom> Here's a new version of patch #5. Tom> I've removed the generated code; let's see if it gets through without Tom> compression. Here's another new revision. Phil noticed that libcc1.so relies on some symbols from libiberty, and these weren't available in some gdb builds. This version of the patch changes the build so that libcc1 is linked against the pic libiberty in the gcc build tree, which should fix the issue. This version also addresses various comments made by Trevor Saunders, though it doesn't switch the code to use C++11. I think it is best here to follow the rest of gcc. Tom 2014-06-19 Phil Muldoon Tom Tromey * Makefile.def: Add libcc1 to host_modules. * configure.ac (host_tools): Add libcc1. * Makefile.in, configure: Rebuild. 2014-06-19 Phil Muldoon Jan Kratochvil Tom Tromey * aclocal.m4: New file. * callbacks.cc: New file. * callbacks.hh: New file. * cc1plugin-config.h.in: New file. * configure: New file. * configure.ac: New file. * connection.cc: New file. * connection.hh: New file. * findcomp.cc: New file. * findcomp.hh: New file. * libcc1.cc: New file. * libcc1plugin.sym: New file. * libcc1.sym: New file. * Makefile.am: New file. * Makefile.in: New file. * marshall.cc: New file. * marshall.hh: New file. * names.cc: New file. * names.hh: New file. * plugin.cc: New file. * rpc.hh: New file. * status.hh: New file. diff --git a/Makefile.def b/Makefile.def index 239ad36..ed9bac6 100644 --- a/Makefile.def +++ b/Makefile.def @@ -121,6 +121,8 @@ host_modules= { module= gnattools; }; host_modules= { module= lto-plugin; bootstrap=true; extra_configure_flags='--enable-shared @extra_linker_plugin_flags@ @extra_linker_plugin_configure_flags@'; extra_make_flags='@extra_linker_plugin_flags@'; }; +host_modules= { module= libcc1; bootstrap=true; + extra_configure_flags=--enable-shared; }; target_modules = { module= libstdc++-v3; bootstrap=true; @@ -352,6 +354,9 @@ dependencies = { module=all-gnattools; on=all-target-libstdc++-v3; }; dependencies = { module=all-lto-plugin; on=all-libiberty; }; dependencies = { module=all-lto-plugin; on=all-libiberty-linker-plugin; }; +dependencies = { module=configure-libcc1; on=configure-gcc; }; +dependencies = { module=all-libcc1; on=all-gcc; }; + dependencies = { module=all-utils; on=all-libiberty; }; dependencies = { module=configure-mpfr; on=all-gmp; }; diff --git a/configure.ac b/configure.ac index 9048cd1..0388dc2 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,5 @@ # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, -# 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 +# 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2014 # Free Software Foundation, Inc. # # This file is free software; you can redistribute it and/or modify it @@ -141,7 +141,7 @@ host_libs="intl libiberty opcodes bfd readline tcl tk itcl libgui zlib libbacktr # binutils, gas and ld appear in that order because it makes sense to run # "make check" in that particular order. # If --enable-gold is used, "gold" may replace "ld". -host_tools="texinfo flex bison binutils gas ld fixincludes gcc cgen sid sim gdb gprof etc expect dejagnu m4 utils guile fastjar gnattools" +host_tools="texinfo flex bison binutils gas ld fixincludes gcc cgen sid sim gdb gprof etc expect dejagnu m4 utils guile fastjar gnattools libcc1" # libgcj represents the runtime libraries only used by gcj. libgcj="target-libffi \ diff --git a/libcc1/Makefile.am b/libcc1/Makefile.am new file mode 100644 index 0000000..b3040c5 --- /dev/null +++ b/libcc1/Makefile.am @@ -0,0 +1,55 @@ +## Copyright (C) 2014 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 +## . + +ACLOCAL_AMFLAGS = -I .. -I ../config +gcc_build_dir = ../$(host_subdir)/gcc +AM_CPPFLAGS = -I $(srcdir)/../include -I $(srcdir)/../libgcc \ + -I $(gcc_build_dir) -I$(srcdir)/../gcc \ + -I $(srcdir)/../gcc/c -I $(srcdir)/../gcc/c-family \ + -I $(srcdir)/../libcpp/include +WERROR_FLAG = -Werror +AM_CXXFLAGS = $(WARN_FLAGS) $(WERROR_FLAG) $(visibility) +libiberty = ../libiberty/pic/libiberty.a + + +plugindir = $(libdir)/gcc/$(target_noncanonical)/$(gcc_version)/plugin +cc1libdir = $(libdir)/$(libsuffix) + +if ENABLE_PLUGIN +plugin_LTLIBRARIES = libcc1plugin.la +cc1lib_LTLIBRARIES = libcc1.la +endif + +BUILT_SOURCES = compiler-name.h + +# Put this in a header so we don't run sed for each compilation. This +# is also simpler to debug as one can easily see the constant. +compiler-name.h: Makefile + echo "#define COMPILER_NAME \"`echo gcc | sed '$(transform)'`\"" > compiler-name.h + + +shared_source = callbacks.cc callbacks.hh connection.cc connection.hh \ + marshall.cc marshall.hh rpc.hh status.hh + +libcc1plugin_la_LDFLAGS = -module -export-symbols $(srcdir)/libcc1plugin.sym +libcc1plugin_la_SOURCES = plugin.cc $(shared_source) +libcc1plugin_la_LIBADD = $(libiberty) + +libcc1_la_LDFLAGS = -module -export-symbols $(srcdir)/libcc1.sym +libcc1_la_SOURCES = findcomp.cc libcc1.cc names.cc names.hh $(shared_source) +libcc1_la_LIBADD = $(libiberty) diff --git a/libcc1/callbacks.cc b/libcc1/callbacks.cc new file mode 100644 index 0000000..3c4eda6 --- /dev/null +++ b/libcc1/callbacks.cc @@ -0,0 +1,90 @@ +/* Callback management. + Copyright (C) 2014 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 +. */ + +#include +#include +#include +#include "callbacks.hh" +#include "libiberty.h" + +// An entry in the hash table. +struct method +{ + const char *name; + cc1_plugin::callback_ftype *func; +}; + +// Hash function for struct method. +static hashval_t +hash_method (const void *a) +{ + const struct method *m = (const struct method *) a; + + return htab_hash_string (m->name); +} + +// Equality function for struct method. +static int +eq_method (const void *a, const void *b) +{ + const struct method *ma = (const struct method *) a; + const struct method *mb = (const struct method *) b; + + return strcmp (ma->name, mb->name) == 0; +} + +cc1_plugin::callbacks::callbacks () + : m_registry (htab_create_alloc (10, hash_method, eq_method, + free, xcalloc, free)) +{ +} + +cc1_plugin::callbacks::~callbacks () +{ + htab_delete (m_registry); +} + +void +cc1_plugin::callbacks::add_callback (const char *name, + cc1_plugin::callback_ftype *func) +{ + method m; + method **slot; + + m.name = name; + m.func = func; + + slot = (method **) htab_find_slot (m_registry, &m, INSERT); + *slot = XNEW (method); + **slot = m; +} + +cc1_plugin::callback_ftype * +cc1_plugin::callbacks::find_callback (const char *name) +{ + method m, *found; + + m.name = name; + + found = (method *) htab_find (m_registry, &m); + if (found == NULL) + return NULL; + + return found->func; +} diff --git a/libcc1/callbacks.hh b/libcc1/callbacks.hh new file mode 100644 index 0000000..bde1100 --- /dev/null +++ b/libcc1/callbacks.hh @@ -0,0 +1,64 @@ +/* Callback management + Copyright (C) 2014 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 +. */ + +#ifndef CC1_PLUGIN_CALLBACKS_HH +#define CC1_PLUGIN_CALLBACKS_HH + +#include "status.hh" +#include "hashtab.h" + +namespace cc1_plugin +{ + class connection; + + // The type of a callback method. + typedef status callback_ftype (connection *); + + // This class manages callback functions. A callback has a name and + // an underlying function. When a query packet arrives, the name is + // inspected and the corresponding function is called. A callback + // function has to know how to decode its own arguments, but + // wrappers are provided elsewhere to automate this. + class callbacks + { + public: + + callbacks (); + ~callbacks (); + + // Add a callback named NAME. FUNC is the function to call when + // this method is invoked. + void add_callback (const char *name, callback_ftype *func); + + // Look up a callback by name. Returns NULL if the method is not + // found. + callback_ftype *find_callback (const char *name); + + private: + + // Declared but not defined to avoid use. + callbacks (const callbacks &); + callbacks &operator= (const callbacks &); + + // The mapping. + htab_t m_registry; + }; +}; + +#endif // CC1_PLUGIN_CALLBACKS_HH diff --git a/libcc1/configure.ac b/libcc1/configure.ac new file mode 100644 index 0000000..7328977 --- /dev/null +++ b/libcc1/configure.ac @@ -0,0 +1,73 @@ +dnl Copyright (C) 2014 Free Software Foundation, Inc. +dnl +dnl This file is part of GCC. +dnl +dnl GCC is free software; you can redistribute it and/or modify it under +dnl the terms of the GNU General Public License as published by the Free +dnl Software Foundation; either version 3, or (at your option) any later +dnl version. +dnl +dnl GCC is distributed in the hope that it will be useful, but WITHOUT ANY +dnl WARRANTY; without even the implied warranty of MERCHANTABILITY or +dnl FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +dnl for more details. +dnl +dnl You should have received a copy of the GNU General Public License +dnl along with GCC; see the file COPYING3. If not see +dnl . + +AC_PREREQ(2.64) +AC_INIT([libcc1], [version-unused]) +AC_CONFIG_SRCDIR([libcc1.cc]) +AC_CONFIG_HEADER(cc1plugin-config.h) + +AC_CANONICAL_SYSTEM +AC_USE_SYSTEM_EXTENSIONS +# Determine the noncanonical target name, for directory use. +ACX_NONCANONICAL_TARGET +GCC_TOPLEV_SUBDIRS + +# 1.11.1: Require that version of automake. +# foreign: Don't require README, INSTALL, NEWS, etc. +# no-define: Don't define PACKAGE and VERSION. +# -Wall: Issue all automake warnings. +# -Wno-portability: Don't warn about constructs supported by GNU make. +# (because GCC requires GNU make anyhow). +AM_INIT_AUTOMAKE([1.11.1 foreign no-dist no-define -Wall -Wno-portability]) +AM_MAINTAINER_MODE + +LT_INIT([disable-static]) +AM_PROG_LIBTOOL +AC_PROG_CXX + +visibility= +if test "$GXX" = yes; then + visibility=-fvisibility=hidden +fi +AC_SUBST(visibility) + +AC_CHECK_DECLS([basename]) + +gcc_version=`cat $srcdir/../gcc/BASE-VER` +AC_SUBST(gcc_version) + +ACX_PROG_CC_WARNING_OPTS([-W -Wall], [WARN_FLAGS]) +WARN_FLAGS="$WARN_FLAGS -Werror" +AC_SUBST(WARN_FLAGS) + +libsuffix= +if test "$GXX" = yes; then + libsuffix=`$CXX -print-multi-os-directory` +fi +AC_SUBST(libsuffix) + +# If any of these functions are missing, simply don't bother building +# this plugin. +GCC_ENABLE_PLUGINS +AC_CHECK_FUNC(socketpair, , enable_plugin=no) +AC_CHECK_FUNC(select, , enable_plugin=no) +AC_CHECK_FUNC(fork, , enable_plugin=no) +AM_CONDITIONAL(ENABLE_PLUGIN, test $enable_plugin = yes) + +AC_CONFIG_FILES([Makefile]) +AC_OUTPUT diff --git a/libcc1/connection.cc b/libcc1/connection.cc new file mode 100644 index 0000000..3e57bbc --- /dev/null +++ b/libcc1/connection.cc @@ -0,0 +1,153 @@ +/* Connect implementation + Copyright (C) 2014 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 +. */ + +#include +#include +#include +#include +#include +#include "marshall.hh" +#include "connection.hh" +#include "rpc.hh" + +cc1_plugin::connection::~connection () +{ +} + +void +cc1_plugin::connection::print (const char *) +{ +} + +cc1_plugin::status +cc1_plugin::connection::send (char c) +{ + if (write (m_fd, &c, 1) != 1) + return FAIL; + return OK; +} + +cc1_plugin::status +cc1_plugin::connection::send (const void *buf, int len) +{ + if (write (m_fd, buf, len) != len) + return FAIL; + return OK; +} + +cc1_plugin::status +cc1_plugin::connection::require (char c) +{ + char result; + + if (read (m_fd, &result, 1) != 1 + || result != c) + return FAIL; + + return OK; +} + +cc1_plugin::status +cc1_plugin::connection::get (void *buf, int len) +{ + if (read (m_fd, buf, len) != len) + return FAIL; + return OK; +} + +cc1_plugin::status +cc1_plugin::connection::do_wait (bool want_result) +{ + while (true) + { + char result; + fd_set read_set; + + FD_ZERO (&read_set); + FD_SET (m_fd, &read_set); + if (m_aux_fd != -1) + FD_SET (m_aux_fd, &read_set); + + int nfds = select (FD_SETSIZE, &read_set, NULL, NULL, NULL); + if (nfds == -1) + { + if (errno == EINTR) + continue; + return FAIL; + } + + // We have to check the stderr fd first, to avoid a possible + // blocking scenario when do_wait is called reentrantly. In + // such a call, if we handle the primary fd first, then we may + // re-enter this function, read from gcc's stderr, causing the + // outer invocation of this function to block when trying to + // read. + if (m_aux_fd != -1 && FD_ISSET (m_aux_fd, &read_set)) + { + char buf[1024]; + int n = read (m_aux_fd, buf, sizeof (buf) - 1); + if (n < 0) + return FAIL; + if (n > 0) + { + buf[n] = '\0'; + print (buf); + } + } + + if (FD_ISSET (m_fd, &read_set)) + { + int n = read (m_fd, &result, 1); + if (n == 0) + return want_result ? FAIL : OK; + if (n != 1) + return FAIL; + + switch (result) + { + case 'R': + // The reply is ready; the caller will unmarshall it. + return want_result ? OK : FAIL; + + case 'Q': + // While waiting for a reply, the other side made a method + // call. + { + // Use an argument_wrapper here to simplify management + // of the string's lifetime. + argument_wrapper method_name; + + if (!method_name.unmarshall (this)) + return FAIL; + + callback_ftype *callback + = m_callbacks.find_callback (method_name); + // The call to CALLBACK is where we may end up in a + // reentrant call. + if (callback == NULL || !callback (this)) + return FAIL; + } + break; + + default: + return FAIL; + } + } + } +} diff --git a/libcc1/connection.hh b/libcc1/connection.hh new file mode 100644 index 0000000..242deec --- /dev/null +++ b/libcc1/connection.hh @@ -0,0 +1,114 @@ +/* Plugin connection declarations + Copyright (C) 2014 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 +. */ + +#ifndef CC1_PLUGIN_CONNECTION_HH +#define CC1_PLUGIN_CONNECTION_HH + +#include "status.hh" +#include "callbacks.hh" + +namespace cc1_plugin +{ + // The connection class represents one side of the connection + // between the gdb-side library and the gcc plugin. It handles the + // low-level details of reading and writing data. + class connection + { + public: + + connection (int fd) + : m_fd (fd), + m_aux_fd (-1), + m_callbacks () + { + } + + connection (int fd, int aux_fd) + : m_fd (fd), + m_aux_fd (aux_fd), + m_callbacks () + { + } + + virtual ~connection (); + + // Send a single character. This is used to introduce various + // higher-level protocol elements. + status send (char c); + + // Send data in bulk. + status send (const void *buf, int len); + + // Read a single byte from the connection and verify that it + // matches the argument C. + status require (char c); + + // Read data in bulk. + status get (void *buf, int len); + + // This is called after a query (remote function call) has been + // sent to the remote. It waits for a response packet. The + // response character is read before returning. Any query packets + // sent from the remote while waiting for a response are handled + // by this function. + status wait_for_result () + { + return do_wait (true); + } + + // Read and respond to query packets sent by the remote. This + // function returns when the connection is closed. + status wait_for_query () + { + return do_wait (false); + } + + // Register a callback with this connection. NAME is the name of + // the method being registered. FUNC is the function. It must + // know how to decode its own arguments. When a query packet is + // received by one of the wait_* methods, the corresponding + // callback is invoked. + void add_callback (const char *name, callback_ftype *func) + { + m_callbacks.add_callback (name, func); + } + + virtual void print (const char *); + + private: + + // Declared but not defined, to prevent use. + connection (const connection &); + connection &operator= (const connection &); + + // Helper function for the wait_* methods. + status do_wait (bool); + + // The file descriptor. + int m_fd; + + // An auxiliary file descriptor, or -1 if none. + int m_aux_fd; + + // Callbacks associated with this connection. + callbacks m_callbacks; + }; +} + +#endif // CC1_PLUGIN_CONNECTION_HH diff --git a/libcc1/findcomp.cc b/libcc1/findcomp.cc new file mode 100644 index 0000000..f02b1df --- /dev/null +++ b/libcc1/findcomp.cc @@ -0,0 +1,139 @@ +/* Find the correct compiler. + Copyright (C) 2014 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 +. */ + +#include +#include +#include +#include + +#include "libiberty.h" +#include "xregex.h" +#include "findcomp.hh" + +class scanner +{ +public: + + scanner (const std::string &dir) + { + m_dir = opendir (dir.c_str ()); + } + + ~scanner () + { + if (m_dir != NULL) + closedir (m_dir); + } + + const char *next () + { + if (m_dir == NULL) + return NULL; + + struct dirent *entry = readdir (m_dir); + if (entry == NULL) + return NULL; + + return entry->d_name; + } + +private: + + DIR *m_dir; +}; + +static bool +search_dir (const regex_t ®exp, const std::string &dir, std::string *result) +{ + scanner scan (dir); + const char *filename; + + while ((filename = scan.next ()) != NULL) + { + if (regexec (®exp, filename, 0, NULL, 0) == 0) + { + *result = filename; + return true; + } + } + + return false; +} + +class tokenizer +{ +public: + + tokenizer (const char *str) + : m_str (str), + m_pos (0) + { + } + + bool done () const + { + return m_pos == std::string::npos; + } + + std::string next () + { + std::string::size_type last_pos = m_pos; + std::string::size_type colon = m_str.find(':', last_pos); + + std::string result; + if (colon == std::string::npos) + { + m_pos = colon; + result = m_str.substr(last_pos, colon); + } + else + { + m_pos = colon + 1; + result = m_str.substr(last_pos, colon - last_pos); + } + if (result == "") + result = "."; + + return result; + } + +private: + + std::string m_str; + std::string::size_type m_pos; +}; + +bool +find_compiler (const regex_t ®exp, std::string *result) +{ + const char *cpath = getenv ("PATH"); + + if (cpath == NULL) + return false; + + tokenizer dirs (cpath); + while (!dirs.done ()) + { + std::string dir = dirs.next (); + if (search_dir (regexp, dir, result)) + return true; + } + + return false; +} diff --git a/libcc1/findcomp.hh b/libcc1/findcomp.hh new file mode 100644 index 0000000..a55a283 --- /dev/null +++ b/libcc1/findcomp.hh @@ -0,0 +1,25 @@ +/* Find the correct compiler. + Copyright (C) 2014 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 +. */ + +#ifndef CC1_PLUGIN_FINDCOMP_HH +#define CC1_PLUGIN_FINDCOMP_HH + +extern bool find_compiler (const regex_t ®exp, std::string *result); + +#endif // CC1_PLUGIN_FINDCOMP_HH diff --git a/libcc1/libcc1.cc b/libcc1/libcc1.cc new file mode 100644 index 0000000..15320c2 --- /dev/null +++ b/libcc1/libcc1.cc @@ -0,0 +1,530 @@ +/* The library used by gdb. + Copyright (C) 2014 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 +. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "rpc.hh" +#include "connection.hh" +#include "names.hh" +#include "callbacks.hh" +#include "gcc-interface.h" +#include "libiberty.h" +#include "xregex.h" +#include "findcomp.hh" +#include "compiler-name.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 args; + std::string source_file; +}; + +// 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 () +{ + base.ops = v; + c_ops = cv; +} + +libcc1::~libcc1 () +{ + delete connection; +} + + + +// 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 +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 +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); +} + + + +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 +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 +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 +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 +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 +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 +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 +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, +#define GCC_METHOD1(R, N, A) \ + rpc, +#define GCC_METHOD2(R, N, A, B) \ + rpc, +#define GCC_METHOD3(R, N, A, B, C) \ + rpc, +#define GCC_METHOD4(R, N, A, B, C, D) \ + rpc, +#define GCC_METHOD5(R, N, A, B, C, D, E) \ + rpc, +#define GCC_METHOD7(R, N, A, B, C, D, E, F, G) \ + rpc, + +#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 char * +libcc1_set_arguments (struct gcc_base_context *s, + const char *triplet_regexp, + int argc, char **argv) +{ + libcc1 *self = (libcc1 *) s; + regex_t triplet; + int code; + + std::string rx = make_regexp (triplet_regexp, COMPILER_NAME); + 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); + } + + std::string compiler; + if (!find_compiler (triplet, &compiler)) + { + regfree (&triplet); + return concat ("Could not find a compiler matching \"", + rx.c_str (), + "\"", + (char *) NULL); + } + regfree (&triplet); + + self->args.push_back (compiler); + + for (int i = 0; i < argc; ++i) + self->args.push_back (argv[i]); + + return NULL; +} + +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_0)) + 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, + int verbose) +{ + libcc1 *self = (libcc1 *) s; + + int fds[2]; + if (socketpair (AF_LOCAL, 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 (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; + self->connection->add_callback ("binding_oracle", fun); + + fun = cc1_plugin::callback; + 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 (self->args[i].c_str ()); + argv[self->args.size ()] = NULL; + + return fork_exec (self, argv, fds, stderr_fds); +} + +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_0, + libcc1_set_arguments, + libcc1_set_source_file, + libcc1_set_print_callback, + libcc1_compile, + libcc1_destroy +}; + +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 || c_version != GCC_C_FE_VERSION_0) + return NULL; + + return new libcc1 (&vtable, &c_vtable); +} diff --git a/libcc1/libcc1.sym b/libcc1/libcc1.sym new file mode 100644 index 0000000..86b1e3e --- /dev/null +++ b/libcc1/libcc1.sym @@ -0,0 +1 @@ +gcc_c_fe_context diff --git a/libcc1/libcc1plugin.sym b/libcc1/libcc1plugin.sym new file mode 100644 index 0000000..05d0f7b --- /dev/null +++ b/libcc1/libcc1plugin.sym @@ -0,0 +1,2 @@ +plugin_init +plugin_is_GPL_compatible diff --git a/libcc1/marshall.cc b/libcc1/marshall.cc new file mode 100644 index 0000000..9119de6 --- /dev/null +++ b/libcc1/marshall.cc @@ -0,0 +1,166 @@ +/* Marshalling and unmarshalling. + Copyright (C) 2014 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 +. */ + +#include +#include +#include +#include "marshall.hh" +#include "connection.hh" + +cc1_plugin::status +cc1_plugin::unmarshall_check (connection *conn, unsigned long long check) +{ + unsigned long long r; + + if (!unmarshall (conn, &r)) + return FAIL; + return check == r ? OK : FAIL; +} + +cc1_plugin::status +cc1_plugin::marshall_intlike (connection *conn, unsigned long long val) +{ + if (!conn->send ('i')) + return FAIL; + return conn->send (&val, sizeof (val)); +} + +cc1_plugin::status +cc1_plugin::unmarshall_intlike (connection *conn, unsigned long long *result) +{ + if (!conn->require ('i')) + return FAIL; + return conn->get (result, sizeof (*result)); +} + +cc1_plugin::status +cc1_plugin::unmarshall (connection *conn, enum gcc_c_symbol_kind *result) +{ + protocol_int p; + if (!unmarshall_intlike (conn, &p)) + return FAIL; + *result = (enum gcc_c_symbol_kind) p; + return OK; +} + +cc1_plugin::status +cc1_plugin::unmarshall (connection *conn, enum gcc_c_oracle_request *result) +{ + protocol_int p; + if (!unmarshall_intlike (conn, &p)) + return FAIL; + *result = (enum gcc_c_oracle_request) p; + return OK; +} + +cc1_plugin::status +cc1_plugin::unmarshall (connection *conn, enum gcc_qualifiers *result) +{ + protocol_int p; + if (!unmarshall_intlike (conn, &p)) + return FAIL; + *result = (enum gcc_qualifiers) p; + return OK; +} + +cc1_plugin::status +cc1_plugin::marshall (connection *conn, const char *str) +{ + if (!conn->send ('s')) + return FAIL; + + unsigned long long len = str == NULL ? -1ULL : strlen (str); + if (!conn->send (&len, sizeof (len))) + return FAIL; + + if (str == NULL) + return OK; + + return conn->send (str, len); +} + +cc1_plugin::status +cc1_plugin::unmarshall (connection *conn, char **result) +{ + unsigned long long len; + + if (!conn->require ('s')) + return FAIL; + if (!conn->get (&len, sizeof (len))) + return FAIL; + + if (len == -1ULL) + { + *result = NULL; + return OK; + } + + char *str = new (std::nothrow) char[len + 1]; + if (str == NULL) + return FAIL; + + if (!conn->get (str, len)) + { + delete[] str; + return FAIL; + } + + str[len] = '\0'; + *result = str; + + return OK; +} + +cc1_plugin::status +cc1_plugin::marshall (connection *conn, const gcc_type_array *a) +{ + if (!conn->send ('a')) + return FAIL; + + unsigned long long r = a->n_elements; + if (!conn->send (&r, sizeof (r))) + return FAIL; + + return conn->send (a->elements, r * sizeof (a->elements[0])); +} + +cc1_plugin::status +cc1_plugin::unmarshall (connection *conn, gcc_type_array **result) +{ + unsigned long long len; + + if (!conn->require ('a')) + return FAIL; + if (!conn->get (&len, sizeof (len))) + return FAIL; + + *result = new gcc_type_array; + + (*result)->n_elements = len; + (*result)->elements = new gcc_type[len]; + + if (!conn->get ((*result)->elements, len * sizeof ((*result)->elements[0]))) + { + delete[] (*result)->elements; + delete *result; + return FAIL; + } + + return OK; +} diff --git a/libcc1/marshall.hh b/libcc1/marshall.hh new file mode 100644 index 0000000..3f936e7 --- /dev/null +++ b/libcc1/marshall.hh @@ -0,0 +1,93 @@ +/* Marshalling and unmarshalling. + Copyright (C) 2014 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 +. */ + +#ifndef CC1_PLUGIN_MARSHALL_HH +#define CC1_PLUGIN_MARSHALL_HH + +#include "status.hh" +#include "gcc-c-interface.h" + +namespace cc1_plugin +{ + class connection; + + // Only a single kind of integer is ever sent over the wire, and + // this is it. + typedef unsigned long long protocol_int; + + // Read an integer from the connection and verify that it has the + // value V. + status unmarshall_check (connection *, protocol_int v); + + // Write an integer, prefixed with the integer type marker, to the + // connection. + status marshall_intlike (connection *, protocol_int); + + // Read a type marker from the connection and verify that it is an + // integer type marker. If not, return FAIL. If so, read an + // integer store it in the out argument. + status unmarshall_intlike (connection *, protocol_int *); + + // A template function that can handle marshalling various integer + // objects to the connection. + template + status marshall (connection *conn, T scalar) + { + return marshall_intlike (conn, scalar); + } + + // A template function that can handle unmarshalling various integer + // objects from the connection. Note that this can't be + // instantiated for enum types. Note also that there's no way at + // the protocol level to distinguish different int types. + template + status unmarshall (connection *conn, T *scalar) + { + protocol_int result; + + if (!unmarshall_intlike (conn, &result)) + return FAIL; + *scalar = result; + return OK; + } + + // Unmarshallers for some specific enum types. With C++11 we + // wouldn't need these, as we could add type traits to the scalar + // unmarshaller. + status unmarshall (connection *, enum gcc_c_symbol_kind *); + status unmarshall (connection *, enum gcc_qualifiers *); + status unmarshall (connection *, enum gcc_c_oracle_request *); + + // Send a string type marker followed by a string. + status marshall (connection *, const char *); + + // Read a string type marker followed by a string. The caller is + // responsible for freeing the resulting string using 'delete[]'. + status unmarshall (connection *, char **); + + // Send a gcc_type_array marker followed by the array. + status marshall (connection *, const gcc_type_array *); + + // Read a gcc_type_array marker, followed by a gcc_type_array. The + // resulting array must be freed by the caller, using 'delete[]' on + // the elements, and 'delete' on the array object itself. + status unmarshall (connection *, struct gcc_type_array **); +}; + +#endif // CC1_PLUGIN_MARSHALL_HH diff --git a/libcc1/names.cc b/libcc1/names.cc new file mode 100644 index 0000000..5ddfa7b --- /dev/null +++ b/libcc1/names.cc @@ -0,0 +1,46 @@ +/* String definitions. + Copyright (C) 2014 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 +. */ + +#include +#include "names.hh" + +#define GCC_METHOD0(R, N) \ + const char *cc1_plugin::N = # N; +#define GCC_METHOD1(R, N, A) \ + const char *cc1_plugin::N = # N; +#define GCC_METHOD2(R, N, A, B) \ + const char *cc1_plugin::N = # N; +#define GCC_METHOD3(R, N, A, B, C) \ + const char *cc1_plugin::N = # N; +#define GCC_METHOD4(R, N, A, B, C, D) \ + const char *cc1_plugin::N = # N; +#define GCC_METHOD5(R, N, A, B, C, D, E) \ + const char *cc1_plugin::N = # N; +#define GCC_METHOD7(R, N, A, B, C, D, E, F, G) \ + const char *cc1_plugin::N = # N; + +#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 diff --git a/libcc1/names.hh b/libcc1/names.hh new file mode 100644 index 0000000..9bda8d5 --- /dev/null +++ b/libcc1/names.hh @@ -0,0 +1,55 @@ +/* String declarations. + Copyright (C) 2014 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 +. */ + +#ifndef CC1_PLUGIN_NAMES_HH +#define CC1_PLUGIN_NAMES_HH + +namespace cc1_plugin +{ + // This code defines global string constants, one for each method in + // gcc-c-fe.def. This is needed so that they can be used as + // template arguments elsewhere. + +#define GCC_METHOD0(R, N) \ + extern const char *N; +#define GCC_METHOD1(R, N, A) \ + extern const char *N; +#define GCC_METHOD2(R, N, A, B) \ + extern const char *N; +#define GCC_METHOD3(R, N, A, B, C) \ + extern const char *N; +#define GCC_METHOD4(R, N, A, B, C, D) \ + extern const char *N; +#define GCC_METHOD5(R, N, A, B, C, D, E) \ + extern const char *N; +#define GCC_METHOD7(R, N, A, B, C, D, E, F, G) \ + extern const char *N; + +#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 +}; + +#endif // CC1_PLUGIN_NAMES_HH diff --git a/libcc1/plugin.cc b/libcc1/plugin.cc new file mode 100644 index 0000000..fbb49d3 --- /dev/null +++ b/libcc1/plugin.cc @@ -0,0 +1,919 @@ +/* Library interface to C front end + Copyright (C) 2014 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 + . */ + +#include + +#undef PACKAGE_NAME +#undef PACKAGE_STRING +#undef PACKAGE_TARNAME +#undef PACKAGE_VERSION + +#include "../gcc/config.h" + +#undef PACKAGE_NAME +#undef PACKAGE_STRING +#undef PACKAGE_TARNAME +#undef PACKAGE_VERSION + +#include "gcc-plugin.h" +#include "system.h" +#include "coretypes.h" +#include "stringpool.h" + +#include "gcc-interface.h" +#include "tree-core.h" +#include "wide-int.h" +#include "stor-layout.h" +#include "c-tree.h" +#include "toplev.h" +#include "timevar.h" +#include "hash-table.h" +#include "tm.h" +#include "c-family/c-pragma.h" +#include "c-lang.h" +#include "diagnostic.h" +#include "langhooks.h" +#include "langhooks-def.h" + +#include "callbacks.hh" +#include "connection.hh" +#include "rpc.hh" + +#include + +#ifdef __GNUC__ +#pragma GCC visibility push(default) +#endif +int plugin_is_GPL_compatible; +#ifdef __GNUC__ +#pragma GCC visibility pop +#endif + + + +// This is put into the lang hooks when the plugin starts. + +static void +plugin_print_error_function (diagnostic_context *context, const char *file, + diagnostic_info *diagnostic) +{ + if (current_function_decl != NULL_TREE + && DECL_NAME (current_function_decl) != NULL_TREE + && strcmp (IDENTIFIER_POINTER (DECL_NAME (current_function_decl)), + GCC_FE_WRAPPER_FUNCTION) == 0) + return; + lhd_print_error_function (context, file, diagnostic); +} + + + +static unsigned long long +convert_out (tree t) +{ + return (unsigned long long) (uintptr_t) t; +} + +static tree +convert_in (unsigned long long v) +{ + return (tree) (uintptr_t) v; +} + + + +struct decl_addr_value +{ + tree decl; + tree address; +}; + +struct decl_addr_hasher : typed_free_remove +{ + typedef decl_addr_value value_type; + typedef decl_addr_value compare_type; + + static inline hashval_t hash (const value_type *); + static inline bool equal (const value_type *, const compare_type *); +}; + +inline hashval_t +decl_addr_hasher::hash (const value_type *e) +{ + return IDENTIFIER_HASH_VALUE (DECL_NAME (e->decl)); +} + +inline bool +decl_addr_hasher::equal (const value_type *p1, const compare_type *p2) +{ + return p1->decl == p2->decl; +} + + + +struct string_hasher : typed_noop_remove +{ + typedef char value_type; + typedef char compare_type; + + static inline hashval_t hash (const value_type *s) + { + return htab_hash_string (s); + } + + static inline bool equal (const value_type *p1, const value_type *p2) + { + return strcmp (p1, p2) == 0; + } +}; + + + +// A wrapper for pushdecl that doesn't let gdb have a chance to +// instantiate a symbol. + +static void +pushdecl_safe (tree decl) +{ + void (*save) (enum c_oracle_request, tree identifier); + + save = c_binding_oracle; + c_binding_oracle = NULL; + pushdecl (decl); + c_binding_oracle = save; +} + + + +struct plugin_context : public cc1_plugin::connection +{ + plugin_context (int fd); + + // Map decls to addresses. + hash_table address_map; + + // A collection of trees that are preserved for the GC. + hash_table< pointer_hash > preserved; + + // File name cache. + hash_table file_names; + + // Perform GC marking. + void mark (); + + // Preserve a tree during the plugin's operation. + tree preserve (tree t) + { + tree_node **slot = preserved.find_slot (t, INSERT); + *slot = t; + return t; + } + + source_location get_source_location (const char *filename, + unsigned int line_number) + { + if (filename == NULL) + return UNKNOWN_LOCATION; + + filename = intern_filename (filename); + linemap_add (line_table, LC_ENTER, false, filename, line_number); + source_location loc = linemap_line_start (line_table, line_number, 0); + linemap_add (line_table, LC_LEAVE, false, NULL, 0); + return loc; + } + +private: + + // Add a file name to FILE_NAMES and return the canonical copy. + const char *intern_filename (const char *filename) + { + char **slot = file_names.find_slot (filename, INSERT); + if (*slot == NULL) + { + /* The file name must live as long as the line map, which + effectively means as long as this compilation. So, we copy + the string here but never free it. */ + *slot = xstrdup (filename); + } + return *slot; + } +}; + +static plugin_context *current_context; + + + +plugin_context::plugin_context (int fd) + : cc1_plugin::connection (fd), + address_map (), + preserved (), + file_names () +{ + address_map.create (20); + preserved.create (20); + file_names.create (20); +} + +void +plugin_context::mark () +{ + for (hash_table::iterator it = address_map.begin (); + it != address_map.end (); + ++it) + { + ggc_mark ((*it).decl); + ggc_mark ((*it).address); + } + + for (hash_table< pointer_hash >::iterator it = preserved.begin (); + it != preserved.end (); + ++it) + ggc_mark (&*it); +} + +static void +plugin_binding_oracle (enum c_oracle_request kind, tree identifier) +{ + enum gcc_c_oracle_request request; + + gcc_assert (current_context != NULL); + + switch (kind) + { + case C_ORACLE_SYMBOL: + request = GCC_C_ORACLE_SYMBOL; + break; + case C_ORACLE_TAG: + request = GCC_C_ORACLE_TAG; + break; + case C_ORACLE_LABEL: + request = GCC_C_ORACLE_LABEL; + break; + default: + abort (); + } + + int ignore; + cc1_plugin::call (current_context, "binding_oracle", &ignore, + request, IDENTIFIER_POINTER (identifier)); +} + +static void +plugin_pragma_user_expression (cpp_reader *) +{ + c_binding_oracle = plugin_binding_oracle; +} + +static void +plugin_init_extra_pragmas (void *, void *) +{ + c_register_pragma ("GCC", "user_expression", plugin_pragma_user_expression); +} + + + +// Maybe rewrite a decl to its address. +static tree +address_rewriter (tree *in, int *walk_subtrees, void *arg) +{ + plugin_context *ctx = (plugin_context *) arg; + + if (!DECL_P (*in) || DECL_NAME (*in) == NULL_TREE) + return NULL_TREE; + + decl_addr_value value; + value.decl = *in; + decl_addr_value *found_value = ctx->address_map.find (&value); + if (found_value != NULL) + { + // At this point we don't need VLA sizes for gdb-supplied + // variables, and having them here confuses later passes, so we + // drop them. + if (C_TYPE_VARIABLE_SIZE (TREE_TYPE (*in))) + { + TREE_TYPE (*in) + = build_array_type_nelts (TREE_TYPE (TREE_TYPE (*in)), 1); + DECL_SIZE (*in) = TYPE_SIZE (TREE_TYPE (*in)); + DECL_SIZE_UNIT (*in) = TYPE_SIZE_UNIT (TREE_TYPE (*in)); + } + } + else if (DECL_IS_BUILTIN (*in)) + { + gcc_address address; + + if (!cc1_plugin::call (ctx, "address_oracle", &address, + IDENTIFIER_POINTER (DECL_NAME (*in)))) + return NULL_TREE; + if (address == 0) + return NULL_TREE; + + // Insert the decl into the address map in case it is referenced + // again. + value.address = build_int_cst_type (ptr_type_node, address); + decl_addr_value **slot = ctx->address_map.find_slot (&value, INSERT); + gcc_assert (*slot == NULL); + *slot + = static_cast (xmalloc (sizeof (decl_addr_value))); + **slot = value; + found_value = *slot; + } + else + return NULL_TREE; + + if (found_value->address != error_mark_node) + { + // We have an address for the decl, so rewrite the tree. + tree ptr_type = build_pointer_type (TREE_TYPE (*in)); + *in = fold_build1 (INDIRECT_REF, TREE_TYPE (*in), + fold_build1 (CONVERT_EXPR, ptr_type, + found_value->address)); + } + + *walk_subtrees = 0; + + return NULL_TREE; +} + +// When generating code for gdb, we want to be able to use absolute +// addresses to refer to otherwise external objects that gdb knows +// about. gdb passes in these addresses when building decls, and then +// before gimplification we go through the trees, rewriting uses to +// the equivalent of "*(TYPE *) ADDR". +static void +rewrite_decls_to_addresses (void *function_in, void *) +{ + tree function = (tree) function_in; + + // Do nothing if we're not in gdb. + if (current_context == NULL) + return; + + walk_tree (&DECL_SAVED_TREE (function), address_rewriter, current_context, + NULL); +} + + + +gcc_decl +plugin_build_decl (cc1_plugin::connection *self, + const char *name, + enum gcc_c_symbol_kind sym_kind, + gcc_type sym_type_in, + const char *substitution_name, + gcc_address address, + const char *filename, + unsigned int line_number) +{ + plugin_context *ctx = static_cast (self); + tree identifier = get_identifier (name); + enum tree_code code; + tree decl; + tree sym_type = convert_in (sym_type_in); + + switch (sym_kind) + { + case GCC_C_SYMBOL_FUNCTION: + code = FUNCTION_DECL; + break; + + case GCC_C_SYMBOL_VARIABLE: + code = VAR_DECL; + break; + + case GCC_C_SYMBOL_TYPEDEF: + code = TYPE_DECL; + break; + + case GCC_C_SYMBOL_LABEL: + // FIXME: we aren't ready to handle labels yet. + // It isn't clear how to translate them properly + // and in any case a "goto" isn't likely to work. + return convert_out (error_mark_node); + + default: + abort (); + } + + source_location loc = ctx->get_source_location (filename, line_number); + + decl = build_decl (loc, code, identifier, sym_type); + TREE_USED (decl) = 1; + TREE_ADDRESSABLE (decl) = 1; + + if (sym_kind != GCC_C_SYMBOL_TYPEDEF) + { + decl_addr_value value; + + value.decl = decl; + if (substitution_name != NULL) + { + // If the translator gave us a name without a binding, + // we can just substitute error_mark_node, since we know the + // translator will be reporting an error anyhow. + value.address + = lookup_name (get_identifier (substitution_name)); + if (value.address == NULL_TREE) + value.address = error_mark_node; + } + else + value.address = build_int_cst_type (ptr_type_node, address); + decl_addr_value **slot = ctx->address_map.find_slot (&value, INSERT); + gcc_assert (*slot == NULL); + *slot + = static_cast (xmalloc (sizeof (decl_addr_value))); + **slot = value; + } + + return convert_out (ctx->preserve (decl)); +} + +int +plugin_bind (cc1_plugin::connection *, + gcc_decl decl_in, int is_global) +{ + tree decl = convert_in (decl_in); + c_bind (DECL_SOURCE_LOCATION (decl), decl, is_global); + rest_of_decl_compilation (decl, is_global, 0); + return 1; +} + +int +plugin_tagbind (cc1_plugin::connection *self, + const char *name, gcc_type tagged_type, + const char *filename, unsigned int line_number) +{ + plugin_context *ctx = static_cast (self); + c_pushtag (ctx->get_source_location (filename, line_number), + get_identifier (name), convert_in (tagged_type)); + return 1; +} + +gcc_type +plugin_build_pointer_type (cc1_plugin::connection *, + gcc_type base_type) +{ + // No need to preserve a pointer type as the base type is preserved. + return convert_out (build_pointer_type (convert_in (base_type))); +} + +gcc_type +plugin_build_record_type (cc1_plugin::connection *self) +{ + plugin_context *ctx = static_cast (self); + return convert_out (ctx->preserve (make_node (RECORD_TYPE))); +} + +gcc_type +plugin_build_union_type (cc1_plugin::connection *self) +{ + plugin_context *ctx = static_cast (self); + return convert_out (ctx->preserve (make_node (UNION_TYPE))); +} + +int +plugin_build_add_field (cc1_plugin::connection *, + gcc_type record_or_union_type_in, + const char *field_name, + gcc_type field_type_in, + unsigned long bitsize, + unsigned long bitpos) +{ + tree record_or_union_type = convert_in (record_or_union_type_in); + tree field_type = convert_in (field_type_in); + + gcc_assert (TREE_CODE (record_or_union_type) == RECORD_TYPE + || TREE_CODE (record_or_union_type) == UNION_TYPE); + + /* Note that gdb does not preserve the location of field decls, so + we can't provide a decent location here. */ + tree decl = build_decl (BUILTINS_LOCATION, FIELD_DECL, + get_identifier (field_name), field_type); + DECL_FIELD_CONTEXT (decl) = record_or_union_type; + + if (TREE_CODE (field_type) == INTEGER_TYPE + && TYPE_PRECISION (field_type) != bitsize) + { + DECL_BIT_FIELD_TYPE (decl) = field_type; + TREE_TYPE (decl) + = c_build_bitfield_integer_type (bitsize, TYPE_UNSIGNED (field_type)); + } + + DECL_MODE (decl) = TYPE_MODE (TREE_TYPE (decl)); + + // There's no way to recover this from DWARF. + SET_DECL_OFFSET_ALIGN (decl, TYPE_PRECISION (pointer_sized_int_node)); + + tree pos = bitsize_int (bitpos); + pos_from_bit (&DECL_FIELD_OFFSET (decl), &DECL_FIELD_BIT_OFFSET (decl), + DECL_OFFSET_ALIGN (decl), pos); + + DECL_SIZE (decl) = bitsize_int (bitsize); + DECL_SIZE_UNIT (decl) = size_int ((bitsize + BITS_PER_UNIT - 1) + / BITS_PER_UNIT); + + DECL_CHAIN (decl) = TYPE_FIELDS (record_or_union_type); + TYPE_FIELDS (record_or_union_type) = decl; + + return 1; +} + +int +plugin_finish_record_or_union (cc1_plugin::connection *, + gcc_type record_or_union_type_in, + unsigned long size_in_bytes) +{ + tree record_or_union_type = convert_in (record_or_union_type_in); + + gcc_assert (TREE_CODE (record_or_union_type) == RECORD_TYPE + || TREE_CODE (record_or_union_type) == UNION_TYPE); + + /* We built the field list in reverse order, so fix it now. */ + TYPE_FIELDS (record_or_union_type) + = nreverse (TYPE_FIELDS (record_or_union_type)); + + if (TREE_CODE (record_or_union_type) == UNION_TYPE) + { + /* Unions can just be handled by the generic code. */ + layout_type (record_or_union_type); + } + else + { + // FIXME there's no way to get this from DWARF, + // or even, it seems, a particularly good way to deduce it. + TYPE_ALIGN (record_or_union_type) + = TYPE_PRECISION (pointer_sized_int_node); + + TYPE_SIZE (record_or_union_type) = bitsize_int (size_in_bytes + * BITS_PER_UNIT); + TYPE_SIZE_UNIT (record_or_union_type) = size_int (size_in_bytes); + + compute_record_mode (record_or_union_type); + finish_bitfield_layout (record_or_union_type); + // FIXME we have no idea about TYPE_PACKED + } + + return 1; +} + +gcc_type +plugin_build_enum_type (cc1_plugin::connection *self, + gcc_type underlying_int_type_in) +{ + tree underlying_int_type = convert_in (underlying_int_type_in); + + if (underlying_int_type == error_mark_node) + return convert_out (error_mark_node); + + tree result = make_node (ENUMERAL_TYPE); + + TYPE_PRECISION (result) = TYPE_PRECISION (underlying_int_type); + TYPE_UNSIGNED (result) = TYPE_UNSIGNED (underlying_int_type); + + plugin_context *ctx = static_cast (self); + return convert_out (ctx->preserve (result)); +} + +int +plugin_build_add_enum_constant (cc1_plugin::connection *, + gcc_type enum_type_in, + const char *name, + unsigned long value) +{ + tree cst, decl, cons; + tree enum_type = convert_in (enum_type_in); + + gcc_assert (TREE_CODE (enum_type) == ENUMERAL_TYPE); + + cst = build_int_cst (enum_type, value); + /* Note that gdb does not preserve the location of enum constants, + so we can't provide a decent location here. */ + decl = build_decl (BUILTINS_LOCATION, CONST_DECL, + get_identifier (name), enum_type); + DECL_INITIAL (decl) = cst; + pushdecl_safe (decl); + + cons = tree_cons (DECL_NAME (decl), cst, TYPE_VALUES (enum_type)); + TYPE_VALUES (enum_type) = cons; + + return 1; +} + +int +plugin_finish_enum_type (cc1_plugin::connection *, + gcc_type enum_type_in) +{ + tree enum_type = convert_in (enum_type_in); + tree minnode, maxnode, iter; + + iter = TYPE_VALUES (enum_type); + minnode = maxnode = TREE_VALUE (iter); + for (iter = TREE_CHAIN (iter); + iter != NULL_TREE; + iter = TREE_CHAIN (iter)) + { + tree value = TREE_VALUE (iter); + if (tree_int_cst_lt (maxnode, value)) + maxnode = value; + if (tree_int_cst_lt (value, minnode)) + minnode = value; + } + TYPE_MIN_VALUE (enum_type) = minnode; + TYPE_MAX_VALUE (enum_type) = maxnode; + + layout_type (enum_type); + + return 1; +} + +gcc_type +plugin_build_function_type (cc1_plugin::connection *self, + gcc_type return_type_in, + const struct gcc_type_array *argument_types_in, + int is_varargs) +{ + tree *argument_types; + tree return_type = convert_in (return_type_in); + tree result; + + argument_types = new tree[argument_types_in->n_elements]; + for (int i = 0; i < argument_types_in->n_elements; ++i) + argument_types[i] = convert_in (argument_types_in->elements[i]); + + if (is_varargs) + result = build_varargs_function_type_array (return_type, + argument_types_in->n_elements, + argument_types); + else + result = build_function_type_array (return_type, + argument_types_in->n_elements, + argument_types); + + delete[] argument_types; + + plugin_context *ctx = static_cast (self); + return convert_out (ctx->preserve (result)); +} + +gcc_type +plugin_int_type (cc1_plugin::connection *self, + int is_unsigned, unsigned long size_in_bytes) +{ + tree result = c_common_type_for_size (BITS_PER_UNIT * size_in_bytes, + is_unsigned); + if (result == NULL_TREE) + result = error_mark_node; + else + { + plugin_context *ctx = static_cast (self); + ctx->preserve (result); + } + return convert_out (result); +} + +gcc_type +plugin_float_type (cc1_plugin::connection *, + unsigned long size_in_bytes) +{ + if (BITS_PER_UNIT * size_in_bytes == TYPE_PRECISION (float_type_node)) + return convert_out (float_type_node); + if (BITS_PER_UNIT * size_in_bytes == TYPE_PRECISION (double_type_node)) + return convert_out (double_type_node); + if (BITS_PER_UNIT * size_in_bytes == TYPE_PRECISION (long_double_type_node)) + return convert_out (long_double_type_node); + return convert_out (error_mark_node); +} + +gcc_type +plugin_void_type (cc1_plugin::connection *) +{ + return convert_out (void_type_node); +} + +gcc_type +plugin_bool_type (cc1_plugin::connection *) +{ + return convert_out (boolean_type_node); +} + +gcc_type +plugin_build_array_type (cc1_plugin::connection *self, + gcc_type element_type_in, int num_elements) +{ + tree element_type = convert_in (element_type_in); + tree result; + + if (num_elements == -1) + result = build_array_type (element_type, NULL_TREE); + else + result = build_array_type_nelts (element_type, num_elements); + + plugin_context *ctx = static_cast (self); + return convert_out (ctx->preserve (result)); +} + +gcc_type +plugin_build_vla_array_type (cc1_plugin::connection *self, + gcc_type element_type_in, + const char *upper_bound_name) +{ + tree element_type = convert_in (element_type_in); + tree upper_bound = lookup_name (get_identifier (upper_bound_name)); + tree range = build_index_type (upper_bound); + + tree result = build_array_type (element_type, range); + C_TYPE_VARIABLE_SIZE (result) = 1; + + plugin_context *ctx = static_cast (self); + return convert_out (ctx->preserve (result)); +} + +gcc_type +plugin_build_qualified_type (cc1_plugin::connection *, + gcc_type unqualified_type_in, + enum gcc_qualifiers qualifiers) +{ + tree unqualified_type = convert_in (unqualified_type_in); + int quals = 0; + + if ((qualifiers & GCC_QUALIFIER_CONST) != 0) + quals |= TYPE_QUAL_CONST; + if ((qualifiers & GCC_QUALIFIER_VOLATILE) != 0) + quals |= TYPE_QUAL_VOLATILE; + if ((qualifiers & GCC_QUALIFIER_RESTRICT) != 0) + quals |= TYPE_QUAL_RESTRICT; + + return convert_out (build_qualified_type (unqualified_type, quals)); +} + +gcc_type +plugin_build_complex_type (cc1_plugin::connection *self, + gcc_type base_type) +{ + plugin_context *ctx = static_cast (self); + return convert_out (ctx->preserve (build_complex_type (convert_in (base_type)))); +} + +gcc_type +plugin_build_vector_type (cc1_plugin::connection *self, + gcc_type base_type, int nunits) +{ + plugin_context *ctx = static_cast (self); + return convert_out (ctx->preserve (build_vector_type (convert_in (base_type), + nunits))); +} + +int +plugin_build_constant (cc1_plugin::connection *self, gcc_type type_in, + const char *name, unsigned long value, + const char *filename, unsigned int line_number) +{ + plugin_context *ctx = static_cast (self); + tree cst, decl; + tree type = convert_in (type_in); + + cst = build_int_cst (type, value); + decl = build_decl (ctx->get_source_location (filename, line_number), + CONST_DECL, get_identifier (name), type); + DECL_INITIAL (decl) = cst; + pushdecl_safe (decl); + + return 1; +} + +gcc_type +plugin_error (cc1_plugin::connection *, + const char *message) +{ + error ("%s", message); + return convert_out (error_mark_node); +} + + + +// Perform GC marking. + +static void +gc_mark (void *, void *) +{ + if (current_context != NULL) + current_context->mark (); +} + +#ifdef __GNUC__ +#pragma GCC visibility push(default) +#endif + +int +plugin_init (struct plugin_name_args *plugin_info, + struct plugin_gcc_version *) +{ + long fd = -1; + for (int i = 0; i < plugin_info->argc; ++i) + { + if (strcmp (plugin_info->argv[i].key, "fd") == 0) + { + char *tail; + errno = 0; + fd = strtol (plugin_info->argv[i].value, &tail, 0); + if (*tail != '\0' || errno != 0) + fatal_error ("%s: invalid file descriptor argument to plugin", + plugin_info->base_name); + break; + } + } + if (fd == -1) + fatal_error ("%s: required plugin argument % is missing", + plugin_info->base_name); + + current_context = new plugin_context (fd); + + // Handshake. + cc1_plugin::protocol_int version; + if (!current_context->require ('H') + || ! ::cc1_plugin::unmarshall (current_context, &version)) + fatal_error ("%s: handshake failed", plugin_info->base_name); + if (version != GCC_C_FE_VERSION_0) + fatal_error ("%s: unknown version in handshake", plugin_info->base_name); + + register_callback (plugin_info->base_name, PLUGIN_PRAGMAS, + plugin_init_extra_pragmas, NULL); + register_callback (plugin_info->base_name, PLUGIN_PRE_GENERICIZE, + rewrite_decls_to_addresses, NULL); + register_callback (plugin_info->base_name, PLUGIN_GGC_MARKING, + gc_mark, NULL); + + lang_hooks.print_error_function = plugin_print_error_function; + +#define GCC_METHOD0(R, N) \ + { \ + cc1_plugin::callback_ftype *fun \ + = cc1_plugin::callback; \ + current_context->add_callback (# N, fun); \ + } +#define GCC_METHOD1(R, N, A) \ + { \ + cc1_plugin::callback_ftype *fun \ + = cc1_plugin::callback; \ + current_context->add_callback (# N, fun); \ + } +#define GCC_METHOD2(R, N, A, B) \ + { \ + cc1_plugin::callback_ftype *fun \ + = cc1_plugin::callback; \ + current_context->add_callback (# N, fun); \ + } +#define GCC_METHOD3(R, N, A, B, C) \ + { \ + cc1_plugin::callback_ftype *fun \ + = cc1_plugin::callback; \ + current_context->add_callback (# N, fun); \ + } +#define GCC_METHOD4(R, N, A, B, C, D) \ + { \ + cc1_plugin::callback_ftype *fun \ + = cc1_plugin::callback; \ + current_context->add_callback (# N, fun); \ + } +#define GCC_METHOD5(R, N, A, B, C, D, E) \ + { \ + cc1_plugin::callback_ftype *fun \ + = cc1_plugin::callback; \ + current_context->add_callback (# N, fun); \ + } +#define GCC_METHOD7(R, N, A, B, C, D, E, F, G) \ + { \ + cc1_plugin::callback_ftype *fun \ + = cc1_plugin::callback; \ + current_context->add_callback (# N, fun); \ + } + +#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 + + return 0; +} diff --git a/libcc1/rpc.hh b/libcc1/rpc.hh new file mode 100644 index 0000000..58758d3 --- /dev/null +++ b/libcc1/rpc.hh @@ -0,0 +1,486 @@ +/* RPC call and callback templates + Copyright (C) 2014 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 +. */ + +#ifndef CC1_PLUGIN_RPC_HH +#define CC1_PLUGIN_RPC_HH + +#include "status.hh" +#include "marshall.hh" +#include "connection.hh" + +namespace cc1_plugin +{ + // The plugin API may contain some "const" method parameters. + // However, when unmarshalling we cannot unmarshall into a const + // object; and furthermore we want to be able to deallocate pointers + // when finished with them. This wrapper class lets us properly + // remove the "const" and handle deallocation from pointer types. + + template + class argument_wrapper + { + public: + + argument_wrapper () { } + ~argument_wrapper () { } + + operator T () const { return m_object; } + + status unmarshall (connection *conn) + { + return ::cc1_plugin::unmarshall (conn, &m_object); + } + + private: + + T m_object; + + // No copying or assignment allowed. + argument_wrapper (const argument_wrapper &); + argument_wrapper &operator= (const argument_wrapper &); + }; + + // Specialization for any kind of pointer. This is declared but not + // defined to avoid bugs if a new pointer type is introduced into + // the API. Instead you will just get a compilation error. + template + class argument_wrapper; + + // Specialization for string types. + template<> + class argument_wrapper + { + public: + argument_wrapper () : m_object (NULL) { } + ~argument_wrapper () + { + delete[] m_object; + } + + operator const char * () const + { + return m_object; + } + + status unmarshall (connection *conn) + { + return ::cc1_plugin::unmarshall (conn, &m_object); + } + + private: + + char *m_object; + + // No copying or assignment allowed. + argument_wrapper (const argument_wrapper &); + argument_wrapper &operator= (const argument_wrapper &); + }; + + // Specialization for gcc_type_array. + template<> + class argument_wrapper + { + public: + argument_wrapper () : m_object (NULL) { } + ~argument_wrapper () + { + // It would be nicer if gcc_type_array could have a destructor. + // But, it is in code shared with gdb and cannot. + if (m_object != NULL) + delete[] m_object->elements; + delete m_object; + } + + operator const gcc_type_array * () const + { + return m_object; + } + + status unmarshall (connection *conn) + { + return ::cc1_plugin::unmarshall (conn, &m_object); + } + + private: + + gcc_type_array *m_object; + + // No copying or assignment allowed. + argument_wrapper (const argument_wrapper &); + argument_wrapper &operator= (const argument_wrapper &); + }; + + // There are two kinds of template functions here: "call" and + // "callback". They are each repeated multiple times to handle + // different numbers of arguments. (This would be improved with + // C++11, though applying a call is still tricky until C++14 can be + // used.) + + // The "call" template is used for making a remote procedure call. + // It starts a query ('Q') packet, marshalls its arguments, waits + // for a result, and finally reads and returns the result via an + // "out" parameter. + + // The "callback" template is used when receiving a remote procedure + // call. This template function is suitable for use with the + // "callbacks" and "connection" classes. It decodes incoming + // arguments, passes them to the wrapped function, and finally + // marshalls a reply packet. + + template + status + call (connection *conn, const char *method, R *result) + { + if (!conn->send ('Q')) + return FAIL; + if (!marshall (conn, method)) + return FAIL; + if (!marshall (conn, 0)) + return FAIL; + if (!conn->wait_for_result ()) + return FAIL; + if (!unmarshall (conn, result)) + return FAIL; + return OK; + } + + template + status + callback (connection *conn) + { + R result; + + if (!unmarshall_check (conn, 0)) + return FAIL; + result = func (conn); + if (!conn->send ('R')) + return FAIL; + return marshall (conn, result); + } + + template + status + call (connection *conn, const char *method, R *result, A arg) + { + if (!conn->send ('Q')) + return FAIL; + if (!marshall (conn, method)) + return FAIL; + if (!marshall (conn, 1)) + return FAIL; + if (!marshall (conn, arg)) + return FAIL; + if (!conn->wait_for_result ()) + return FAIL; + if (!unmarshall (conn, result)) + return FAIL; + return OK; + } + + template + status + callback (connection *conn) + { + argument_wrapper arg; + R result; + + if (!unmarshall_check (conn, 1)) + return FAIL; + if (!arg.unmarshall (conn)) + return FAIL; + result = func (conn, arg); + if (!conn->send ('R')) + return FAIL; + return marshall (conn, result); + } + + template + status + call (connection *conn, const char *method, R *result, A1 arg1, A2 arg2) + { + if (!conn->send ('Q')) + return FAIL; + if (!marshall (conn, method)) + return FAIL; + if (!marshall (conn, 2)) + return FAIL; + if (!marshall (conn, arg1)) + return FAIL; + if (!marshall (conn, arg2)) + return FAIL; + if (!conn->wait_for_result ()) + return FAIL; + if (!unmarshall (conn, result)) + return FAIL; + return OK; + } + + template + status + callback (connection *conn) + { + argument_wrapper arg1; + argument_wrapper arg2; + R result; + + if (!unmarshall_check (conn, 2)) + return FAIL; + if (!arg1.unmarshall (conn)) + return FAIL; + if (!arg2.unmarshall (conn)) + return FAIL; + result = func (conn, arg1, arg2); + if (!conn->send ('R')) + return FAIL; + return marshall (conn, result); + } + + template + status + call (connection *conn, const char *method, R *result, A1 arg1, A2 arg2, + A3 arg3) + { + if (!conn->send ('Q')) + return FAIL; + if (!marshall (conn, method)) + return FAIL; + if (!marshall (conn, 3)) + return FAIL; + if (!marshall (conn, arg1)) + return FAIL; + if (!marshall (conn, arg2)) + return FAIL; + if (!marshall (conn, arg3)) + return FAIL; + if (!conn->wait_for_result ()) + return FAIL; + if (!unmarshall (conn, result)) + return FAIL; + return OK; + } + + template + status + callback (connection *conn) + { + argument_wrapper arg1; + argument_wrapper arg2; + argument_wrapper arg3; + R result; + + if (!unmarshall_check (conn, 3)) + return FAIL; + if (!arg1.unmarshall (conn)) + return FAIL; + if (!arg2.unmarshall (conn)) + return FAIL; + if (!arg3.unmarshall (conn)) + return FAIL; + result = func (conn, arg1, arg2, arg3); + if (!conn->send ('R')) + return FAIL; + return marshall (conn, result); + } + + template + status + call (connection *conn, const char *method, R *result, A1 arg1, A2 arg2, + A3 arg3, A4 arg4) + { + if (!conn->send ('Q')) + return FAIL; + if (!marshall (conn, method)) + return FAIL; + if (!marshall (conn, 4)) + return FAIL; + if (!marshall (conn, arg1)) + return FAIL; + if (!marshall (conn, arg2)) + return FAIL; + if (!marshall (conn, arg3)) + return FAIL; + if (!marshall (conn, arg4)) + return FAIL; + if (!conn->wait_for_result ()) + return FAIL; + if (!unmarshall (conn, result)) + return FAIL; + return OK; + } + + template + status + callback (connection *conn) + { + argument_wrapper arg1; + argument_wrapper arg2; + argument_wrapper arg3; + argument_wrapper arg4; + R result; + + if (!unmarshall_check (conn, 4)) + return FAIL; + if (!arg1.unmarshall (conn)) + return FAIL; + if (!arg2.unmarshall (conn)) + return FAIL; + if (!arg3.unmarshall (conn)) + return FAIL; + if (!arg4.unmarshall (conn)) + return FAIL; + result = func (conn, arg1, arg2, arg3, arg4); + if (!conn->send ('R')) + return FAIL; + return marshall (conn, result); + } + + template + status + call (connection *conn, const char *method, R *result, A1 arg1, A2 arg2, + A3 arg3, A4 arg4, A5 arg5) + { + if (!conn->send ('Q')) + return FAIL; + if (!marshall (conn, method)) + return FAIL; + if (!marshall (conn, 5)) + return FAIL; + if (!marshall (conn, arg1)) + return FAIL; + if (!marshall (conn, arg2)) + return FAIL; + if (!marshall (conn, arg3)) + return FAIL; + if (!marshall (conn, arg4)) + return FAIL; + if (!marshall (conn, arg5)) + return FAIL; + if (!conn->wait_for_result ()) + return FAIL; + if (!unmarshall (conn, result)) + return FAIL; + return OK; + } + + template + status + callback (connection *conn) + { + argument_wrapper arg1; + argument_wrapper arg2; + argument_wrapper arg3; + argument_wrapper arg4; + argument_wrapper arg5; + R result; + + if (!unmarshall_check (conn, 5)) + return FAIL; + if (!arg1.unmarshall (conn)) + return FAIL; + if (!arg2.unmarshall (conn)) + return FAIL; + if (!arg3.unmarshall (conn)) + return FAIL; + if (!arg4.unmarshall (conn)) + return FAIL; + if (!arg5.unmarshall (conn)) + return FAIL; + result = func (conn, arg1, arg2, arg3, arg4, arg5); + if (!conn->send ('R')) + return FAIL; + return marshall (conn, result); + } + + template + status + call (connection *conn, const char *method, R *result, A1 arg1, A2 arg2, + A3 arg3, A4 arg4, A5 arg5, A6 arg6, A7 arg7) + { + if (!conn->send ('Q')) + return FAIL; + if (!marshall (conn, method)) + return FAIL; + if (!marshall (conn, 7)) + return FAIL; + if (!marshall (conn, arg1)) + return FAIL; + if (!marshall (conn, arg2)) + return FAIL; + if (!marshall (conn, arg3)) + return FAIL; + if (!marshall (conn, arg4)) + return FAIL; + if (!marshall (conn, arg5)) + return FAIL; + if (!marshall (conn, arg6)) + return FAIL; + if (!marshall (conn, arg7)) + return FAIL; + if (!conn->wait_for_result ()) + return FAIL; + if (!unmarshall (conn, result)) + return FAIL; + return OK; + } + + template + status + callback (connection *conn) + { + argument_wrapper arg1; + argument_wrapper arg2; + argument_wrapper arg3; + argument_wrapper arg4; + argument_wrapper arg5; + argument_wrapper arg6; + argument_wrapper arg7; + R result; + + if (!unmarshall_check (conn, 7)) + return FAIL; + if (!arg1.unmarshall (conn)) + return FAIL; + if (!arg2.unmarshall (conn)) + return FAIL; + if (!arg3.unmarshall (conn)) + return FAIL; + if (!arg4.unmarshall (conn)) + return FAIL; + if (!arg5.unmarshall (conn)) + return FAIL; + if (!arg6.unmarshall (conn)) + return FAIL; + if (!arg7.unmarshall (conn)) + return FAIL; + result = func (conn, arg1, arg2, arg3, arg4, arg5, arg6, arg7); + if (!conn->send ('R')) + return FAIL; + return marshall (conn, result); + } +}; + +#endif // CC1_PLUGIN_RPC_HH diff --git a/libcc1/status.hh b/libcc1/status.hh new file mode 100644 index 0000000..764c7ff --- /dev/null +++ b/libcc1/status.hh @@ -0,0 +1,33 @@ +/* status type definition + Copyright (C) 2014 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 +. */ + +#ifndef CC1_PLUGIN_STATUS_HH +#define CC1_PLUGIN_STATUS_HH + +namespace cc1_plugin +{ + // The status returned by various connection functions. + enum status + { + FAIL = 0, + OK = 1 + }; +} + +#endif // CC1_PLUGIN_STATUS_HH