* Re: [PATCH] Vtable pointer verification, main gcc changes (patch 2 of 3)
@ 2013-01-23 22:36 Caroline Tice
0 siblings, 0 replies; only message in thread
From: Caroline Tice @ 2013-01-23 22:36 UTC (permalink / raw)
To: GCC Patches
[-- Attachment #1: Type: text/plain, Size: 1895 bytes --]
I have made the requested changes to the previous patches I submitted
for the vtable verification feature. As requested, I have broken this
into three patches: the C++ front end changes, the main gcc changes,
and the runtime library changes. Here are the main gcc changes for
the vtable verification features.
-- Caroline Tice
cmtice@google.com
ChangeLog:
2013-01-23 Caroline Tice <cmtice@google.com>
* ligbcc/config.host (extra_parts): Add vtv_start.o and vtv_end.o to
the list.
* libgcc/Makefile.in: Add definitin for gcc_srcdir; add rules for
building vtv_start.o and vtv_end.o.
* vtv_start.c: New file.
* tree.h: Add extern function declaration for save_vtable_map_decl.
* tree-pass.h (pass_vtable_verify): Declare new pass.
* vtv_end.c: New file.
* timevar.def (TV_VTABLE_VERIFICATION): Declare new time var.
* flag-types.h (vtv_priority): New enum, for values for new
'-fvtable-verify=' option.
* tree-vtable-verify.c: New file, contains vtable verification tree
pass.
* tree-vtable-verify.h: New file.
* common.opt: (fvtable-verify=): New option. Also define vtv_priority
values for the option.
* varasm.c (assemble_variable): Add code for handling variables that
go into the ".vtable_map_vars" section.
(assemble_vtv_perinit_initializer): New function.
(default_section_type_flags): Add SECTION_LINKONCE to
".vtable_map_vars" section items.
* output.h (assemble_vtv_preinit_initializer): External function decl.
* Makefile.in (OBJS): Add tree-vtable-verify.o to list of object
files.
(tree-vtable-verify.o): Add rule for building object file.
(GTFILES): Add tree-vtable-verify.c to list of files that use GCC's
garbage collector.
* passes.c (init_optimization_pass): Add pass_vtable_verify.
* config/gnu-user.h: Add vtv_start.o to STARTFILE_SPEC if
fvtable-verify is present; Add vtv_end.o to ENDFILE_SPEC if
fvtable-verify is present.
[-- Attachment #2: fsf-vtable-verification.v4.gcc-main.patch --]
[-- Type: application/octet-stream, Size: 68298 bytes --]
Index: libgcc/config.host
===================================================================
--- libgcc/config.host (revision 194823)
+++ libgcc/config.host (working copy)
@@ -198,7 +198,7 @@ case ${host} in
;;
*-*-linux* | frv-*-*linux* | *-*-kfreebsd*-gnu | *-*-knetbsd*-gnu | *-*-gnu* | *-*-kopensolaris*-gnu)
tmake_file="$tmake_file t-crtstuff-pic t-libgcc-pic t-eh-dw2-dip t-slibgcc t-slibgcc-gld t-slibgcc-elf-ver t-linux"
- extra_parts="crtbegin.o crtbeginS.o crtbeginT.o crtend.o crtendS.o"
+ extra_parts="crtbegin.o crtbeginS.o crtbeginT.o crtend.o crtendS.o vtv_start.o vtv_end.o"
;;
*-*-lynxos*)
tmake_file="$tmake_file t-lynx $cpu_type/t-crtstuff t-crtstuff-pic t-libgcc-pic"
Index: libgcc/Makefile.in
===================================================================
--- libgcc/Makefile.in (revision 194823)
+++ libgcc/Makefile.in (working copy)
@@ -22,6 +22,7 @@
libgcc_topdir = @libgcc_topdir@
host_subdir = @host_subdir@
+gcc_srcdir = $(libgcc_topdir)/gcc
gcc_objdir = $(MULTIBUILDTOP)../../$(host_subdir)/gcc
srcdir = @srcdir@
@@ -969,6 +970,16 @@ crtendS$(objext): $(srcdir)/crtstuff.c
# This is a version of crtbegin for -static links.
crtbeginT$(objext): $(srcdir)/crtstuff.c
$(crt_compile) $(CRTSTUFF_T_CFLAGS) -c $< -DCRT_BEGIN -DCRTSTUFFT_O
+
+# These are used in vtable verification; see comments in source files for
+# more details.
+vtv_start$(objext): $(gcc_srcdir)/vtv_start.c
+ $(crt_compile) $(CRTSTUFF_T_CFLAGS_S) \
+ -c $(gcc_srcdir)/vtv_start.c
+
+vtv_end$(objext): $(gcc_srcdir)/vtv_end.c
+ $(crt_compile) $(CRTSTUFF_T_CFLAGS_S) \
+ -c $(gcc_srcdir)/vtv_end.c
endif
ifeq ($(CUSTOM_CRTIN),)
Index: gcc/vtv_start.c
===================================================================
--- gcc/vtv_start.c (revision 0)
+++ gcc/vtv_start.c (revision 0)
@@ -0,0 +1,60 @@
+/* Copyright (C) 2012, 2013
+ 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/>. */
+
+/* This file is part of the vtable verification feature (for a
+ detailed description of the feature, see comments in
+ tree-vtable-verify.c). The vtable verification feature creates
+ certain global symbols that need to be read-write sometimes during
+ program execution, and read-only at others. It uses 'mprotect' to
+ change the memory protections of the pages on which these variables
+ are stored. In order to not affect the protections of other
+ program variables, these variables are put into a special named
+ section, ".vtable_map_vars", which is page-aligned at the start,
+ and which is padded with a page-sized amount of zeros at the end.
+ To make this section page aligned, we create a special symbol,
+ "_vtable_map_vars_start" which we make the very first thing that
+ goes into the section. This file defines that symbol (and only
+ that symbol). GCC compiles this file into vtv_start.o, and
+ inserts vtv_start.o into the link line immediately after
+ crtbegin.o, if the program is compiled with -fvtable.verify.
+
+ In order to pad the ".vtable_map_vars" section with a page-sized
+ amount of zeros at the end, there is a second symbol,
+ _vtable_map_vars_end, which is defined in another file, vtv_end.c.
+ This second symbol is a page-sized array of chars, zero-filled, and
+ is the very last thing to go into the section. When the GCC driver
+ inserts vtv_start.o into the link line (just after crtbegin.o) it
+ also inserts vtv_end.o into the link line, just before crtend.o.
+ This has the desired effect of making our section page-aligned and
+ page-size paded, ensuring that no other program data lands on our
+ pages. */
+
+#define PAGE_SIZE 4096
+
+#ifdef BIG_PAGE_SIZE
+/* TODO - Replace '4096' below with correct big page size. */
+#define PAGE_SIZE 4096
+#endif
+
+
+/* Page-aligned symbol to mark beginning of .vtable_map_vars section. */
+char _vtable_map_vars_start []
+__attribute__ ((__visibility__ ("protected"), used, aligned(PAGE_SIZE),
+ section(".vtable_map_vars")))
+ = { };
Index: gcc/tree.h
===================================================================
--- gcc/tree.h (revision 195313)
+++ gcc/tree.h (working copy)
@@ -6439,6 +6439,9 @@ is_lang_specific (tree t)
/* In gimple-low.c. */
extern bool block_may_fallthru (const_tree);
+/* In tree-vtable-verify.c. */
+extern void save_vtable_map_decl (tree);
+
\f
/* Functional interface to the builtin functions. */
@@ -6529,4 +6532,5 @@ builtin_decl_implicit_p (enum built_in_f
&& builtin_info.implicit_p[uns_fncode]);
}
+
#endif /* GCC_TREE_H */
Index: gcc/tree-pass.h
===================================================================
--- gcc/tree-pass.h (revision 195313)
+++ gcc/tree-pass.h (working copy)
@@ -364,6 +364,7 @@ extern struct gimple_opt_pass pass_tm_ed
extern struct gimple_opt_pass pass_split_functions;
extern struct gimple_opt_pass pass_feedback_split_functions;
extern struct gimple_opt_pass pass_strength_reduction;
+extern struct gimple_opt_pass pass_vtable_verify;
/* IPA Passes */
extern struct simple_ipa_opt_pass pass_ipa_lower_emutls;
Index: gcc/vtv_end.c
===================================================================
--- gcc/vtv_end.c (revision 0)
+++ gcc/vtv_end.c (revision 0)
@@ -0,0 +1,60 @@
+/* Copyright (C) 2012, 2013
+ 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/>. */
+
+/* This file is part of the vtable verification feature (for a
+ detailed description of the feature, see comments in
+ tree-vtable-verify.c). The vtable verification feature creates
+ certain global symbols that need to be read-write sometimes during
+ program execution, and read-only at others. It uses 'mprotect' to
+ change the memory protections of the pages on which these variables
+ are stored. In order to not affect the protections of other
+ program variables, these variables are put into a special named
+ section, ".vtable_map_vars", which is page-aligned at the start,
+ and which is padded with a page-sized amount of zeros at the end.
+ To make this section page aligned, we create a special symbol,
+ "_vtable_map_vars_start" which we make the very first thing that
+ goes into the section. That is defined in vtv_start.c (which
+ contains nothing else). vtv_start.c gest compiled into
+ vtv_start.o, and vtv_start.o gets inserted into the link line
+ immediately after crtbegin.o, if the program is compiled with
+ -fvtable.verify.
+
+ In order to pad the ".vtable_map_vars" section with a page-sized
+ amount of zeros at the end, there is a second symbol,
+ _vtable_map_vars_end. This file defines that symbol (and only this
+ symbol). This second symbol is a page-sized array of chars,
+ zero-filled, and is the very last thing to go into the section.
+ When the GCC driver inserts vtv_start.o into the link line (just
+ after crtbegin.o) it also inserts vtv_end.o into the link line,
+ just before crtend.o. This has the desired effect of making our
+ section page-aligned and page-size paded, ensuring that no other
+ program data lands on our pages. */
+
+
+#define PAGE_SIZE 4096
+
+#ifdef BIG_PAGE_SIZE
+/* TODO - Replace '4096' below with correct big page size. */
+#define PAGE_SIZE 4096
+#endif
+
+/* Page-sized variable to mark end of .vtable_map_vars section. */
+char _vtable_map_vars_end[PAGE_SIZE]
+ __attribute__ ((__visibility__ ("protected"), used,
+ section(".vtable_map_vars")));
Index: gcc/timevar.def
===================================================================
--- gcc/timevar.def (revision 195313)
+++ gcc/timevar.def (working copy)
@@ -255,6 +255,7 @@ DEFTIMEVAR (TV_TREE_UNINIT , "
DEFTIMEVAR (TV_PLUGIN_INIT , "plugin initialization")
DEFTIMEVAR (TV_PLUGIN_RUN , "plugin execution")
DEFTIMEVAR (TV_GIMPLE_SLSR , "straight-line strength reduction")
+DEFTIMEVAR (TV_VTABLE_VERIFICATION , "vtable verification")
/* Everything else in rest_of_compilation not included above. */
DEFTIMEVAR (TV_EARLY_LOCAL , "early local passes")
Index: gcc/flag-types.h
===================================================================
--- gcc/flag-types.h (revision 195313)
+++ gcc/flag-types.h (working copy)
@@ -191,4 +191,10 @@ enum fp_contract_mode {
FP_CONTRACT_FAST = 2
};
+/* flag_vtable_verify initialization levels. */
+enum vtv_priority {
+ VTV_NO_PRIORITY = 0, /* i.E. Do NOT do vtable verification. */
+ VTV_STANDARD_PRIORITY = 1,
+ VTV_PREINIT_PRIORITY = 2
+};
#endif /* ! GCC_FLAG_TYPES_H */
Index: gcc/tree-vtable-verify.c
===================================================================
--- gcc/tree-vtable-verify.c (revision 0)
+++ gcc/tree-vtable-verify.c (revision 0)
@@ -0,0 +1,1212 @@
+/* Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011
+ 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/>. */
+
+/* Virtual Table Pointer Security Pass - Detect corruption of vtable pointers
+ before using them for virtual method dispatches. */
+
+/* This file is part of the vtable security feature implementation.
+ The vtable security feature is designed to detect when a virtual
+ call is about to be made through an invalid vtable pointer
+ (possibly due to data corruption or malicious attacks). The
+ compiler finds every virtual call, and inserts a verification call
+ before the virtual call. The verification call takes the actual
+ vtable pointer value in the object through which the virtual call
+ is being made, and compares the vtable pointer against a set of all
+ valid vtable pointers that the object could contain (this set is
+ based on the declared type of the object). If the pointer is in
+ the valid set, execution is allowed to continue; otherwise the
+ program is halted.
+
+ There are several pieces needed in order to make this work: 1. For
+ every virtual class in the program (i.e. a class that contains
+ virtual methods), we need to build the set of all possible valid
+ vtables that an object of that class could point to. This includes
+ vtables for any class(es) that inherit from the class under
+ consideration. 2. For every such data set we build up, we need a
+ way to find and reference the data set. This is complicated by the
+ fact that the real vtable addresses are not known until runtime,
+ when the program is loaded into memory, but we need to reference the
+ sets at compile time when we are inserting verification calls into
+ the program. 3. We need to find every virtual call in the program,
+ and insert the verification call (with the appropriate arguments)
+ before the virtual call. 4. We need some runtime library pieces:
+ the code to build up the data sets at runtime; the code to actually
+ perform the verification using the data sets; and some code to set
+ protections on the data sets, so they themselves do not become
+ hacker targets.
+
+ To find and reference the set of valid vtable pointers for any given
+ virtual class, we create a special global varible for each virtual
+ class. We refer to this as the "vtable map variable" for that
+ class. The vtable map variable has the type "void *", and is
+ initialized by the compiler to NULL. At runtime when the set of
+ valid vtable pointers for a virtual class, e.g. class Foo, is built,
+ the vtable map variable for class Foo is made to point to the set.
+ During compile time, when the compiler is inserting verification
+ calls into the program, it passes the vtable map variable for the
+ appropriate class to the verification call, so that at runtime the
+ verification call can find the appropriate data set.
+
+ The actual set of valid vtable pointers for a virtual class,
+ e.g. class Foo, cannot be built until runtime, when the vtables get
+ loaded into memory and their addresses are known. But the knowledge
+ about which vtables belong in which class' hierarchy is only known
+ at compile time. Therefore at compile time we collect class
+ hierarchy and vtable information about every virtual class, and we
+ generate calls to build up the data sets at runtime. To build the
+ data sets, we call one of the functions we add to the runtime
+ library, __VLTRegisterPair. __VLTRegisterPair takes two arguments,
+ a vtable map variable and the address of a vtable. If the vtable
+ map variable is currently NULL, it creates a new data set (hash
+ table), makes the vtable map variable point to the new data set, and
+ inserts the vtable address into the data set. If the vtable map
+ variable is not NULL, it just inserts the vtable address into the
+ data set. In order to make sure that our data sets are built before
+ any verification calls happen, we create a special constructor
+ initialization function for each compilation unit, give it a very
+ high initialization priority, and insert all of our calls to
+ __VLTRegisterPair into our special constructor initialization
+ function.
+
+ The vtable verification feature is controlled by the flag
+ '-fvtable-verify='. There are three flavors of this:
+ '-fvtable-verify=std', '-fvtable-verify=preinit', and
+ '-fvtable-verify=none'. If the option '-fvtable-verfy=preinit' is
+ used, then our constructor initialization function gets put into the
+ preinit array. This is necessary if there are data sets that need
+ to be built very early in execution. If the constructor
+ initialization function gets put into the preinit array, the we also
+ add calls to __VLTChangePermission at the beginning and end of the
+ function. The call at the beginning sets the permissions on the
+ data sets and vtable map variables to read/write, and the one at the
+ end makes them read-only. If the '-fvtable-verify=std' option is
+ used, the constructor initialization functions are executed at their
+ normal time, and the __VLTChangePermission calls are handled
+ differently (see the comments in libstdc++-v3/libsupc++/vtv_rts.cc).
+ The option '-fvtable-verify=none' turns off vtable verification.
+
+ This file contains code for the tree pass that goes through all the
+ statements in each basic block, looking for virtual calls, and
+ inserting a call to __VLTVerifyVtablePointer (with appropriate
+ arguments) before each one. It also contains the hash table
+ functions for the data structures used for collecting the class
+ hierarchy data and building/maintaining the vtable map variable data
+ are defined in gcc/tree-vtable-verify.h. These data structures are
+ shared with the code in the C++ front end that collects the class
+ hierarchy & vtable information and generates the vtable map
+ variables (see cp/vtable-class-hierarchy.c). This tree pass should
+ run just before the gimple is converted to RTL.
+
+ Some implementation details for this pass:
+
+ To find the all of the virtual calls, we iterate through all the
+ gimple statements in each basic block, looking for any call
+ statement with the code "OBJ_TYPE_REF". Once we have found the
+ virtual call, we need to find the vtable pointer through which the
+ call is being made, and the type of the object containing the
+ pointer (to find the appropriate vtable map variable). We then use
+ these to build a call to __VLTVerifyVtablePointer, passing the
+ vtable map variable, and the vtable pointer. We insert the
+ verification call just after the gimple statement that gets the
+ vtable pointer out of the object, and we update the next
+ statement to depend on the result returned from
+ __VLTVerifyVtablePointer (the vtable pointer value), to ensure
+ subsequent compiler phases don't remove or reorder the call (it's no
+ good to have the verification occur after the virtual call, for
+ example). To find the vtable pointer being used (and the type of
+ the object) we search backwards through the def_stmts chain from the
+ virtual call (see verify_bb_vtables for more details). */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "tree.h"
+#include "cp/cp-tree.h"
+#include "tm_p.h"
+#include "basic-block.h"
+#include "output.h"
+#include "tree-flow.h"
+#include "tree-dump.h"
+#include "tree-pass.h"
+#include "timevar.h"
+#include "cfgloop.h"
+#include "flags.h"
+#include "tree-inline.h"
+#include "tree-scalar-evolution.h"
+#include "diagnostic-core.h"
+#include "gimple-pretty-print.h"
+#include "toplev.h"
+#include "langhooks.h"
+
+#include "tree-vtable-verify.h"
+
+bool vtv_debug = false;
+unsigned num_vtable_map_nodes = 0;
+bool any_verification_calls_generated = false;
+
+static GTY(()) tree verify_vtbl_ptr_fndecl = NULL_TREE;
+
+unsigned int vtable_verify_main (void);
+
+/* The following few functions are for the vtbl pointer hash table
+ in the 'registered' field of the struct vtable_map_node. The hash
+ table keeps track of which vtable pointers have been used in
+ calls to __VLTRegisterPair with that particular vtable map variable. */
+
+/* This function checks to see if a particular VTABLE_DECL and OFFSET are
+ already in the 'registered' hash table for NODE. */
+
+bool
+vtbl_map_node_registration_find (struct vtbl_map_node *node,
+ tree vtable_decl,
+ unsigned offset)
+{
+ struct vtable_registration key;
+ struct vtable_registration **slot;
+
+ gcc_assert (node && node->registered);
+
+ key.vtable_decl = vtable_decl;
+ slot = (struct vtable_registration **) htab_find_slot (node->registered,
+ &key, NO_INSERT);
+
+ if (slot && (*slot) && (*slot)->offsets)
+ {
+ unsigned i;
+ for (i = 0; i < (*slot)->cur_offset; ++i)
+ if ((*slot)->offsets[i] == offset)
+ return true;
+ }
+
+ return false;
+}
+
+/* This function inserts VTABLE_DECL and OFFSET into the 'registered'
+ hash table for NODE. */
+
+void
+vtbl_map_node_registration_insert (struct vtbl_map_node *node,
+ tree vtable_decl,
+ unsigned offset)
+{
+ struct vtable_registration key;
+ struct vtable_registration **slot;
+
+ if (!node || !node->registered)
+ return;
+
+ key.vtable_decl = vtable_decl;
+ slot = (struct vtable_registration **) htab_find_slot (node->registered,
+ &key, INSERT);
+
+ if (! *slot)
+ {
+ unsigned i;
+ struct vtable_registration *node;
+ node = XNEW (struct vtable_registration);
+ node->vtable_decl = vtable_decl;
+
+ /* We expect the number of different offsets in any given vtable
+ that will be valid vtable pointers to be small (but we know
+ it can be greater than 1). To avoid having to resize this
+ very often, we randomly chose 10 as a reasonable-seeming
+ size. */
+ node->offsets = (unsigned *) xmalloc (10 * sizeof (unsigned));
+ for (i= 0; i < 10; ++i)
+ node->offsets[i] = 0;
+ node->offsets[0] = offset;
+ node->cur_offset = 1;
+ node->max_offsets = 10;
+ *slot = node;
+ }
+ else
+ {
+ /* We found the vtable_decl slot; we need to see if it already
+ contains the offset. If not, we need to add the offset. */
+ unsigned i;
+ bool found = false;
+ for (i = 0; i < (*slot)->cur_offset && !found; ++i)
+ if ((*slot)->offsets[i] == offset)
+ found = true;
+
+ if (!found)
+ {
+ /* Re-size the offset array if necessary. */
+ if ((*slot)->cur_offset == (*slot)->max_offsets)
+ {
+ unsigned new_max = 2 * (*slot)->max_offsets;
+ (*slot)->offsets = (unsigned *)
+ xrealloc ((*slot)->offsets, new_max * sizeof (unsigned));
+
+ for (i = (*slot)->max_offsets; i < new_max; ++i)
+ (*slot)->offsets[i] = 0;
+ (*slot)->max_offsets = new_max;
+ }
+ /* Insert the new offset. */
+ (*slot)->offsets[(*slot)->cur_offset] = offset;
+ (*slot)->cur_offset = (*slot)->cur_offset + 1;
+ }
+ }
+}
+
+/* Hashtable functions for vtable_registration hashtables. */
+
+static hashval_t
+hash_vtable_registration (const void *p)
+{
+ const struct vtable_registration *n = (const struct vtable_registration *) p;
+ return (hashval_t) (DECL_UID (n->vtable_decl));
+}
+
+static int
+eq_vtable_registration (const void *p1, const void *p2)
+{
+ const struct vtable_registration *n1 =
+ (const struct vtable_registration *) p1;
+ const struct vtable_registration *n2 =
+ (const struct vtable_registration *) p2;
+ return (DECL_UID (n1->vtable_decl) == DECL_UID (n2->vtable_decl));
+}
+
+/* End of hashtable functions for "registered" hashtables. */
+
+
+/* Here are the three data structures into which we insert vtable map nodes.
+ We use three data structures because of the vastly different ways we need
+ to find the nodes for various tasks (see comments in tree-vtable-verify.h
+ for more details. */
+
+/* Vtable map variable nodes stored in a hash table. */
+
+static htab_t vtbl_map_hash = NULL;
+
+/* Vtable map variable nodes stored in a linked list. */
+
+struct vtbl_map_node *vtbl_map_nodes = NULL;
+
+/* Vtable map variable nodes stored in a vector. */
+
+vec<struct vtbl_map_node *> vtbl_map_nodes_vec;
+
+
+/* Hashtable functions for vtbl_map_hash. */
+
+/* Returns a hash code for P. */
+
+static hashval_t
+hash_vtbl_map_node (const void *p)
+{
+ const struct vtbl_map_node *n = (const struct vtbl_map_node *) p;
+ return (hashval_t) IDENTIFIER_HASH_VALUE (n->class_name);
+}
+
+/* Returns nonzero if P1 and P2 are equal. */
+
+static int
+eq_vtbl_map_node (const void *p1, const void *p2)
+{
+ const struct vtbl_map_node *n1 = (const struct vtbl_map_node *) p1;
+ const struct vtbl_map_node *n2 = (const struct vtbl_map_node *) p2;
+ return (IDENTIFIER_HASH_VALUE (n1->class_name) ==
+ IDENTIFIER_HASH_VALUE (n2->class_name));
+}
+
+/* Return vtbl_map node for CLASS_NAME without creating a new one. */
+
+struct vtbl_map_node *
+vtbl_map_get_node (const_tree class_name)
+{
+ struct vtbl_map_node key;
+ struct vtbl_map_node **slot;
+
+ if (!vtbl_map_hash)
+ return NULL;
+
+ key.class_name = CONST_CAST2 (tree, const_tree, class_name);
+ slot = (struct vtbl_map_node **) htab_find_slot (vtbl_map_hash, &key,
+ NO_INSERT);
+ if (!slot)
+ return NULL;
+ return *slot;
+}
+
+/* Return vtbl_map node assigned to BASE_CLASS_TYPE. Create new one
+ when needed. */
+
+struct vtbl_map_node *
+find_or_create_vtbl_map_node (tree base_class_type)
+{
+ struct vtbl_map_node key;
+ struct vtbl_map_node *node;
+ struct vtbl_map_node **slot;
+ unsigned i;
+ tree class_type_decl;
+ tree type_decl_type;
+ unsigned int save_quals;
+ unsigned int null_quals = TYPE_UNQUALIFIED;
+ /* Our data shows 90% of classes have no more than 4 parents or children,
+ so we will use 4 as our default hierarchy array size. */
+ unsigned int default_array_size = 4;
+
+ if (!vtbl_map_hash)
+ vtbl_map_hash = htab_create (10, hash_vtbl_map_node,
+ eq_vtbl_map_node, NULL);
+
+ if (TREE_CHAIN (base_class_type))
+ class_type_decl = TREE_CHAIN (base_class_type);
+ else
+ class_type_decl = TYPE_NAME (base_class_type);
+
+ /* Temporarily remove any type qualifiers on type. */
+ type_decl_type = TREE_TYPE (class_type_decl);
+ save_quals = TYPE_QUALS (type_decl_type);
+ reset_type_qualifiers (null_quals, type_decl_type);
+
+ key.class_name = DECL_ASSEMBLER_NAME (class_type_decl);
+ slot = (struct vtbl_map_node **) htab_find_slot (vtbl_map_hash, &key,
+ INSERT);
+
+ /* Restore any type qualifiers. */
+ reset_type_qualifiers (save_quals, type_decl_type);
+
+ if (*slot)
+ return *slot;
+
+ node = XNEW (struct vtbl_map_node);
+ node->vtbl_map_decl = NULL_TREE;
+ node->class_name = key.class_name;
+ node->uid = num_vtable_map_nodes++;
+
+ node->class_info = XNEW (struct vtv_graph_node);
+ node->class_info->class_type = base_class_type;
+ node->class_info->class_uid = node->uid;
+ node->class_info->max_parents = default_array_size;
+ node->class_info->max_children = default_array_size;
+ node->class_info->num_parents = 0;
+ node->class_info->num_children = 0;
+ node->class_info->num_processed_children = 0;
+ node->class_info->parents =
+ (struct vtv_graph_node **)
+ xmalloc (default_array_size * sizeof (struct vtv_graph_node *));
+ node->class_info->children =
+ (struct vtv_graph_node **)
+ xmalloc (default_array_size * sizeof (struct vtv_graph_node *));
+
+ for (i = 0; i < default_array_size; ++i)
+ {
+ node->class_info->parents[i] = NULL;
+ node->class_info->children[i] = NULL;
+ }
+
+ node->registered = htab_create (16, hash_vtable_registration,
+ eq_vtable_registration, NULL);
+ node->is_used = false;
+ node->next = vtbl_map_nodes;
+ if (vtbl_map_nodes)
+ vtbl_map_nodes->prev = node;
+
+ vtbl_map_nodes_vec.safe_push (node);
+ gcc_assert (vtbl_map_nodes_vec[node->uid] == node);
+
+ vtbl_map_nodes = node;
+ *slot = node;
+ return node;
+}
+
+/* End of hashtable functions for vtable_map variables hash table. */
+
+/* As part of vtable verification the compiler generates and inserts
+ calls to __VLTVerifyVtablePointer, which is in libstdc++. This
+ function builds and initializes the function decl that is used
+ in generating those function calls.
+
+ In addition to __VLTVerifyVtablePointer there is also
+ __VLTVerifyVtablePointerDebug which can be used in place of
+ __VLTVerifyVtablePointer, and which takes extra parameters and
+ outputs extra information, to help debug problems. The debug
+ version of this function is generated and used if vtv_debug is
+ true.
+
+ The signatures for these functions are:
+
+ void * __VLTVerifyVtablePointer (void **, void*);
+ void * __VLTVerifyVtablePointerDebug (void**, void *, char *, int, char *,
+ int);
+*/
+
+static void
+build_vtable_verify_fndecl (void)
+{
+ tree void_ptr_type = build_pointer_type (void_type_node);
+ tree arg_types = NULL_TREE;
+ tree func_type = NULL_TREE;
+ struct lang_decl *ld;
+ tree const_char_ptr_type = build_pointer_type
+ (build_qualified_type (char_type_node,
+ TYPE_QUAL_CONST));
+
+ if (verify_vtbl_ptr_fndecl != NULL_TREE)
+ return;
+
+ ld = ggc_alloc_cleared_lang_decl (sizeof (struct lang_decl_fn));
+ ld->u.base.selector = 1;
+
+ arg_types = build_tree_list (NULL_TREE, build_pointer_type (void_ptr_type));
+ arg_types = chainon (arg_types, build_tree_list (NULL_TREE,
+ const_ptr_type_node));
+
+ if (vtv_debug)
+ {
+ /* Start: Arg types for debugging version of function. */
+ arg_types = chainon (arg_types, build_tree_list (NULL_TREE,
+ const_char_ptr_type));
+ arg_types = chainon (arg_types, build_tree_list (NULL_TREE,
+ const_char_ptr_type));
+ /* End: Arg types for debugging. */
+ }
+
+ arg_types = chainon (arg_types, build_tree_list (NULL_TREE, void_type_node));
+ func_type = build_function_type (const_ptr_type_node, arg_types);
+
+ if (vtv_debug)
+ verify_vtbl_ptr_fndecl = build_fn_decl ("__VLTVerifyVtablePointerDebug",
+ func_type);
+ else
+ verify_vtbl_ptr_fndecl = build_fn_decl ("__VLTVerifyVtablePointer",
+ func_type);
+
+ TREE_NOTHROW (verify_vtbl_ptr_fndecl) = 1;
+ DECL_ATTRIBUTES (verify_vtbl_ptr_fndecl)
+ = tree_cons (get_identifier ("leaf"), NULL,
+ DECL_ATTRIBUTES (verify_vtbl_ptr_fndecl));
+ DECL_PURE_P (verify_vtbl_ptr_fndecl) = 1;
+ TREE_PUBLIC (verify_vtbl_ptr_fndecl) = 1;
+ /* DECL_VISIBILITY (verify_vtbl_ptr_fndecl) = 1; */
+ DECL_PRESERVE_P (verify_vtbl_ptr_fndecl) = 1;
+ DECL_LANG_SPECIFIC (verify_vtbl_ptr_fndecl) = ld;
+ SET_DECL_LANGUAGE (verify_vtbl_ptr_fndecl, lang_cplusplus);
+}
+
+/* This function takes a tree type, TYPE_NODE, and a set of type qualifier
+ flags, NEW_QUALS, and set the type qualifiers on TYPE_NODE to those
+ specified in NEW_QUALS. */
+
+void
+reset_type_qualifiers (unsigned int new_quals, tree type_node)
+{
+ if (new_quals & TYPE_QUAL_CONST)
+ TYPE_READONLY (type_node) = 1;
+ else
+ TYPE_READONLY (type_node) = 0;
+
+ if (new_quals & TYPE_QUAL_VOLATILE)
+ TYPE_VOLATILE (type_node) = 1;
+ else
+ TYPE_VOLATILE (type_node) = 0;
+
+ if (new_quals & TYPE_QUAL_RESTRICT)
+ TYPE_RESTRICT (type_node) = 1;
+ else
+ TYPE_RESTRICT (type_node) = 0;
+}
+
+/* This function takes the LHS of a gimple assignment statement, which
+ has already been verified to be an ssa_name, finds the type name of
+ it, and checks to see if the name of the type is
+ "__vtbl_ptr_type". */
+
+static bool
+type_name_is_vtable_pointer (tree lhs)
+{
+ tree node;
+
+ if (!POINTER_TYPE_P (TREE_TYPE (lhs))
+ || !POINTER_TYPE_P (TREE_TYPE (TREE_TYPE (lhs))))
+ return false;
+
+ node = TREE_TYPE (TREE_TYPE (lhs));
+
+ if (TYPE_NAME (node))
+ {
+ if (TREE_CODE (TYPE_NAME (node)) == IDENTIFIER_NODE)
+ return (strcmp (IDENTIFIER_POINTER (TYPE_NAME (node)),
+ "__vtbl_ptr_type") == 0);
+ else if (TREE_CODE (TYPE_NAME (node)) == TYPE_DECL
+ && DECL_NAME (TYPE_NAME (node)))
+ return (strcmp (IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (node))),
+ "__vtbl_ptr_type") == 0);
+ else
+ return false;
+ }
+
+ return false;
+}
+
+/* Given a gimple STMT, this function checks to see if the statement
+ is an assignment, the rhs of which is getting the vtable pointer
+ value out of an object. (i.e. it's the value we need to verify
+ because its the vtable pointer that will be used for a virtual
+ call). */
+
+static bool
+is_vtable_assignment_stmt (gimple stmt)
+{
+
+ if (gimple_code (stmt) != GIMPLE_ASSIGN)
+ return false;
+ else
+ {
+ tree lhs = gimple_assign_lhs (stmt);
+ tree rhs = gimple_assign_rhs1 (stmt);
+
+ if (TREE_CODE (lhs) != SSA_NAME)
+ return false;
+
+ if (! type_name_is_vtable_pointer (lhs))
+ return false;
+
+ if (TREE_CODE (rhs) != COMPONENT_REF)
+ return false;
+
+ if (! (TREE_OPERAND (rhs, 1))
+ || (TREE_CODE (TREE_OPERAND (rhs, 1)) != FIELD_DECL))
+ return false;
+
+ if (! (DECL_NAME (TREE_OPERAND (rhs, 1)))
+ || (strncmp (IDENTIFIER_POINTER (DECL_NAME (TREE_OPERAND (rhs, 1))),
+ "_vptr.", 6) != 0))
+ return false;
+ }
+
+ return true;
+}
+
+/* Given the BINFO for a virtual type, gets the vtable decl for that
+ BINFO. */
+
+static tree
+my_get_vtbl_decl_for_binfo (tree binfo)
+{
+ tree decl;
+
+ decl = BINFO_VTABLE (binfo);
+ if (decl && TREE_CODE (decl) == POINTER_PLUS_EXPR)
+ {
+ gcc_assert (TREE_CODE (TREE_OPERAND (decl, 0)) == ADDR_EXPR);
+ decl = TREE_OPERAND (TREE_OPERAND (decl, 0), 0);
+ }
+ if (decl)
+ gcc_assert (TREE_CODE (decl) == VAR_DECL);
+
+ return decl;
+}
+
+static tree
+find_and_replace_var_1 (tree *tp, int *walk_subtrees, void *data)
+{
+ tree t = *tp;
+ struct walk_stmt_info *wi = (struct walk_stmt_info *) data;
+ tree old_var = ((tree *) wi->info)[0];
+ tree new_var = ((tree *) wi->info)[1];
+ gimple stmt = ((gimple *) wi->info)[2];
+ bool found = false;
+
+ if ((TREE_CODE (t) == SSA_NAME) && (t == old_var))
+ {
+ if (gimple_code (stmt) == GIMPLE_ASSIGN)
+ {
+ enum tree_code code = gimple_expr_code (stmt);
+ switch (get_gimple_rhs_class (code))
+ {
+ case GIMPLE_TERNARY_RHS:
+ if (gimple_assign_rhs3 (stmt) == old_var)
+ {
+ gimple_assign_set_rhs3 (stmt, new_var);
+ found = true;
+ }
+
+ /* Fall through */
+
+ case GIMPLE_BINARY_RHS:
+ if (gimple_assign_rhs2 (stmt) == old_var)
+ {
+ gimple_assign_set_rhs2 (stmt, new_var);
+ found = true;
+ }
+
+ /* Fall through */
+
+ case GIMPLE_SINGLE_RHS:
+ if (gimple_assign_rhs1 (stmt) == old_var)
+ {
+ gimple_assign_set_rhs1 (stmt, new_var);
+ found = true;
+ }
+
+ break;
+
+ default:
+ break;
+ }
+ }
+ else if (gimple_code (stmt) == GIMPLE_CALL)
+ {
+ tree call = gimple_call_fn (stmt);
+ if (TREE_CODE (call) == OBJ_TYPE_REF)
+ {
+ int num_operands = TREE_OPERAND_LENGTH (call);
+ int i;
+ for (i = 0; i < num_operands; ++i)
+ if (TREE_OPERAND (call, i) == old_var)
+ {
+ TREE_OPERAND (call, i) = new_var;
+ found = true;
+ }
+ }
+ }
+ else
+ { }
+ }
+ else
+ {
+ int num_operands = TREE_OPERAND_LENGTH (t);
+ int i;
+ for (i = 0; i < num_operands; ++i)
+ if (TREE_OPERAND (t, i) == old_var)
+ {
+ TREE_OPERAND (t, i) = new_var;
+ found = true;
+ }
+ }
+
+ if (found)
+ {
+ *walk_subtrees = 0;
+ return new_var;
+ }
+ else
+ return NULL_TREE;
+}
+
+/* STMT is a gimple statment that uses OLD_VAR. This function finds the
+ use of OLD_VAR in STMT and replaces it with NEW_VAR. STMT is either
+ an assignment statement or a call statement. */
+
+static bool
+find_and_replace_var (gimple stmt, tree old_var, tree new_var)
+{
+ bool found = false;
+ struct walk_stmt_info wi;
+ tree params[3];
+ tree result;
+
+ if (!stmt || ! old_var || !new_var)
+ return found;
+
+ if ((TREE_CODE (old_var) != SSA_NAME)
+ || (TREE_CODE (new_var) != SSA_NAME))
+ return found;
+
+ params[0] = old_var;
+ params[1] = new_var;
+ params[2] = (tree) stmt;
+
+ wi.info = ¶ms;
+ wi.pset = NULL;
+
+ result = walk_gimple_op (stmt, find_and_replace_var_1, &wi);
+
+ if (result != NULL_TREE)
+ found = true;
+
+ if (found)
+ update_stmt (stmt);
+
+ return found;
+}
+
+/* Given a virtual funciton call (FNCALL) in gimple STMT, this
+ function chains back through the def stmts of the virtual call,
+ looking for the most recent statement that assigned a _vptr field
+ out of the object used in the function call. This is the vtable
+ pointer value that needs to be verified. */
+
+static gimple
+find_vtable_ptr_assignment_stmt (tree fncall, gimple stmt, gimple *prev_use)
+{
+ gimple def_stmt;
+
+ /* Find the first operand of the function call; this should be the
+ SSA_NAME variable that contains the pointer to the virtual
+ function. */
+ if (TREE_OPERAND (fncall, 0)
+ && TREE_CODE (TREE_OPERAND (fncall, 0)) == SSA_NAME)
+ {
+ tree rhs = NULL_TREE;
+ tree func_ptr_var = NULL_TREE;
+
+ func_ptr_var = TREE_OPERAND (fncall, 0);
+ *prev_use = stmt;
+
+ /* Find the statment that calculated the function pointer, i.e.
+ func_ptr_var = *(vtbl_ptr + offset). */
+
+ def_stmt = SSA_NAME_DEF_STMT (func_ptr_var);
+
+ /* Search backwards through the def_stmt chain, to try
+ to find the assignment statement where the rhs of
+ the assignment contains the "._vptr" field (the vtable
+ pointer). */
+
+ /* TODO: In the future we need to handle def_stmts
+ that are phi stmts. For now we punt on them. */
+
+ if (def_stmt && gimple_code (def_stmt) == GIMPLE_PHI)
+ return NULL;
+
+ gcc_assert ((def_stmt != NULL)
+ && (gimple_code (def_stmt) == GIMPLE_ASSIGN));
+
+
+ /* def_stmt should now be of the form: var1 = *var2
+ (dereferencding var 2 to get the actual address of the
+ virtual method. We need to extract 'var2' from the rhs, and
+ find its def_stmt. */
+
+ rhs = gimple_assign_rhs1 (def_stmt);
+ if (rhs
+ && TREE_CODE (rhs) == MEM_REF
+ && TREE_CODE (TREE_OPERAND (rhs, 0)) == SSA_NAME)
+ {
+ *prev_use = def_stmt;
+ def_stmt = SSA_NAME_DEF_STMT (TREE_OPERAND (rhs, 0));
+ }
+
+ if (gimple_code (def_stmt) == GIMPLE_PHI)
+ def_stmt = NULL;
+
+ /* Search backwards, through the SSA_NAME_DEF_STMT statements in
+ the RHS of our def_stmt(s), until we find an assignment
+ statement whose lhs is an SSA_NAME and whose rhs contains a
+ "._vptr" field. */
+
+ while (def_stmt
+ && !is_vtable_assignment_stmt (def_stmt))
+ {
+ tree lhs = gimple_assign_lhs (def_stmt);
+ if (!lhs || TREE_CODE (lhs) != SSA_NAME)
+ {
+ def_stmt = NULL;
+ break;
+ }
+
+ /* Try to find the SSA_NAME variable in the RHS; start by trying
+ the first (only) rhs piece. */
+ if (gimple_assign_rhs1 (def_stmt)
+ && TREE_CODE (gimple_assign_rhs1 (def_stmt)) == SSA_NAME)
+ {
+ *prev_use = def_stmt;
+ rhs = gimple_assign_rhs1 (def_stmt);
+
+ /* Get our new def_stmt. */
+ def_stmt = SSA_NAME_DEF_STMT (rhs);
+ }
+ /* The first piece didn't work, so try the second piece, if
+ there is one. */
+ else if ((get_gimple_rhs_class (gimple_expr_code (def_stmt))
+ != GIMPLE_SINGLE_RHS)
+ && gimple_assign_rhs2 (def_stmt)
+ && TREE_CODE (gimple_assign_rhs2 (def_stmt)) == SSA_NAME)
+ {
+ *prev_use = def_stmt;
+ rhs = gimple_assign_rhs2 (def_stmt);
+ def_stmt = SSA_NAME_DEF_STMT (rhs);
+ }
+ else
+ def_stmt = NULL;
+
+ if (def_stmt && gimple_code (def_stmt) == GIMPLE_PHI)
+ def_stmt = NULL;
+
+ if (!def_stmt)
+ break;
+ }
+ }
+
+ return def_stmt;
+}
+
+/* This function takes a gimple statment (STMT) and an iteratpr
+ (GSI_TEMP) and makes the iterator point to the STMTs location in
+ its basic block's sequence of statements.*/
+
+static bool
+find_stmt_in_bb_stmts (gimple stmt, gimple_stmt_iterator *gsi_temp)
+{
+ basic_block def_bb;
+ gimple_seq def_bb_stmts;
+ gimple tmp_stmt;
+ bool found = false;
+
+ def_bb = gimple_bb (stmt);
+ def_bb_stmts = bb_seq (def_bb);
+ *gsi_temp = gsi_start (def_bb_stmts);
+ for (; !gsi_end_p (*gsi_temp) && !found; gsi_next (gsi_temp))
+ {
+ tmp_stmt = gsi_stmt (*gsi_temp);
+ if (tmp_stmt == stmt)
+ {
+ found = true;
+ break; /* Exit loop immediately; do no do another gsi_next. */
+ }
+ }
+
+ return found;
+}
+
+/* This function attempts to recover the declared class of an object
+ that is used in making a virtual call. First we try looking
+ directly at the THIS_OBJECT itself. If that does not work, we try to
+ get the type from the gimple assignment statement that extracts the
+ vtable pointer from the object (DEF_STMT). The gimple
+ statment usually looks something like this:
+
+ D.2201_4 = MEM[(struct Event *)this_1(D)]._vptr.Event */
+
+static tree
+extract_object_class_type (tree this_object, gimple def_stmt)
+{
+ tree object_rhs = TREE_TYPE (this_object);
+ tree rhs = NULL_TREE;
+
+ /* First try to get the type out of the 'this' object. */
+
+ if (POINTER_TYPE_P (object_rhs)
+ && TREE_CODE (TREE_TYPE (object_rhs)) == RECORD_TYPE)
+ rhs = TREE_TYPE (object_rhs);
+ else if (TREE_CODE (object_rhs) == REFERENCE_TYPE
+ && TREE_CODE (TREE_TYPE (object_rhs)) == RECORD_TYPE)
+ rhs = TREE_TYPE (object_rhs);
+
+ /* Check to see if the type from the 'this' object will work or not.
+ Sometimes the type of the 'this' object is not usable (usually
+ due to optimizations which change the type of the 'this' object
+ to 'void *'); try to get the type out of the type cast in the rhs
+ of the vtable pointer assignment statement. */
+
+ if (!rhs || TREE_CODE (rhs) != RECORD_TYPE || !TYPE_BINFO (rhs)
+ || !BINFO_VTABLE (TYPE_BINFO (rhs))
+ || !my_get_vtbl_decl_for_binfo (TYPE_BINFO (rhs)))
+ {
+ /* The type of the 'this' object did not work, so try to find
+ the type from the rhs of the def_stmt. Try to find and
+ extract the type cast from that stmt. */
+
+ rhs = gimple_assign_rhs1 (def_stmt);
+ if (TREE_CODE (rhs) == COMPONENT_REF)
+ {
+ while (TREE_CODE (rhs) == COMPONENT_REF
+ && (TREE_CODE (TREE_OPERAND (rhs, 0)) == COMPONENT_REF))
+ rhs = TREE_OPERAND (rhs, 0);
+
+ if (TREE_CODE (rhs) == COMPONENT_REF
+ && TREE_CODE (TREE_OPERAND (rhs, 0)) == MEM_REF
+ && TREE_CODE (TREE_TYPE (TREE_OPERAND (rhs, 0)))== RECORD_TYPE)
+ rhs = TREE_TYPE (TREE_OPERAND (rhs, 0));
+ else
+ rhs = NULL_TREE;
+ }
+ else
+ rhs = NULL_TREE;
+ }
+
+ return rhs;
+}
+
+/* Given a CLASS_TYPE, this function returns the corresponding vtable
+ map variable node from the vtable map data structures. */
+
+static struct vtbl_map_node *
+find_vtbl_map_node_for_type (tree class_type)
+{
+ tree class_type_decl;
+ tree type_decl_type;
+ tree var_id;
+ unsigned int save_quals;
+ unsigned int null_quals = TYPE_UNQUALIFIED;
+ struct vtbl_map_node *vtable_map_node = NULL;
+
+ /* Get the type decl for the class. */
+ if (TREE_CHAIN (class_type))
+ class_type_decl = TREE_CHAIN (class_type);
+ else
+ class_type_decl = TYPE_NAME (class_type);
+
+ /* Temporarily remove any qualifiers on type, for looking up vtable
+ map var. */
+ type_decl_type = TREE_TYPE (class_type_decl);
+ save_quals = TYPE_QUALS (type_decl_type);
+ reset_type_qualifiers (null_quals, type_decl_type);
+
+ /* Use mangled name for the unqualifed class type to Look up vtable
+ map variable. */
+ var_id = DECL_ASSEMBLER_NAME (class_type_decl);
+ vtable_map_node = vtbl_map_get_node (var_id);
+
+ /* Restore any type qualifiers. */
+ reset_type_qualifiers (save_quals, type_decl_type);
+
+ return vtable_map_node;
+}
+
+/* Search through all the statements in a basic block (BB), searching
+ for virtual method calls. For each virtual method dispatch, find
+ the vptr value used, and the statically declared type of the
+ object; retrieve the vtable map variable for the type of the
+ object; generate a call to __VLTVerifyVtablePointer; and insert the
+ generated call into the basic block, after the point where the vptr
+ value is gotten out of the object and before the virtual method
+ dispatch. Make the virtual method dispatch depend on the return
+ value from the verification call, so that subsequent optimizations
+ cannot reorder the two calls. */
+
+static void
+verify_bb_vtables (basic_block bb)
+{
+ gimple_seq stmts;
+ gimple stmt = NULL;
+ gimple_stmt_iterator gsi_vtbl_assign;
+ gimple_stmt_iterator gsi_virtual_call;
+ tree this_object;
+
+ stmts = bb_seq (bb);
+ gsi_virtual_call = gsi_start (stmts);
+ this_object = NULL_TREE;
+ for (; !gsi_end_p (gsi_virtual_call); gsi_next (&gsi_virtual_call))
+ {
+ stmt = gsi_stmt (gsi_virtual_call);
+ if (gimple_code (stmt) == GIMPLE_CALL)
+ {
+ /* Found a call; see if it's a virtual call. */
+ tree fncall = gimple_call_fn (stmt);
+ if (TREE_CODE (fncall) == OBJ_TYPE_REF)
+ {
+ bool found = false;
+ tree lhs = NULL_TREE;
+ gimple vptr_stmt;
+ gimple prev_use = NULL;
+
+ /* We have found a virtual call; search backwards,
+ through the object's SSA_NAME_DEF_STMTs, to find the place
+ were the vtable pointer is gotten out of the object
+ (also need to find the object's declared static
+ type). */
+
+ /* The first argument to the function must be "this", a
+ pointer to the object itself. */
+ this_object = gimple_call_arg (stmt, 0);
+
+ /* Find the previousg statement that gets the "_vptr"
+ field out of the object. */
+ vptr_stmt = find_vtable_ptr_assignment_stmt (fncall,
+ stmt,
+ &prev_use);
+
+ if (!vptr_stmt)
+ continue;
+
+ lhs = gimple_assign_lhs (vptr_stmt);
+
+ /* Find the vptr_stmt in its basic block's sequence, so
+ we have an insertion point for adding our
+ verification call. */
+ found = find_stmt_in_bb_stmts (vptr_stmt, &gsi_vtbl_assign);
+
+ if (found)
+ {
+ tree vtbl_var_decl = NULL_TREE;
+ tree vtbl = NULL_TREE;
+ gimple_seq pre_p = NULL;
+ struct vtbl_map_node *vtable_map_node;
+ tree vtbl_decl = NULL_TREE;
+ tree expr_tree = NULL_TREE;
+ struct gimplify_ctx gctx;
+ const char *vtable_name = "<unknown>";
+ gimple assign_stmt;
+ tree tmp0;
+ bool status;
+ int len1 = 0;
+ int len2 = 0;
+
+ /* Now we have found the virtual method dispatch and
+ the preceding access of the _vptr.* field... Next
+ we need to find the statically declared type of
+ the object, so we can find and use the right
+ vtable map variable in the verification call. */
+ tree class_type = extract_object_class_type (this_object,
+ vptr_stmt);
+
+ /* Make sure we found a valid type. */
+ gcc_assert (class_type
+ && (TREE_CODE (class_type) == RECORD_TYPE)
+ && TYPE_BINFO (class_type));
+
+ /* Get the vtable VAR_DECL for the type. */
+ vtbl_var_decl = my_get_vtbl_decl_for_binfo
+ (TYPE_BINFO (class_type));
+ vtbl = BINFO_VTABLE (TYPE_BINFO (class_type));
+
+ gcc_assert (vtbl_var_decl && vtbl);
+
+ vtbl_decl = vtbl_var_decl;
+
+ if (POINTER_TYPE_P (TREE_TYPE (vtbl)))
+ force_gimple_operand (vtbl, &pre_p, 1, NULL);
+
+ vtable_map_node = find_vtbl_map_node_for_type (class_type);
+ if (vtable_map_node)
+ {
+ vtbl_var_decl = vtable_map_node->vtbl_map_decl;
+ vtable_map_node->is_used = true;
+ }
+ else
+ vtbl_var_decl = NULL;
+
+ /* Build the FUNC_DECL to use for the verification
+ call. */
+ build_vtable_verify_fndecl ();
+
+ /* Given the vtable pointer for the base class of
+ the object, build the call to
+ __VLTVerifyVtablePointer to verify that the
+ object's vtable pointer (contained in lhs) is in
+ the set of valid vtable pointers for the base
+ class. */
+
+ gcc_assert (vtbl_var_decl && vtbl && verify_vtbl_ptr_fndecl);
+
+ if (TREE_CODE (vtbl_decl) == VAR_DECL)
+ vtable_name = IDENTIFIER_POINTER (DECL_NAME (vtbl_decl));
+
+ push_gimplify_context (&gctx);
+ len1 = strlen (IDENTIFIER_POINTER
+ (DECL_NAME (vtbl_var_decl)));
+ len2 = strlen (vtable_name);
+
+ /* Call different routines if we are interested in
+ trace information to debug problems. */
+ if (vtv_debug)
+ expr_tree = build_call_expr
+ (verify_vtbl_ptr_fndecl, 4,
+ build1 (ADDR_EXPR,
+ TYPE_POINTER_TO
+ (TREE_TYPE (vtbl_var_decl)),
+ vtbl_var_decl),
+ lhs,
+ build_string_literal
+ (len1 + 1,
+ IDENTIFIER_POINTER
+ (DECL_NAME
+ (vtbl_var_decl))),
+ build_string_literal (len2 + 1,
+ vtable_name));
+ else
+ expr_tree = build_call_expr
+ (verify_vtbl_ptr_fndecl, 2,
+ build1 (ADDR_EXPR,
+ TYPE_POINTER_TO
+ (TREE_TYPE (vtbl_var_decl)),
+ vtbl_var_decl),
+ lhs);
+
+ /* Assign the result of the call to the original
+ variable receiving the assignment of the object's
+ vtable pointer; mark that variable to be updated
+ by update_ssa. */
+
+ cfun->gimple_df->ssa_renaming_needed = 1;
+ tmp0 = make_temp_ssa_name (TREE_TYPE (lhs), NULL, "VTV");
+ assign_stmt = gimplify_assign (tmp0, expr_tree, &pre_p);
+ status = find_and_replace_var (prev_use, lhs, tmp0);
+ update_stmt (assign_stmt);
+ gcc_assert (status == true);
+ pop_gimplify_context (NULL);
+
+ /* Insert the new call just after the original
+ assignment of the object's vtable pointer. */
+
+ gsi_insert_seq_after (&gsi_vtbl_assign, pre_p, GSI_NEW_STMT);
+ any_verification_calls_generated = true;
+ }
+ }
+ }
+ }
+}
+
+/* Main function, called from pass->excute(). Loop through all the
+ basic blocks in the current function, passing them to
+ verify_bb_vtables, which searches for virtual calls, and inserts
+ calls to __VLTVerifyVtablePointer. */
+
+unsigned int
+vtable_verify_main (void)
+{
+ unsigned int ret = 1;
+ basic_block bb;
+
+ FOR_ALL_BB (bb)
+ verify_bb_vtables (bb);
+
+ return ret;
+}
+
+/* Gate function for the pass. */
+
+static bool
+gate_tree_vtable_verify (void)
+{
+ return (flag_vtable_verify
+ && (strcmp (lang_hooks.name, "GNU C++") == 0));
+}
+
+/* Definition of this optimization pass. */
+
+struct gimple_opt_pass pass_vtable_verify =
+{
+ {
+ GIMPLE_PASS,
+ "vtable-verify", /* name */
+ OPTGROUP_NONE, /* optinfo_flags */
+ gate_tree_vtable_verify, /* gate */
+ vtable_verify_main, /* execute */
+ NULL, /* sub */
+ NULL, /* next */
+ 0, /* static_pass_number */
+ TV_VTABLE_VERIFICATION, /* tv_id */
+ PROP_cfg | PROP_ssa, /* properties_required */
+ 0, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ TODO_update_ssa
+ | TODO_ggc_collect /* todo_flags_finish */
+ }
+};
+
+#include "gt-tree-vtable-verify.h"
Index: gcc/tree-vtable-verify.h
===================================================================
--- gcc/tree-vtable-verify.h (revision 0)
+++ gcc/tree-vtable-verify.h (revision 0)
@@ -0,0 +1,148 @@
+/* Interprocedural constant propagation
+ Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011
+ 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/>. */
+
+/* Virtual Table Pointer Security. */
+
+#ifndef TREE_VTABLE_VERIFY_H
+#define TREE_VTABLE_VERIFY_H
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "timevar.h"
+#include "cpplib.h"
+#include "tree.h"
+#include "hashtab.h"
+#include "sbitmap.h"
+
+/* Global variable keeping track of how many vtable map variables we
+ have created. */
+extern unsigned num_vtable_map_nodes;
+
+/* Global variable that records whether or not any vtable verification
+ calls have been generated. */
+extern bool any_verification_calls_generated;
+
+/* Each vtable map variable corresponds to a virtual class. Each
+ vtable map variable has a hash table associated with it, that keeps
+ track of the vtable pointers for which we have generated a call to
+ __VLTRegisterPair (with the current vtable map variable). This is
+ the hash table node that is used for each entry in this hash table
+ of vtable pointers.
+
+ Sometimes there are multiple valid vtable pointer entries that use
+ the same vtable pointer decl with different offsets. Therefore,
+ for each vtable pointer in the hash table, there is also an array
+ of offsets used with that vtable. */
+
+struct vtable_registration
+{
+ tree vtable_decl; /* The var decl of the vtable. */
+ unsigned max_offsets; /* The allocated size of the offsets array. */
+ unsigned cur_offset; /* The next availabe entry in the offsets
+ array. */
+ unsigned *offsets; /* The offsets array. */
+};
+
+/* This struct is used to represent the class hierarchy information
+ that we need. Each vtable map variable has an associated class
+ hierarchy node (struct vtv_graph_node). Note: In this struct,
+ 'children' means immediate descendants in the class hierarchy;
+ 'descendant' means any descendant however many levels deep. */
+
+struct vtv_graph_node {
+ tree class_type; /* The record_type of the class. */
+ unsigned class_uid; /* A unique, monotonically
+ ascending id for class node.
+ Each vtable map node also has
+ an id. The class uid is the
+ same as the vtable map node id
+ for nodes corresponding to the
+ same class. */
+ unsigned max_parents; /* Allocated size of the parents array. */
+ unsigned max_children; /* Allocated size of the children array. */
+ unsigned num_parents; /* # of entries in the parents array. */
+ unsigned num_children; /* # of entries in the children array. */
+ unsigned num_processed_children; /* # of children for whom we have
+ computed the class hierarchy
+ transitive closure. */
+ struct vtv_graph_node **parents; /* Array of parents in the graph. */
+ struct vtv_graph_node **children; /* Array of children in the graph. */
+ sbitmap descendants; /* Bitmap representing all this node's
+ descendants in the graph. */
+};
+
+/* This is the node used for our hashtable of vtable map variable
+ information. When we create a vtable map variable (var decl) we
+ put it into one of these nodes; create a corresponding
+ vtv_graph_node for our class hierarchy info and store that in this
+ node; generate a unique (monotonically ascending) id for both the
+ vtbl_map_node and the vtv_graph_node; and insert the node into
+ THREE data structures (to make it easy to find in several different
+ ways): 1). A hash table ("vtbl_map_hash" in tree-vtable-verify.c).
+ This gives us an easy way to check to see if we already have a node
+ for the vtable map variable or not. 2). A linked list of all
+ vtbl_map_nodes ("vtbl_map_nodes") for easy iteration through all of
+ them; and 3). An array of vtbl_map_nodes, where the array index
+ corresponds to the unique id of the vtbl_map_node, which gives us
+ an easy way to use bitmaps to represent and find the vtable map
+ nodes. */
+
+struct vtbl_map_node {
+ tree vtbl_map_decl; /* The var decl for the vtable map
+ variable. */
+ tree class_name; /* The DECL_ASSEMBLER_NAME of the
+ class. */
+ struct vtv_graph_node *class_info; /* Our class hierarchy info for the
+ class. */
+ unsigned uid; /* The unique id for the vtable map
+ variable. */
+ struct vtbl_map_node *next, *prev; /* Pointers for the linked list
+ structure. */
+ htab_t registered; /* Hashtable of vtable pointers for which we have
+ generated a _VLTRegisterPair call with this vtable
+ map variable. */
+ bool is_used; /* Boolean indicating if we used this vtable map
+ variable in a call to __VLTVerifyVtablePointer. */
+};
+
+/* Controls debugging for vtable verification. */
+
+extern bool vtv_debug;
+
+/* The global linked list of vtbl_map_nodes. */
+
+extern struct vtbl_map_node *vtbl_map_nodes;
+
+/* The global vector of vtbl_map_nodes. */
+
+extern vec<struct vtbl_map_node *> vtbl_map_nodes_vec;
+
+extern struct vtbl_map_node *vtbl_map_get_node (const_tree);
+extern struct vtbl_map_node *find_or_create_vtbl_map_node (tree);
+extern void vtbl_map_node_class_insert (struct vtbl_map_node *, unsigned);
+extern bool vtbl_map_node_registration_find (struct vtbl_map_node *,
+ tree, unsigned);
+extern void vtbl_map_node_registration_insert (struct vtbl_map_node *,
+ tree, unsigned);
+extern void reset_type_qualifiers (unsigned int, tree);
+
+#endif /* TREE_VTABLE_VERIFY_H */
Index: gcc/common.opt
===================================================================
--- gcc/common.opt (revision 195313)
+++ gcc/common.opt (working copy)
@@ -2265,6 +2265,22 @@ Enum(symbol_visibility) String(hidden) V
EnumValue
Enum(symbol_visibility) String(protected) Value(VISIBILITY_PROTECTED)
+fvtable-verify=
+Common Joined RejectNegative Enum(vtv_priority) Var(flag_vtable_verify) Init(VTV_NO_PRIORITY)
+Validate vtable pointers before using them.
+
+Enum
+Name(vtv_priority) Type(enum vtv_priority) UnknownError(unknown vtable verify initialization priority %qs)
+
+EnumValue
+Enum(vtv_priority) String(none) Value(VTV_NO_PRIORITY)
+
+EnumValue
+Enum(vtv_priority) String(std) Value(VTV_STANDARD_PRIORITY)
+
+EnumValue
+Enum(vtv_priority) String(preinit) Value(VTV_PREINIT_PRIORITY)
+
fvpt
Common Report Var(flag_value_profile_transformations) Optimization
Use expression value profiles in optimizations
Index: gcc/varasm.c
===================================================================
--- gcc/varasm.c (revision 195313)
+++ gcc/varasm.c (working copy)
@@ -2031,7 +2031,21 @@ assemble_variable (tree decl, int top_le
assemble_noswitch_variable (decl, name, sect);
else
{
- switch_to_section (sect);
+ if (sect->named.name
+ && (strcmp (sect->named.name, ".vtable_map_vars") == 0))
+ {
+#if defined (OBJECT_FORMAT_ELF)
+ targetm.asm_out.named_section (sect->named.name,
+ sect->named.common.flags
+ | SECTION_LINKONCE,
+ DECL_NAME (decl));
+ in_section = sect;
+#else
+ switch_to_section (sect);
+#endif
+ }
+ else
+ switch_to_section (sect);
if (DECL_ALIGN (decl) > BITS_PER_UNIT)
ASM_OUTPUT_ALIGN (asm_out_file, floor_log2 (DECL_ALIGN_UNIT (decl)));
assemble_variable_contents (decl, name, dont_output_data);
@@ -2044,6 +2058,23 @@ assemble_variable (tree decl, int top_le
}
}
+
+/* Given a function declaration (FN_DECL), this function assembles the
+ function into the .preinit_array section. */
+
+void
+assemble_vtv_preinit_initializer (tree fn_decl)
+{
+ section *sect;
+ unsigned flags = SECTION_WRITE;
+ rtx symbol = XEXP (DECL_RTL (fn_decl), 0);
+
+ flags |= SECTION_NOTYPE;
+ sect = get_section (".preinit_array", flags, fn_decl);
+ switch_to_section (sect);
+ assemble_addr_to_section (symbol, sect);
+}
+
/* Return 1 if type TYPE contains any pointers. */
static int
@@ -5991,6 +6022,9 @@ default_section_type_flags (tree decl, c
if (decl && DECL_ONE_ONLY (decl))
flags |= SECTION_LINKONCE;
+ if (strcmp (name, ".vtable_map_vars") == 0)
+ flags |= SECTION_LINKONCE;
+
if (decl && TREE_CODE (decl) == VAR_DECL && DECL_THREAD_LOCAL_P (decl))
flags |= SECTION_TLS | SECTION_WRITE;
Index: gcc/output.h
===================================================================
--- gcc/output.h (revision 195313)
+++ gcc/output.h (working copy)
@@ -197,6 +197,10 @@ extern void assemble_end_function (tree,
initial value (that will be done by the caller). */
extern void assemble_variable (tree, int, int, int);
+/* Put the vtable verification constructor initialization function
+ into the preinit array. */
+extern void assemble_vtv_preinit_initializer (tree);
+
/* Compute the alignment of variable specified by DECL.
DONT_OUTPUT_DATA is from assemble_variable. */
extern void align_variable (tree decl, bool dont_output_data);
Index: gcc/Makefile.in
===================================================================
--- gcc/Makefile.in (revision 195313)
+++ gcc/Makefile.in (working copy)
@@ -1447,6 +1447,7 @@ OBJS = \
tree-vect-loop-manip.o \
tree-vect-slp.o \
tree-vectorizer.o \
+ tree-vtable-verify.o \
tree-vrp.o \
tree.o \
valtrack.o \
@@ -2618,6 +2619,11 @@ tree-vectorizer.o: tree-vectorizer.c $(C
dumpfile.h $(TM_H) $(GGC_H) $(TREE_H) $(TREE_FLOW_H) \
$(CFGLOOP_H) $(TREE_PASS_H) $(TREE_VECTORIZER_H) \
$(TREE_PRETTY_PRINT_H)
+tree-vtable-verify.o: tree-vtable-verify.c tree-vtable-verify.h $(CONFIG_H) \
+ $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) cp/cp-tree.h $(TM_P_H) \
+ $(BASIC_BLOCK_H) output.h $(TREE_FLOW_H) $(TREE_DUMP_H) $(TREE_PASS_H) \
+ $(TIMEVAR_H) $(CFGLOOP_H) $(FLAGS_H) $(TREE_INLINE_H) $(SCEV_H) \
+ $(DIAGNOSTIC_CORE_H) $(GIMPLE_PRETTY_PRINT_H) toplev.h langhooks.h
tree-loop-distribution.o: tree-loop-distribution.c $(CONFIG_H) $(SYSTEM_H) \
coretypes.h $(TREE_FLOW_H) $(CFGLOOP_H) $(TREE_DATA_REF_H) $(TREE_PASS_H)
tree-parloops.o: tree-parloops.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \
@@ -3757,6 +3763,7 @@ GTFILES = $(CPP_ID_DATA_H) $(srcdir)/inp
$(srcdir)/lto-streamer.h \
$(srcdir)/target-globals.h \
$(srcdir)/ipa-inline.h \
+ $(srcdir)/tree-vtable-verify.c \
$(srcdir)/asan.c \
$(srcdir)/tsan.c \
@all_gtfiles@
Index: gcc/passes.c
===================================================================
--- gcc/passes.c (revision 195313)
+++ gcc/passes.c (working copy)
@@ -1554,6 +1554,7 @@ init_optimization_passes (void)
NEXT_PASS (pass_tm_memopt);
NEXT_PASS (pass_tm_edges);
}
+ NEXT_PASS (pass_vtable_verify);
NEXT_PASS (pass_lower_complex_O0);
NEXT_PASS (pass_asan_O0);
NEXT_PASS (pass_tsan_O0);
Index: gcc/config/gnu-user.h
===================================================================
--- gcc/config/gnu-user.h (revision 195313)
+++ gcc/config/gnu-user.h (working copy)
@@ -43,11 +43,13 @@ see the files COPYING3 and COPYING.RUNTI
#if defined HAVE_LD_PIE
#define GNU_USER_TARGET_STARTFILE_SPEC \
"%{!shared: %{pg|p|profile:gcrt1.o%s;pie:Scrt1.o%s;:crt1.o%s}} \
- crti.o%s %{static:crtbeginT.o%s;shared|pie:crtbeginS.o%s;:crtbegin.o%s}"
+ crti.o%s %{static:crtbeginT.o%s;shared|pie:crtbeginS.o%s;:crtbegin.o%s} \
+ %{fvtable-verify*:vtv_start.o%s}"
#else
#define GNU_USER_TARGET_STARTFILE_SPEC \
"%{!shared: %{pg|p|profile:gcrt1.o%s;:crt1.o%s}} \
- crti.o%s %{static:crtbeginT.o%s;shared|pie:crtbeginS.o%s;:crtbegin.o%s}"
+ crti.o%s %{static:crtbeginT.o%s;shared|pie:crtbeginS.o%s;:crtbegin.o%s} \
+ %{fvtable-verify*:vtv_start.o%s}"
#endif
#undef STARTFILE_SPEC
#define STARTFILE_SPEC GNU_USER_TARGET_STARTFILE_SPEC
@@ -59,7 +61,8 @@ see the files COPYING3 and COPYING.RUNTI
GNU userspace "finalizer" file, `crtn.o'. */
#define GNU_USER_TARGET_ENDFILE_SPEC \
- "%{shared|pie:crtendS.o%s;:crtend.o%s} crtn.o%s"
+ "%{fvtable-verify*:vtv_end.o%s} \
+ %{shared|pie:crtendS.o%s;:crtend.o%s} crtn.o%s"
#undef ENDFILE_SPEC
#define ENDFILE_SPEC GNU_USER_TARGET_ENDFILE_SPEC
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2013-01-23 22:36 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-01-23 22:36 [PATCH] Vtable pointer verification, main gcc changes (patch 2 of 3) Caroline Tice
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).